Changeset View
Changeset View
Standalone View
Standalone View
usr.sbin/bhyve/pci_emul.c
Show First 20 Lines • Show All 95 Lines • ▼ Show 20 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 | ||||||||||
▲ Show 20 Lines • Show All 340 Lines • ▼ Show 20 Lines | |||||||||||
/* | /* | ||||||||||
* Register (or unregister) the MMIO or I/O region associated with the BAR | * Register (or unregister) the MMIO or I/O region associated with the BAR | ||||||||||
* register 'idx' of an emulated pci device. | * register 'idx' of an emulated pci device. | ||||||||||
*/ | */ | ||||||||||
static void | static void | ||||||||||
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; | |||||||||||
int error; | int error; | ||||||||||
struct inout_port iop; | struct inout_port iop; | ||||||||||
struct mem_range mr; | struct mem_range mr; | ||||||||||
pe = pi->pi_d; | |||||||||||
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 20 Lines • Show All 68 Lines • ▼ Show 20 Lines | update_bar_address(struct pci_devinst *pi, uint64_t addr, int idx, int type) | ||||||||||
default: | default: | ||||||||||
assert(0); | assert(0); | ||||||||||
} | } | ||||||||||
if (decode) | if (decode) | ||||||||||
register_bar(pi, idx); | register_bar(pi, idx); | ||||||||||
} | } | ||||||||||
/* add BAR to BAR-List */ | |||||||||||
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)); | |||||||||||
markjUnsubmitted Done Inline Actions
markj: | |||||||||||
newBar->pdi = pdi; | |||||||||||
newBar->idx = idx; | |||||||||||
newBar->type = type; | |||||||||||
newBar->size = size; | |||||||||||
if (pci_bars == NULL) { | |||||||||||
/* first BAR */ | |||||||||||
pci_bars = newBar; | |||||||||||
} else { | |||||||||||
Done Inline Actions
Then the line doesn't need to be split. markj: Then the line doesn't need to be split. | |||||||||||
struct pcibarlist *bar = pci_bars; | |||||||||||
Done Inline Actions
markj: | |||||||||||
struct pcibarlist *lastBar = NULL; | |||||||||||
do { | |||||||||||
if (bar->size < size) | |||||||||||
break; | |||||||||||
lastBar = bar; | |||||||||||
bar = bar->next; | |||||||||||
Done Inline ActionsRather than having comments like this, I think it'd be more useful to say that this code is creating a list of BARs sorted by size, with larger BARs appearing first, to reduce fragmentation in the MMIO space. markj: Rather than having comments like this, I think it'd be more useful to say that this code is… | |||||||||||
} 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, bar; | |||||||||||
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; | ||||||||||
Done Inline Actions
markj: | |||||||||||
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; | ||||||||||
Done Inline ActionsIMO it'd be cleaner to pass the parameters individually. That would also be consistent with pci_emul_alloc_bar(). markj: IMO it'd be cleaner to pass the parameters individually. That would also be consistent with… | |||||||||||
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 (128MB currently). | ||||||||||
*/ | */ | ||||||||||
if (size > 128 * 1024 * 1024) { | if (size > 128 * 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 { | } else { | ||||||||||
baseptr = &pci_emul_membase32; | baseptr = &pci_emul_membase32; | ||||||||||
limit = PCI_EMUL_MEMLIMIT32; | limit = pci_emul_memlim32; | ||||||||||
Done Inline ActionsLong line. markj: Long line. | |||||||||||
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; | ||||||||||
} | } | ||||||||||
enbit = PCIM_CMD_MEMEN; | enbit = PCIM_CMD_MEMEN; | ||||||||||
break; | break; | ||||||||||
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); | ||||||||||
} | } | ||||||||||
▲ Show 20 Lines • Show All 443 Lines • ▼ Show 20 Lines | init_pci(struct vmctx *ctx) | ||||||||||
struct slotinfo *si; | struct slotinfo *si; | ||||||||||
struct funcinfo *fi; | struct funcinfo *fi; | ||||||||||
size_t lowmem; | size_t lowmem; | ||||||||||
uint64_t cpu_maxphysaddr, pci_emul_memresv64; | uint64_t cpu_maxphysaddr, pci_emul_memresv64; | ||||||||||
u_int regs[4]; | 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); | do_cpuid(0x80000008, regs); | ||||||||||
cpu_maxphysaddr = 1ULL << (regs[0] & 0xff); | cpu_maxphysaddr = 1ULL << (regs[0] & 0xff); | ||||||||||
if (cpu_maxphysaddr > VM_MAXUSER_ADDRESS_LA48) | if (cpu_maxphysaddr > VM_MAXUSER_ADDRESS_LA48) | ||||||||||
cpu_maxphysaddr = VM_MAXUSER_ADDRESS_LA48; | cpu_maxphysaddr = VM_MAXUSER_ADDRESS_LA48; | ||||||||||
pci_emul_memresv64 = cpu_maxphysaddr / 4; | pci_emul_memresv64 = cpu_maxphysaddr / 4; | ||||||||||
/* | /* | ||||||||||
* Max power of 2 that is less then | * Max power of 2 that is less then | ||||||||||
Show All 9 Lines | for (bus = 0; bus < MAXBUSES; 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]; | ||||||||||
if (fi->fi_name == NULL) | if (fi->fi_name == NULL) | ||||||||||
continue; | continue; | ||||||||||
pde = pci_emul_finddev(fi->fi_name); | pde = pci_emul_finddev(fi->fi_name); | ||||||||||
assert(pde != NULL); | assert(pde != NULL); | ||||||||||
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) { | |||||||||||
Done Inline Actions
markj: | |||||||||||
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 | ||||||||||
Done Inline ActionsSeems it would be simpler to write: TAILQ_FOREACH_SAFE(bar, &pci_bars, chain, tmp) { pci_emul_assign_bar(bar); free(bar); } TAILQ_INIT(&pci_bars); markj: Seems it would be simpler to write:
```
TAILQ_FOREACH_SAFE(bar, &pci_bars, chain, tmp) {… | |||||||||||
* 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; | ||||||||||
pci_emul_membase32 += BUSMEM_ROUNDUP; | pci_emul_membase32 += BUSMEM_ROUNDUP; | ||||||||||
▲ Show 20 Lines • Show All 1,197 Lines • Show Last 20 Lines |