PCI helper find_cap
[akaros.git] / kern / arch / x86 / ioapic.c
index 85fd392..a4f8f58 100644 (file)
@@ -65,12 +65,14 @@ static spinlock_t idtnolock;
 static int idtno = IdtIOAPIC;
 
 struct apic xioapic[Napic];
-int pcimsimask(struct pci_device *p, int mask);
 
 static bool ioapic_exists(void)
 {
        /* not foolproof, if we called this before parsing */
-       return xioapic[0].useable ? TRUE : FALSE;
+       for (int i = 0; i < Napic; i++)
+               if (xioapic[i].useable)
+                       return TRUE;
+       return FALSE;
 }
 
 static void rtblget(struct apic *apic, int sel, uint32_t * hi, uint32_t * lo)
@@ -150,12 +152,14 @@ void ioapicintrinit(int busno, int ioapicno, int intin, int devno, int lo)
                rdt->lo = lo;
                rdt->hi = 0;
        } else {
-               if (lo != rdt->lo) {
+               /* Polarity/trigger check.  Stored lo also has the vector in 0xff */
+               if (lo != (rdt->lo & ~0xff)) {
                        printk("multiple irq botch bus %d %d/%d/%d lo %d vs %d\n",
                                   busno, ioapicno, intin, devno, lo, rdt->lo);
                        return;
                }
        }
+       /* TODO: this shit is racy.  (refcnt, linked list addition) */
        rdt->ref++;
        rbus = kzmalloc(sizeof *rbus, 0);
        rbus->rdt = rdt;
@@ -241,15 +245,8 @@ static int acpi_make_rdt(int tbdf, int irq, int busno, int devno)
                        gsi_irq = irq;
                } else {
                        /* Need to query ACPI at some point to handle this */
-                       printk("Non-ISA IRQ %d not found in MADT", irq);
-                       if (BUSTYPE(tbdf) != BusPCI) {
-                               printk(", aborting...\n");
-                               return -1;
-                       }
-                       /* Going to just guess some values for PCI */
-                       printk(", guessing...\n");
-                       lo = IPlow | TMlevel;
-                       gsi_irq = irq;
+                       printk("Non-ISA IRQ %d not found in MADT, aborting\n", irq);
+                       return -1;
                }
        }
        ioapic_nr = acpi_irq2ioapic(gsi_irq);
@@ -374,74 +371,77 @@ int nextvec(void)
 
        return vecno;
 }
-static struct pci_device *pcimatchtbdf(int tbdf)
+
+static void msi_mask_irq(struct irq_handler *irq_h, int apic_vector)
 {
-       /* don't we have macros for this somewhere? */
-       int bus, dev, func;
-       bus = tbdf >> 16;
-       dev = (tbdf>>11)&0x1f;
-       func = (tbdf>>8)&3;
-
-       struct pci_device *search;
-       STAILQ_FOREACH(search, &pci_devices, all_dev) {
-               if ((search->bus == bus) &&
-                   (search->dev == dev) &&
-                   (search->func == func))
-                       return search;
-       }
-       return NULL;
+       pci_msi_mask(irq_h->dev_private);
 }
-static int msimask(struct vkey *v, int mask)
+
+static void msi_unmask_irq(struct irq_handler *irq_h, int apic_vector)
 {
-       int pcimsimask(struct pci_device *p, int mask);
+       pci_msi_unmask(irq_h->dev_private);
+}
 
-       struct pci_device *p;
-       p = pcimatchtbdf(v->tbdf);
-       if (p == NULL)
-               return -1;
-       return pcimsimask(p, mask);
+static void msi_route_irq(struct irq_handler *irq_h, int apic_vector, int dest)
+{
+       pci_msi_route(irq_h->dev_private, dest);
 }
 
-static int intrenablemsi(struct irq_handler *v, struct pci_device *p)
+static void msix_mask_irq(struct irq_handler *irq_h, int apic_vector)
 {
-       int pcimsienable(struct pci_device *p, uint64_t vec);
+       pci_msix_mask_vector(irq_h->dev_private);
+}
 
