The branch main has been updated by corvink: URL: https://cgit.FreeBSD.org/src/commit/?id=df6c805ca12527d87051136ee5cf32f28e32d8cb
commit df6c805ca12527d87051136ee5cf32f28e32d8cb Author: Corvin Köhne <corv...@freebsd.org> AuthorDate: 2024-01-08 13:55:49 +0000 Commit: Corvin Köhne <corv...@freebsd.org> CommitDate: 2025-08-05 14:02:08 +0000 bhyve: add BAR handler list for passthru devices We have to emulate some specific register of a BAR for passthru devices. Our current use case are Intels integrated graphic devices. They mirror some of their PCI config space into the MMIO space. Unfortunately, the Windows driver reads from MMIO instead of PCI config space. For that reason, we have to trap and emulate those register. Instead of implementing a quirk for this special device, we're implementing a generic approach by using a list of trapped register. That's much cleaner and can be reused. E.g. Nvidia GPUs mirror their PCI config space in MMIO too and we can reuse it to trap the MSI-X table in the future. Note that the handling of this new list requires a larger patch. For that reason, we split it into multiple commits. This means that the list isn't used yet. This commit adds the callback on BAR reads and writes. Some subsequent commit will add the trap for BAR regions and an interface to easily add protected regions. Reviewed by: jhb, markj MFC after: 1 week Sponsored by: Beckhoff Automation GmbH & Co. KG Differential Revision: https://reviews.freebsd.org/D45339 --- usr.sbin/bhyve/pci_passthru.c | 59 ++++++++++++++++++++++++++++++++++++++++--- usr.sbin/bhyve/pci_passthru.h | 4 +++ 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/usr.sbin/bhyve/pci_passthru.c b/usr.sbin/bhyve/pci_passthru.c index a82078f6e036..0cc5b777b842 100644 --- a/usr.sbin/bhyve/pci_passthru.c +++ b/usr.sbin/bhyve/pci_passthru.c @@ -79,6 +79,14 @@ static int pcifd = -1; SET_DECLARE(passthru_dev_set, struct passthru_dev); +struct passthru_bar_handler { + TAILQ_ENTRY(passthru_bar_handler) chain; + uint64_t off; + uint64_t size; + passthru_read_handler read; + passthru_write_handler write; +}; + struct passthru_softc { struct pci_devinst *psc_pi; /* ROM is handled like a BAR */ @@ -96,6 +104,9 @@ struct passthru_softc { struct passthru_mmio_mapping psc_mmio_map[PASSTHRU_MMIO_MAX]; cfgread_handler psc_pcir_rhandler[PCI_REGMAX + 1]; cfgwrite_handler psc_pcir_whandler[PCI_REGMAX + 1]; + + TAILQ_HEAD(, + passthru_bar_handler) psc_bar_handler[PCI_BARMAX_WITH_ROM + 1]; }; static int @@ -947,6 +958,9 @@ passthru_init(struct pci_devinst *pi, nvlist_t *nvl) pi->pi_arg = sc; sc->psc_pi = pi; + for (uint8_t i = 0; i < PCI_BARMAX_WITH_ROM + 1; ++i) + TAILQ_INIT(&sc->psc_bar_handler[i]); + /* initialize config space */ if ((error = cfginit(pi, bus, slot, func)) != 0) goto done; @@ -1166,6 +1180,7 @@ passthru_write(struct pci_devinst *pi, int baridx, uint64_t offset, int size, uint64_t value) { struct passthru_softc *sc; + struct passthru_bar_handler *handler; struct pci_bar_ioreq pio; sc = pi->pi_arg; @@ -1173,9 +1188,27 @@ passthru_write(struct pci_devinst *pi, int baridx, uint64_t offset, int size, if (baridx == pci_msix_table_bar(pi)) { msix_table_write(sc, offset, size, value); } else { - assert(pi->pi_bar[baridx].type == PCIBAR_IO); assert(size == 1 || size == 2 || size == 4); - assert(offset <= UINT32_MAX && offset + size <= UINT32_MAX); + + TAILQ_FOREACH(handler, &sc->psc_bar_handler[baridx], chain) { + if (offset >= handler->off + handler->size) { + continue; + } else if (offset < handler->off) { + assert(offset + size < handler->off); + /* + * The list is sorted in ascending order, so all + * remaining handlers will have an even larger + * offset. + */ + break; + } + + assert(offset + size <= handler->off + handler->size); + + handler->write(pi, baridx, + offset - handler->off, size, value); + return; + } bzero(&pio, sizeof(pio)); pio.pbi_sel = sc->psc_sel; @@ -1193,6 +1226,7 @@ static uint64_t passthru_read(struct pci_devinst *pi, int baridx, uint64_t offset, int size) { struct passthru_softc *sc; + struct passthru_bar_handler *handler; struct pci_bar_ioreq pio; uint64_t val; @@ -1201,9 +1235,26 @@ passthru_read(struct pci_devinst *pi, int baridx, uint64_t offset, int size) if (baridx == pci_msix_table_bar(pi)) { val = msix_table_read(sc, offset, size); } else { - assert(pi->pi_bar[baridx].type == PCIBAR_IO); assert(size == 1 || size == 2 || size == 4); - assert(offset <= UINT32_MAX && offset + size <= UINT32_MAX); + + TAILQ_FOREACH(handler, &sc->psc_bar_handler[baridx], chain) { + if (offset >= handler->off + handler->size) { + continue; + } else if (offset < handler->off) { + assert(offset + size < handler->off); + /* + * The list is sorted in ascending order, so all + * remaining handlers will have an even larger + * offset. + */ + break; + } + + assert(offset + size <= handler->off + handler->size); + + return (handler->read(pi, baridx, + offset - handler->off, size)); + } bzero(&pio, sizeof(pio)); pio.pbi_sel = sc->psc_sel; diff --git a/usr.sbin/bhyve/pci_passthru.h b/usr.sbin/bhyve/pci_passthru.h index a89ad287cbc5..fc3fdbc1719f 100644 --- a/usr.sbin/bhyve/pci_passthru.h +++ b/usr.sbin/bhyve/pci_passthru.h @@ -35,6 +35,10 @@ typedef int (*cfgread_handler)(struct passthru_softc *sc, struct pci_devinst *pi, int coff, int bytes, uint32_t *rv); typedef int (*cfgwrite_handler)(struct passthru_softc *sc, struct pci_devinst *pi, int coff, int bytes, uint32_t val); +typedef uint64_t (*passthru_read_handler)(struct pci_devinst *pi, int baridx, + uint64_t offset, int size); +typedef void (*passthru_write_handler)(struct pci_devinst *pi, int baridx, uint64_t offset, + int size, uint64_t val); uint32_t pci_host_read_config(const struct pcisel *sel, long reg, int width); void pci_host_write_config(const struct pcisel *sel, long reg, int width,