Changeset View
Changeset View
Standalone View
Standalone View
usr.sbin/bhyve/pci_emul.c
| Show First 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | ||||||||||
| #include <machine/vmm_snapshot.h> | #include <machine/vmm_snapshot.h> | |||||||||
| #include <vmmapi.h> | #include <vmmapi.h> | |||||||||
| #include "acpi.h" | #include "acpi.h" | |||||||||
| #include "bhyverun.h" | #include "bhyverun.h" | |||||||||
| #include "config.h" | #include "config.h" | |||||||||
| #include "debug.h" | #include "debug.h" | |||||||||
| #include "inout.h" | #include "inout.h" | |||||||||
| #include "ioapic.h" | #ifdef __amd64__ | |||||||||
| #include "amd64/ioapic.h" | ||||||||||
| #endif | ||||||||||
| #include "mem.h" | #include "mem.h" | |||||||||
| #include "pci_emul.h" | #include "pci_emul.h" | |||||||||
| #include "pci_irq.h" | #ifdef __amd64__ | |||||||||
| #include "pci_lpc.h" | #include "amd64/pci_irq.h" | |||||||||
| #include "amd64/pci_lpc.h" | ||||||||||
| #endif | ||||||||||
| #include "pci_passthru.h" | #include "pci_passthru.h" | |||||||||
| #include "qemu_fwcfg.h" | #include "qemu_fwcfg.h" | |||||||||
| #define CONF1_ADDR_PORT 0x0cf8 | #define CONF1_ADDR_PORT 0x0cf8 | |||||||||
| #define CONF1_DATA_PORT 0x0cfc | #define CONF1_DATA_PORT 0x0cfc | |||||||||
| #define CONF1_ENABLE 0x80000000ul | #define CONF1_ENABLE 0x80000000ul | |||||||||
| ▲ Show 20 Lines • Show All 70 Lines • ▼ Show 20 Lines | ||||||||||
| /* | /* | |||||||||
| * OVMF always uses 0xC0000000 as base address for 32 bit PCI MMIO. Don't | * OVMF always uses 0xC0000000 as base address for 32 bit PCI MMIO. Don't | |||||||||
| * change this address without changing it in OVMF. | * change this address without changing it in OVMF. | |||||||||
| */ | */ | |||||||||
| #define PCI_EMUL_MEMBASE32 0xC0000000 | #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) | #define PCI_EMUL_MEMSIZE64 (32*GB) | |||||||||
| static struct pci_devemu *pci_emul_finddev(const char *name); | #ifdef __amd64__ | |||||||||
| 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); | |||||||||
| #endif | ||||||||||
| static struct pci_devemu *pci_emul_finddev(const char *name); | ||||||||||
| static void pci_cfgrw(int in, int bus, int slot, int func, int coff, | static void pci_cfgrw(int in, int bus, int slot, int func, int coff, | |||||||||
| int bytes, uint32_t *val); | int bytes, uint32_t *val); | |||||||||
| static __inline void | static __inline void | |||||||||
| CFGWRITE(struct pci_devinst *pi, int coff, uint32_t val, int bytes) | CFGWRITE(struct pci_devinst *pi, int coff, uint32_t val, int bytes) | |||||||||
| { | { | |||||||||
| if (bytes == 1) | if (bytes == 1) | |||||||||
| ▲ Show 20 Lines • Show All 899 Lines • ▼ Show 20 Lines | pci_emul_init(struct vmctx *ctx, struct pci_devemu *pde, int bus, int slot, | |||||||||
| int err; | int err; | |||||||||
| pdi = calloc(1, sizeof(struct pci_devinst)); | pdi = calloc(1, sizeof(struct pci_devinst)); | |||||||||
| pdi->pi_vmctx = ctx; | pdi->pi_vmctx = ctx; | |||||||||
| pdi->pi_bus = bus; | pdi->pi_bus = bus; | |||||||||
| pdi->pi_slot = slot; | pdi->pi_slot = slot; | |||||||||
| pdi->pi_func = func; | pdi->pi_func = func; | |||||||||
| #ifdef __amd64__ | ||||||||||
| pthread_mutex_init(&pdi->pi_lintr.lock, NULL); | pthread_mutex_init(&pdi->pi_lintr.lock, NULL); | |||||||||
| pdi->pi_lintr.pin = 0; | pdi->pi_lintr.pin = 0; | |||||||||
| pdi->pi_lintr.state = IDLE; | pdi->pi_lintr.state = IDLE; | |||||||||
| pdi->pi_lintr.pirq_pin = 0; | pdi->pi_lintr.pirq_pin = 0; | |||||||||
| pdi->pi_lintr.ioapic_irq = 0; | pdi->pi_lintr.ioapic_irq = 0; | |||||||||
| #endif | ||||||||||
| pdi->pi_d = pde; | pdi->pi_d = pde; | |||||||||
| snprintf(pdi->pi_name, PI_NAMESZ, "%s@pci.%d.%d.%d", pde->pe_emu, bus, | snprintf(pdi->pi_name, PI_NAMESZ, "%s@pci.%d.%d.%d", pde->pe_emu, bus, | |||||||||
| slot, func); | slot, func); | |||||||||
| /* Disable legacy interrupts */ | /* Disable legacy interrupts */ | |||||||||
| pci_set_cfgdata8(pdi, PCIR_INTLINE, 255); | pci_set_cfgdata8(pdi, PCIR_INTLINE, 255); | |||||||||
| pci_set_cfgdata8(pdi, PCIR_INTPIN, 0); | pci_set_cfgdata8(pdi, PCIR_INTPIN, 0); | |||||||||
| ▲ Show 20 Lines • Show All 121 Lines • ▼ Show 20 Lines | if (off == 2 && bytes == 2) { | |||||||||
| rwmask = PCIM_MSIXCTRL_MSIX_ENABLE | PCIM_MSIXCTRL_FUNCTION_MASK; | rwmask = PCIM_MSIXCTRL_MSIX_ENABLE | PCIM_MSIXCTRL_FUNCTION_MASK; | |||||||||
| msgctrl = pci_get_cfgdata16(pi, offset); | msgctrl = pci_get_cfgdata16(pi, offset); | |||||||||
| msgctrl &= ~rwmask; | msgctrl &= ~rwmask; | |||||||||
| msgctrl |= val & rwmask; | msgctrl |= val & rwmask; | |||||||||
| val = msgctrl; | val = msgctrl; | |||||||||
| pi->pi_msix.enabled = val & PCIM_MSIXCTRL_MSIX_ENABLE; | pi->pi_msix.enabled = val & PCIM_MSIXCTRL_MSIX_ENABLE; | |||||||||
| pi->pi_msix.function_mask = val & PCIM_MSIXCTRL_FUNCTION_MASK; | pi->pi_msix.function_mask = val & PCIM_MSIXCTRL_FUNCTION_MASK; | |||||||||
| #ifdef __amd64__ | ||||||||||
| pci_lintr_update(pi); | pci_lintr_update(pi); | |||||||||
| #endif | ||||||||||
| } | } | |||||||||
| CFGWRITE(pi, offset, val, bytes); | CFGWRITE(pi, offset, val, bytes); | |||||||||
| } | } | |||||||||
| static void | static void | |||||||||
| msicap_cfgwrite(struct pci_devinst *pi, int capoff, int offset, | msicap_cfgwrite(struct pci_devinst *pi, int capoff, int offset, | |||||||||
| int bytes, uint32_t val) | int bytes, uint32_t val) | |||||||||
| Show All 25 Lines | msicap_cfgwrite(struct pci_devinst *pi, int capoff, int offset, | |||||||||
| pi->pi_msi.enabled = msgctrl & PCIM_MSICTRL_MSI_ENABLE ? 1 : 0; | pi->pi_msi.enabled = msgctrl & PCIM_MSICTRL_MSI_ENABLE ? 1 : 0; | |||||||||
| if (pi->pi_msi.enabled) { | if (pi->pi_msi.enabled) { | |||||||||
| pi->pi_msi.addr = addrlo; | pi->pi_msi.addr = addrlo; | |||||||||
| pi->pi_msi.msg_data = msgdata; | pi->pi_msi.msg_data = msgdata; | |||||||||
| pi->pi_msi.maxmsgnum = 1 << (mme >> 4); | pi->pi_msi.maxmsgnum = 1 << (mme >> 4); | |||||||||
| } else { | } else { | |||||||||
| pi->pi_msi.maxmsgnum = 0; | pi->pi_msi.maxmsgnum = 0; | |||||||||
| } | } | |||||||||
| #ifdef __amd64__ | ||||||||||
| pci_lintr_update(pi); | pci_lintr_update(pi); | |||||||||
| #endif | ||||||||||
| } | } | |||||||||
| static void | static void | |||||||||
| pciecap_cfgwrite(struct pci_devinst *pi, int capoff __unused, int offset, | pciecap_cfgwrite(struct pci_devinst *pi, int capoff __unused, int offset, | |||||||||
| int bytes, uint32_t val) | int bytes, uint32_t val) | |||||||||
| { | { | |||||||||
| /* XXX don't write to the readonly parts */ | /* XXX don't write to the readonly parts */ | |||||||||
| ▲ Show 20 Lines • Show All 276 Lines • ▼ Show 20 Lines | for (bus = 0; bus < MAXBUSES; bus++) { | |||||||||
| bi->memlimit32 = pci_emul_membase32; | bi->memlimit32 = pci_emul_membase32; | |||||||||
| pci_emul_membase64 += BUSMEM64_ROUNDUP; | pci_emul_membase64 += BUSMEM64_ROUNDUP; | |||||||||
| pci_emul_membase64 = roundup2(pci_emul_membase64, | pci_emul_membase64 = roundup2(pci_emul_membase64, | |||||||||
| BUSMEM64_ROUNDUP); | BUSMEM64_ROUNDUP); | |||||||||
| bi->memlimit64 = pci_emul_membase64; | bi->memlimit64 = pci_emul_membase64; | |||||||||
| } | } | |||||||||
| #ifdef __amd64__ | ||||||||||
| /* | /* | |||||||||
| * PCI backends are initialized before routing INTx interrupts | * PCI backends are initialized before routing INTx interrupts | |||||||||
| * so that LPC devices are able to reserve ISA IRQs before | * so that LPC devices are able to reserve ISA IRQs before | |||||||||
| * routing PIRQ pins. | * routing PIRQ pins. | |||||||||
| */ | */ | |||||||||
| for (bus = 0; bus < MAXBUSES; bus++) { | for (bus = 0; bus < MAXBUSES; bus++) { | |||||||||
| if ((bi = pci_businfo[bus]) == NULL) | if ((bi = pci_businfo[bus]) == NULL) | |||||||||
| continue; | continue; | |||||||||
| 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_devi == NULL) | if (fi->fi_devi == NULL) | |||||||||
| continue; | continue; | |||||||||
| pci_lintr_route(fi->fi_devi); | pci_lintr_route(fi->fi_devi); | |||||||||
| } | } | |||||||||
| } | } | |||||||||
| } | } | |||||||||
| lpc_pirq_routed(); | lpc_pirq_routed(); | |||||||||
| #endif | ||||||||||
| if ((error = init_bootorder()) != 0) { | if ((error = init_bootorder()) != 0) { | |||||||||
| warnx("%s: Unable to init bootorder", __func__); | warnx("%s: Unable to init bootorder", __func__); | |||||||||
| return (error); | return (error); | |||||||||
| } | } | |||||||||
| /* | /* | |||||||||
| * The guest physical memory map looks like the following: | * The guest physical memory map looks like the following: | |||||||||
| Show All 27 Lines | #endif | |||||||||
| mr.size = PCI_EMUL_ECFG_SIZE; | mr.size = PCI_EMUL_ECFG_SIZE; | |||||||||
| mr.handler = pci_emul_ecfg_handler; | mr.handler = pci_emul_ecfg_handler; | |||||||||
| error = register_mem(&mr); | error = register_mem(&mr); | |||||||||
| assert(error == 0); | assert(error == 0); | |||||||||
| return (0); | return (0); | |||||||||
| } | } | |||||||||
| #ifdef __amd64__ | ||||||||||
| static void | static void | |||||||||
| pci_apic_prt_entry(int bus __unused, int slot, int pin, int pirq_pin __unused, | pci_apic_prt_entry(int bus __unused, int slot, int pin, int pirq_pin __unused, | |||||||||
| int ioapic_irq, void *arg __unused) | int ioapic_irq, void *arg __unused) | |||||||||
| { | { | |||||||||
| dsdt_line(" Package ()"); | dsdt_line(" Package ()"); | |||||||||
| dsdt_line(" {"); | dsdt_line(" {"); | |||||||||
| dsdt_line(" 0x%X,", slot << 16 | 0xffff); | dsdt_line(" 0x%X,", slot << 16 | 0xffff); | |||||||||
| Show All 16 Lines | pci_pirq_prt_entry(int bus __unused, int slot, int pin, int pirq_pin, | |||||||||
| dsdt_line(" {"); | dsdt_line(" {"); | |||||||||
| dsdt_line(" 0x%X,", slot << 16 | 0xffff); | dsdt_line(" 0x%X,", slot << 16 | 0xffff); | |||||||||
| dsdt_line(" 0x%02X,", pin - 1); | dsdt_line(" 0x%02X,", pin - 1); | |||||||||
| dsdt_line(" %s,", name); | dsdt_line(" %s,", name); | |||||||||
| dsdt_line(" 0x00"); | dsdt_line(" 0x00"); | |||||||||
| dsdt_line(" },"); | dsdt_line(" },"); | |||||||||
| free(name); | free(name); | |||||||||
| } | } | |||||||||
| #endif | ||||||||||
| /* | /* | |||||||||
| * A bhyve virtual machine has a flat PCI hierarchy with a root port | * A bhyve virtual machine has a flat PCI hierarchy with a root port | |||||||||
| * corresponding to each PCI bus. | * corresponding to each PCI bus. | |||||||||
| */ | */ | |||||||||
| static void | static void | |||||||||
| pci_bus_write_dsdt(int bus) | pci_bus_write_dsdt(int bus) | |||||||||
| { | { | |||||||||
| struct businfo *bi; | struct businfo *bi; | |||||||||
| struct slotinfo *si; | struct slotinfo *si; | |||||||||
| struct pci_devinst *pi; | struct pci_devinst *pi; | |||||||||
| int count, func, slot; | int func, slot; | |||||||||
| /* | /* | |||||||||
| * If there are no devices on this 'bus' then just return. | * If there are no devices on this 'bus' then just return. | |||||||||
| */ | */ | |||||||||
| if ((bi = pci_businfo[bus]) == NULL) { | if ((bi = pci_businfo[bus]) == NULL) { | |||||||||
| /* | /* | |||||||||
| * Bus 0 is special because it decodes the I/O ports used | * Bus 0 is special because it decodes the I/O ports used | |||||||||
| * for PCI config space access even if there are no devices | * for PCI config space access even if there are no devices | |||||||||
| ▲ Show 20 Lines • Show All 86 Lines • ▼ Show 20 Lines | pci_bus_write_dsdt(int bus) | |||||||||
| dsdt_line(" 0x%016lX, // Range Maximum\n", | dsdt_line(" 0x%016lX, // Range Maximum\n", | |||||||||
| bi->memlimit64 - 1); | bi->memlimit64 - 1); | |||||||||
| dsdt_line(" 0x0000000000000000, // Translation Offset"); | dsdt_line(" 0x0000000000000000, // Translation Offset"); | |||||||||
| dsdt_line(" 0x%016lX, // Length\n", | dsdt_line(" 0x%016lX, // Length\n", | |||||||||
| bi->memlimit64 - bi->membase64); | bi->memlimit64 - bi->membase64); | |||||||||
| dsdt_line(" ,, , AddressRangeMemory, TypeStatic)"); | dsdt_line(" ,, , AddressRangeMemory, TypeStatic)"); | |||||||||
| dsdt_line(" })"); | dsdt_line(" })"); | |||||||||
| count = pci_count_lintr(bus); | #ifdef __amd64__ | |||||||||
| if (count != 0) { | if (pci_count_lintr(bus) != 0) { | |||||||||
jhbUnsubmitted Done Inline Actions
jhb: | ||||||||||
| dsdt_indent(2); | dsdt_indent(2); | |||||||||
| dsdt_line("Name (PPRT, Package ()"); | dsdt_line("Name (PPRT, Package ()"); | |||||||||
| dsdt_line("{"); | dsdt_line("{"); | |||||||||
| pci_walk_lintr(bus, pci_pirq_prt_entry, NULL); | pci_walk_lintr(bus, pci_pirq_prt_entry, NULL); | |||||||||
| dsdt_line("})"); | dsdt_line("})"); | |||||||||
| dsdt_line("Name (APRT, Package ()"); | dsdt_line("Name (APRT, Package ()"); | |||||||||
| dsdt_line("{"); | dsdt_line("{"); | |||||||||
| pci_walk_lintr(bus, pci_apic_prt_entry, NULL); | pci_walk_lintr(bus, pci_apic_prt_entry, NULL); | |||||||||
| dsdt_line("})"); | dsdt_line("})"); | |||||||||
| dsdt_line("Method (_PRT, 0, NotSerialized)"); | dsdt_line("Method (_PRT, 0, NotSerialized)"); | |||||||||
| dsdt_line("{"); | dsdt_line("{"); | |||||||||
| dsdt_line(" If (PICM)"); | dsdt_line(" If (PICM)"); | |||||||||
| dsdt_line(" {"); | dsdt_line(" {"); | |||||||||
| dsdt_line(" Return (APRT)"); | dsdt_line(" Return (APRT)"); | |||||||||
| dsdt_line(" }"); | dsdt_line(" }"); | |||||||||
| dsdt_line(" Else"); | dsdt_line(" Else"); | |||||||||
| dsdt_line(" {"); | dsdt_line(" {"); | |||||||||
| dsdt_line(" Return (PPRT)"); | dsdt_line(" Return (PPRT)"); | |||||||||
| dsdt_line(" }"); | dsdt_line(" }"); | |||||||||
| dsdt_line("}"); | dsdt_line("}"); | |||||||||
| dsdt_unindent(2); | dsdt_unindent(2); | |||||||||
| } | } | |||||||||
| #endif | ||||||||||
| dsdt_indent(2); | dsdt_indent(2); | |||||||||
| 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++) { | |||||||||
| pi = si->si_funcs[func].fi_devi; | pi = si->si_funcs[func].fi_devi; | |||||||||
| if (pi != NULL && pi->pi_d->pe_write_dsdt != NULL) | if (pi != NULL && pi->pi_d->pe_write_dsdt != NULL) | |||||||||
| pi->pi_d->pe_write_dsdt(pi); | pi->pi_d->pe_write_dsdt(pi); | |||||||||
| ▲ Show 20 Lines • Show All 79 Lines • ▼ Show 20 Lines | ||||||||||
| { | { | |||||||||
| if (pci_msi_enabled(pi) && index < pci_msi_maxmsgnum(pi)) { | if (pci_msi_enabled(pi) && index < pci_msi_maxmsgnum(pi)) { | |||||||||
| vm_lapic_msi(pi->pi_vmctx, pi->pi_msi.addr, | vm_lapic_msi(pi->pi_vmctx, pi->pi_msi.addr, | |||||||||
| pi->pi_msi.msg_data + index); | pi->pi_msi.msg_data + index); | |||||||||
| } | } | |||||||||
| } | } | |||||||||
| #ifdef __amd64__ | ||||||||||
| static bool | static bool | |||||||||
| pci_lintr_permitted(struct pci_devinst *pi) | pci_lintr_permitted(struct pci_devinst *pi) | |||||||||
| { | { | |||||||||
| uint16_t cmd; | uint16_t cmd; | |||||||||
| cmd = pci_get_cfgdata16(pi, PCIR_COMMAND); | cmd = pci_get_cfgdata16(pi, PCIR_COMMAND); | |||||||||
| return (!(pi->pi_msi.enabled || pi->pi_msix.enabled || | return (!(pi->pi_msi.enabled || pi->pi_msix.enabled || | |||||||||
| (cmd & PCIM_CMD_INTxDIS))); | (cmd & PCIM_CMD_INTxDIS))); | |||||||||
| ▲ Show 20 Lines • Show All 144 Lines • ▼ Show 20 Lines | for (slot = 0; slot < MAXSLOTS; slot++) { | |||||||||
| for (pin = 0; pin < 4; pin++) { | for (pin = 0; pin < 4; pin++) { | |||||||||
| ii = &si->si_intpins[pin]; | ii = &si->si_intpins[pin]; | |||||||||
| if (ii->ii_count != 0) | if (ii->ii_count != 0) | |||||||||
| cb(bus, slot, pin + 1, ii->ii_pirq_pin, | cb(bus, slot, pin + 1, ii->ii_pirq_pin, | |||||||||
| ii->ii_ioapic_irq, arg); | ii->ii_ioapic_irq, arg); | |||||||||
| } | } | |||||||||
| } | } | |||||||||
| } | } | |||||||||
| #endif /* __amd64__ */ | ||||||||||
| /* | /* | |||||||||
| * Return 1 if the emulated device in 'slot' is a multi-function device. | * Return 1 if the emulated device in 'slot' is a multi-function device. | |||||||||
| * Return 0 otherwise. | * Return 0 otherwise. | |||||||||
| */ | */ | |||||||||
| static int | static int | |||||||||
| pci_emul_is_mfdev(int bus, int slot) | pci_emul_is_mfdev(int bus, int slot) | |||||||||
| { | { | |||||||||
| ▲ Show 20 Lines • Show All 88 Lines • ▼ Show 20 Lines | switch (pi->pi_bar[i].type) { | |||||||||
| unregister_bar(pi, i); | unregister_bar(pi, i); | |||||||||
| } | } | |||||||||
| break; | break; | |||||||||
| default: | default: | |||||||||
| assert(0); | assert(0); | |||||||||
| } | } | |||||||||
| } | } | |||||||||
| #ifdef __amd64__ | ||||||||||
| /* | /* | |||||||||
| * If INTx has been unmasked and is pending, assert the | * If INTx has been unmasked and is pending, assert the | |||||||||
| * interrupt. | * interrupt. | |||||||||
| */ | */ | |||||||||
| pci_lintr_update(pi); | pci_lintr_update(pi); | |||||||||
| #endif | ||||||||||
| } | } | |||||||||
| static void | static void | |||||||||
| pci_emul_cmdsts_write(struct pci_devinst *pi, int coff, uint32_t new, int bytes) | pci_emul_cmdsts_write(struct pci_devinst *pi, int coff, uint32_t new, int bytes) | |||||||||
| { | { | |||||||||
| int rshift; | int rshift; | |||||||||
| uint32_t cmd, old, readonly; | uint32_t cmd, old, readonly; | |||||||||
| ▲ Show 20 Lines • Show All 560 Lines • Show Last 20 Lines | ||||||||||