Author: jhb
Date: Thu May 15 14:16:55 2014
New Revision: 266125
URL: http://svnweb.freebsd.org/changeset/base/266125

Log:
  Implement a PCI interrupt router to route PCI legacy INTx interrupts to
  the legacy 8259A PICs.
  - Implement an ICH-comptabile PCI interrupt router on the lpc device with
    8 steerable pins configured via config space access to byte-wide
    registers at 0x60-63 and 0x68-6b.
  - For each configured PCI INTx interrupt, route it to both an I/O APIC
    pin and a PCI interrupt router pin.  When a PCI INTx interrupt is
    asserted, ensure that both pins are asserted.
  - Provide an initial routing of PCI interrupt router (PIRQ) pins to
    8259A pins (ISA IRQs) and initialize the interrupt line config register
    for the corresponding PCI function with the ISA IRQ as this matches
    existing hardware.
  - Add a global _PIC method for OSPM to select the desired interrupt routing
    configuration.
  - Update the _PRT methods for PCI bridges to provide both APIC and legacy
    PRT tables and return the appropriate table based on the configured
    routing configuration.  Note that if the lpc device is not configured, no
    routing information is provided.
  - When the lpc device is enabled, provide ACPI PCI link devices corresponding
    to each PIRQ pin.
  - Add a VMM ioctl to adjust the trigger mode (edge vs level) for 8259A
    pins via the ELCR.
  - Mark the power management SCI as level triggered.
  - Don't hardcode the number of elements in Packages in the source for
    the DSDT.  iasl(8) will fill in the actual number of elements, and
    this makes it simpler to generate a Package with a variable number of
    elements.
  
  Reviewed by:  tycho

Added:
  head/usr.sbin/bhyve/pci_irq.c   (contents, props changed)
  head/usr.sbin/bhyve/pci_irq.h   (contents, props changed)
Modified:
  head/lib/libvmmapi/vmmapi.c
  head/lib/libvmmapi/vmmapi.h
  head/sys/amd64/include/vmm.h
  head/sys/amd64/include/vmm_dev.h
  head/sys/amd64/vmm/io/vatpic.c
  head/sys/amd64/vmm/io/vatpic.h
  head/sys/amd64/vmm/vmm_dev.c
  head/usr.sbin/bhyve/Makefile
  head/usr.sbin/bhyve/acpi.c
  head/usr.sbin/bhyve/acpi.h
  head/usr.sbin/bhyve/bhyverun.c
  head/usr.sbin/bhyve/mptbl.c
  head/usr.sbin/bhyve/pci_emul.c
  head/usr.sbin/bhyve/pci_emul.h
  head/usr.sbin/bhyve/pci_lpc.c
  head/usr.sbin/bhyve/pci_lpc.h
  head/usr.sbin/bhyve/pm.c

