diff --git a/usr.sbin/bhyve/pci_emul.h b/usr.sbin/bhyve/pci_emul.h --- a/usr.sbin/bhyve/pci_emul.h +++ b/usr.sbin/bhyve/pci_emul.h @@ -92,6 +92,7 @@ PCIBAR_MEM64, PCIBAR_MEMHI64, PCIBAR_ROM, + PCIBAR_MAX }; struct pcibar { diff --git a/usr.sbin/bhyve/pci_emul.c b/usr.sbin/bhyve/pci_emul.c --- a/usr.sbin/bhyve/pci_emul.c +++ b/usr.sbin/bhyve/pci_emul.c @@ -29,6 +29,8 @@ #include #include #include +#include +#include #include #include @@ -95,19 +97,16 @@ uint32_t membase32, memlimit32; /* mmio window below 4GB */ uint64_t membase64, memlimit64; /* mmio window above 4GB */ struct slotinfo slotinfo[MAXSLOTS]; + vmem_t *resources[PCIBAR_MAX]; }; static struct businfo *pci_businfo[MAXBUSES]; SET_DECLARE(pci_devemu_set, struct pci_devemu); -static uint64_t pci_emul_iobase; static uint8_t *pci_emul_rombase; static uint64_t pci_emul_romoffset; static uint8_t *pci_emul_romlim; -static uint64_t pci_emul_membase32; -static uint64_t pci_emul_membase64; -static uint64_t pci_emul_memlim64; struct pci_bar_allocation { TAILQ_ENTRY(pci_bar_allocation) chain; @@ -610,25 +609,6 @@ return (0); } - -static int -pci_emul_alloc_resource(uint64_t *baseptr, uint64_t limit, uint64_t size, - uint64_t *addr) -{ - uint64_t base; - - assert((size & (size - 1)) == 0); /* must be a power of 2 */ - - base = roundup2(*baseptr, size); - - if (base + size <= limit) { - *addr = base; - *baseptr = base + size; - return (0); - } else - return (-1); -} - /* * Register (or unregister) the MMIO or I/O region associated with the BAR * register 'idx' of an emulated pci device. @@ -762,7 +742,12 @@ static void update_bar_address(struct pci_devinst *pi, uint64_t addr, int idx, int type) { - int decode; + bool alloc; + vmem_t *arena; + int decode, error; + struct pcibar *bp; + struct businfo *bi; + uint64_t mask, old_addr; if (pi->pi_bar[idx].type == PCIBAR_IO) decode = porten(pi); @@ -772,23 +757,45 @@ if (decode) unregister_bar(pi, idx); + bi = pci_businfo[pi->pi_bus]; + bp = &pi->pi_bar[idx]; + old_addr = pi->pi_bar[idx].addr; + mask = ~(pi->pi_bar[idx].size - 1); switch (type) { case PCIBAR_IO: + bp->addr = addr; + alloc = addr != ((uint16_t)-1 & mask); + break; case PCIBAR_MEM32: - pi->pi_bar[idx].addr = addr; + bp->addr = addr; + alloc = addr != ((uint32_t)-1 & mask); break; case PCIBAR_MEM64: - pi->pi_bar[idx].addr &= ~0xffffffffUL; - pi->pi_bar[idx].addr |= addr; + bp->addr &= ~0xffffffffUL; + bp->addr |= addr; + alloc = addr != ((uint64_t)-1 & mask); break; case PCIBAR_MEMHI64: - pi->pi_bar[idx].addr &= 0xffffffff; - pi->pi_bar[idx].addr |= addr; + bp->addr &= 0xffffffff; + bp->addr |= addr; break; default: assert(0); } + arena = bi->resources[type]; + if (arena != NULL) { + if (!alloc) + vmem_free(arena, old_addr, bp->size); + else { + error = vmem_xalloc(arena, bp->size, bp->size, 0, 0, + bp->addr, bp->addr + bp->size, M_BESTFIT, + &bp->addr); + assert(error == 0); + assert(bp->addr == addr); + } + } + if (decode) register_bar(pi, idx); } @@ -892,17 +899,14 @@ pci_emul_assign_bar(struct pci_devinst *const pdi, const int idx, const enum pcibar_type type, const uint64_t size) { - int error; - uint64_t *baseptr, limit, addr, mask, lobits, bar; + uint64_t addr, mask, lobits, bar; + vmem_t *arena; switch (type) { case PCIBAR_NONE: - baseptr = NULL; addr = mask = lobits = 0; - break; + return (0); case PCIBAR_IO: - baseptr = &pci_emul_iobase; - limit = PCI_EMUL_IOLIMIT; mask = PCIM_BAR_IO_BASE; lobits = PCIM_BAR_IO_SPACE; break; @@ -915,43 +919,34 @@ * number (128MB currently). */ if (size > 128 * 1024 * 1024) { - baseptr = &pci_emul_membase64; - limit = pci_emul_memlim64; mask = PCIM_BAR_MEM_BASE; lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64 | PCIM_BAR_MEM_PREFETCH; } else { - baseptr = &pci_emul_membase32; - limit = PCI_EMUL_MEMLIMIT32; mask = PCIM_BAR_MEM_BASE; lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64; } break; case PCIBAR_MEM32: - baseptr = &pci_emul_membase32; - limit = PCI_EMUL_MEMLIMIT32; mask = PCIM_BAR_MEM_BASE; lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_32; break; case PCIBAR_ROM: /* do not claim memory for ROM. OVMF will do it for us. */ - baseptr = NULL; - limit = 0; + addr = 0; mask = PCIM_BIOS_ADDR_MASK; lobits = 0; break; default: - printf("pci_emul_alloc_base: invalid bar type %d\n", type); - assert(0); + printf("%s: invalid bar type %d\n", __func__, type); + return (-1); } - if (baseptr != NULL) { - error = pci_emul_alloc_resource(baseptr, limit, size, &addr); - if (error != 0) - return (error); - } else { - addr = 0; - } + assert((size & (size - 1)) == 0); /* must be a power of 2 */ + arena = pci_businfo[pdi->pi_bus]->resources[type]; + if (arena != NULL && + (vmem_xalloc(arena, size, size, 0, 0, 0, ~0ul, M_BESTFIT, &addr) != 0)) + return (-1); pdi->pi_bar[idx].type = type; pdi->pi_bar[idx].addr = addr; @@ -1516,6 +1511,9 @@ int init_pci(struct vmctx *ctx) { + size_t io_range_size, mem32_range_size, mem64_range_size; + uint64_t pci_emul_membase32, pci_emul_membase64; + uint64_t pci_emul_iobase, pci_emul_memlim64; char node_name[sizeof("pci.XXX.XX.X")]; struct mem_range mr; struct pci_devemu *pde; @@ -1526,11 +1524,20 @@ const char *emul; size_t lowmem; int bus, slot, func; - int error; + int error, nbuses; if (vm_get_lowmem_limit(ctx) > PCI_EMUL_MEMBASE32) errx(EX_OSERR, "Invalid lowmem limit"); + nbuses = 0; + for (bus = 0; bus < MAXBUSES; bus++) { + snprintf(node_name, sizeof(node_name), "pci.%d", bus); + nvl = find_config_node(node_name); + if (nvl == NULL) + continue; + nbuses++; + } + pci_emul_iobase = PCI_EMUL_IOBASE; pci_emul_membase32 = PCI_EMUL_MEMBASE32; @@ -1539,6 +1546,10 @@ pci_emul_membase64 = roundup2(pci_emul_membase64, PCI_EMUL_MEMSIZE64); pci_emul_memlim64 = pci_emul_membase64 + PCI_EMUL_MEMSIZE64; + io_range_size = (PCI_EMUL_IOLIMIT - pci_emul_iobase) / nbuses; + mem32_range_size = (PCI_EMUL_MEMLIMIT32 - pci_emul_membase32) / nbuses; + mem64_range_size = (pci_emul_memlim64 - pci_emul_membase64) / nbuses; + TAILQ_INIT(&boot_devices); for (bus = 0; bus < MAXBUSES; bus++) { @@ -1557,6 +1568,24 @@ bi->membase32 = pci_emul_membase32; bi->membase64 = pci_emul_membase64; + pci_emul_iobase += io_range_size; + pci_emul_membase32 += mem32_range_size; + pci_emul_membase64 += mem64_range_size; + + bi->iolimit = pci_emul_iobase - 1; + bi->memlimit32 = pci_emul_membase32 - 1; + bi->memlimit64 = pci_emul_membase64 - 1; + + bi->resources[PCIBAR_IO] = vmem_create("io", bi->iobase, + io_range_size, 0, 0, 0); + assert(bi->resources[PCIBAR_IO] != NULL); + bi->resources[PCIBAR_MEM32] = vmem_create("mem32", + bi->membase32, mem32_range_size, 0, 0, 0); + assert(bi->resources[PCIBAR_MEM32] != NULL); + bi->resources[PCIBAR_MEM64] = vmem_create("mem64", + bi->membase64, mem64_range_size, 0, 0, 0); + assert(bi->resources[PCIBAR_MEM64] != NULL); + /* first run: init devices */ for (slot = 0; slot < MAXSLOTS; slot++) { si = &bi->slotinfo[slot]; @@ -1606,25 +1635,6 @@ free(bar); } TAILQ_INIT(&pci_bars); - - /* - * Add some slop to the I/O and memory resources decoded by - * this bus to give a guest some flexibility if it wants to - * reprogram the BARs. - */ - pci_emul_iobase += BUSIO_ROUNDUP; - pci_emul_iobase = roundup2(pci_emul_iobase, BUSIO_ROUNDUP); - bi->iolimit = pci_emul_iobase; - - pci_emul_membase32 += BUSMEM32_ROUNDUP; - pci_emul_membase32 = roundup2(pci_emul_membase32, - BUSMEM32_ROUNDUP); - bi->memlimit32 = pci_emul_membase32; - - pci_emul_membase64 += BUSMEM64_ROUNDUP; - pci_emul_membase64 = roundup2(pci_emul_membase64, - BUSMEM64_ROUNDUP); - bi->memlimit64 = pci_emul_membase64; } /* @@ -1785,6 +1795,9 @@ #ifdef __amd64__ if (bus == 0) { + int error; + vmem_t *arena; + dsdt_indent(3); dsdt_fixed_ioport(0xCF8, 8); dsdt_unindent(3); @@ -1813,6 +1826,13 @@ dsdt_line(" })"); goto done; } + + /* + * Register the bus's IO BAR address range. + */ + arena = bi->resources[PCIBAR_IO]; + error = vmem_add(arena, 0x0D00, PCI_EMUL_IOBASE - 0x0D00, 0); + assert(error == 0); } #endif assert(bi != NULL);