--- //depot/vendor/freebsd/src/lib/libvmmapi/vmmapi.c +++ //depot/user/jhb/bhyve/lib/libvmmapi/vmmapi.c @@ -454,6 +454,13 @@ } int +vm_ioapic_pincount(struct vmctx *ctx, int *pincount) +{ + + return (ioctl(ctx->fd, VM_IOAPIC_PINCOUNT, pincount)); +} + +int vm_inject_nmi(struct vmctx *ctx, int vcpu) { struct vm_nmi vmnmi; --- //depot/vendor/freebsd/src/lib/libvmmapi/vmmapi.h +++ //depot/user/jhb/bhyve/lib/libvmmapi/vmmapi.h @@ -72,6 +72,7 @@ int vm_ioapic_assert_irq(struct vmctx *ctx, int irq); int vm_ioapic_deassert_irq(struct vmctx *ctx, int irq); int vm_ioapic_pulse_irq(struct vmctx *ctx, int irq); +int vm_ioapic_pincount(struct vmctx *ctx, int *pincount); int vm_inject_nmi(struct vmctx *ctx, int vcpu); int vm_capability_name2type(const char *capname); const char *vm_capability_type2name(int type); --- //depot/vendor/freebsd/src/sys/amd64/include/vmm_dev.h +++ //depot/user/jhb/bhyve/sys/amd64/include/vmm_dev.h @@ -181,7 +181,8 @@ IOCNUM_IOAPIC_DEASSERT_IRQ = 34, IOCNUM_IOAPIC_PULSE_IRQ = 35, IOCNUM_LAPIC_MSI = 36, - IOCNUM_LAPIC_LOCAL_IRQ = 37, + IOCNUM_LAPIC_LOCAL_IRQ = 37, + IOCNUM_IOAPIC_PINCOUNT = 38, /* PCI pass-thru */ IOCNUM_BIND_PPTDEV = 40, @@ -228,6 +229,8 @@ _IOW('v', IOCNUM_IOAPIC_DEASSERT_IRQ, struct vm_ioapic_irq) #define VM_IOAPIC_PULSE_IRQ \ _IOW('v', IOCNUM_IOAPIC_PULSE_IRQ, struct vm_ioapic_irq) +#define VM_IOAPIC_PINCOUNT \ + _IOR('v', IOCNUM_IOAPIC_PINCOUNT, int) #define VM_SET_CAPABILITY \ _IOW('v', IOCNUM_SET_CAPABILITY, struct vm_capability) #define VM_GET_CAPABILITY \ --- //depot/vendor/freebsd/src/sys/amd64/vmm/io/vioapic.c +++ //depot/user/jhb/bhyve/sys/amd64/vmm/io/vioapic.c @@ -44,6 +44,7 @@ #include "vmm_ktr.h" #include "vmm_lapic.h" +#include "vmm_stat.h" #include "vlapic.h" #include "vioapic.h" @@ -94,6 +95,9 @@ } #endif +static VMM_STAT_ARRAY(IOINTS_TRIGGERRED, REDIR_ENTRIES, + "I/O APIC ints triggered"); + static void vioapic_send_intr(struct vioapic *vioapic, int pin) { @@ -154,6 +158,7 @@ if (oldcnt == 0 && newcnt == 1) { needintr = true; VIOAPIC_CTR1(vioapic, "ioapic pin%d: asserted", pin); + vmm_stat_array_incr(vioapic->vm, 0, IOINTS_TRIGGERRED, pin, 1); } else if (oldcnt == 1 && newcnt == 0) { VIOAPIC_CTR1(vioapic, "ioapic pin%d: deasserted", pin); } else { --- //depot/vendor/freebsd/src/sys/amd64/vmm/vmm_dev.c +++ //depot/user/jhb/bhyve/sys/amd64/vmm/vmm_dev.c @@ -318,6 +318,9 @@ ioapic_irq = (struct vm_ioapic_irq *)data; error = vioapic_pulse_irq(sc->vm, ioapic_irq->irq); break; + case VM_IOAPIC_PINCOUNT: + *(int *)data = vioapic_pincount(sc->vm); + break; case VM_MAP_MEMORY: seg = (struct vm_memory_segment *)data; error = vm_malloc(sc->vm, seg->gpa, seg->len); --- //depot/vendor/freebsd/src/usr.sbin/bhyve/Makefile +++ //depot/user/jhb/bhyve/usr.sbin/bhyve/Makefile @@ -8,7 +8,7 @@ MAN= bhyve.8 SRCS= acpi.c atpic.c bhyverun.c block_if.c consport.c dbgport.c elcr.c -SRCS+= inout.c legacy_irq.c mem.c mevent.c mptbl.c pci_ahci.c +SRCS+= inout.c ioapic.c mem.c mevent.c mptbl.c pci_ahci.c SRCS+= pci_emul.c pci_hostbridge.c pci_lpc.c pci_passthru.c pci_virtio_block.c SRCS+= pci_virtio_net.c pci_uart.c pit_8254.c pm.c pmtmr.c post.c rtc.c SRCS+= uart_emul.c virtio.c xmsr.c spinup_ap.c --- //depot/vendor/freebsd/src/usr.sbin/bhyve/bhyve.8 +++ //depot/user/jhb/bhyve/usr.sbin/bhyve/bhyve.8 @@ -37,7 +37,6 @@ .Op Fl g Ar gdbport .Op Fl p Ar pinnedcpu .Op Fl s Ar slot,emulation Ns Op , Ns Ar conf -.Op Fl S Ar slot,emulation Ns Op , Ns Ar conf .Op Fl l Ar lpcdev Ns Op , Ns Ar conf .Ar vmname .Sh DESCRIPTION @@ -202,13 +201,6 @@ loader variable as described in .Xr vmm 4 . .El -.It Fl S Ar slot , Ns Ar emulation Ns Op , Ns Ar conf -Identical to the -s option except the device is instructed to use legacy -ISA addresses if possible. -Currently this only has an effect with the -.Li uart -device emulation. -This option will be deprecated in a future version. .It Fl l Ar lpcdev Ns Op , Ns Ar conf Allow devices behind the LPC PCI-ISA bridge to be configured. The only supported devices are the TTY-class devices, --- //depot/vendor/freebsd/src/usr.sbin/bhyve/bhyverun.c +++ //depot/user/jhb/bhyve/usr.sbin/bhyve/bhyverun.c @@ -55,7 +55,7 @@ #include "acpi.h" #include "inout.h" #include "dbgport.h" -#include "legacy_irq.h" +#include "ioapic.h" #include "mem.h" #include "mevent.h" #include "mptbl.h" @@ -124,7 +124,7 @@ { fprintf(stderr, - "Usage: %s [-aehwAHIPW] [-g ] [-s ] [-S ]\n" + "Usage: %s [-aehwAHIPW] [-g ] [-s ]\n" " %*s [-c vcpus] [-p pincpu] [-m mem] [-l ] \n" " -a: local apic is in XAPIC mode (default is X2APIC)\n" " -A: create an ACPI table\n" @@ -137,7 +137,6 @@ " -e: exit on unhandled I/O access\n" " -h: help\n" " -s: PCI slot config\n" - " -S: legacy PCI slot config\n" " -l: LPC device configuration\n" " -m: memory size in MB\n" " -w: ignore unimplemented MSRs\n", @@ -599,7 +598,7 @@ guest_ncpus = 1; memsize = 256 * MB; - while ((c = getopt(argc, argv, "abehwAHIPWp:g:c:s:S:m:l:")) != -1) { + while ((c = getopt(argc, argv, "abehwAHIPWp:g:c:s:m:l:")) != -1) { switch (c) { case 'a': disable_x2apic = 1; @@ -626,12 +625,7 @@ } break; case 's': - if (pci_parse_slot(optarg, 0) != 0) - exit(1); - else - break; - case 'S': - if (pci_parse_slot(optarg, 1) != 0) + if (pci_parse_slot(optarg) != 0) exit(1); else break; @@ -701,7 +695,7 @@ init_mem(); init_inout(); - legacy_irq_init(); + ioapic_init(ctx); rtc_init(ctx); --- /dev/null +++ //depot/user/jhb/bhyve/usr.sbin/bhyve/ioapic.c @@ -0,0 +1,75 @@ +/*- + * XXX + */ + +#include +__FBSDID("$FreeBSD$"); + +#include + +#include +#include + +#include +#include + +#include "ioapic.h" + +/* + * Track the number of interrupts allocated for each pin and assign them + * in a round-robin fashion. Note that we have no idea what the HPET is + * using, but the HPET is also programmable whereas this is intended for + * hardwired PCI interrupts. + * + * This assumes a single I/O APIC where pins >= 16 are permitted for + * PCI devices. + */ +int *pci_pin_counts; +int pci_pins; + +void +ioapic_init(struct vmctx *ctx) +{ + + if (vm_ioapic_pincount(ctx, &pci_pins) < 0) { + pci_pins = 0; + return; + } + + /* Ignore the first 16 pins. */ + if (pci_pins <= 16) { + pci_pins = 0; + return; + } + pci_pins -= 16; + + pci_pin_counts = calloc(pci_pins, sizeof(int)); +} + +int +ioapic_pci_alloc_irq(void) +{ + int best_pin, best_count, i; + + if (pci_pins == 0) + return (-1); + best_pin = 0; + best_count = pci_pin_counts[0]; + for (i = 1; i < pci_pins; i++) { + if (pci_pin_counts[i] < best_count) { + best_pin = i; + best_count = pci_pin_counts[i]; + } + } + pci_pin_counts[best_pin]++; + return (best_pin + 16); +} + +void +ioapic_pci_release_irq(int irq) +{ + + assert(irq >= 16); + assert(irq - 16 < pci_pins); + pci_pin_counts[irq - 16]--; +} --- /dev/null +++ //depot/user/jhb/bhyve/usr.sbin/bhyve/ioapic.h @@ -0,0 +1,17 @@ +/*- + * XXX + * + * $FreeBSD: head/usr.sbin/bhyve/legacy_irq.h 257293 2013-10-29 00:18:11Z neel $ + */ + +#ifndef _IOAPIC_H_ +#define _IOAPIC_H_ + +/* + * Allocate a PCI IRQ from the I/O APIC. + */ +void ioapic_init(struct vmctx *ctx); +int ioapic_pci_alloc_irq(void); +void ioapic_pci_release_irq(int irq); + +#endif --- //depot/vendor/freebsd/src/usr.sbin/bhyve/legacy_irq.c +++ /dev/null 2014-01-24 16:33:00.000000000 -0500 @@ -1,80 +0,0 @@ -/*- - * Copyright (c) 2013 Neel Natu - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $FreeBSD: head/usr.sbin/bhyve/legacy_irq.c 257293 2013-10-29 00:18:11Z neel $ - */ - -#include -#include - -/* - * Used to keep track of legacy interrupt owners/requestors - */ -#define NLIRQ 16 - -static struct lirqinfo { - bool li_generic; - bool li_allocated; -} lirq[NLIRQ]; - -void -legacy_irq_init(void) -{ - - /* - * Allow ISA IRQs 5,10,11,12, and 15 to be available for generic use. - */ - lirq[5].li_generic = true; - lirq[10].li_generic = true; - lirq[11].li_generic = true; - lirq[12].li_generic = true; - lirq[15].li_generic = true; -} - -int -legacy_irq_alloc(int irq) -{ - int i; - - assert(irq < NLIRQ); - - if (irq < 0) { - for (i = 0; i < NLIRQ; i++) { - if (lirq[i].li_generic && !lirq[i].li_allocated) { - irq = i; - break; - } - } - } else { - if (lirq[irq].li_allocated) - irq = -1; - } - - if (irq >= 0) { - lirq[irq].li_allocated = true; - return (irq); - } else - return (-1); -} --- //depot/vendor/freebsd/src/usr.sbin/bhyve/legacy_irq.h +++ /dev/null 2014-01-24 16:33:00.000000000 -0500 @@ -1,41 +0,0 @@ -/*- - * Copyright (c) 2013 Neel Natu - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $FreeBSD: head/usr.sbin/bhyve/legacy_irq.h 257293 2013-10-29 00:18:11Z neel $ - */ - -#ifndef _LEGACY_IRQ_H_ -#define _LEGACY_IRQ_H_ - -/* - * Allocate a legacy irq. The argument 'irq' can be set to -1 to allocate any - * available irq. - * - * Returns -1 on failure or the allocated irq number on success. - */ -int legacy_irq_alloc(int irq); -void legacy_irq_init(void); - -#endif --- //depot/vendor/freebsd/src/usr.sbin/bhyve/mptbl.c +++ //depot/user/jhb/bhyve/usr.sbin/bhyve/mptbl.c @@ -39,6 +39,7 @@ #include "acpi.h" #include "bhyverun.h" #include "mptbl.h" +#include "pci_emul.h" #define MPTABLE_BASE 0xF0000 @@ -75,9 +76,6 @@ /* Number of local intr entries */ #define MPEII_NUM_LOCAL_IRQ 2 -/* Number of i/o intr entries */ -#define MPEII_MAX_IRQ 24 - /* Bus entry defines */ #define MPE_NUM_BUSES 2 #define MPE_BUSNAME_LEN 6 @@ -195,8 +193,42 @@ mpei->apic_address = IOAPIC_PADDR; } +static int +mpt_count_ioint_entries(void) +{ + + /* + * Always include entries for the first 16 pins along with a entry + * for each active PCI INTx pin. + */ + return (16 + pci_count_lintr()); +} + +static void +mpt_generate_pci_int(int slot, int pin, int ioapic_irq, void *arg) +{ + int_entry_ptr *mpiep, mpie; + + mpiep = arg; + mpie = *mpiep; + memset(mpie, 0, sizeof(*mpie)); + + /* + * This is always after another I/O interrupt entry, so cheat + * and fetch the I/O APIC ID from the prior entry. + */ + mpie->type = MPCT_ENTRY_INT; + mpie->int_type = INTENTRY_TYPE_INT; + mpie->src_bus_id = 0; + mpie->src_bus_irq = slot << 2 | (pin - 1); + mpie->dst_apic_id = mpie[-1].dst_apic_id; + mpie->dst_apic_int = ioapic_irq; + + *mpiep = mpie + 1; +} + static void -mpt_build_ioint_entries(int_entry_ptr mpie, int num_pins, int id) +mpt_build_ioint_entries(int_entry_ptr mpie, int id) { int pin; @@ -206,8 +238,8 @@ * just use the default config, tweek later if needed. */ - /* Run through all 16 pins. */ - for (pin = 0; pin < num_pins; pin++) { + /* First, generate the first 16 pins. */ + for (pin = 0; pin < 16; pin++) { memset(mpie, 0, sizeof(*mpie)); mpie->type = MPCT_ENTRY_INT; mpie->src_bus_id = 1; @@ -235,16 +267,6 @@ mpie->int_type = INTENTRY_TYPE_INT; mpie->src_bus_irq = SCI_INT; break; - case 5: - case 10: - case 11: - /* - * PCI Irqs set to level triggered and active-lo. - */ - mpie->int_flags = INTENTRY_FLAGS_POLARITY_ACTIVELO | - INTENTRY_FLAGS_TRIGGER_LEVEL; - mpie->src_bus_id = 0; - /* fall through.. */ default: /* All other pins are identity mapped. */ mpie->int_type = INTENTRY_TYPE_INT; @@ -254,6 +276,8 @@ mpie++; } + /* Next, generate entries for any PCI INTx interrupts. */ + pci_walk_lintr(mpt_generate_pci_int, &mpie); } void @@ -273,6 +297,7 @@ proc_entry_ptr mpep; mpfps_t mpfp; int_entry_ptr mpie; + int ioints; char *curraddr; char *startaddr; @@ -307,9 +332,10 @@ mpch->entry_count++; mpie = (int_entry_ptr) curraddr; - mpt_build_ioint_entries(mpie, MPEII_MAX_IRQ, 0); - curraddr += sizeof(*mpie) * MPEII_MAX_IRQ; - mpch->entry_count += MPEII_MAX_IRQ; + ioints = mpt_count_ioint_entries(); + mpt_build_ioint_entries(mpie, 0); + curraddr += sizeof(*mpie) * ioints; + mpch->entry_count += ioints; mpie = (int_entry_ptr)curraddr; mpt_build_localint_entries(mpie); --- //depot/vendor/freebsd/src/usr.sbin/bhyve/pci_emul.c +++ //depot/user/jhb/bhyve/usr.sbin/bhyve/pci_emul.c @@ -47,7 +47,7 @@ #include "acpi.h" #include "bhyverun.h" #include "inout.h" -#include "legacy_irq.h" +#include "ioapic.h" #include "mem.h" #include "pci_emul.h" #include "pci_lpc.h" @@ -71,12 +71,21 @@ #define MAXSLOTS (PCI_SLOTMAX + 1) #define MAXFUNCS (PCI_FUNCMAX + 1) -static struct slotinfo { - char *si_name; - char *si_param; - struct pci_devinst *si_devi; - int si_legacy; -} pci_slotinfo[MAXSLOTS][MAXFUNCS]; +struct funcinfo { + char *fi_name; + char *fi_param; + struct pci_devinst *fi_devi; +}; + +struct intxinfo { + int ii_count; + int ii_ioapic_irq; +}; + +struct slotinfo { + struct intxinfo si_intpins[4]; + struct funcinfo si_funcs[MAXFUNCS]; +} pci_slotinfo[MAXSLOTS]; SET_DECLARE(pci_devemu_set, struct pci_devemu); @@ -93,6 +102,7 @@ #define PCI_EMUL_MEMLIMIT64 0xFD00000000UL static struct pci_devemu *pci_emul_finddev(char *name); +static void pci_lintr_update(struct pci_devinst *pi); static int pci_emul_devices; static struct mem_range pci_mem_hole; @@ -123,7 +133,7 @@ } int -pci_parse_slot(char *opt, int legacy) +pci_parse_slot(char *opt) { char *slot, *func, *emul, *config; char *str, *cpy; @@ -155,7 +165,7 @@ goto done; } - if (pci_slotinfo[snum][fnum].si_name != NULL) { + if (pci_slotinfo[snum].si_funcs[fnum].fi_name != NULL) { fprintf(stderr, "pci slot %d:%d already occupied!\n", snum, fnum); goto done; @@ -168,9 +178,8 @@ } error = 0; - pci_slotinfo[snum][fnum].si_name = emul; - pci_slotinfo[snum][fnum].si_param = config; - pci_slotinfo[snum][fnum].si_legacy = legacy; + pci_slotinfo[snum].si_funcs[fnum].fi_name = emul; + pci_slotinfo[snum].si_funcs[fnum].fi_param = config; done: if (error) @@ -521,13 +530,7 @@ addr = mask = lobits = 0; break; case PCIBAR_IO: - if (hostbase && - pci_slotinfo[pdi->pi_slot][pdi->pi_func].si_legacy) { - assert(hostbase < PCI_EMUL_IOBASE); - baseptr = &hostbase; - } else { - baseptr = &pci_emul_iobase; - } + baseptr = &pci_emul_iobase; limit = PCI_EMUL_IOLIMIT; mask = PCIM_BAR_IO_BASE; lobits = PCIM_BAR_IO_SPACE; @@ -674,7 +677,8 @@ pdi->pi_bus = 0; pdi->pi_slot = slot; pdi->pi_func = func; - pdi->pi_lintr_pin = -1; + pdi->pi_lintr_pin = 0; + pdi->pi_lintr_ioapic_irq = 0; pdi->pi_d = pde; snprintf(pdi->pi_name, PI_NAMESZ, "%s-pci-%d", pde->pe_emu, slot); @@ -690,7 +694,7 @@ free(pdi); } else { pci_emul_devices++; - pci_slotinfo[slot][func].si_devi = pdi; + pci_slotinfo[slot].si_funcs[func].fi_devi = pdi; } return (err); @@ -824,6 +828,7 @@ pi->pi_msix.enabled = val & PCIM_MSIXCTRL_MSIX_ENABLE; pi->pi_msix.function_mask = val & PCIM_MSIXCTRL_FUNCTION_MASK; + pci_lintr_update(pi); } CFGWRITE(pi, offset, val, bytes); @@ -862,6 +867,7 @@ } else { pi->pi_msi.maxmsgnum = 0; } + pci_lintr_update(pi); } CFGWRITE(pi, offset, val, bytes); @@ -1001,7 +1007,7 @@ init_pci(struct vmctx *ctx) { struct pci_devemu *pde; - struct slotinfo *si; + struct funcinfo *fi; size_t lowmem; int slot, func; int error; @@ -1012,12 +1018,12 @@ for (slot = 0; slot < MAXSLOTS; slot++) { for (func = 0; func < MAXFUNCS; func++) { - si = &pci_slotinfo[slot][func]; - if (si->si_name != NULL) { - pde = pci_emul_finddev(si->si_name); + fi = &pci_slotinfo[slot].si_funcs[func]; + if (fi->fi_name != NULL) { + pde = pci_emul_finddev(fi->fi_name); assert(pde != NULL); error = pci_emul_init(ctx, pde, slot, func, - si->si_param); + fi->fi_param); if (error) return (error); } @@ -1050,11 +1056,27 @@ return (0); } +static void +pci_prt_entry(int slot, int pin, int ioapic_irq, void *arg) +{ + int *count; + + count = arg; + dsdt_line(" Package (0x04)"); + dsdt_line(" {"); + dsdt_line(" 0x%X,", slot << 16 | 0xffff); + dsdt_line(" 0x%02X,", pin - 1); + dsdt_line(" Zero,"); + dsdt_line(" 0x%X", ioapic_irq); + dsdt_line(" }%s", *count == 1 ? "" : ","); + (*count)--; +} + void pci_write_dsdt(void) { struct pci_devinst *pi; - int slot, func; + int count, slot, func; dsdt_indent(1); dsdt_line("Scope (_SB)"); @@ -1115,11 +1137,20 @@ PCI_EMUL_MEMLIMIT64 - PCI_EMUL_MEMBASE64); dsdt_line(" ,, , AddressRangeMemory, TypeStatic)"); dsdt_line(" })"); + count = pci_count_lintr(); + if (count != 0) { + dsdt_indent(2); + dsdt_line("Name (_PRT, Package (0x%02X)", count); + dsdt_line("{"); + pci_walk_lintr(pci_prt_entry, &count); + dsdt_line("})"); + dsdt_unindent(2); + } dsdt_indent(2); for (slot = 0; slot < MAXSLOTS; slot++) { for (func = 0; func < MAXFUNCS; func++) { - pi = pci_slotinfo[slot][func].si_devi; + pi = pci_slotinfo[slot].si_funcs[func].fi_devi; if (pi != NULL && pi->pi_d->pe_write_dsdt != NULL) pi->pi_d->pe_write_dsdt(pi); } @@ -1184,25 +1215,54 @@ } } -int -pci_is_legacy(struct pci_devinst *pi) +static bool +pci_lintr_permitted(struct pci_devinst *pi) { + uint16_t cmd; - return (pci_slotinfo[pi->pi_slot][pi->pi_func].si_legacy); + cmd = pci_get_cfgdata16(pi, PCIR_COMMAND); + return (!(pi->pi_msi.enabled || pi->pi_msix.enabled || + (cmd & PCIM_CMD_INTxDIS))); } int -pci_lintr_request(struct pci_devinst *pi, int req) +pci_lintr_request(struct pci_devinst *pi) { - int irq; + struct slotinfo *si; + int bestpin, bestcount, irq, pin; + + /* + * First, allocate a pin from our slot. + */ + si = &pci_slotinfo[pi->pi_slot]; + bestpin = 0; + bestcount = si->si_intpins[0].ii_count; + for (pin = 1; pin < 4; pin++) { + if (si->si_intpins[pin].ii_count < bestcount) { + bestpin = pin; + bestcount = si->si_intpins[pin].ii_count; + } + } - irq = legacy_irq_alloc(req); - if (irq < 0) - return (-1); + /* + * Attempt to allocate an I/O APIC pin for this intpin. If + * 8259A support is added we will need a separate field to + * assign the intpin to an input pin on the PCI interrupt + * router. + */ + if (si->si_intpins[bestpin].ii_count == 0) { + irq = ioapic_pci_alloc_irq(); + if (irq < 0) + return (-1); + si->si_intpins[bestpin].ii_ioapic_irq = irq; + } else + irq = si->si_intpins[bestpin].ii_ioapic_irq; + si->si_intpins[bestpin].ii_count++; - pi->pi_lintr_pin = irq; + pi->pi_lintr_pin = bestpin + 1; + pi->pi_lintr_ioapic_irq = irq; pci_set_cfgdata8(pi, PCIR_INTLINE, irq); - pci_set_cfgdata8(pi, PCIR_INTPIN, 1); + pci_set_cfgdata8(pi, PCIR_INTPIN, bestpin + 1); return (0); } @@ -1210,11 +1270,15 @@ pci_lintr_assert(struct pci_devinst *pi) { - assert(pi->pi_lintr_pin >= 0); + assert(pi->pi_lintr_pin > 0); if (pi->pi_lintr_state == 0) { - pi->pi_lintr_state = 1; - vm_ioapic_assert_irq(pi->pi_vmctx, pi->pi_lintr_pin); + if (pci_lintr_permitted(pi)) { + pi->pi_lintr_state = 1; + vm_ioapic_assert_irq(pi->pi_vmctx, + pi->pi_lintr_ioapic_irq); + } else + pi->pi_lintr_state = 2; } } @@ -1222,11 +1286,55 @@ pci_lintr_deassert(struct pci_devinst *pi) { - assert(pi->pi_lintr_pin >= 0); + assert(pi->pi_lintr_pin > 0); if (pi->pi_lintr_state == 1) { pi->pi_lintr_state = 0; - vm_ioapic_deassert_irq(pi->pi_vmctx, pi->pi_lintr_pin); + vm_ioapic_deassert_irq(pi->pi_vmctx, pi->pi_lintr_ioapic_irq); + } else if (pi->pi_lintr_state == 2) + pi->pi_lintr_state = 0; +} + +static void +pci_lintr_update(struct pci_devinst *pi) +{ + + if (pi->pi_lintr_state == 1 && !pci_lintr_permitted(pi)) { + vm_ioapic_deassert_irq(pi->pi_vmctx, pi->pi_lintr_ioapic_irq); + pi->pi_lintr_state = 2; + } else if (pi->pi_lintr_state == 2 && pci_lintr_permitted(pi)) { + pi->pi_lintr_state = 1; + vm_ioapic_assert_irq(pi->pi_vmctx, pi->pi_lintr_ioapic_irq); + } +} + +int +pci_count_lintr(void) +{ + int count, slot, pin; + + count = 0; + for (slot = 0; slot < MAXSLOTS; slot++) { + for (pin = 0; pin < 4; pin++) { + if (pci_slotinfo[slot].si_intpins[pin].ii_count != 0) + count++; + } + } + return (count); +} + +void +pci_walk_lintr(pci_lintr_cb cb, void *arg) +{ + struct intxinfo *ii; + int slot, pin; + + for (slot = 0; slot < MAXSLOTS; slot++) { + for (pin = 0; pin < 4; pin++) { + ii = &pci_slotinfo[slot].si_intpins[pin]; + if (ii->ii_count != 0) + cb(slot, pin + 1, ii->ii_ioapic_irq, arg); + } } } @@ -1241,7 +1349,7 @@ numfuncs = 0; for (f = 0; f < MAXFUNCS; f++) { - if (pci_slotinfo[slot][f].si_devi != NULL) { + if (pci_slotinfo[slot].si_funcs[f].fi_devi != NULL) { numfuncs++; } } @@ -1363,6 +1471,12 @@ assert(0); } } + + /* + * If INTx has been unmasked and is pending, assert the + * interrupt. + */ + pci_lintr_update(pi); } static int @@ -1377,7 +1491,7 @@ assert(bytes == 1 || bytes == 2 || bytes == 4); if (cfgbus == 0) - pi = pci_slotinfo[cfgslot][cfgfunc].si_devi; + pi = pci_slotinfo[cfgslot].si_funcs[cfgfunc].fi_devi; else pi = NULL; --- //depot/vendor/freebsd/src/usr.sbin/bhyve/pci_emul.h +++ //depot/user/jhb/bhyve/usr.sbin/bhyve/pci_emul.h @@ -108,6 +108,7 @@ uint8_t pi_bus, pi_slot, pi_func; int8_t pi_lintr_pin; int8_t pi_lintr_state; + int pi_lintr_ioapic_irq; char pi_name[PI_NAMESZ]; int pi_bar_getsize; @@ -187,6 +188,8 @@ uint16_t slot_status2; } __packed; +typedef void (*pci_lintr_cb)(int slot, int pin, int ioapic_irq, void *arg); + int init_pci(struct vmctx *ctx); void msicap_cfgwrite(struct pci_devinst *pi, int capoff, int offset, int bytes, uint32_t val); @@ -199,23 +202,24 @@ uint64_t hostbase, enum pcibar_type type, uint64_t size); int pci_emul_add_msicap(struct pci_devinst *pi, int msgnum); int pci_emul_add_pciecap(struct pci_devinst *pi, int pcie_device_type); -int pci_is_legacy(struct pci_devinst *pi); void pci_generate_msi(struct pci_devinst *pi, int msgnum); void pci_generate_msix(struct pci_devinst *pi, int msgnum); void pci_lintr_assert(struct pci_devinst *pi); void pci_lintr_deassert(struct pci_devinst *pi); -int pci_lintr_request(struct pci_devinst *pi, int ivec); +int pci_lintr_request(struct pci_devinst *pi); int pci_msi_enabled(struct pci_devinst *pi); int pci_msix_enabled(struct pci_devinst *pi); int pci_msix_table_bar(struct pci_devinst *pi); int pci_msix_pba_bar(struct pci_devinst *pi); int pci_msi_msgnum(struct pci_devinst *pi); -int pci_parse_slot(char *opt, int legacy); +int pci_parse_slot(char *opt); void pci_populate_msicap(struct msicap *cap, int msgs, int nextptr); int pci_emul_add_msixcap(struct pci_devinst *pi, int msgnum, int barnum); int pci_emul_msix_twrite(struct pci_devinst *pi, uint64_t offset, int size, uint64_t value); uint64_t pci_emul_msix_tread(struct pci_devinst *pi, uint64_t offset, int size); +int pci_count_lintr(void); +void pci_walk_lintr(pci_lintr_cb cb, void *arg); void pci_write_dsdt(void); static __inline void --- //depot/vendor/freebsd/src/usr.sbin/bhyve/pci_uart.c +++ //depot/user/jhb/bhyve/usr.sbin/bhyve/pci_uart.c @@ -85,28 +85,13 @@ return (val); } -static int pci_uart_nldevs; /* number of legacy uart ports allocated */ - static int pci_uart_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) { struct uart_softc *sc; - int ioaddr, ivec; - if (pci_is_legacy(pi)) { - if (uart_legacy_alloc(pci_uart_nldevs, &ioaddr, &ivec) != 0) { - fprintf(stderr, "Unable to allocate resources for " - "legacy COM%d port at pci device %d:%d\n", - pci_uart_nldevs + 1, pi->pi_slot, pi->pi_func); - return (-1); - } - pci_uart_nldevs++; - pci_emul_alloc_pbar(pi, 0, ioaddr, PCIBAR_IO, UART_IO_BAR_SIZE); - } else { - ivec = -1; - pci_emul_alloc_bar(pi, 0, PCIBAR_IO, UART_IO_BAR_SIZE); - } - pci_lintr_request(pi, ivec); + pci_emul_alloc_bar(pi, 0, PCIBAR_IO, UART_IO_BAR_SIZE); + pci_lintr_request(pi); /* initialize config space */ pci_set_cfgdata16(pi, PCIR_DEVICE, COM_DEV); --- //depot/vendor/freebsd/src/usr.sbin/bhyve/pci_virtio_block.c +++ //depot/user/jhb/bhyve/usr.sbin/bhyve/pci_virtio_block.c @@ -117,6 +117,7 @@ */ struct pci_vtblk_softc { struct virtio_softc vbsc_vs; + pthread_mutex_t vsc_mtx; struct vqueue_info vbsc_vq; int vbsc_fd; struct vtblk_config vbsc_cfg; @@ -304,8 +305,12 @@ /* record fd of storage device/file */ sc->vbsc_fd = fd; + pthread_mutex_init(&sc->vsc_mtx, NULL); + /* init virtio softc and virtqueues */ vi_softc_linkup(&sc->vbsc_vs, &vtblk_vi_consts, sc, pi, &sc->vbsc_vq); + sc->vbsc_vs.vs_mtx = &sc->vsc_mtx; + sc->vbsc_vq.vq_qsize = VTBLK_RINGSZ; /* sc->vbsc_vq.vq_notify = we have no per-queue notify */ @@ -339,6 +344,8 @@ pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_STORAGE); pci_set_cfgdata16(pi, PCIR_SUBDEV_0, VIRTIO_TYPE_BLOCK); + pci_lintr_request(pi); + if (vi_intr_init(&sc->vbsc_vs, 1, fbsdrun_virtio_msix())) return (1); vi_set_io_bar(&sc->vbsc_vs, 0); --- //depot/vendor/freebsd/src/usr.sbin/bhyve/pci_virtio_net.c +++ //depot/user/jhb/bhyve/usr.sbin/bhyve/pci_virtio_net.c @@ -519,6 +519,8 @@ pthread_mutex_init(&sc->vsc_mtx, NULL); vi_softc_linkup(&sc->vsc_vs, &vtnet_vi_consts, sc, pi, sc->vsc_queues); + sc->vsc_vs.vs_mtx = &sc->vsc_mtx; + sc->vsc_queues[VTNET_RXQ].vq_qsize = VTNET_RINGSZ; sc->vsc_queues[VTNET_RXQ].vq_notify = pci_vtnet_ping_rxq; sc->vsc_queues[VTNET_TXQ].vq_qsize = VTNET_RINGSZ; @@ -608,6 +610,8 @@ pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_NETWORK); pci_set_cfgdata16(pi, PCIR_SUBDEV_0, VIRTIO_TYPE_NET); + pci_lintr_request(pi); + /* link always up */ sc->vsc_config.status = 1; --- //depot/vendor/freebsd/src/usr.sbin/bhyve/virtio.c +++ //depot/user/jhb/bhyve/usr.sbin/bhyve/virtio.c @@ -99,7 +99,11 @@ vs->vs_negotiated_caps = 0; vs->vs_curq = 0; /* vs->vs_status = 0; -- redundant */ + VS_LOCK(vs); + if (vs->vs_isr) + pci_lintr_deassert(vs->vs_pi); vs->vs_isr = 0; + VS_UNLOCK(vs); vs->vs_msix_cfg_idx = VIRTIO_MSI_NO_VECTOR; } @@ -137,11 +141,10 @@ nvec = vs->vs_vc->vc_nvq + 1; if (pci_emul_add_msixcap(vs->vs_pi, nvec, barnum)) return (1); - } else { + } else vs->vs_flags &= ~VIRTIO_USE_MSIX; - /* Only 1 MSI vector for bhyve */ - pci_emul_add_msicap(vs->vs_pi, 1); - } + /* Only 1 MSI vector for bhyve */ + pci_emul_add_msicap(vs->vs_pi, 1); return (0); } @@ -591,6 +594,8 @@ case VTCFG_R_ISR: value = vs->vs_isr; vs->vs_isr = 0; /* a read clears this flag */ + if (value) + pci_lintr_deassert(pi); break; case VTCFG_R_CFGVEC: value = vs->vs_msix_cfg_idx; --- //depot/vendor/freebsd/src/usr.sbin/bhyve/virtio.h +++ //depot/user/jhb/bhyve/usr.sbin/bhyve/virtio.h @@ -328,6 +328,18 @@ uint16_t vs_msix_cfg_idx; /* MSI-X vector for config event */ }; +#define VS_LOCK(vs) \ +do { \ + if (vs->vs_mtx) \ + pthread_mutex_lock(vs->vs_mtx); \ +} while (0) + +#define VS_UNLOCK(vs) \ +do { \ + if (vs->vs_mtx) \ + pthread_mutex_unlock(vs->vs_mtx); \ +} while (0) + struct virtio_consts { const char *vc_name; /* name of driver (for diagnostics) */ int vc_nvq; /* number of virtual queues */ @@ -431,11 +443,14 @@ vq_interrupt(struct virtio_softc *vs, struct vqueue_info *vq) { - if (vs->vs_flags & VIRTIO_USE_MSIX) + if (pci_msix_enabled(vs->vs_pi)) pci_generate_msix(vs->vs_pi, vq->vq_msix_idx); else { + VS_LOCK(vs); vs->vs_isr |= VTCFG_ISR_QUEUES; pci_generate_msi(vs->vs_pi, 0); + pci_lintr_assert(vs->vs_pi); + VS_UNLOCK(vs); } }