+static void msix_unmask_irq(struct irq_handler *irq_h, int apic_vector)
+{
+       pci_msix_unmask_vector(irq_h->dev_private);
+}
+
+static void msix_route_irq(struct irq_handler *irq_h, int apic_vector, int dest)
+{
+       pci_msix_route_vector(irq_h->dev_private, dest);
+}
+
+static int msi_irq_enable(struct irq_handler *irq_h, struct pci_device *p)
+{
        unsigned int vno, lo, hi = 0;
        uint64_t msivec;
+       struct msix_irq_vector *linkage;
 
        vno = nextvec();
 
-       lo = IPlow | TMedge | vno;
-#warning "what happened to ioapicintrdd"
-//     ioapicintrdd(&hi, &lo);
-
-       if (lo & Lm)
-               lo |= MTlp;
+       /* routing the IRQ to core 0 (hi = 0) in physical mode (Pm) */
+       lo = IPlow | TMedge | Pm | vno;
 
        msivec = (uint64_t) hi << 32 | lo;
-       if (pcimsienable(p, msivec) == -1)
-               return -1;
-#warning "apicisr? apiceoi? "
-//     v->isr = apicisr;
-//     v->eoi = apiceoi;
-       v->apic_vector = vno;
-       v->type = "msi";
-       v->mask = msimask;
-
-       printk("msiirq: (%d,%d,%d): enabling %.16llp %s irq %d vno %d\n", 
-              p->bus, p->dev, p->func, msivec,
-                  v->name, v->apic_vector, vno);
+       irq_h->dev_private = pci_msix_enable(p, msivec);
+       if (!irq_h->dev_private) {
+               if (pci_msi_enable(p, msivec) == -1) {
+                       /* TODO: should free vno here */
+                       return -1;
+               }
+               irq_h->dev_private = p;
+               irq_h->check_spurious = lapic_check_spurious;
+               irq_h->eoi = lapic_send_eoi;
+               irq_h->mask = msi_mask_irq;
+               irq_h->unmask = msi_unmask_irq;
+               irq_h->route_irq = msi_route_irq;
+               irq_h->type = "msi";
+               printk("MSI irq: (%x,%x,%x): enabling %p %s vno %d\n",
+                          p->bus, p->dev, p->func, msivec, irq_h->name, vno);
+               return vno;
+       }
+       irq_h->check_spurious = lapic_check_spurious;
+       irq_h->eoi = lapic_send_eoi;
+       irq_h->mask = msix_mask_irq;
+       irq_h->unmask = msix_unmask_irq;
+       irq_h->route_irq = msix_route_irq;
+       irq_h->type = "msi-x";
+       printk("MSI-X irq: (%x,%x,%x): enabling %p %s vno %d\n",
+              p->bus, p->dev, p->func, msivec, irq_h->name, vno);
        return vno;
 }
 
-int disablemsi(void *unused, struct pci_device *p)
-{
-
-       if (p == NULL)
-               return -1;
-       return pcimsimask(p, 1);
-}
-
 static struct Rdt *ioapic_vector2rdt(int apic_vector)
 {
        struct Rdt *rdt;
@@ -459,40 +459,33 @@ static struct Rdt *ioapic_vector2rdt(int apic_vector)
        return rdt;
 }
 
