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) | ||||||||||||||
#define max(a, b) (((a) > (b)) ? (a) : (b)) | ||||||||||||||
#define min(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_gsmbase; | ||||||||||||||
static uint64_t pci_emul_gsmlim; | ||||||||||||||
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_MEMBASE32 0xC0000000 | ||||||||||||||
#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); | |||||||||||||
} | } | |||||||||||||
#define ALIGNED_SUBTRACT(base, size) ((base - size) & ~(size - 1)) | ||||||||||||||
int | ||||||||||||||
pci_emul_adjust_gsmbase(struct vmctx *ctx, uint64_t size) | ||||||||||||||
{ | ||||||||||||||
if (size > pci_emul_gsmbase) | ||||||||||||||
return (-1); | ||||||||||||||
pci_emul_gsmbase = ALIGNED_SUBTRACT(pci_emul_gsmbase, size); | ||||||||||||||
vm_set_lowmem_limit( | ||||||||||||||
ctx, min(vm_get_lowmem_limit(ctx), pci_emul_gsmbase)); | ||||||||||||||
return (0); | ||||||||||||||
} | ||||||||||||||
uint64_t | ||||||||||||||
pci_emul_alloc_gsm(uint64_t size) | ||||||||||||||
{ | ||||||||||||||
if (size > pci_emul_gsmlim) | ||||||||||||||
return 0; | ||||||||||||||
uint64_t addr = ALIGNED_SUBTRACT(pci_emul_gsmlim, size); | ||||||||||||||
if (addr < pci_emul_gsmbase) | ||||||||||||||
return 0; | ||||||||||||||
pci_emul_gsmlim = 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 389 Lines • ▼ Show 20 Lines | ||||||||||||||
uint64_t | uint64_t | |||||||||||||
pci_ecfg_base(void) | pci_ecfg_base(void) | |||||||||||||
{ | { | |||||||||||||
return (PCI_EMUL_ECFG_BASE); | return (PCI_EMUL_ECFG_BASE); | |||||||||||||
} | } | |||||||||||||
int | ||||||||||||||
pci_early_quirks(struct vmctx *ctx) | ||||||||||||||
{ | ||||||||||||||
pci_emul_gsmbase = PCI_EMUL_MEMBASE32; | ||||||||||||||
pci_emul_gsmlim = PCI_EMUL_MEMBASE32; | ||||||||||||||
for (int bus = 0; bus < MAXBUSES; ++bus) { | ||||||||||||||
for (int slot = 0; slot < MAXSLOTS; ++slot) { | ||||||||||||||
for (int func = 0; func < MAXFUNCS; ++func) { | ||||||||||||||
char node_name[sizeof("pci.XXX.XX.X")]; | ||||||||||||||
snprintf(node_name, sizeof(node_name), | ||||||||||||||
"pci.%d.%d.%d", bus, slot, func); | ||||||||||||||
nvlist_t *nvl = find_config_node(node_name); | ||||||||||||||
if (nvl == NULL) | ||||||||||||||
continue; | ||||||||||||||
const char *emul = get_config_value_node( | ||||||||||||||
nvl, "device"); | ||||||||||||||
struct pci_devemu *pde = pci_emul_finddev(emul); | ||||||||||||||
if (pde == NULL) { | ||||||||||||||
EPRINTLN("pci slot %d:%d:%d: unknown " | ||||||||||||||
"device \"%s\"", | ||||||||||||||
bus, slot, func, emul); | ||||||||||||||
return (EINVAL); | ||||||||||||||
} | ||||||||||||||
if (pde->pe_early_quirks) { | ||||||||||||||
const int error = pde->pe_early_quirks( | ||||||||||||||
ctx, nvl); | ||||||||||||||
if (error) | ||||||||||||||
return (error); | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
return (0); | ||||||||||||||
} | ||||||||||||||
#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_membase32 = vm_get_lowmem_limit(ctx); | pci_emul_iolim = PCI_EMUL_IOLIMIT; | |||||||||||||
do_cpuid(0x80000008, regs); | pci_emul_membase32 = PCI_EMUL_MEMBASE32; | |||||||||||||
cpu_maxphysaddr = 1ULL << (regs[0] & 0xff); | pci_emul_memlim32 = PCI_EMUL_MEMLIMIT32; | |||||||||||||
if (cpu_maxphysaddr > VM_MAXUSER_ADDRESS_LA48) | ||||||||||||||
cpu_maxphysaddr = VM_MAXUSER_ADDRESS_LA48; | ||||||||||||||
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; | ||||||||||||||
pci_emul_membase64 = 4 * GB + vm_get_highmem_size(ctx); | ||||||||||||||
pci_emul_membase64 = ALIGN_VALUE( | ||||||||||||||
pci_emul_membase64, PCI_EMUL_MEMSIZE64); | ||||||||||||||
pci_emul_memlim64 = pci_emul_membase64 + PCI_EMUL_MEMSIZE64; | ||||||||||||||
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 All 29 Lines | for (bus = 0; bus < MAXBUSES; bus++) { | |||||||||||||
} | } | |||||||||||||
} | } | |||||||||||||
lpc_pirq_routed(); | lpc_pirq_routed(); | |||||||||||||
/* | /* | |||||||||||||
* The guest physical memory map looks like the following: | * The guest physical memory map looks like the following: | |||||||||||||
* [0, lowmem) guest system memory | * [0, lowmem) guest system memory | |||||||||||||
* [lowmem, lowmem_limit) memory hole (may be absent) | * [lowmem, lowmem_limit) memory hole (may be absent) | |||||||||||||
* [lowmem_limit, 0xE0000000) PCI hole (32-bit BAR allocation) | * [lowmem_limit, 0xC0000000) Graphics Stolen Memory (may be absent) | |||||||||||||
* [0xC0000000, 0xE0000000) PCI hole (32-bit BAR allocation) | ||||||||||||||
* [0xE0000000, 0xF0000000) PCI extended config window | * [0xE0000000, 0xF0000000) PCI extended config window | |||||||||||||
* [0xF0000000, 4GB) LAPIC, IOAPIC, HPET, firmware | * [0xF0000000, 4GB) LAPIC, IOAPIC, HPET, firmware | |||||||||||||
* [4GB, 4GB + highmem) | * [4GB, 4GB + highmem) | |||||||||||||
*/ | */ | |||||||||||||
/* | /* | |||||||||||||
* Accesses to memory addresses that are not allocated to system | * Accesses to memory addresses that are not allocated to system | |||||||||||||
* memory or PCI devices return 0xff's. | * memory or PCI devices return 0xff's. | |||||||||||||
▲ Show 20 Lines • Show All 582 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 |