--- //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/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 @@ -17,7 +17,7 @@ dbgport.c \ elcr.c \ inout.c \ - legacy_irq.c \ + ioapic.c \ mem.c \ mevent.c \ mptbl.c \ --- //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" @@ -695,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,74 @@ +/*- + * Copyright (c) 2014 Advanced Computing Technologies LLC + * Written by: John H. Baldwin + * 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 THE AUTHOR AND CONTRIBUTORS ``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 THE AUTHOR 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include + +#include +#include + +#include "ioapic.h" + +/* + * Assign PCI INTx interrupts to I/O APIC pins 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. + */ +static 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; +} + +int +ioapic_pci_alloc_irq(void) +{ + static int last_pin; + + if (pci_pins == 0) + return (-1); + return (16 + (last_pin++ % pci_pins)); +} --- /dev/null +++ //depot/user/jhb/bhyve/usr.sbin/bhyve/ioapic.h @@ -0,0 +1,39 @@ +/*- + * Copyright (c) 2014 Advanced Computing Technologies LLC + * Written by: John H. Baldwin + * 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 THE AUTHOR AND CONTRIBUTORS ``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 THE AUTHOR 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$ + */ + +#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); + +#endif --- //depot/vendor/freebsd/src/usr.sbin/bhyve/legacy_irq.c +++ /dev/null 2014-01-28 12:40: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-28 12:40: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 @@ -34,6 +34,7 @@ #include #include +#include #include #include #include @@ -47,7 +48,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,11 +72,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; -} 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); @@ -92,6 +103,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; @@ -154,7 +166,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; @@ -167,8 +179,8 @@ } error = 0; - pci_slotinfo[snum][fnum].si_name = emul; - pci_slotinfo[snum][fnum].si_param = config; + pci_slotinfo[snum].si_funcs[fnum].fi_name = emul; + pci_slotinfo[snum].si_funcs[fnum].fi_param = config; done: if (error) @@ -666,7 +678,10 @@ pdi->pi_bus = 0; pdi->pi_slot = slot; pdi->pi_func = func; - pdi->pi_lintr_pin = -1; + pthread_mutex_init(&pdi->pi_lintr.lock, NULL); + pdi->pi_lintr.pin = 0; + pdi->pi_lintr.state = IDLE; + pdi->pi_lintr.ioapic_irq = 0; pdi->pi_d = pde; snprintf(pdi->pi_name, PI_NAMESZ, "%s-pci-%d", pde->pe_emu, slot); @@ -682,7 +697,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); @@ -816,6 +831,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); @@ -854,6 +870,7 @@ } else { pi->pi_msi.maxmsgnum = 0; } + pci_lintr_update(pi); } CFGWRITE(pi, offset, val, bytes); @@ -993,7 +1010,7 @@ init_pci(struct vmctx *ctx) { struct pci_devemu *pde; - struct slotinfo *si; + struct funcinfo *fi; size_t lowmem; int slot, func; int error; @@ -1004,12 +1021,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); } @@ -1042,11 +1059,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)"); @@ -1107,11 +1140,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); } @@ -1176,18 +1218,54 @@ } } +static bool +pci_lintr_permitted(struct pci_devinst *pi) +{ + uint16_t cmd; + + 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); } @@ -1195,23 +1273,77 @@ 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); + pthread_mutex_lock(&pi->pi_lintr.lock); + if (pi->pi_lintr.state == IDLE) { + if (pci_lintr_permitted(pi)) { + pi->pi_lintr.state = ASSERTED; + vm_ioapic_assert_irq(pi->pi_vmctx, + pi->pi_lintr.ioapic_irq); + } else + pi->pi_lintr.state = PENDING; } + pthread_mutex_unlock(&pi->pi_lintr.lock); } void pci_lintr_deassert(struct pci_devinst *pi) { - assert(pi->pi_lintr_pin >= 0); + assert(pi->pi_lintr.pin > 0); + + pthread_mutex_lock(&pi->pi_lintr.lock); + if (pi->pi_lintr.state == ASSERTED) { + pi->pi_lintr.state = IDLE; + vm_ioapic_deassert_irq(pi->pi_vmctx, pi->pi_lintr.ioapic_irq); + } else if (pi->pi_lintr.state == PENDING) + pi->pi_lintr.state = IDLE; + pthread_mutex_unlock(&pi->pi_lintr.lock); +} + +static void +pci_lintr_update(struct pci_devinst *pi) +{ + + pthread_mutex_lock(&pi->pi_lintr.lock); + if (pi->pi_lintr.state == ASSERTED && !pci_lintr_permitted(pi)) { + vm_ioapic_deassert_irq(pi->pi_vmctx, pi->pi_lintr.ioapic_irq); + pi->pi_lintr.state = PENDING; + } else if (pi->pi_lintr.state == PENDING && pci_lintr_permitted(pi)) { + pi->pi_lintr.state = ASSERTED; + vm_ioapic_assert_irq(pi->pi_vmctx, pi->pi_lintr.ioapic_irq); + } + pthread_mutex_unlock(&pi->pi_lintr.lock); +} + +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; - if (pi->pi_lintr_state == 1) { - pi->pi_lintr_state = 0; - vm_ioapic_deassert_irq(pi->pi_vmctx, pi->pi_lintr_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); + } } } @@ -1226,7 +1358,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++; } } @@ -1348,6 +1480,12 @@ assert(0); } } + + /* + * If INTx has been unmasked and is pending, assert the + * interrupt. + */ + pci_lintr_update(pi); } static int @@ -1362,7 +1500,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 @@ -32,6 +32,7 @@ #include #include #include +#include #include @@ -102,16 +103,27 @@ #define MAX_MSIX_TABLE_ENTRIES 2048 #define PBA_TABLE_ENTRY_SIZE 8 +enum lintr_stat { + IDLE, + ASSERTED, + PENDING +}; + struct pci_devinst { struct pci_devemu *pi_d; struct vmctx *pi_vmctx; uint8_t pi_bus, pi_slot, pi_func; - int8_t pi_lintr_pin; - int8_t pi_lintr_state; char pi_name[PI_NAMESZ]; int pi_bar_getsize; struct { + int8_t pin; + enum lintr_stat state; + int ioapic_irq; + pthread_mutex_t lock; + } pi_lintr; + + struct { int enabled; uint64_t addr; uint64_t msg_data; @@ -187,6 +199,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); @@ -203,7 +217,7 @@ 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); @@ -215,6 +229,8 @@ 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 @@ -91,7 +91,7 @@ struct uart_softc *sc; pci_emul_alloc_bar(pi, 0, PCIBAR_IO, UART_IO_BAR_SIZE); - pci_lintr_request(pi, -1); + 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); } }