Changeset View
Changeset View
Standalone View
Standalone View
usr.sbin/bhyve/pci_emul.c
Show First 20 Lines • Show All 538 Lines • ▼ Show 20 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; | |||||
break; | |||||
case PCIBAR_MEM64: | case PCIBAR_MEM64: | ||||
pi->pi_bar[idx].addr &= ~0xffffffffUL; | { | ||||
pi->pi_bar[idx].addr |= addr; | struct pcibar *bar = &pi->pi_bar[update_idx]; | ||||
if (decode && (bar->addr != 0)) | |||||
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_MEMHI64: | } | ||||
pi->pi_bar[idx].addr &= 0xffffffff; | |||||
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; | ||||
} | |||||
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; | |||||
} | |||||
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; | int error; | ||||
uint64_t *baseptr, limit, addr, mask, lobits, bar; | uint64_t *baseptr, limit, addr, mask, lobits, bar; | ||||
uint16_t cmd, enbit; | uint16_t cmd, enbit; | ||||
▲ Show 20 Lines • Show All 61 Lines • ▼ Show 20 Lines | 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 = addr; | ||||
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 the BAR register in config space */ | ||||
bar = (addr & mask) | lobits; | bar = (addr & mask) | lobits; | ||||
pci_set_cfgdata32(pdi, PCIR_BAR(idx), bar); | pci_set_cfgdata32(pdi, PCIR_BAR(idx), bar); | ||||
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; | ||||
▲ Show 20 Lines • Show All 1,110 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); | |||||
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); | |||||
update_bar_address(pi, idx, *eax); | |||||
} 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 434 Lines • Show Last 20 Lines |