Changeset View
Changeset View
Standalone View
Standalone View
usr.sbin/bhyve/pci_emul.c
| Show First 20 Lines • Show All 67 Lines • ▼ Show 20 Lines | ||||||||||||||
| #define CONF1_DATA_PORT 0x0cfc | #define CONF1_DATA_PORT 0x0cfc | |||||||||||||
| #define CONF1_ENABLE 0x80000000ul | #define CONF1_ENABLE 0x80000000ul | |||||||||||||
| #define MAXBUSES (PCI_BUSMAX + 1) | #define MAXBUSES (PCI_BUSMAX + 1) | |||||||||||||
| #define MAXSLOTS (PCI_SLOTMAX + 1) | #define MAXSLOTS (PCI_SLOTMAX + 1) | |||||||||||||
| #define MAXFUNCS (PCI_FUNCMAX + 1) | #define MAXFUNCS (PCI_FUNCMAX + 1) | |||||||||||||
| #define GB (1024 * 1024 * 1024UL) | ||||||||||||||
| struct funcinfo { | struct funcinfo { | |||||||||||||
| nvlist_t *fi_config; | nvlist_t *fi_config; | |||||||||||||
| struct pci_devemu *fi_pde; | struct pci_devemu *fi_pde; | |||||||||||||
| struct pci_devinst *fi_devi; | struct pci_devinst *fi_devi; | |||||||||||||
| }; | }; | |||||||||||||
| struct intxinfo { | struct intxinfo { | |||||||||||||
| int ii_count; | int ii_count; | |||||||||||||
| Show All 13 Lines | struct businfo { | |||||||||||||
| struct slotinfo slotinfo[MAXSLOTS]; | struct slotinfo slotinfo[MAXSLOTS]; | |||||||||||||
| }; | }; | |||||||||||||
| static struct businfo *pci_businfo[MAXBUSES]; | static struct businfo *pci_businfo[MAXBUSES]; | |||||||||||||
| SET_DECLARE(pci_devemu_set, struct pci_devemu); | SET_DECLARE(pci_devemu_set, struct pci_devemu); | |||||||||||||
| static uint64_t pci_emul_iobase; | static uint64_t pci_emul_iobase; | |||||||||||||
| static uint64_t pci_emul_iolim; | ||||||||||||||
| static uint64_t pci_emul_membase32; | static uint64_t pci_emul_membase32; | |||||||||||||
| static uint64_t pci_emul_memlim32; | ||||||||||||||
| static uint64_t pci_emul_membase64; | static uint64_t pci_emul_membase64; | |||||||||||||
| static uint64_t pci_emul_memlim64; | static uint64_t pci_emul_memlim64; | |||||||||||||
| struct pcibarlist { | ||||||||||||||
| struct pci_devinst *pdi; | ||||||||||||||
| int idx; | ||||||||||||||
| enum pcibar_type type; | ||||||||||||||
| uint64_t size; | ||||||||||||||
| struct pcibarlist *next; | ||||||||||||||
| }; | ||||||||||||||
| struct pcibarlist *pci_bars; | ||||||||||||||
| #define PCI_EMUL_IOBASE 0x2000 | #define PCI_EMUL_IOBASE 0x2000 | |||||||||||||
| #define PCI_EMUL_IOLIMIT 0x10000 | #define PCI_EMUL_IOLIMIT 0x10000 | |||||||||||||
| #define PCI_EMUL_ECFG_BASE 0xE0000000 /* 3.5GB */ | #define PCI_EMUL_ECFG_BASE 0xE0000000 /* 3.5GB */ | |||||||||||||
| #define PCI_EMUL_ECFG_SIZE (MAXBUSES * 1024 * 1024) /* 1MB per bus */ | #define PCI_EMUL_ECFG_SIZE (MAXBUSES * 1024 * 1024) /* 1MB per bus */ | |||||||||||||
| SYSRES_MEM(PCI_EMUL_ECFG_BASE, PCI_EMUL_ECFG_SIZE); | SYSRES_MEM(PCI_EMUL_ECFG_BASE, PCI_EMUL_ECFG_SIZE); | |||||||||||||
| #define PCI_EMUL_MEMLIMIT32 PCI_EMUL_ECFG_BASE | #define PCI_EMUL_MEMLIMIT32 PCI_EMUL_ECFG_BASE | |||||||||||||
| #define PCI_EMUL_MEMSIZE64 (32 * GB) | ||||||||||||||
| static struct pci_devemu *pci_emul_finddev(const char *name); | static struct pci_devemu *pci_emul_finddev(const char *name); | |||||||||||||
| static void pci_lintr_route(struct pci_devinst *pi); | static void pci_lintr_route(struct pci_devinst *pi); | |||||||||||||
| static void pci_lintr_update(struct pci_devinst *pi); | static void pci_lintr_update(struct pci_devinst *pi); | |||||||||||||
| static void pci_cfgrw(struct vmctx *ctx, int vcpu, int in, int bus, int slot, | static void pci_cfgrw(struct vmctx *ctx, int vcpu, int in, int bus, int slot, | |||||||||||||
| int func, int coff, int bytes, uint32_t *val); | int func, int coff, int bytes, uint32_t *val); | |||||||||||||
| static __inline void | static __inline void | |||||||||||||
| ▲ Show 20 Lines • Show All 382 Lines • ▼ Show 20 Lines | ||||||||||||||
| modify_bar_registration(struct pci_devinst *pi, int idx, int registration) | modify_bar_registration(struct pci_devinst *pi, int idx, int registration) | |||||||||||||
| { | { | |||||||||||||
| struct pci_devemu *pe; | struct pci_devemu *pe; | |||||||||||||
| int error; | int error; | |||||||||||||
| struct inout_port iop; | struct inout_port iop; | |||||||||||||
| struct mem_range mr; | struct mem_range mr; | |||||||||||||
| pe = pi->pi_d; | pe = pi->pi_d; | |||||||||||||
| if (pe->pe_baraddr != NULL && | ||||||||||||||
| (*pe->pe_baraddr)( | ||||||||||||||
| pi->pi_vmctx, pi, idx, registration, pi->pi_bar[idx].addr) == 0) | ||||||||||||||
| return; | ||||||||||||||
| switch (pi->pi_bar[idx].type) { | switch (pi->pi_bar[idx].type) { | |||||||||||||
| case PCIBAR_IO: | case PCIBAR_IO: | |||||||||||||
| bzero(&iop, sizeof(struct inout_port)); | bzero(&iop, sizeof(struct inout_port)); | |||||||||||||
| iop.name = pi->pi_name; | iop.name = pi->pi_name; | |||||||||||||
| iop.port = pi->pi_bar[idx].addr; | iop.port = pi->pi_bar[idx].addr; | |||||||||||||
| iop.size = pi->pi_bar[idx].size; | iop.size = pi->pi_bar[idx].size; | |||||||||||||
| if (registration) { | if (registration) { | |||||||||||||
| iop.flags = IOPORT_F_INOUT; | iop.flags = IOPORT_F_INOUT; | |||||||||||||
| iop.handler = pci_emul_io_handler; | iop.handler = pci_emul_io_handler; | |||||||||||||
| iop.arg = pi; | iop.arg = pi; | |||||||||||||
| error = register_inout(&iop); | error = register_inout(&iop); | |||||||||||||
| } else | } else | |||||||||||||
| error = unregister_inout(&iop); | error = unregister_inout(&iop); | |||||||||||||
| if (pe->pe_baraddr != NULL) | ||||||||||||||
| (*pe->pe_baraddr)(pi->pi_vmctx, pi, idx, registration, | ||||||||||||||
| pi->pi_bar[idx].addr); | ||||||||||||||
| break; | break; | |||||||||||||
| case PCIBAR_MEM32: | case PCIBAR_MEM32: | |||||||||||||
| case PCIBAR_MEM64: | case PCIBAR_MEM64: | |||||||||||||
| bzero(&mr, sizeof(struct mem_range)); | bzero(&mr, sizeof(struct mem_range)); | |||||||||||||
| mr.name = pi->pi_name; | mr.name = pi->pi_name; | |||||||||||||
| mr.base = pi->pi_bar[idx].addr; | mr.base = pi->pi_bar[idx].addr; | |||||||||||||
| mr.size = pi->pi_bar[idx].size; | mr.size = pi->pi_bar[idx].size; | |||||||||||||
| if (registration) { | if (registration) { | |||||||||||||
| mr.flags = MEM_F_RW; | mr.flags = MEM_F_RW; | |||||||||||||
| mr.handler = pci_emul_mem_handler; | mr.handler = pci_emul_mem_handler; | |||||||||||||
| mr.arg1 = pi; | mr.arg1 = pi; | |||||||||||||
| mr.arg2 = idx; | mr.arg2 = idx; | |||||||||||||
| error = register_mem(&mr); | error = register_mem(&mr); | |||||||||||||
| } else | } else | |||||||||||||
| error = unregister_mem(&mr); | error = unregister_mem(&mr); | |||||||||||||
| if (pe->pe_baraddr != NULL) | ||||||||||||||
| (*pe->pe_baraddr)(pi->pi_vmctx, pi, idx, registration, | ||||||||||||||
| pi->pi_bar[idx].addr); | ||||||||||||||
| break; | break; | |||||||||||||
| default: | default: | |||||||||||||
| error = EINVAL; | error = EINVAL; | |||||||||||||
| break; | break; | |||||||||||||
| } | } | |||||||||||||
| assert(error == 0); | assert(error == 0); | |||||||||||||
| } | } | |||||||||||||
| Show All 35 Lines | ||||||||||||||
| /* | /* | |||||||||||||
| * Update the MMIO or I/O address that is decoded by the BAR register. | * Update the MMIO or I/O address that is decoded by the BAR register. | |||||||||||||
| * | * | |||||||||||||
| * If the pci device has enabled the address space decoding then intercept | * If the pci device has enabled the address space decoding then intercept | |||||||||||||
| * the address range decoded by the BAR register. | * the address range decoded by the BAR register. | |||||||||||||
| */ | */ | |||||||||||||
| static void | static void | |||||||||||||
| update_bar_address(struct pci_devinst *pi, uint64_t addr, int idx, int type) | update_bar_address(struct pci_devinst *pi, int idx, uint32_t val) | |||||||||||||
| { | { | |||||||||||||
| int update_idx = idx; | ||||||||||||||
| int decode; | int decode; | |||||||||||||
| if (pi->pi_bar[idx].type == PCIBAR_IO) | if (pi->pi_bar[idx].type == PCIBAR_IO) | |||||||||||||
| decode = porten(pi); | decode = porten(pi); | |||||||||||||
| else | else | |||||||||||||
| decode = memen(pi); | decode = memen(pi); | |||||||||||||
| if (decode) | switch (pi->pi_bar[idx].type) { | |||||||||||||
| unregister_bar(pi, idx); | case PCIBAR_MEMHI64: | |||||||||||||
| --update_idx; | ||||||||||||||
| switch (type) { | ||||||||||||||
| case PCIBAR_IO: | case PCIBAR_IO: | |||||||||||||
| case PCIBAR_MEM32: | case PCIBAR_MEM32: | |||||||||||||
| pi->pi_bar[idx].addr = addr; | case PCIBAR_MEM64: { | |||||||||||||
| struct pcibar *bar = &pi->pi_bar[update_idx]; | ||||||||||||||
| if (decode && bar->addr) | ||||||||||||||
| unregister_bar(pi, update_idx); | ||||||||||||||
| if (val == ~0U) { | ||||||||||||||
| /* guest wants to read size of BAR */ | ||||||||||||||
| pci_set_cfgdata32(pi, PCIR_BAR(idx), ~0U); | ||||||||||||||
| bar->addr = 0; | ||||||||||||||
| break; | break; | |||||||||||||
| case PCIBAR_MEM64: | } | |||||||||||||
| pi->pi_bar[idx].addr &= ~0xffffffffUL; | ||||||||||||||
| pi->pi_bar[idx].addr |= addr; | /* guest sets address of BAR */ | |||||||||||||
| uint64_t mask; | ||||||||||||||
| uint32_t bar_val; | ||||||||||||||
| mask = ~(bar->size - 1UL); | ||||||||||||||
| if (pi->pi_bar[idx].type == PCIBAR_MEMHI64) | ||||||||||||||
| mask >>= 32UL; | ||||||||||||||
| bar_val = val & mask; | ||||||||||||||
| bar_val |= pi->pi_bar[idx].lobits; | ||||||||||||||
| pci_set_cfgdata32(pi, PCIR_BAR(idx), bar_val); | ||||||||||||||
| /* Only register BAR if it contains a valid address */ | ||||||||||||||
| uint32_t lo, hi; | ||||||||||||||
| lo = pci_get_cfgdata32(pi, PCIR_BAR(update_idx)); | ||||||||||||||
| hi = 0; | ||||||||||||||
| if (bar->type == PCIBAR_MEM64) | ||||||||||||||
| hi = pci_get_cfgdata32(pi, PCIR_BAR(update_idx + 1)); | ||||||||||||||
| if (lo == ~0U || hi == ~0U) { | ||||||||||||||
| bar->addr = 0; | ||||||||||||||
| break; | break; | |||||||||||||
| case PCIBAR_MEMHI64: | } | |||||||||||||
| pi->pi_bar[idx].addr &= 0xffffffff; | ||||||||||||||
| pi->pi_bar[idx].addr |= addr; | if (bar->type == PCIBAR_IO) | |||||||||||||
| lo &= PCIM_BAR_IO_BASE; | ||||||||||||||
| else | ||||||||||||||
| lo &= PCIM_BAR_MEM_BASE; | ||||||||||||||
| bar->addr = (uint64_t)lo | ((uint64_t)hi << 32UL); | ||||||||||||||
| if (decode) | ||||||||||||||
| register_bar(pi, update_idx); | ||||||||||||||
| break; | break; | |||||||||||||
| } | ||||||||||||||
| case PCIBAR_NONE: | ||||||||||||||
| break; | ||||||||||||||
| default: | default: | |||||||||||||
| assert(0); | assert(0); | |||||||||||||
| } | } | |||||||||||||
| } | ||||||||||||||
| if (decode) | static uint32_t | |||||||||||||
| register_bar(pi, idx); | read_bar_value(struct pci_devinst *pi, int coff, int bytes) | |||||||||||||
| { | ||||||||||||||
| uint8_t idx; | ||||||||||||||
| idx = (coff - PCIR_BAR(0)) / 4; | ||||||||||||||
| assert(idx <= PCI_BARMAX); | ||||||||||||||
| uint8_t update_idx = idx; | ||||||||||||||
| uint64_t val; | ||||||||||||||
| if (pi->pi_bar[idx].type == PCIBAR_MEMHI64) | ||||||||||||||
| --update_idx; | ||||||||||||||
| val = pci_get_cfgdata32(pi, PCIR_BAR(idx)); | ||||||||||||||
| /* return size of BAR */ | ||||||||||||||
| if (val == ~0U) { | ||||||||||||||
| val = ~(pi->pi_bar[update_idx].size - 1); | ||||||||||||||
| val |= pi->pi_bar[update_idx].lobits; | ||||||||||||||
| if (pi->pi_bar[idx].type == PCIBAR_MEMHI64) | ||||||||||||||
| val >>= 32; | ||||||||||||||
| } | } | |||||||||||||
| switch (bytes) { | ||||||||||||||
| case 1: | ||||||||||||||
| val = (val >> (8 * (coff & 0x03))) & 0xFF; | ||||||||||||||
| break; | ||||||||||||||
| case 2: | ||||||||||||||
| assert((coff & 0x01) == 0); | ||||||||||||||
| val = (val >> (8 * (coff & 0x02))) & 0xFFFF; | ||||||||||||||
| break; | ||||||||||||||
| case 4: | ||||||||||||||
| assert((coff & 0x03) == 0); | ||||||||||||||
| val = (uint32_t)val; | ||||||||||||||
| break; | ||||||||||||||
| default: | ||||||||||||||
| assert(0); | ||||||||||||||
| } | ||||||||||||||
| return val; | ||||||||||||||
| } | ||||||||||||||
| int | int | |||||||||||||
| pci_emul_alloc_bar(struct pci_devinst *pdi, int idx, enum pcibar_type type, | pci_emul_alloc_bar(struct pci_devinst *pdi, int idx, enum pcibar_type type, | |||||||||||||
| uint64_t size) | uint64_t size) | |||||||||||||
| { | { | |||||||||||||
| int error; | ||||||||||||||
| uint64_t *baseptr, limit, addr, mask, lobits, bar; | ||||||||||||||
| uint16_t cmd, enbit; | ||||||||||||||
| assert(idx >= 0 && idx <= PCI_BARMAX); | assert(idx >= 0 && idx <= PCI_BARMAX); | |||||||||||||
| if ((size & (size - 1)) != 0) | if ((size & (size - 1)) != 0) | |||||||||||||
| size = 1UL << flsl(size); /* round up to a power of 2 */ | size = 1UL << flsl(size); /* round up to a power of 2 */ | |||||||||||||
| /* Enforce minimum BAR sizes required by the PCI standard */ | /* Enforce minimum BAR sizes required by the PCI standard */ | |||||||||||||
| if (type == PCIBAR_IO) { | if (type == PCIBAR_IO) { | |||||||||||||
| if (size < 4) | if (size < 4) | |||||||||||||
| size = 4; | size = 4; | |||||||||||||
| } else { | } else { | |||||||||||||
| if (size < 16) | if (size < 16) | |||||||||||||
| size = 16; | size = 16; | |||||||||||||
| } | } | |||||||||||||
| struct pcibarlist *newBar = malloc(sizeof(struct pcibarlist)); | ||||||||||||||
| memset(newBar, 0, sizeof(struct pcibarlist)); | ||||||||||||||
| newBar->pdi = pdi; | ||||||||||||||
| newBar->idx = idx; | ||||||||||||||
| newBar->type = type; | ||||||||||||||
| newBar->size = size; | ||||||||||||||
| if (pci_bars == NULL) { | ||||||||||||||
| /* first BAR */ | ||||||||||||||
| pci_bars = newBar; | ||||||||||||||
| } else { | ||||||||||||||
| struct pcibarlist *bar = pci_bars; | ||||||||||||||
| struct pcibarlist *lastBar = NULL; | ||||||||||||||
| do { | ||||||||||||||
| if (bar->size < size) | ||||||||||||||
| break; | ||||||||||||||
| lastBar = bar; | ||||||||||||||
| bar = bar->next; | ||||||||||||||
| } while (bar != NULL); | ||||||||||||||
| newBar->next = bar; | ||||||||||||||
| if (lastBar != NULL) | ||||||||||||||
| lastBar->next = newBar; | ||||||||||||||
| else | ||||||||||||||
| pci_bars = newBar; | ||||||||||||||
| } | ||||||||||||||
| return (0); | ||||||||||||||
| } | ||||||||||||||
| static int | ||||||||||||||
| pci_emul_assign_bar(struct pcibarlist *pci_bar) | ||||||||||||||
| { | ||||||||||||||
| struct pci_devinst *pdi = pci_bar->pdi; | ||||||||||||||
| int idx = pci_bar->idx; | ||||||||||||||
| enum pcibar_type type = pci_bar->type; | ||||||||||||||
| uint64_t size = pci_bar->size; | ||||||||||||||
| int error; | ||||||||||||||
| uint64_t *baseptr, limit, addr, mask, lobits; | ||||||||||||||
| uint16_t cmd, enbit; | ||||||||||||||
| switch (type) { | switch (type) { | |||||||||||||
| case PCIBAR_NONE: | case PCIBAR_NONE: | |||||||||||||
| baseptr = NULL; | baseptr = NULL; | |||||||||||||
| addr = mask = lobits = enbit = 0; | addr = mask = lobits = enbit = 0; | |||||||||||||
| break; | break; | |||||||||||||
| case PCIBAR_IO: | case PCIBAR_IO: | |||||||||||||
| baseptr = &pci_emul_iobase; | baseptr = &pci_emul_iobase; | |||||||||||||
| limit = PCI_EMUL_IOLIMIT; | limit = pci_emul_iolim; | |||||||||||||
| mask = PCIM_BAR_IO_BASE; | mask = PCIM_BAR_IO_BASE; | |||||||||||||
| lobits = PCIM_BAR_IO_SPACE; | lobits = PCIM_BAR_IO_SPACE; | |||||||||||||
| enbit = PCIM_CMD_PORTEN; | enbit = PCIM_CMD_PORTEN; | |||||||||||||
| break; | break; | |||||||||||||
| case PCIBAR_MEM64: | case PCIBAR_MEM64: | |||||||||||||
| /* | /* | |||||||||||||
| * XXX | * XXX | |||||||||||||
| * Some drivers do not work well if the 64-bit BAR is allocated | * Some drivers do not work well if the 64-bit BAR is allocated | |||||||||||||
| * above 4GB. Allow for this by allocating small requests under | * above 4GB. Allow for this by allocating small requests under | |||||||||||||
| * 4GB unless then allocation size is larger than some arbitrary | * 4GB unless then allocation size is larger than some arbitrary | |||||||||||||
| * number (128MB currently). | * number (256MB currently). | |||||||||||||
| */ | */ | |||||||||||||
| if (size > 128 * 1024 * 1024) { | if (size > 256 * 1024 * 1024) { | |||||||||||||
| baseptr = &pci_emul_membase64; | baseptr = &pci_emul_membase64; | |||||||||||||
| limit = pci_emul_memlim64; | limit = pci_emul_memlim64; | |||||||||||||
| mask = PCIM_BAR_MEM_BASE; | mask = PCIM_BAR_MEM_BASE; | |||||||||||||
| lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64 | | lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64 | | |||||||||||||
| PCIM_BAR_MEM_PREFETCH; | 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; | ||||||||||||||
| } | ||||||||||||||
| enbit = PCIM_CMD_MEMEN; | enbit = PCIM_CMD_MEMEN; | |||||||||||||
| break; | break; | |||||||||||||
| } | ||||||||||||||
| /* | ||||||||||||||
| * Use 32 bit BARs for small requests: | ||||||||||||||
| * Fallthrough into MEM32 case | ||||||||||||||
| */ | ||||||||||||||
| type = PCIBAR_MEM32; | ||||||||||||||
| pdi->pi_bar[idx + 1].type = PCIBAR_NONE; | ||||||||||||||
| /* clear 64-bit flag */ | ||||||||||||||
| pdi->pi_bar[idx].lobits &= ~PCIM_BAR_MEM_64; | ||||||||||||||
| /* [fallthrough] */ | ||||||||||||||
| case PCIBAR_MEM32: | case PCIBAR_MEM32: | |||||||||||||
| baseptr = &pci_emul_membase32; | baseptr = &pci_emul_membase32; | |||||||||||||
| limit = PCI_EMUL_MEMLIMIT32; | limit = pci_emul_memlim32; | |||||||||||||
| mask = PCIM_BAR_MEM_BASE; | mask = PCIM_BAR_MEM_BASE; | |||||||||||||
| lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_32; | lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_32; | |||||||||||||
| enbit = PCIM_CMD_MEMEN; | enbit = PCIM_CMD_MEMEN; | |||||||||||||
| break; | break; | |||||||||||||
| default: | default: | |||||||||||||
| printf("pci_emul_alloc_base: invalid bar type %d\n", type); | printf("pci_emul_alloc_base: invalid bar type %d\n", type); | |||||||||||||
| assert(0); | assert(0); | |||||||||||||
| } | } | |||||||||||||
| if (baseptr != NULL) { | if (baseptr != NULL) { | |||||||||||||
| error = pci_emul_alloc_resource(baseptr, limit, size, &addr); | error = pci_emul_alloc_resource(baseptr, limit, size, &addr); | |||||||||||||
| if (error != 0) | if (error != 0) | |||||||||||||
| return (error); | return (error); | |||||||||||||
| } | } | |||||||||||||
| pdi->pi_bar[idx].type = type; | pdi->pi_bar[idx].type = type; | |||||||||||||
| pdi->pi_bar[idx].addr = addr; | pdi->pi_bar[idx].addr = 0; | |||||||||||||
| pdi->pi_bar[idx].size = size; | pdi->pi_bar[idx].size = size; | |||||||||||||
| /* passthru devices are using same lobits as physical device | ||||||||||||||
| * they set this property | ||||||||||||||
| */ | ||||||||||||||
| if (pdi->pi_bar[idx].lobits != 0) | ||||||||||||||
| lobits = pdi->pi_bar[idx].lobits; | ||||||||||||||
| else | ||||||||||||||
| pdi->pi_bar[idx].lobits = lobits; | ||||||||||||||
| /* Initialize the BAR register in config space */ | /* Initialize CMD register in config space */ | |||||||||||||
| bar = (addr & mask) | lobits; | cmd = pci_get_cfgdata16(pdi, PCIR_COMMAND); | |||||||||||||
| pci_set_cfgdata32(pdi, PCIR_BAR(idx), bar); | if ((cmd & enbit) != enbit) | |||||||||||||
| pci_set_cfgdata16(pdi, PCIR_COMMAND, cmd | enbit); | ||||||||||||||
| /* Initialize the BAR register in config space */ | ||||||||||||||
| if (type == PCIBAR_MEM64) { | if (type == PCIBAR_MEM64) { | |||||||||||||
| assert(idx + 1 <= PCI_BARMAX); | assert(idx + 1 <= PCI_BARMAX); | |||||||||||||
| pdi->pi_bar[idx + 1].type = PCIBAR_MEMHI64; | pdi->pi_bar[idx + 1].type = PCIBAR_MEMHI64; | |||||||||||||
| pci_set_cfgdata32(pdi, PCIR_BAR(idx + 1), bar >> 32); | update_bar_address(pdi, idx + 1, addr); | |||||||||||||
khngUnsubmitted Done Inline Actions
khng: | ||||||||||||||
| } | } | |||||||||||||
| cmd = pci_get_cfgdata16(pdi, PCIR_COMMAND); | update_bar_address(pdi, idx, addr); | |||||||||||||
| if ((cmd & enbit) != enbit) | ||||||||||||||
| pci_set_cfgdata16(pdi, PCIR_COMMAND, cmd | enbit); | ||||||||||||||
| register_bar(pdi, idx); | ||||||||||||||
| return (0); | return (0); | |||||||||||||
| } | } | |||||||||||||
| uint64_t | ||||||||||||||
| pci_emul_alloc_gsm(uint64_t size) | ||||||||||||||
| { | ||||||||||||||
| uint64_t *baseptr = &pci_emul_membase32; | ||||||||||||||
| uint64_t *limptr = &pci_emul_memlim32; | ||||||||||||||
| /* align addr */ | ||||||||||||||
| const uint64_t addr = ((*limptr) - size) & ~(size - 1); | ||||||||||||||
| /* if limit < base ==> ENOMEM */ | ||||||||||||||
| if ((*limptr) < (*baseptr)) | ||||||||||||||
| return 0; | ||||||||||||||
| *limptr = addr; | ||||||||||||||
| return addr; | ||||||||||||||
| } | ||||||||||||||
| #define CAP_START_OFFSET 0x40 | #define CAP_START_OFFSET 0x40 | |||||||||||||
| static int | static int | |||||||||||||
| pci_emul_add_capability(struct pci_devinst *pi, u_char *capdata, int caplen) | pci_emul_add_capability(struct pci_devinst *pi, u_char *capdata, int caplen) | |||||||||||||
| { | { | |||||||||||||
| int i, capoff, reallen; | int i, capoff, reallen; | |||||||||||||
| uint16_t sts; | uint16_t sts; | |||||||||||||
| assert(caplen > 0); | assert(caplen > 0); | |||||||||||||
| ▲ Show 20 Lines • Show All 392 Lines • ▼ Show 20 Lines | ||||||||||||||
| { | { | |||||||||||||
| return (PCI_EMUL_ECFG_BASE); | return (PCI_EMUL_ECFG_BASE); | |||||||||||||
| } | } | |||||||||||||
| #define BUSIO_ROUNDUP 32 | #define BUSIO_ROUNDUP 32 | |||||||||||||
| #define BUSMEM_ROUNDUP (1024 * 1024) | #define BUSMEM_ROUNDUP (1024 * 1024) | |||||||||||||
| #define ALIGN_VALUE(Value, Alignment) \ | ||||||||||||||
| ((Value) + (((Alignment) - (Value)) & ((Alignment)-1))) | ||||||||||||||
| int | int | |||||||||||||
| init_pci(struct vmctx *ctx) | init_pci(struct vmctx *ctx) | |||||||||||||
| { | { | |||||||||||||
| char node_name[sizeof("pci.XXX.XX.X")]; | char node_name[sizeof("pci.XXX.XX.X")]; | |||||||||||||
| struct mem_range mr; | struct mem_range mr; | |||||||||||||
| struct pci_devemu *pde; | struct pci_devemu *pde; | |||||||||||||
| struct businfo *bi; | struct businfo *bi; | |||||||||||||
| struct slotinfo *si; | struct slotinfo *si; | |||||||||||||
| struct funcinfo *fi; | struct funcinfo *fi; | |||||||||||||
| nvlist_t *nvl; | nvlist_t *nvl; | |||||||||||||
| const char *emul; | const char *emul; | |||||||||||||
| size_t lowmem; | size_t lowmem; | |||||||||||||
| uint64_t cpu_maxphysaddr, pci_emul_memresv64; | ||||||||||||||
| u_int regs[4]; | ||||||||||||||
| int bus, slot, func, error; | int bus, slot, func, error; | |||||||||||||
| pci_emul_iobase = PCI_EMUL_IOBASE; | pci_emul_iobase = PCI_EMUL_IOBASE; | |||||||||||||
| pci_emul_iolim = PCI_EMUL_IOLIMIT; | ||||||||||||||
| pci_emul_membase32 = vm_get_lowmem_limit(ctx); | pci_emul_membase32 = vm_get_lowmem_limit(ctx); | |||||||||||||
| pci_emul_memlim32 = PCI_EMUL_MEMLIMIT32; | ||||||||||||||
| do_cpuid(0x80000008, regs); | pci_emul_membase64 = 4 * GB + vm_get_highmem_size(ctx); | |||||||||||||
| cpu_maxphysaddr = 1ULL << (regs[0] & 0xff); | pci_emul_membase64 = ALIGN_VALUE( | |||||||||||||
| if (cpu_maxphysaddr > VM_MAXUSER_ADDRESS_LA48) | pci_emul_membase64, PCI_EMUL_MEMSIZE64); | |||||||||||||
| cpu_maxphysaddr = VM_MAXUSER_ADDRESS_LA48; | pci_emul_memlim64 = pci_emul_membase64 + PCI_EMUL_MEMSIZE64; | |||||||||||||
| pci_emul_memresv64 = cpu_maxphysaddr / 4; | ||||||||||||||
| /* | ||||||||||||||
| * Max power of 2 that is less then | ||||||||||||||
| * cpu_maxphysaddr - pci_emul_memresv64. | ||||||||||||||
| */ | ||||||||||||||
| pci_emul_membase64 = 1ULL << (flsl(cpu_maxphysaddr - | ||||||||||||||
| pci_emul_memresv64) - 1); | ||||||||||||||
| pci_emul_memlim64 = cpu_maxphysaddr; | ||||||||||||||
| for (bus = 0; bus < MAXBUSES; bus++) { | for (bus = 0; bus < MAXBUSES; bus++) { | |||||||||||||
| snprintf(node_name, sizeof(node_name), "pci.%d", bus); | snprintf(node_name, sizeof(node_name), "pci.%d", bus); | |||||||||||||
| nvl = find_config_node(node_name); | nvl = find_config_node(node_name); | |||||||||||||
| if (nvl == NULL) | if (nvl == NULL) | |||||||||||||
| continue; | continue; | |||||||||||||
| pci_businfo[bus] = calloc(1, sizeof(struct businfo)); | pci_businfo[bus] = calloc(1, sizeof(struct businfo)); | |||||||||||||
| bi = pci_businfo[bus]; | bi = pci_businfo[bus]; | |||||||||||||
| /* | /* | |||||||||||||
| * Keep track of the i/o and memory resources allocated to | * Keep track of the i/o and memory resources allocated to | |||||||||||||
| * this bus. | * this bus. | |||||||||||||
| */ | */ | |||||||||||||
| bi->iobase = pci_emul_iobase; | bi->iobase = pci_emul_iobase; | |||||||||||||
| bi->membase32 = pci_emul_membase32; | bi->membase32 = pci_emul_membase32; | |||||||||||||
| bi->membase64 = pci_emul_membase64; | bi->membase64 = pci_emul_membase64; | |||||||||||||
| /* first run: init devices */ | ||||||||||||||
| for (slot = 0; slot < MAXSLOTS; slot++) { | for (slot = 0; slot < MAXSLOTS; slot++) { | |||||||||||||
| si = &bi->slotinfo[slot]; | si = &bi->slotinfo[slot]; | |||||||||||||
| for (func = 0; func < MAXFUNCS; func++) { | for (func = 0; func < MAXFUNCS; func++) { | |||||||||||||
| fi = &si->si_funcs[func]; | fi = &si->si_funcs[func]; | |||||||||||||
| snprintf(node_name, sizeof(node_name), | snprintf(node_name, sizeof(node_name), | |||||||||||||
| "pci.%d.%d.%d", bus, slot, func); | "pci.%d.%d.%d", bus, slot, func); | |||||||||||||
| nvl = find_config_node(node_name); | nvl = find_config_node(node_name); | |||||||||||||
| if (nvl == NULL) | if (nvl == NULL) | |||||||||||||
| Show All 23 Lines | for (slot = 0; slot < MAXSLOTS; slot++) { | |||||||||||||
| fi->fi_pde = pde; | fi->fi_pde = pde; | |||||||||||||
| error = pci_emul_init(ctx, pde, bus, slot, | error = pci_emul_init(ctx, pde, bus, slot, | |||||||||||||
| func, fi); | func, fi); | |||||||||||||
| if (error) | if (error) | |||||||||||||
| return (error); | return (error); | |||||||||||||
| } | } | |||||||||||||
| } | } | |||||||||||||
| /* second run: assign BARs and free BAR list */ | ||||||||||||||
| struct pcibarlist *bar = pci_bars; | ||||||||||||||
| while (bar != NULL) { | ||||||||||||||
| pci_emul_assign_bar(bar); | ||||||||||||||
| struct pcibarlist *old = bar; | ||||||||||||||
| bar = bar->next; | ||||||||||||||
| free(old); | ||||||||||||||
| } | ||||||||||||||
| /* | /* | |||||||||||||
| * Add some slop to the I/O and memory resources decoded by | * Add some slop to the I/O and memory resources decoded by | |||||||||||||
| * this bus to give a guest some flexibility if it wants to | * this bus to give a guest some flexibility if it wants to | |||||||||||||
| * reprogram the BARs. | * reprogram the BARs. | |||||||||||||
| */ | */ | |||||||||||||
| pci_emul_iobase += BUSIO_ROUNDUP; | pci_emul_iobase += BUSIO_ROUNDUP; | |||||||||||||
| pci_emul_iobase = roundup2(pci_emul_iobase, BUSIO_ROUNDUP); | pci_emul_iobase = roundup2(pci_emul_iobase, BUSIO_ROUNDUP); | |||||||||||||
| bi->iolimit = pci_emul_iobase; | bi->iolimit = pci_emul_iobase; | |||||||||||||
| ▲ Show 20 Lines • Show All 628 Lines • ▼ Show 20 Lines | ||||||||||||||
| pci_cfgrw(struct vmctx *ctx, int vcpu, int in, int bus, int slot, int func, | pci_cfgrw(struct vmctx *ctx, int vcpu, int in, int bus, int slot, int func, | |||||||||||||
| int coff, int bytes, uint32_t *eax) | int coff, int bytes, uint32_t *eax) | |||||||||||||
| { | { | |||||||||||||
| struct businfo *bi; | struct businfo *bi; | |||||||||||||
| struct slotinfo *si; | struct slotinfo *si; | |||||||||||||
| struct pci_devinst *pi; | struct pci_devinst *pi; | |||||||||||||
| struct pci_devemu *pe; | struct pci_devemu *pe; | |||||||||||||
| int idx, needcfg; | int idx, needcfg; | |||||||||||||
| uint64_t addr, bar, mask; | ||||||||||||||
| if ((bi = pci_businfo[bus]) != NULL) { | if ((bi = pci_businfo[bus]) != NULL) { | |||||||||||||
| si = &bi->slotinfo[slot]; | si = &bi->slotinfo[slot]; | |||||||||||||
| pi = si->si_funcs[func].fi_devi; | pi = si->si_funcs[func].fi_devi; | |||||||||||||
| } else | } else | |||||||||||||
| pi = NULL; | pi = NULL; | |||||||||||||
| /* | /* | |||||||||||||
| Show All 35 Lines | if (in) { | |||||||||||||
| /* Let the device emulation override the default handler */ | /* Let the device emulation override the default handler */ | |||||||||||||
| if (pe->pe_cfgread != NULL) { | if (pe->pe_cfgread != NULL) { | |||||||||||||
| needcfg = pe->pe_cfgread(ctx, vcpu, pi, coff, bytes, | needcfg = pe->pe_cfgread(ctx, vcpu, pi, coff, bytes, | |||||||||||||
| eax); | eax); | |||||||||||||
| } else { | } else { | |||||||||||||
| needcfg = 1; | needcfg = 1; | |||||||||||||
| } | } | |||||||||||||
| if (needcfg) | if (needcfg) { | |||||||||||||
| if (coff >= PCIR_BAR(0) && | ||||||||||||||
| coff < PCIR_BAR(PCI_BARMAX + 1)) { | ||||||||||||||
| *eax = read_bar_value(pi, coff, bytes); | ||||||||||||||
| } else { | ||||||||||||||
| *eax = CFGREAD(pi, coff, bytes); | *eax = CFGREAD(pi, coff, bytes); | |||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
| pci_emul_hdrtype_fixup(bus, slot, coff, bytes, eax); | pci_emul_hdrtype_fixup(bus, slot, coff, bytes, eax); | |||||||||||||
| } else { | } else { | |||||||||||||
| /* Let the device emulation override the default handler */ | /* Let the device emulation override the default handler */ | |||||||||||||
| if (pe->pe_cfgwrite != NULL && | if (pe->pe_cfgwrite != NULL && | |||||||||||||
| (*pe->pe_cfgwrite)(ctx, vcpu, pi, coff, bytes, *eax) == 0) | (*pe->pe_cfgwrite)(ctx, vcpu, pi, coff, bytes, *eax) == 0) | |||||||||||||
| return; | return; | |||||||||||||
| /* | /* | |||||||||||||
| * Special handling for write to BAR registers | * Special handling for write to BAR registers | |||||||||||||
| */ | */ | |||||||||||||
| if (coff >= PCIR_BAR(0) && coff < PCIR_BAR(PCI_BARMAX + 1)) { | if (coff >= PCIR_BAR(0) && coff < PCIR_BAR(PCI_BARMAX + 1)) { | |||||||||||||
| /* | /* | |||||||||||||
| * Ignore writes to BAR registers that are not | * Ignore writes to BAR registers that are not | |||||||||||||
| * 4-byte aligned. | * 4-byte aligned. | |||||||||||||
| */ | */ | |||||||||||||
| if (bytes != 4 || (coff & 0x3) != 0) | if (bytes != 4 || (coff & 0x3) != 0) | |||||||||||||
| return; | return; | |||||||||||||
| idx = (coff - PCIR_BAR(0)) / 4; | idx = (coff - PCIR_BAR(0)) / 4; | |||||||||||||
| mask = ~(pi->pi_bar[idx].size - 1); | update_bar_address(pi, idx, *eax); | |||||||||||||
| switch (pi->pi_bar[idx].type) { | ||||||||||||||
| case PCIBAR_NONE: | ||||||||||||||
| pi->pi_bar[idx].addr = bar = 0; | ||||||||||||||
| break; | ||||||||||||||
| case PCIBAR_IO: | ||||||||||||||
| addr = *eax & mask; | ||||||||||||||
| addr &= 0xffff; | ||||||||||||||
| bar = addr | PCIM_BAR_IO_SPACE; | ||||||||||||||
| /* | ||||||||||||||
| * Register the new BAR value for interception | ||||||||||||||
| */ | ||||||||||||||
| if (addr != pi->pi_bar[idx].addr) { | ||||||||||||||
| update_bar_address(pi, addr, idx, | ||||||||||||||
| PCIBAR_IO); | ||||||||||||||
| } | ||||||||||||||
| break; | ||||||||||||||
| case PCIBAR_MEM32: | ||||||||||||||
| addr = bar = *eax & mask; | ||||||||||||||
| bar |= PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_32; | ||||||||||||||
| if (addr != pi->pi_bar[idx].addr) { | ||||||||||||||
| update_bar_address(pi, addr, idx, | ||||||||||||||
| PCIBAR_MEM32); | ||||||||||||||
| } | ||||||||||||||
| break; | ||||||||||||||
| case PCIBAR_MEM64: | ||||||||||||||
| addr = bar = *eax & mask; | ||||||||||||||
| bar |= PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64 | | ||||||||||||||
| PCIM_BAR_MEM_PREFETCH; | ||||||||||||||
| if (addr != (uint32_t)pi->pi_bar[idx].addr) { | ||||||||||||||
| update_bar_address(pi, addr, idx, | ||||||||||||||
| PCIBAR_MEM64); | ||||||||||||||
| } | ||||||||||||||
| break; | ||||||||||||||
| case PCIBAR_MEMHI64: | ||||||||||||||
| mask = ~(pi->pi_bar[idx - 1].size - 1); | ||||||||||||||
| addr = ((uint64_t)*eax << 32) & mask; | ||||||||||||||
| bar = addr >> 32; | ||||||||||||||
| if (bar != pi->pi_bar[idx - 1].addr >> 32) { | ||||||||||||||
| update_bar_address(pi, addr, idx - 1, | ||||||||||||||
| PCIBAR_MEMHI64); | ||||||||||||||
| } | ||||||||||||||
| break; | ||||||||||||||
| default: | ||||||||||||||
| assert(0); | ||||||||||||||
| } | ||||||||||||||
| pci_set_cfgdata32(pi, coff, bar); | ||||||||||||||
| } else if (pci_emul_iscap(pi, coff)) { | } else if (pci_emul_iscap(pi, coff)) { | |||||||||||||
| pci_emul_capwrite(pi, coff, bytes, *eax, 0, 0); | pci_emul_capwrite(pi, coff, bytes, *eax, 0, 0); | |||||||||||||
| } else if (coff >= PCIR_COMMAND && coff < PCIR_REVID) { | } else if (coff >= PCIR_COMMAND && coff < PCIR_REVID) { | |||||||||||||
| pci_emul_cmdsts_write(pi, coff, *eax, bytes); | pci_emul_cmdsts_write(pi, coff, *eax, bytes); | |||||||||||||
| } else { | } else { | |||||||||||||
| CFGWRITE(pi, coff, *eax, bytes); | CFGWRITE(pi, coff, *eax, bytes); | |||||||||||||
| } | } | |||||||||||||
| } | } | |||||||||||||
| ▲ Show 20 Lines • Show All 432 Lines • Show Last 20 Lines | ||||||||||||||