-/* Routes the IRQ to the os_coreid.  Will take effect immediately.  Route
- * masking from rdt->lo will take effect. */
-static int ioapic_route_irq(int apic_vector, int os_coreid)
+/* Routes the IRQ to the hw_coreid.  Will take effect immediately.  Route
+ * masking from rdt->lo will take effect.  The early return cases are probably
+ * bugs in IOAPIC irq_h setup. */
+static void ioapic_route_irq(struct irq_handler *unused, int apic_vector,
+                             int hw_coreid)
 {
-       int hw_coreid;
        struct Rdt *rdt = ioapic_vector2rdt(apic_vector);
-       if (!rdt)
-               return -1;
-       if (os_coreid >= MAX_NUM_CPUS) {
-               printk("os_coreid %d out of range!\n", os_coreid);
-               return -1;
-       }
-       /* using the old akaros-style lapic id lookup */
-       hw_coreid = get_hw_coreid(os_coreid);
-       if (hw_coreid == -1) {
-               printk("os_coreid %d not a valid hw core!", os_coreid);
-               return -1;
+       if (!rdt) {
+               printk("Missing IOAPIC route for vector!\n", apic_vector);
+               return;
        }
        spin_lock(&rdt->apic->lock);
        /* this bit gets set in apicinit, only if we found it via MP or ACPI */
        if (!xlapic[hw_coreid].useable) {
                printk("Can't route to uninitialized LAPIC %d!\n", hw_coreid);
                spin_unlock(&rdt->apic->lock);
-               return -1;
+               return;
        }
        rdt->hi = hw_coreid << 24;
        rdt->lo |= Pm | MTf;
        rtblput(rdt->apic, rdt->intin, rdt->hi, rdt->lo);
        spin_unlock(&rdt->apic->lock);
-       return 0;
 }
 
-static void ioapic_mask_irq(int apic_vector)
+static void ioapic_mask_irq(struct irq_handler *unused, int apic_vector)
 {
+       /* could store the rdt in the irq_h */
        struct Rdt *rdt = ioapic_vector2rdt(apic_vector);
        if (!rdt)
                return;
@@ -507,7 +500,7 @@ static void ioapic_mask_irq(int apic_vector)
        spin_unlock(&rdt->apic->lock);
 }
 
-static void ioapic_unmask_irq(int apic_vector)
+static void ioapic_unmask_irq(struct irq_handler *unused, int apic_vector)
 {
        struct Rdt *rdt = ioapic_vector2rdt(apic_vector);
        if (!rdt)
@@ -528,7 +521,8 @@ static void ioapic_unmask_irq(int apic_vector)
  * appropriately.
  *
  * Callers init irq_h->dev_irq and ->tbdf.  tbdf encodes the bus type and the
- * classic PCI bus:dev:func.
+ * classic PCI bus:dev:func.  dev_irq may be ignored based on the bus type (e.g.
+ * PCI, esp MSI).
  *
  * In plan9, this was ioapicintrenable(), which also unmasked.  We don't have a
  * deinit/disable method that would tear down the route yet.  All the plan9 one
@@ -537,19 +531,24 @@ int bus_irq_setup(struct irq_handler *irq_h)
 {
        struct Rbus *rbus;
        struct Rdt *rdt;
-       int busno = 0, devno, vecno;
-       struct pci_device pcidev;
-       struct pci_device *msidev;
-
-       if (!ioapic_exists() && (BUSTYPE(irq_h->tbdf) != BusLAPIC)) {
-               irq_h->check_spurious = pic_check_spurious;
-               irq_h->eoi = pic_send_eoi;
-               irq_h->mask = pic_mask_irq;
-               irq_h->unmask = pic_unmask_irq;
-               irq_h->route_irq = 0;
-               irq_h->type = "pic";
-               /* PIC devices have vector = irq + 32 */
-               return irq_h->dev_irq + IdtPIC;
+       int busno, devno, vecno;
+       struct pci_device *pcidev;
+
+       if (!ioapic_exists()) {
+               switch (BUSTYPE(irq_h->tbdf)) {
+                       case BusLAPIC:
+                       case BusIPI:
+                               break;
+                       default:
+                               irq_h->check_spurious = pic_check_spurious;
+                               irq_h->eoi = pic_send_eoi;
+                               irq_h->mask = pic_mask_irq;
+                               irq_h->unmask = pic_unmask_irq;
+                               irq_h->route_irq = 0;
+                               irq_h->type = "pic";
+                               /* PIC devices have vector = irq + 32 */
+                               return irq_h->dev_irq + IdtPIC;
+               }
        }
        switch (BUSTYPE(irq_h->tbdf)) {
                case BusLAPIC:
@@ -583,21 +582,24 @@ int bus_irq_setup(struct irq_handler *irq_h)
                        devno = irq_h->dev_irq << 2;
                        break;
                case BusPCI:
-                       /* TODO: we'll assume it's there.  (fix when adding MSI) */
-
-                       if ((msidev = pcimatchtbdf(irq_h->tbdf)) == NULL) {
-                               printk("no PCI dev for tbdf %p", irq_h->tbdf);
-                               if ((vecno = intrenablemsi(irq_h, msidev)) != -1)
-                                       return vecno;
-                               disablemsi(irq_h, msidev);
+                       pcidev = pci_match_tbdf(irq_h->tbdf);
+                       if (!pcidev) {
+                               printk("No PCI dev for tbdf %p!", irq_h->tbdf);
+                               return -1;
                        }
+                       if ((vecno = msi_irq_enable(irq_h, pcidev)) != -1)
+                               return vecno;
                        busno = BUSBNO(irq_h->tbdf);
-                       explode_tbdf(irq_h->tbdf);
-                       devno = pcidev_read8(&pcidev, PciINTP);
+                       assert(busno == pcidev->bus);
+                       devno = pcidev_read8(pcidev, PciINTP);
 
+                       /* this might not be a big deal - some PCI devices have no INTP.  if
+                        * so, change our devno - 1 below. */
                        if (devno == 0)
                                panic("no INTP for tbdf %p", irq_h->tbdf);
-                       /* remember, devno is the device shifted with irq pin in bits 0-1 */
+                       /* remember, devno is the device shifted with irq pin in bits 0-1.
+                        * we subtract 1, since the PCI intp maps 1 -> INTA, 2 -> INTB, etc,
+                        * and the MP spec uses 0 -> INTA, 1 -> INTB, etc. */
                        devno = BUSDNO(irq_h->tbdf) << 2 | (devno - 1);
                        break;
                default: