Changeset View
Changeset View
Standalone View
Standalone View
usr.sbin/bhyve/pci_emul.c
Show All 32 Lines | |||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/linker_set.h> | #include <sys/linker_set.h> | ||||
#include <vm/vm.h> | #include <vm/vm.h> | ||||
#include <vm/vm_param.h> | #include <vm/vm_param.h> | ||||
#include <vm/pmap.h> | #include <vm/pmap.h> | ||||
#include <ctype.h> | #include <ctype.h> | ||||
#include <err.h> | |||||
#include <errno.h> | #include <errno.h> | ||||
#include <pthread.h> | #include <pthread.h> | ||||
#include <stdio.h> | #include <stdio.h> | ||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include <string.h> | #include <string.h> | ||||
#include <strings.h> | #include <strings.h> | ||||
#include <assert.h> | #include <assert.h> | ||||
#include <stdbool.h> | #include <stdbool.h> | ||||
Show All 19 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) | |||||
#define max(a, b) (((a) > (b)) ? (a) : (b)) | |||||
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; | ||||
case PCIBAR_ROM: | |||||
/* ROM emulation should be handled by pe_baraddr */ | |||||
if (pi->pi_bar[idx].addr != 0) | |||||
error = EFAULT; | |||||
else | |||||
error = 0; | |||||
break; | |||||
default: | default: | ||||
error = EINVAL; | error = EINVAL; | ||||
break; | break; | ||||
} | } | ||||
assert(error == 0); | assert(error == 0); | ||||
} | } | ||||
static void | static void | ||||
Show All 34 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); | ||||
switch (pi->pi_bar[idx].type) { | |||||
case PCIBAR_MEMHI64: | |||||
--update_idx; | |||||
case PCIBAR_IO: | |||||
case PCIBAR_MEM32: | |||||
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; | |||||
} | |||||
/* 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; | |||||
} | |||||
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) | if (decode) | ||||
register_bar(pi, update_idx); | |||||
break; | |||||
} | |||||
case PCIBAR_ROM: { | |||||
struct pcibar *bar = &pi->pi_bar[update_idx]; | |||||
if (decode && bar->lobits && bar->addr) | |||||
unregister_bar(pi, idx); | unregister_bar(pi, idx); | ||||
switch (type) { | pci_set_cfgdata32(pi, PCIR_BIOS, val); | ||||
/* Update enable bit */ | |||||
bar->lobits = val & PCIM_BIOS_ENABLE; | |||||
/* Update ROM location */ | |||||
if ((val & PCIM_BIOS_ADDR_MASK) == PCIM_BIOS_ADDR_MASK) { | |||||
/* guest wants to read size of ROM */ | |||||
bar->addr = 0; | |||||
} else { | |||||
bar->addr = val & PCIM_BIOS_ADDR_MASK; | |||||
} | |||||
if (decode && bar->lobits && bar->addr) | |||||
register_bar(pi, idx); | |||||
break; | |||||
} | |||||
case PCIBAR_NONE: | |||||
break; | |||||
default: | |||||
assert(0); | |||||
} | |||||
} | |||||
static uint32_t | |||||
read_bar_value(struct pci_devinst *pi, int coff, int bytes) | |||||
{ | |||||
uint8_t idx; | |||||
if (coff >= PCIR_BAR(0) && coff < PCIR_BAR(PCI_BARMAX + 1)) { | |||||
idx = (coff - PCIR_BAR(0)) / 4; | |||||
} else if (coff >= PCIR_BIOS && coff < PCIR_BIOS + 4) { | |||||
idx = PCI_BARMAX + 1; | |||||
} else { | |||||
warnx("%02x is no BAR", coff); | |||||
return 0; | |||||
} | |||||
uint8_t update_idx = idx; | |||||
uint64_t val; | |||||
switch (pi->pi_bar[idx].type) { | |||||
case PCIBAR_MEMHI64: | |||||
--update_idx; | |||||
/* fallthrough */ | |||||
case PCIBAR_IO: | case PCIBAR_IO: | ||||
case PCIBAR_MEM32: | case PCIBAR_MEM32: | ||||
pi->pi_bar[idx].addr = addr; | |||||
break; | |||||
case PCIBAR_MEM64: | case PCIBAR_MEM64: | ||||
pi->pi_bar[idx].addr &= ~0xffffffffUL; | val = pci_get_cfgdata32(pi, PCIR_BAR(idx)); | ||||
pi->pi_bar[idx].addr |= addr; | |||||
break; | break; | ||||
case PCIBAR_MEMHI64: | case PCIBAR_ROM: | ||||
pi->pi_bar[idx].addr &= 0xffffffff; | val = pci_get_cfgdata32(pi, PCIR_BIOS); | ||||
pi->pi_bar[idx].addr |= addr; | /* check if size should be returned instead of address of ROM */ | ||||
if ((val & PCIM_BIOS_ADDR_MASK) == PCIM_BIOS_ADDR_MASK) | |||||
val = ~0U; | |||||
break; | break; | ||||
case PCIBAR_NONE: | |||||
return 0; | |||||
default: | default: | ||||
warnx("%x is no valid BAR type", pi->pi_bar[idx].type); | |||||
return 0; | |||||
} | |||||
/* 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); | assert(0); | ||||
} | } | ||||
if (decode) | return val; | ||||
register_bar(pi, idx); | |||||
} | } | ||||
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; | if ((type != PCIBAR_ROM) && (idx < 0 || idx > PCI_BARMAX)) { | ||||
uint64_t *baseptr, limit, addr, mask, lobits, bar; | errx(4, "Illegal BAR idx"); | ||||
uint16_t cmd, enbit; | } else if ((type == PCIBAR_ROM) && (idx != PCI_ROM_IDX)) { | ||||
errx(4, "Illegal ROM idx"); | |||||
} | |||||
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) | size = max(size, 4); | ||||
size = 4; | } else if (type == PCIBAR_ROM) { | ||||
size = max(size, ~PCIM_BIOS_ADDR_MASK); | |||||
} else { | } else { | ||||
if (size < 16) | size = max(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; | ||||
case PCIBAR_ROM: | |||||
/* do not claim memory for ROM. OVMF will do it for us. */ | |||||
baseptr = NULL; | |||||
limit = 0; | |||||
mask = PCIM_BIOS_ADDR_MASK; | |||||
lobits = 0; | |||||
enbit = PCIM_CMD_MEMEN; | |||||
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); | ||||
} | } | ||||
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); | ||||
} | } | ||||
#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) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 396 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; | ||||
khngUnsubmitted Done Inline Actionskhng: | |||||
Done Inline ActionsUnused variables. khng: Unused variables. | |||||
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 557 Lines • ▼ Show 20 Lines | pci_emul_cmd_changed(struct pci_devinst *pi, uint16_t old) | ||||
new = pci_get_cfgdata16(pi, PCIR_COMMAND); | new = pci_get_cfgdata16(pi, PCIR_COMMAND); | ||||
changed = old ^ new; | changed = old ^ new; | ||||
/* | /* | ||||
* If the MMIO or I/O address space decoding has changed then | * If the MMIO or I/O address space decoding has changed then | ||||
* register/unregister all BARs that decode that address space. | * register/unregister all BARs that decode that address space. | ||||
*/ | */ | ||||
for (i = 0; i <= PCI_BARMAX; i++) { | for (i = 0; i <= PCI_BARMAX_WITH_ROM; i++) { | ||||
switch (pi->pi_bar[i].type) { | switch (pi->pi_bar[i].type) { | ||||
case PCIBAR_NONE: | case PCIBAR_NONE: | ||||
case PCIBAR_MEMHI64: | case PCIBAR_MEMHI64: | ||||
break; | break; | ||||
case PCIBAR_IO: | case PCIBAR_IO: | ||||
/* I/O address space decoding changed? */ | /* I/O address space decoding changed? */ | ||||
if (changed & PCIM_CMD_PORTEN) { | if (changed & PCIM_CMD_PORTEN) { | ||||
if (new & PCIM_CMD_PORTEN) | if (new & PCIM_CMD_PORTEN) | ||||
register_bar(pi, i); | register_bar(pi, i); | ||||
else | else | ||||
unregister_bar(pi, i); | unregister_bar(pi, i); | ||||
} | } | ||||
break; | break; | ||||
case PCIBAR_ROM: | |||||
/* skip (un-)register of ROM if it not enabled | |||||
*/ | |||||
if (pi->pi_bar[i].lobits == 0) | |||||
break; | |||||
case PCIBAR_MEM32: | case PCIBAR_MEM32: | ||||
case PCIBAR_MEM64: | case PCIBAR_MEM64: | ||||
/* MMIO address space decoding changed? */ | /* MMIO address space decoding changed? */ | ||||
if (changed & PCIM_CMD_MEMEN) { | if (changed & PCIM_CMD_MEMEN) { | ||||
if (new & PCIM_CMD_MEMEN) | if (new & PCIM_CMD_MEMEN) | ||||
register_bar(pi, i); | register_bar(pi, i); | ||||
else | else | ||||
unregister_bar(pi, i); | unregister_bar(pi, i); | ||||
▲ Show 20 Lines • Show All 41 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)) || | |||||
(coff >= PCIR_BIOS && coff < PCIR_BIOS + 4)) { | |||||
*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 and ROM registers | ||||
*/ | */ | ||||
if (coff >= PCIR_BAR(0) && coff < PCIR_BAR(PCI_BARMAX + 1)) { | if ((coff >= PCIR_BAR(0) && coff <= PCIR_BAR(PCI_BARMAX)) || | ||||
(coff >= PCIR_BIOS && coff < PCIR_BIOS + 4)) { | |||||
/* | /* | ||||
* 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; | |||||
mask = ~(pi->pi_bar[idx].size - 1); | |||||
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 | * coff is equal to PCIR_BIOS on ROM writes because | ||||
* it's 4-byte aligned | |||||
*/ | */ | ||||
if (addr != pi->pi_bar[idx].addr) { | if (coff == PCIR_BIOS) | ||||
update_bar_address(pi, addr, idx, | idx = PCI_ROM_IDX; | ||||
PCIBAR_IO); | else | ||||
} | idx = (coff - PCIR_BAR(0)) / 4; | ||||
break; | update_bar_address(pi, idx, *eax); | ||||
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 |