Modified: head/lib/libvmmapi/vmmapi.c
==============================================================================
--- head/lib/libvmmapi/vmmapi.c Thu May 15 14:01:34 2014        (r266124)
+++ head/lib/libvmmapi/vmmapi.c Thu May 15 14:16:55 2014        (r266125)
@@ -507,6 +507,7 @@ int
 vm_isa_pulse_irq(struct vmctx *ctx, int atpic_irq, int ioapic_irq)
 {
        struct vm_isa_irq isa_irq;
+
        bzero(&isa_irq, sizeof(struct vm_isa_irq));
        isa_irq.atpic_irq = atpic_irq;
        isa_irq.ioapic_irq = ioapic_irq;
@@ -515,6 +516,19 @@ vm_isa_pulse_irq(struct vmctx *ctx, int 
 }
 
 int
+vm_isa_set_irq_trigger(struct vmctx *ctx, int atpic_irq,
+    enum vm_intr_trigger trigger)
+{
+       struct vm_isa_irq_trigger isa_irq_trigger;
+
+       bzero(&isa_irq_trigger, sizeof(struct vm_isa_irq_trigger));
+       isa_irq_trigger.atpic_irq = atpic_irq;
+       isa_irq_trigger.trigger = trigger;
+
+       return (ioctl(ctx->fd, VM_ISA_SET_IRQ_TRIGGER, &isa_irq_trigger));
+}
+
+int
 vm_inject_nmi(struct vmctx *ctx, int vcpu)
 {
        struct vm_nmi vmnmi;

Modified: head/lib/libvmmapi/vmmapi.h
==============================================================================
--- head/lib/libvmmapi/vmmapi.h Thu May 15 14:01:34 2014        (r266124)
+++ head/lib/libvmmapi/vmmapi.h Thu May 15 14:16:55 2014        (r266125)
@@ -78,6 +78,8 @@ int   vm_ioapic_pincount(struct vmctx *ctx
 int    vm_isa_assert_irq(struct vmctx *ctx, int atpic_irq, int ioapic_irq);
 int    vm_isa_deassert_irq(struct vmctx *ctx, int atpic_irq, int ioapic_irq);
 int    vm_isa_pulse_irq(struct vmctx *ctx, int atpic_irq, int ioapic_irq);
+int    vm_isa_set_irq_trigger(struct vmctx *ctx, int atpic_irq,
+           enum vm_intr_trigger trigger);
 int    vm_inject_nmi(struct vmctx *ctx, int vcpu);
 int    vm_capability_name2type(const char *capname);
 const char *vm_capability_type2name(int type);

Modified: head/sys/amd64/include/vmm.h
==============================================================================
--- head/sys/amd64/include/vmm.h        Thu May 15 14:01:34 2014        
(r266124)
+++ head/sys/amd64/include/vmm.h        Thu May 15 14:16:55 2014        
(r266125)
@@ -301,6 +301,11 @@ enum x2apic_state {
        X2APIC_STATE_LAST
 };
 
+enum vm_intr_trigger {
+       EDGE_TRIGGER,
+       LEVEL_TRIGGER
+};
+       
 /*
  * The 'access' field has the format specified in Table 21-2 of the Intel
  * Architecture Manual vol 3b.

Modified: head/sys/amd64/include/vmm_dev.h
==============================================================================
--- head/sys/amd64/include/vmm_dev.h    Thu May 15 14:01:34 2014        
(r266124)
+++ head/sys/amd64/include/vmm_dev.h    Thu May 15 14:16:55 2014        
(r266125)
@@ -84,6 +84,11 @@ struct vm_isa_irq {
        int             ioapic_irq;
 };
 
+struct vm_isa_irq_trigger {
+       int             atpic_irq;
+       enum vm_intr_trigger trigger;
+};
+
 struct vm_capability {
        int             cpuid;
        enum vm_cap_type captype;
@@ -213,6 +218,7 @@ enum {
        IOCNUM_ISA_ASSERT_IRQ = 80,
        IOCNUM_ISA_DEASSERT_IRQ = 81,
        IOCNUM_ISA_PULSE_IRQ = 82,
+       IOCNUM_ISA_SET_IRQ_TRIGGER = 83,
 };
 
 #define        VM_RUN          \
@@ -253,6 +259,8 @@ enum {
        _IOW('v', IOCNUM_ISA_DEASSERT_IRQ, struct vm_isa_irq)
 #define        VM_ISA_PULSE_IRQ        \
        _IOW('v', IOCNUM_ISA_PULSE_IRQ, struct vm_isa_irq)
+#define        VM_ISA_SET_IRQ_TRIGGER  \
+       _IOW('v', IOCNUM_ISA_SET_IRQ_TRIGGER, struct vm_isa_irq_trigger)
 #define        VM_SET_CAPABILITY \
        _IOW('v', IOCNUM_SET_CAPABILITY, struct vm_capability)
 #define        VM_GET_CAPABILITY \

Modified: head/sys/amd64/vmm/io/vatpic.c
==============================================================================
--- head/sys/amd64/vmm/io/vatpic.c      Thu May 15 14:01:34 2014        
(r266124)
+++ head/sys/amd64/vmm/io/vatpic.c      Thu May 15 14:16:55 2014        
(r266125)
@@ -446,6 +446,43 @@ vatpic_pulse_irq(struct vm *vm, int irq)
        return (vatpic_set_irqstate(vm, irq, IRQSTATE_PULSE));
 }
 
+int
+vatpic_set_irq_trigger(struct vm *vm, int irq, enum vm_intr_trigger trigger)
+{
+       struct vatpic *vatpic;
+
+       if (irq < 0 || irq > 15)
+               return (EINVAL);
+
+       /*
+        * See comment in vatpic_elc_handler.  These IRQs must be
+        * edge triggered.
+        */
+       if (trigger == LEVEL_TRIGGER) {
+               switch (irq) {
+               case 0:
+               case 1:
+               case 2:
+               case 8:
+               case 13:
+                       return (EINVAL);
+               }
+       }
+
+       vatpic = vm_atpic(vm);
+
+       VATPIC_LOCK(vatpic);
+
+       if (trigger == LEVEL_TRIGGER)
+               vatpic->elc[irq >> 3] |=  1 << (irq & 0x7);
+       else
+               vatpic->elc[irq >> 3] &=  ~(1 << (irq & 0x7));
+
+       VATPIC_UNLOCK(vatpic);
+
+       return (0);
+}
+
 void
 vatpic_pending_intr(struct vm *vm, int *vecptr)
 {

Modified: head/sys/amd64/vmm/io/vatpic.h
==============================================================================
--- head/sys/amd64/vmm/io/vatpic.h      Thu May 15 14:01:34 2014        
(r266124)
+++ head/sys/amd64/vmm/io/vatpic.h      Thu May 15 14:16:55 2014        
(r266125)
@@ -49,6 +49,7 @@ int vatpic_elc_handler(void *vm, int vcp
 int vatpic_assert_irq(struct vm *vm, int irq);
 int vatpic_deassert_irq(struct vm *vm, int irq);
 int vatpic_pulse_irq(struct vm *vm, int irq);
+int vatpic_set_irq_trigger(struct vm *vm, int irq, enum vm_intr_trigger 
trigger);
 
 void vatpic_pending_intr(struct vm *vm, int *vecptr);
 void vatpic_intr_accepted(struct vm *vm, int vector);

Modified: head/sys/amd64/vmm/vmm_dev.c
==============================================================================
--- head/sys/amd64/vmm/vmm_dev.c        Thu May 15 14:01:34 2014        
(r266124)
+++ head/sys/amd64/vmm/vmm_dev.c        Thu May 15 14:16:55 2014        
(r266125)
@@ -156,6 +156,7 @@ vmmdev_ioctl(struct cdev *cdev, u_long c
        struct vm_lapic_msi *vmmsi;
        struct vm_ioapic_irq *ioapic_irq;
        struct vm_isa_irq *isa_irq;
+       struct vm_isa_irq_trigger *isa_irq_trigger;
        struct vm_capability *vmcap;
        struct vm_pptdev *pptdev;
        struct vm_pptdev_mmio *pptmmio;
@@ -346,6 +347,11 @@ vmmdev_ioctl(struct cdev *cdev, u_long c
                if (error == 0 && isa_irq->ioapic_irq != -1)
                        error = vioapic_pulse_irq(sc->vm, isa_irq->ioapic_irq);
                break;
+       case VM_ISA_SET_IRQ_TRIGGER:
+               isa_irq_trigger = (struct vm_isa_irq_trigger *)data;
+               error = vatpic_set_irq_trigger(sc->vm,
+                   isa_irq_trigger->atpic_irq, isa_irq_trigger->trigger);
+               break;
        case VM_MAP_MEMORY:
                seg = (struct vm_memory_segment *)data;
                error = vm_malloc(sc->vm, seg->gpa, seg->len);

Modified: head/usr.sbin/bhyve/Makefile
==============================================================================
--- head/usr.sbin/bhyve/Makefile        Thu May 15 14:01:34 2014        
(r266124)
+++ head/usr.sbin/bhyve/Makefile        Thu May 15 14:16:55 2014        
(r266125)
@@ -23,6 +23,7 @@ SRCS= \
        pci_ahci.c              \
        pci_emul.c              \
        pci_hostbridge.c        \
+       pci_irq.c               \
        pci_lpc.c               \
        pci_passthru.c          \
        pci_virtio_block.c      \

Modified: head/usr.sbin/bhyve/acpi.c
==============================================================================
--- head/usr.sbin/bhyve/acpi.c  Thu May 15 14:01:34 2014        (r266124)
+++ head/usr.sbin/bhyve/acpi.c  Thu May 15 14:16:55 2014        (r266125)
@@ -704,7 +704,7 @@ basl_fwrite_dsdt(FILE *fp)
        dsdt_line("DefinitionBlock (\"bhyve_dsdt.aml\", \"DSDT\", 2,"
                 "\"BHYVE \", \"BVDSDT  \", 0x00000001)");
        dsdt_line("{");
-       dsdt_line("  Name (_S5, Package (0x02)");
+       dsdt_line("  Name (_S5, Package ()");
        dsdt_line("  {");
        dsdt_line("      0x05,");
        dsdt_line("      Zero,");

Modified: head/usr.sbin/bhyve/acpi.h
==============================================================================
--- head/usr.sbin/bhyve/acpi.h  Thu May 15 14:01:34 2014        (r266124)
+++ head/usr.sbin/bhyve/acpi.h  Thu May 15 14:16:55 2014        (r266125)
@@ -49,5 +49,6 @@ void  dsdt_fixed_irq(uint8_t irq);
 void   dsdt_fixed_mem32(uint32_t base, uint32_t length);
 void   dsdt_indent(int levels);
 void   dsdt_unindent(int levels);
+void   sci_init(struct vmctx *ctx);
 
 #endif /* _ACPI_H_ */

Modified: head/usr.sbin/bhyve/bhyverun.c
==============================================================================
--- head/usr.sbin/bhyve/bhyverun.c      Thu May 15 14:01:34 2014        
(r266124)
+++ head/usr.sbin/bhyve/bhyverun.c      Thu May 15 14:16:55 2014        
(r266125)
@@ -60,6 +60,7 @@ __FBSDID("$FreeBSD$");
 #include "mevent.h"
 #include "mptbl.h"
 #include "pci_emul.h"
+#include "pci_irq.h"
 #include "pci_lpc.h"
 #include "smbiostbl.h"
 #include "xmsr.h"
@@ -770,9 +771,11 @@ main(int argc, char *argv[])
 
        init_mem();
        init_inout();
+       pci_irq_init(ctx);
        ioapic_init(ctx);
 
        rtc_init(ctx);
+       sci_init(ctx);
 
        /*
         * Exit if a device emulation finds an error in it's initilization

Modified: head/usr.sbin/bhyve/mptbl.c
==============================================================================
--- head/usr.sbin/bhyve/mptbl.c Thu May 15 14:01:34 2014        (r266124)
+++ head/usr.sbin/bhyve/mptbl.c Thu May 15 14:16:55 2014        (r266125)
@@ -210,7 +210,8 @@ mpt_count_ioint_entries(void)
 }
 
 static void
-mpt_generate_pci_int(int bus, int slot, int pin, int ioapic_irq, void *arg)
+mpt_generate_pci_int(int bus, int slot, int pin, int pirq_pin, int ioapic_irq,
+    void *arg)
 {
        int_entry_ptr *mpiep, mpie;
 

Modified: head/usr.sbin/bhyve/pci_emul.c
==============================================================================
--- head/usr.sbin/bhyve/pci_emul.c      Thu May 15 14:01:34 2014        
(r266124)
+++ head/usr.sbin/bhyve/pci_emul.c      Thu May 15 14:16:55 2014        
(r266125)
@@ -51,6 +51,7 @@ __FBSDID("$FreeBSD$");
 #include "ioapic.h"
 #include "mem.h"
 #include "pci_emul.h"
+#include "pci_irq.h"
 #include "pci_lpc.h"
 
 #define CONF1_ADDR_PORT    0x0cf8
@@ -81,6 +82,7 @@ struct funcinfo {
 
 struct intxinfo {
        int     ii_count;
+       int     ii_pirq_pin;
        int     ii_ioapic_irq;
 };
 
@@ -113,6 +115,7 @@ static uint64_t pci_emul_membase64;
 #define        PCI_EMUL_MEMLIMIT64     0xFD00000000UL
 
 static struct pci_devemu *pci_emul_finddev(char *name);
+static void    pci_lintr_route(struct pci_devinst *pi);
 static void    pci_lintr_update(struct pci_devinst *pi);
 
 static struct mem_range pci_mem_hole;
@@ -697,6 +700,7 @@ pci_emul_init(struct vmctx *ctx, struct 
        pthread_mutex_init(&pdi->pi_lintr.lock, NULL);
        pdi->pi_lintr.pin = 0;
        pdi->pi_lintr.state = IDLE;
+       pdi->pi_lintr.pirq_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);
@@ -1067,6 +1071,27 @@ init_pci(struct vmctx *ctx)
        }
 
        /*
+        * PCI backends are initialized before routing INTx interrupts
+        * so that LPC devices are able to reserve ISA IRQs before
+        * routing PIRQ pins.
+        */
+       for (bus = 0; bus < MAXBUSES; bus++) {
+               if ((bi = pci_businfo[bus]) == NULL)
+                       continue;
+
+               for (slot = 0; slot < MAXSLOTS; slot++) {
+                       si = &bi->slotinfo[slot];
+                       for (func = 0; func < MAXFUNCS; func++) {
+                               fi = &si->si_funcs[func];
+                               if (fi->fi_devi == NULL)
+                                       continue;
+                               pci_lintr_route(fi->fi_devi);
+                       }
+               }
+       }
+       lpc_pirq_routed();
+
+       /*
         * The guest physical memory map looks like the following:
         * [0,              lowmem)             guest system memory
         * [lowmem,         lowmem_limit)       memory hole (may be absent)
@@ -1093,19 +1118,36 @@ init_pci(struct vmctx *ctx)
 }
 
 static void
-pci_prt_entry(int bus, int slot, int pin, int ioapic_irq, void *arg)
+pci_apic_prt_entry(int bus, int slot, int pin, int pirq_pin, int ioapic_irq,
+    void *arg)
 {
-       int *count;
 
-       count = arg;
-       dsdt_line("  Package (0x04)");
+       dsdt_line("  Package ()");
        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)--;
+       dsdt_line("  },");
+}
+
+static void
+pci_pirq_prt_entry(int bus, int slot, int pin, int pirq_pin, int ioapic_irq,
+    void *arg)
+{
+       char *name;
+
+       name = lpc_pirq_name(pirq_pin);
+       if (name == NULL)
+               return;
+       dsdt_line("  Package ()");
+       dsdt_line("  {");
+       dsdt_line("    0x%X,", slot << 16 | 0xffff);
+       dsdt_line("    0x%02X,", pin - 1);
+       dsdt_line("    %s,", name);
+       dsdt_line("    0x00");
+       dsdt_line("  },");
+       free(name);
 }
 
 /*
@@ -1118,7 +1160,7 @@ pci_bus_write_dsdt(int bus)
        struct businfo *bi;
        struct slotinfo *si;
        struct pci_devinst *pi;
-       int count, slot, func;
+       int count, func, slot;
 
        /*
         * If there are no devices on this 'bus' then just return.
@@ -1133,9 +1175,6 @@ pci_bus_write_dsdt(int bus)
                        return;
        }
 
-       dsdt_indent(1);
-       dsdt_line("Scope (_SB)");
-       dsdt_line("{");
        dsdt_line("  Device (PC%02X)", bus);
        dsdt_line("  {");
        dsdt_line("    Name (_HID, EisaId (\"PNP0A03\"))");
@@ -1228,10 +1267,25 @@ pci_bus_write_dsdt(int bus)
        count = pci_count_lintr(bus);
        if (count != 0) {
                dsdt_indent(2);
-               dsdt_line("Name (_PRT, Package (0x%02X)", count);
+               dsdt_line("Name (PPRT, Package ()");
                dsdt_line("{");
-               pci_walk_lintr(bus, pci_prt_entry, &count);
-               dsdt_line("})");
+               pci_walk_lintr(bus, pci_pirq_prt_entry, NULL);
+               dsdt_line("})");
+               dsdt_line("Name (APRT, Package ()");
+               dsdt_line("{");
+               pci_walk_lintr(bus, pci_apic_prt_entry, NULL);
+               dsdt_line("})");
+               dsdt_line("Method (_PRT, 0, NotSerialized)");
+               dsdt_line("{");
+               dsdt_line("  If (PICM)");
+               dsdt_line("  {");
+               dsdt_line("    Return (APRT)");
+               dsdt_line("  }");
+               dsdt_line("  Else");
+               dsdt_line("  {");
+               dsdt_line("    Return (PPRT)");
+               dsdt_line("  }");
+               dsdt_line("}");
                dsdt_unindent(2);
        }
 
@@ -1247,8 +1301,6 @@ pci_bus_write_dsdt(int bus)
        dsdt_unindent(2);
 done:
        dsdt_line("  }");
-       dsdt_line("}");
-       dsdt_unindent(1);
 }
 
 void
@@ -1256,8 +1308,19 @@ pci_write_dsdt(void)
 {
        int bus;
 
+       dsdt_indent(1);
+       dsdt_line("Name (PICM, 0x00)");
+       dsdt_line("Method (_PIC, 1, NotSerialized)");
+       dsdt_line("{");
+       dsdt_line("  Store (Arg0, PICM)");
+       dsdt_line("}");
+       dsdt_line("");
+       dsdt_line("Scope (_SB)");
+       dsdt_line("{");
        for (bus = 0; bus < MAXBUSES; bus++)
                pci_bus_write_dsdt(bus);
+       dsdt_line("}");
+       dsdt_unindent(1);
 }
 
 int
@@ -1330,18 +1393,19 @@ pci_lintr_permitted(struct pci_devinst *
                (cmd & PCIM_CMD_INTxDIS)));
 }
 
-int
+void
 pci_lintr_request(struct pci_devinst *pi)
 {
        struct businfo *bi;
        struct slotinfo *si;
-       int bestpin, bestcount, irq, pin;
+       int bestpin, bestcount, pin;
 
        bi = pci_businfo[pi->pi_bus];
        assert(bi != NULL);
 
        /*
-        * First, allocate a pin from our slot.
+        * Just allocate a pin from our slot.  The pin will be
+        * assigned IRQs later when interrupts are routed.
         */
        si = &bi->slotinfo[pi->pi_slot];
        bestpin = 0;
@@ -1353,26 +1417,43 @@ pci_lintr_request(struct pci_devinst *pi
                }
        }
 
-       /*
-        * 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 = bestpin + 1;
-       pi->pi_lintr.ioapic_irq = irq;
-       pci_set_cfgdata8(pi, PCIR_INTLINE, irq);
        pci_set_cfgdata8(pi, PCIR_INTPIN, bestpin + 1);
-       return (0);
+}
+
+static void
+pci_lintr_route(struct pci_devinst *pi)
+{
+       struct businfo *bi;
+       struct intxinfo *ii;
+
+       if (pi->pi_lintr.pin == 0)
+               return;
+
+       bi = pci_businfo[pi->pi_bus];
+       assert(bi != NULL);
+       ii = &bi->slotinfo[pi->pi_slot].si_intpins[pi->pi_lintr.pin - 1];
+
+       /*
+        * Attempt to allocate an I/O APIC pin for this intpin if one
+        * is not yet assigned.
+        */
+       if (ii->ii_ioapic_irq == 0)
+               ii->ii_ioapic_irq = ioapic_pci_alloc_irq();
+       assert(ii->ii_ioapic_irq > 0);
+
+       /*
+        * Attempt to allocate a PIRQ pin for this intpin if one is
+        * not yet assigned.
+        */
+       if (ii->ii_pirq_pin == 0)
+               ii->ii_pirq_pin = pirq_alloc_pin(pi->pi_vmctx);
+       assert(ii->ii_pirq_pin > 0);
+
+       pi->pi_lintr.ioapic_irq = ii->ii_ioapic_irq;
+       pi->pi_lintr.pirq_pin = ii->ii_pirq_pin;
+       pci_set_cfgdata8(pi, PCIR_INTLINE, pirq_irq(ii->ii_pirq_pin));
 }
 
 void
@@ -1385,8 +1466,7 @@ pci_lintr_assert(struct pci_devinst *pi)
        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);
+                       pci_irq_assert(pi);
                } else
                        pi->pi_lintr.state = PENDING;
        }
@@ -1402,7 +1482,7 @@ pci_lintr_deassert(struct pci_devinst *p
        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);
+               pci_irq_deassert(pi);
        } else if (pi->pi_lintr.state == PENDING)
                pi->pi_lintr.state = IDLE;
        pthread_mutex_unlock(&pi->pi_lintr.lock);
@@ -1414,11 +1494,11 @@ 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);
+               pci_irq_deassert(pi);
                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);
+               pci_irq_assert(pi);
        }
        pthread_mutex_unlock(&pi->pi_lintr.lock);
 }
@@ -1458,7 +1538,8 @@ pci_walk_lintr(int bus, pci_lintr_cb cb,
                for (pin = 0; pin < 4; pin++) {
                        ii = &si->si_intpins[pin];
                        if (ii->ii_count != 0)
-                               cb(bus, slot, pin + 1, ii->ii_ioapic_irq, arg);
+                               cb(bus, slot, pin + 1, ii->ii_pirq_pin,
+                                   ii->ii_ioapic_irq, arg);
                }
        }
 }
@@ -1755,20 +1836,6 @@ INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+
 INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+2, IOPORT_F_INOUT, pci_emul_cfgdata);
 INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+3, IOPORT_F_INOUT, pci_emul_cfgdata);
 
-/*
- * I/O ports to configure PCI IRQ routing. We ignore all writes to it.
- */
-static int
-pci_irq_port_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
-                    uint32_t *eax, void *arg)
-{
-       assert(in == 0);
-       return (0);
-}
-INOUT_PORT(pci_irq, 0xC00, IOPORT_F_OUT, pci_irq_port_handler);
-INOUT_PORT(pci_irq, 0xC01, IOPORT_F_OUT, pci_irq_port_handler);
-SYSRES_IO(0xC00, 2);
-
 #define PCI_EMUL_TEST
 #ifdef PCI_EMUL_TEST
 /*

Modified: head/usr.sbin/bhyve/pci_emul.h
==============================================================================
--- head/usr.sbin/bhyve/pci_emul.h      Thu May 15 14:01:34 2014        
(r266124)
+++ head/usr.sbin/bhyve/pci_emul.h      Thu May 15 14:16:55 2014        
(r266125)
@@ -120,6 +120,7 @@ struct pci_devinst {
        struct {
                int8_t          pin;
                enum lintr_stat state;
+               int             pirq_pin;
                int             ioapic_irq;
                pthread_mutex_t lock;
        } pi_lintr;
@@ -200,7 +201,8 @@ struct pciecap {
        uint16_t        slot_status2;
 } __packed;
 
-typedef void (*pci_lintr_cb)(int b, int s, int pin, int ioapic_irq, void *arg);
+typedef void (*pci_lintr_cb)(int b, int s, int pin, int pirq_pin,
+    int ioapic_irq, void *arg);
 
 int    init_pci(struct vmctx *ctx);
 void   msicap_cfgwrite(struct pci_devinst *pi, int capoff, int offset,
@@ -218,7 +220,7 @@ void        pci_generate_msi(struct pci_devinst
 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);
+void   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);

Added: head/usr.sbin/bhyve/pci_irq.c
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/usr.sbin/bhyve/pci_irq.c       Thu May 15 14:16:55 2014        
(r266125)
@@ -0,0 +1,349 @@
+/*-
+ * Copyright (c) 2014 Advanced Computing Technologies LLC
+ * Written by: John H. Baldwin <j...@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <machine/vmm.h>
+
+#include <assert.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <vmmapi.h>
+
+#include "acpi.h"
+#include "inout.h"
+#include "pci_emul.h"
+#include "pci_irq.h"
+#include "pci_lpc.h"
+
+/*
+ * Implement an 8 pin PCI interrupt router compatible with the router
+ * present on Intel's ICH10 chip.
+ */
+
+/* Fields in each PIRQ register. */
+#define        PIRQ_DIS        0x80
+#define        PIRQ_IRQ        0x0f
+
+/* Only IRQs 3-7, 9-12, and 14-15 are permitted. */
+#define        PERMITTED_IRQS  0xdef8
+#define        IRQ_PERMITTED(irq)      (((1U << (irq)) & PERMITTED_IRQS) != 0)
+
+/* IRQ count to disable an IRQ. */
+#define        IRQ_DISABLED    0xff
+
+static struct pirq {
+       uint8_t reg;
+       int     use_count;
+       int     active_count;
+       pthread_mutex_t lock;
+} pirqs[8];
+
+static u_char irq_counts[16];
+static int pirq_cold = 1;
+
+/*
+ * Returns true if this pin is enabled with a valid IRQ.  Setting the
+ * register to a reserved IRQ causes interrupts to not be asserted as
+ * if the pin was disabled.
+ */
+static bool
+pirq_valid_irq(int reg)
+{
+
+       if (reg & PIRQ_DIS)
+               return (false);
+       return (IRQ_PERMITTED(reg & PIRQ_IRQ));
+}
+
+uint8_t
+pirq_read(int pin)
+{
+
+       assert(pin > 0 && pin <= nitems(pirqs));
+       return (pirqs[pin - 1].reg);
+}
+
+void
+pirq_write(struct vmctx *ctx, int pin, uint8_t val)
+{
+       struct pirq *pirq;
+
+       assert(pin > 0 && pin <= nitems(pirqs));
+       pirq = &pirqs[pin - 1];
+       pthread_mutex_lock(&pirq->lock);
+       if (pirq->reg != (val & (PIRQ_DIS | PIRQ_IRQ))) {
+               if (pirq->active_count != 0 && pirq_valid_irq(pirq->reg))
+                       vm_isa_deassert_irq(ctx, pirq->reg & PIRQ_IRQ, -1);
+               pirq->reg = val & (PIRQ_DIS | PIRQ_IRQ);
+               if (pirq->active_count != 0 && pirq_valid_irq(pirq->reg))
+                       vm_isa_assert_irq(ctx, pirq->reg & PIRQ_IRQ, -1);
+       }
+       pthread_mutex_unlock(&pirq->lock);
+}
+
+void
+pci_irq_reserve(int irq)
+{
+
+       assert(irq < nitems(irq_counts));
+       assert(pirq_cold);
+       assert(irq_counts[irq] == 0 || irq_counts[irq] == IRQ_DISABLED);
+       irq_counts[irq] = IRQ_DISABLED;
+}
+
+void
+pci_irq_use(int irq)
+{
+
+       assert(irq < nitems(irq_counts));
+       assert(pirq_cold);
+       if (irq_counts[irq] != IRQ_DISABLED)
+               irq_counts[irq]++;
+}
+
+void
+pci_irq_init(struct vmctx *ctx)
+{
+       int i;
+
+       for (i = 0; i < nitems(pirqs); i++) {
+               pirqs[i].reg = PIRQ_DIS;
+               pirqs[i].use_count = 0;
+               pirqs[i].active_count = 0;
+               pthread_mutex_init(&pirqs[i].lock, NULL);
+       }
+       for (i = 0; i < nitems(irq_counts); i++) {
+               if (IRQ_PERMITTED(i))
+                       irq_counts[i] = 0;
+               else
+                       irq_counts[i] = IRQ_DISABLED;
+       }
+}
+
+void
+pci_irq_assert(struct pci_devinst *pi)
+{
+       struct pirq *pirq;
+
+       if (pi->pi_lintr.pirq_pin > 0) {
+               assert(pi->pi_lintr.pirq_pin <= nitems(pirqs));
+               pirq = &pirqs[pi->pi_lintr.pirq_pin - 1];
+               pthread_mutex_lock(&pirq->lock);
+               pirq->active_count++;
+               if (pirq->active_count == 1 && pirq_valid_irq(pirq->reg)) {
+                       vm_isa_assert_irq(pi->pi_vmctx, pirq->reg & PIRQ_IRQ,
+                           pi->pi_lintr.ioapic_irq);
+                       pthread_mutex_unlock(&pirq->lock);
+                       return;
+               }
+               pthread_mutex_unlock(&pirq->lock);
+       }
+       vm_ioapic_assert_irq(pi->pi_vmctx, pi->pi_lintr.ioapic_irq);
+}
+
+void
+pci_irq_deassert(struct pci_devinst *pi)
+{
+       struct pirq *pirq;
+
+       if (pi->pi_lintr.pirq_pin > 0) {
+               assert(pi->pi_lintr.pirq_pin <= nitems(pirqs));
+               pirq = &pirqs[pi->pi_lintr.pirq_pin - 1];
+               pthread_mutex_lock(&pirq->lock);
+               pirq->active_count--;
+               if (pirq->active_count == 0 && pirq_valid_irq(pirq->reg)) {
+                       vm_isa_deassert_irq(pi->pi_vmctx, pirq->reg & PIRQ_IRQ,
+                           pi->pi_lintr.ioapic_irq);
+                       pthread_mutex_unlock(&pirq->lock);
+                       return;
+               }
+               pthread_mutex_unlock(&pirq->lock);
+       }
+       vm_ioapic_deassert_irq(pi->pi_vmctx, pi->pi_lintr.ioapic_irq);
+}
+
+int
+pirq_alloc_pin(struct vmctx *ctx)
+{
+       int best_count, best_irq, best_pin, irq, pin;
+
+       pirq_cold = 1;
+
+       /* First, find the least-used PIRQ pin. */
+       best_pin = 0;
+       best_count = pirqs[0].use_count;
+       for (pin = 1; pin < nitems(pirqs); pin++) {
+               if (pirqs[pin].use_count < best_count) {
+                       best_pin = pin;
+                       best_count = pirqs[pin].use_count;
+               }
+       }
+       pirqs[best_pin].use_count++;
+
+       /* Second, route this pin to an IRQ. */
+       if (pirqs[best_pin].reg == PIRQ_DIS) {
+               best_irq = -1;
+               best_count = 0;
+               for (irq = 0; irq < nitems(irq_counts); irq++) {
+                       if (irq_counts[irq] == IRQ_DISABLED)
+                               continue;
+                       if (best_irq == -1 || irq_counts[irq] < best_count) {
+                               best_irq = irq;
+                               best_count = irq_counts[irq];
+                       }
+               }
+               assert(best_irq != 0);
+               irq_counts[best_irq]++;
+               pirqs[best_pin].reg = best_irq;
+               vm_isa_set_irq_trigger(ctx, best_irq, LEVEL_TRIGGER);
+       }
+
+       return (best_pin + 1);
+}
+
+int
+pirq_irq(int pin)
+{
+
+       if (pin == -1)
+               return (255);
+       assert(pin > 0 && pin <= nitems(pirqs));
+       return (pirqs[pin - 1].reg & PIRQ_IRQ);
+}
+
+/* XXX: Generate $PIR table. */
+
+static void
+pirq_dsdt(void)
+{
+       char *irq_prs, *old;
+       int irq, pin;
+
+       irq_prs = NULL;
+       for (irq = 0; irq < nitems(irq_counts); irq++) {
+               if (!IRQ_PERMITTED(irq))
+                       continue;
+               if (irq_prs == NULL)
+                       asprintf(&irq_prs, "%d", irq);
+               else {
+                       old = irq_prs;
+                       asprintf(&irq_prs, "%s,%d", old, irq);
+                       free(old);
+               }
+       }
+
+       /*
+        * A helper method to validate a link register's value.  This
+        * duplicates pirq_valid_irq().
+        */
+       dsdt_line("");
+       dsdt_line("Method (PIRV, 1, NotSerialized)");
+       dsdt_line("{");
+       dsdt_line("  If (And (Arg0, 0x%02X))", PIRQ_DIS);
+       dsdt_line("  {");
+       dsdt_line("    Return (0x00)");
+       dsdt_line("  }");
+       dsdt_line("  And (Arg0, 0x%02X, Local0)", PIRQ_IRQ);
+       dsdt_line("  If (LLess (Local0, 0x03))");
+       dsdt_line("  {");
+       dsdt_line("    Return (0x00)");
+       dsdt_line("  }");
+       dsdt_line("  If (LEqual (Local0, 0x08))");
+       dsdt_line("  {");
+       dsdt_line("    Return (0x00)");
+       dsdt_line("  }");
+       dsdt_line("  If (LEqual (Local0, 0x0D))");
+       dsdt_line("  {");
+       dsdt_line("    Return (0x00)");
+       dsdt_line("  }");
+       dsdt_line("  Return (0x01)");
+       dsdt_line("}");
+
+       for (pin = 0; pin < nitems(pirqs); pin++) {
+               dsdt_line("");
+               dsdt_line("Device (LNK%c)", 'A' + pin);
+               dsdt_line("{");
+               dsdt_line("  Name (_HID, EisaId (\"PNP0C0F\"))");
+               dsdt_line("  Name (_UID, 0x%02X)", pin + 1);
+               dsdt_line("  Method (_STA, 0, NotSerialized)");
+               dsdt_line("  {");
+               dsdt_line("    If (PIRV (PIR%c))", 'A' + pin);
+               dsdt_line("    {");
+               dsdt_line("       Return (0x0B)");
+               dsdt_line("    }");
+               dsdt_line("    Else");
+               dsdt_line("    {");
+               dsdt_line("       Return (0x09)");
+               dsdt_line("    }");
+               dsdt_line("  }");
+               dsdt_line("  Name (_PRS, ResourceTemplate ()");
+               dsdt_line("  {");
+               dsdt_line("    IRQ (Level, ActiveLow, Shared, )");
+               dsdt_line("      {%s}", irq_prs);
+               dsdt_line("  })");
+               dsdt_line("  Name (CB%02X, ResourceTemplate ()", pin + 1);
+               dsdt_line("  {");
+               dsdt_line("    IRQ (Level, ActiveLow, Shared, )");
+               dsdt_line("      {}");
+               dsdt_line("  })");
+               dsdt_line("  CreateWordField (CB%02X, 0x01, CIR%c)",
+                   pin + 1, 'A' + pin);
+               dsdt_line("  Method (_CRS, 0, NotSerialized)");
+               dsdt_line("  {");
+               dsdt_line("    And (PIR%c, 0x%02X, Local0)", 'A' + pin,
+                   PIRQ_DIS | PIRQ_IRQ);
+               dsdt_line("    If (PIRV (Local0))");
+               dsdt_line("    {");
+               dsdt_line("      ShiftLeft (0x01, Local0, CIR%c)", 'A' + pin);
+               dsdt_line("    }");
+               dsdt_line("    Else");
+               dsdt_line("    {");
+               dsdt_line("      Store (0x00, CIR%c)", 'A' + pin);
+               dsdt_line("    }");
+               dsdt_line("    Return (CB%02X)", pin + 1);
+               dsdt_line("  }");
+               dsdt_line("  Method (_DIS, 0, NotSerialized)");
+               dsdt_line("  {");
+               dsdt_line("    Store (0x80, PIR%c)", 'A' + pin);
+               dsdt_line("  }");
+               dsdt_line("  Method (_SRS, 1, NotSerialized)");
+               dsdt_line("  {");
+               dsdt_line("    CreateWordField (Arg0, 0x01, SIR%c)", 'A' + pin);
+               dsdt_line("    FindSetRightBit (SIR%c, Local0)", 'A' + pin);
+               dsdt_line("    Store (Decrement (Local0), PIR%c)", 'A' + pin);
+               dsdt_line("  }");
+               dsdt_line("}");
+       }
+       free(irq_prs);
+}
+LPC_DSDT(pirq_dsdt);

Added: head/usr.sbin/bhyve/pci_irq.h
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/usr.sbin/bhyve/pci_irq.h       Thu May 15 14:16:55 2014        
(r266125)
@@ -0,0 +1,45 @@
+/*-
+ * Copyright (c) 2014 Advanced Computing Technologies LLC
+ * Written by: John H. Baldwin <j...@freebsd.org>
+ * 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 __PCI_IRQ_H__
+#define        __PCI_IRQ_H__
+
+struct pci_devinst;
+
+void   pci_irq_assert(struct pci_devinst *pi);
+void   pci_irq_deassert(struct pci_devinst *pi);
+void   pci_irq_init(struct vmctx *ctx);
+void   pci_irq_reserve(int irq);
+void   pci_irq_use(int irq);
+int    pirq_alloc_pin(struct vmctx *ctx);
+int    pirq_irq(int pin);
+uint8_t        pirq_read(int pin);
+void   pirq_write(struct vmctx *ctx, int pin, uint8_t val);
+
+#endif

Modified: head/usr.sbin/bhyve/pci_lpc.c
==============================================================================
--- head/usr.sbin/bhyve/pci_lpc.c       Thu May 15 14:01:34 2014        
(r266124)
+++ head/usr.sbin/bhyve/pci_lpc.c       Thu May 15 14:16:55 2014        
(r266125)
@@ -43,6 +43,7 @@ __FBSDID("$FreeBSD$");
 #include "acpi.h"
 #include "inout.h"
 #include "pci_emul.h"
+#include "pci_irq.h"
 #include "pci_lpc.h"
 #include "uart_emul.h"
 
@@ -173,6 +174,7 @@ lpc_init(void)
                            "LPC device %s\n", name);
                        return (-1);

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to