PCI helper find_cap
[akaros.git] / kern / arch / x86 / ioapic.c
index 6487ca2..a4f8f58 100644 (file)
 #include <acpi.h>
 #include <trap.h>
 
+/* Rbus chains, one for each device bus: each rbus matches a device to an rdt */
 struct Rbus {
        struct Rbus *next;
        int devno;
        struct Rdt *rdt;
 };
 
+/* Each rdt describes an ioapic input pin (intin, from the bus/device) */
 struct Rdt {
        struct apic *apic;
        int intin;
-       uint32_t lo;
+       uint32_t lo;                            /* matches the lo in the intin, incl Im */
+       uint32_t hi;                            /* matches the hi in the intin, incl routing */
 
        int ref;                                        /* could map to multiple busses */
        int enabled;                            /* times enabled */
@@ -66,13 +69,12 @@ struct apic xioapic[Napic];
 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;
 }
 
-/* TODO: put these in a header */
-int apiceoi(int);
-int apicisr(int);
-
 static void rtblget(struct apic *apic, int sel, uint32_t * hi, uint32_t * lo)
 {
        sel = Ioredtbl + 2 * sel;
@@ -106,51 +108,58 @@ struct Rdt *rdtlookup(struct apic *apic, int intin)
        return NULL;
 }
 
-/* busno is the source bus
- * apic is the destination apic
- * intin is the INTIN pin on the destination apic
- * devno is the device number in the style of a PCI Interrupt
- * Assignment Entry. Which is devno << 2? 
- * lo is the vector table entry. We need to figure out how
- * to compute this from acpi. We used to get it from the
- * mptable but we would like to avoid that.
- */
-void ioapicintrinit(int busno, int apicno, int intin, int devno, int lo)
+struct Rdt *rbus_get_rdt(int busno, int devno)
+{
+       struct Rbus *rbus;
+       for (rbus = rdtbus[busno]; rbus != NULL; rbus = rbus->next) {
+               if (rbus->devno == devno)
+                       return rbus->rdt;
+       }
+       return 0;
+}
+
+/* builds RDT and Rbus entries, given the wiring of bus:dev to ioapicno:intin.
+ * - busno is the source bus
+ * - devno is the device number in the style of a PCI Interrupt Assignment
+ * Entry.  Which is the irq << 2 (check MP spec D.3).
+ * - ioapic is the ioapic the device is connected to
+ * - intin is the INTIN pin on the ioapic
+ * - lo is the lower part of the IOAPIC apic-message, which has the polarity and
+ * trigger mode flags. */
+void ioapicintrinit(int busno, int ioapicno, int intin, int devno, int lo)
 {
        struct Rbus *rbus;
        struct Rdt *rdt;
-       struct apic *apic;
-       printk("%s: busno %d apicno %d intin %d devno %p lo %p\n", __func__,
-                  busno, apicno, intin, devno, lo);
+       struct apic *ioapic;
 
-       if (busno >= Nbus || apicno >= Napic || nrdtarray >= Nrdt) {
-               printk("FAIL 1\n");
+       if (busno >= Nbus || ioapicno >= Napic || nrdtarray >= Nrdt) {
+               printk("Bad bus %d ioapic %d or nrdtarray %d too big\n", busno,
+                      ioapicno, nrdtarray);
                return;
        }
-       apic = &xioapic[apicno];
-       if (!apic->useable || intin >= apic->nrdt) {
-               printk("apic->usable %d intin %d apic->nrdt %d OOR\n", apic->useable,
-                          intin, apic->nrdt);
-               printk("apicno %d, apic %p\n", apicno, apic);
+       ioapic = &xioapic[ioapicno];
+       if (!ioapic->useable || intin >= ioapic->nrdt) {
+               printk("IOAPIC unusable (%d) or not enough nrdt (%d) for %d\n",
+                      ioapic->useable, ioapic->nrdt, intin);
                return;
        }
 
-       rdt = rdtlookup(apic, intin);
+       rdt = rdtlookup(ioapic, intin);
        if (rdt == NULL) {
-               printk("NO RDT, install it for apic %d intin %d lo %p\n", apicno, intin,
-                          lo);
                rdt = &rdtarray[nrdtarray++];
-               rdt->apic = apic;
+               rdt->apic = ioapic;
                rdt->intin = intin;
                rdt->lo = lo;
+               rdt->hi = 0;
        } else {
-               if (lo != rdt->lo) {
-                       printd("mutiple irq botch bus %d %d/%d/%d lo %d vs %d\n",
-                                  busno, apicno, intin, devno, 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;
                }
-               printk("dup rdt %d %d %d %d %.8p\n", busno, apicno, intin, devno, lo);
        }
+       /* TODO: this shit is racy.  (refcnt, linked list addition) */
        rdt->ref++;
        rbus = kzmalloc(sizeof *rbus, 0);
        rbus->rdt = rdt;
@@ -167,42 +176,86 @@ static int map_edge_level[4] = {
        -1, TMedge, -1, TMlevel
 };
 
-int ioapic_route_irq(int irq, int apicno, int devno)
+static int acpi_irq2ioapic(int irq)
+{
+       int ioapic_idx = 0;
+       struct apic *ioapic;
+       /* with acpi, the ioapics map a global interrupt space.  each covers a
+        * window of the space from [ibase, ibase + nrdt). */
+       for (ioapic = xioapic; ioapic < &xioapic[Napic]; ioapic++, ioapic_idx++) {
+               /* addr check is just for sanity */
+               if (!ioapic->useable || !ioapic->addr)
+                       continue;
+               if ((ioapic->ibase <= irq) && (irq < ioapic->ibase + ioapic->nrdt))
+                       return ioapic_idx;
+       }
+       return -1;
+}
+
+/* Build an RDT route, like we would have had from the MP tables had they been
+ * parsed, via ACPI.
+ *
+ * This only really deals with the ISA IRQs and maybe PCI ones that happen to
+ * have an override.  FWIW, on qemu the PCI NIC shows up as an ACPI intovr.
+ *
+ * From Brendan http://f.osdev.org/viewtopic.php?f=1&t=25951:
+ *
+ *             Before parsing the MADT you should begin by assuming that redirection
+ *             entries 0 to 15 are used for ISA IRQs 0 to 15. The MADT's "Interrupt
+ *             Source Override Structures" will tell you when this initial/default
+ *             assumption is wrong. For example, the MADT might tell you that ISA IRQ 9
+ *             is connected to IO APIC 44 and is level triggered; and (in this case)
+ *             it'd be silly to assume that ISA IRQ 9 is also connected to IO APIC
+ *             input 9 just because IO APIC input 9 is not listed.
+ *
+ *             For PCI IRQs, the MADT tells you nothing and you can't assume anything
+ *             at all. Sadly, you have to interpret the ACPI AML to determine how PCI
+ *             IRQs are connected to IO APIC inputs (or find some other work-around;
+ *             like implementing a motherboard driver for each different motherboard,
+ *             or some complex auto-detection scheme, or just configure PCI devices to
+ *             use MSI instead). */
+static int acpi_make_rdt(int tbdf, int irq, int busno, int devno)
 {
-       extern struct Madt *apics;
-       struct Madt *a = apics;
        struct Apicst *st;
        uint32_t lo;
-       int pol, edge_level;
-       printk("%s(%d,%d);\n", __func__, irq, apicno);
-       /* find it. */
+       int pol, edge_level, ioapic_nr, gsi_irq;
+
        for (st = apics->st; st != NULL; st = st->next) {
-               printk("Check %d, ", st->type);
                if (st->type == ASintovr) {
-                       printk("irq of st is %d\n", st->intovr.irq);
                        if (st->intovr.irq == irq)
                                break;
                }
        }
-       if (!st) {
-               printk("IRQ %d not found in MADT\n", irq);
-               return -1;
-       }
-
-       pol = map_polarity[st->intovr.flags & AFpmask];
-       if (pol < 0) {
-               printk("BAD POLARITY\n");
-               return -1;
+       if (st) {
+               pol = map_polarity[st->intovr.flags & AFpmask];
+               if (pol < 0) {
+                       printk("ACPI override had bad polarity\n");
+                       return -1;
+               }
+               edge_level = map_edge_level[(st->intovr.flags & AFlevel) >> 2];
+               if (edge_level < 0) {
+                       printk("ACPI override had bad edge/level\n");
+                       return -1;
+               }
+               lo = pol | edge_level;
+               gsi_irq = st->intovr.intr;
+       } else {
+               if (BUSTYPE(tbdf) == BusISA) {
+                       lo = IPhigh | TMedge;
+                       gsi_irq = irq;
+               } else {
+                       /* Need to query ACPI at some point to handle this */
+                       printk("Non-ISA IRQ %d not found in MADT, aborting\n", irq);
+                       return -1;
+               }
        }
-
-       edge_level = map_edge_level[(st->intovr.flags & AFlevel) >> 2];
-       if (edge_level < 0) {
-               printk("BAD edge/level\n");
+       ioapic_nr = acpi_irq2ioapic(gsi_irq);
+       if (ioapic_nr < 0) {
+               printk("Could not find an IOAPIC for global irq %d!\n", gsi_irq);
                return -1;
        }
-       lo = pol | edge_level;
-       ioapicintrinit(0, 8, 0 /*st->intovr.intr */ , devno, lo);
-       printk("FOUND the MADT for %d\n", irq);
+       ioapicintrinit(busno, ioapic_nr, gsi_irq - xioapic[ioapic_nr].ibase,
+                      devno, lo);
        return 0;
 }
 
@@ -224,7 +277,6 @@ void ioapicinit(int id, int ibase, uintptr_t pa)
        if (apic->useable)
                return;
        apic->useable = 1;
-       printk("\t\tioapicinit %d: it's useable, apic %p\n", id, apic);
        apic->paddr = pa;
 
        /*
@@ -235,6 +287,9 @@ void ioapicinit(int id, int ibase, uintptr_t pa)
        spin_lock(&apic->lock);
        write_mmreg32(apic->addr + Ioregsel, Ioapicver);
        apic->nrdt = ((read_mmreg32(apic->addr + Iowin) >> 16) & 0xff) + 1;
+       /* the ibase is the global system interrupt base, told to us by ACPI.  if
+        * it's -1, we're called from mpparse, and just guess/make up our own
+        * assignments. */
        if (ibase != -1)
                apic->ibase = ibase;
        else {
@@ -244,6 +299,7 @@ void ioapicinit(int id, int ibase, uintptr_t pa)
        write_mmreg32(apic->addr + Ioregsel, Ioapicid);
        write_mmreg32(apic->addr + Iowin, id << 24);
        spin_unlock(&apic->lock);
+       printk("IOAPIC initialized at %p\n", apic->addr);
 }
 
 char *ioapicdump(char *start, char *end)
@@ -260,7 +316,7 @@ char *ioapicdump(char *start, char *end)
                apic = &xioapic[i];
                if (!apic->useable || apic->addr == 0)
                        continue;
-               start = seprintf(start, end, "ioapic %d addr %#p nrdt %d ibase %d\n",
+               start = seprintf(start, end, "ioapic %d addr %p nrdt %d ibase %d\n",
                                                 i, apic->addr, apic->nrdt, apic->ibase);
                for (n = 0; n < apic->nrdt; n++) {
                        spin_lock(&apic->lock);
@@ -276,9 +332,9 @@ char *ioapicdump(char *start, char *end)
                for (; rbus != NULL; rbus = rbus->next) {
                        rdt = rbus->rdt;
                        start = seprintf(start, end,
-                                                        " apic %ld devno %#p (%d %d) intin %d lo %#p ref %d\n",
+                                                        " apic %ld devno %p(%d %d) intin %d hi %p lo %p\n",
                                                         rdt->apic - xioapic, rbus->devno, rbus->devno >> 2,
-                                                        rbus->devno & 0x03, rdt->intin, rdt->lo, rdt->ref);
+                                                        rbus->devno & 0x03, rdt->intin, rdt->hi, rdt->lo);
                }
        }
        return start;
@@ -301,66 +357,6 @@ void ioapiconline(void)
        }
 }
 
-static int dfpolicy = 0;
-
-static void ioapicintrdd(uint32_t * hi, uint32_t * lo)
-{
-       int i;
-       static int df;
-       static spinlock_t dflock;
-
-       /*
-        * Set delivery mode (lo) and destination field (hi),
-        * according to interrupt routing policy.
-        */
-       /*
-        * The bulk of this code was written ~1995, when there was
-        * one architecture and one generation of hardware, the number
-        * of CPUs was up to 4(8) and the choices for interrupt routing
-        * were physical, or flat logical (optionally with lowest
-        * priority interrupt). Logical mode hasn't scaled well with
-        * the increasing number of packages/cores/threads, so the
-        * fall-back is to physical mode, which works across all processor
-        * generations, both AMD and Intel, using the APIC and xAPIC.
-        *
-        * Interrupt routing policy can be set here.
-        */
-       switch (dfpolicy) {
-               default:        /* noise core 0 */
-#warning "sys->machptr[0]->apicno --- what is this in Akaros?"
-                       *hi = 0;        //sys->machptr[0]->apicno<<24;
-                       break;
-               case 1: /* round-robin */
-                       /*
-                        * Assign each interrupt to a different CPU on a round-robin
-                        * Some idea of the packages/cores/thread topology would be
-                        * useful here, e.g. to not assign interrupts to more than one
-                        * thread in a core. But, as usual, Intel make that an onerous
-                        * task.
-                        */
-                       spin_lock(&dflock);
-                       for (;;) {
-#if 0
-                               i = df++;
-                               if (df >= sys->nmach + 1)
-                                       df = 0;
-                               if (sys->machptr[i] == NULL || !sys->machptr[i]->online)
-                                       continue;
-                               i = sys->machptr[i]->apicno;
-#endif
-#warning "always picking acpino 0"
-                               i = 0;
-                               if (xlapic[i].useable && xlapic[i].addr == 0)
-                                       break;
-                       }
-                       spin_unlock(&dflock);
-
-                       *hi = i << 24;
-                       break;
-       }
-       *lo |= Pm | MTf;
-}
-
 int nextvec(void)
 {
        unsigned int vecno;
@@ -376,61 +372,148 @@ int nextvec(void)
        return vecno;
 }
 
-#warning "no msi mask yet"
-#if 0
-static int msimask(struct Vkey *v, int mask)
+static void msi_mask_irq(struct irq_handler *irq_h, int apic_vector)
 {
-       Pcidev *p;
+       pci_msi_mask(irq_h->dev_private);
+}
 
-       p = pcimatchtbdf(v->tbdf);
-       if (p == NULL)
-               return -1;
-       return pcimsimask(p, mask);
+static void msi_unmask_irq(struct irq_handler *irq_h, int apic_vector)
+{
+       pci_msi_unmask(irq_h->dev_private);
 }
-#endif
 
-#warning "No msi yet"
-#if 0
-static int intrenablemsi(struct vctl *v, Pcidev * p)
+static void msi_route_irq(struct irq_handler *irq_h, int apic_vector, int dest)
 {
-       unsigned int vno, lo, hi;
+       pci_msi_route(irq_h->dev_private, dest);
+}
+
+static void msix_mask_irq(struct irq_handler *irq_h, int apic_vector)
+{
+       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;
-       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;
-       v->isr = apicisr;
-       v->eoi = apiceoi;
-       v->vno = vno;
-       v->type = "msi";
-       v->mask = msimask;
-
-       printk("msiirq: %T: enabling %.16llp %s irq %d vno %d\n", p->tbdf, msivec,
-                  v->name, v->irq, 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;
 }
-#endif
-#warning "no disable msi yet"
-#if 0
-int disablemsi(Vctl *, Pcidev * p)
+
+static struct Rdt *ioapic_vector2rdt(int apic_vector)
 {
-       if (p == NULL)
-               return -1;
-       return pcimsimask(p, 1);
+       struct Rdt *rdt;
+       if (apic_vector < IdtIOAPIC || apic_vector > MaxIdtIOAPIC) {
+               printk("ioapic vector %d out of range", apic_vector);
+               return 0;
+       }
+       /* Fortunately rdtvecno[vecno] is static once assigned. o/w, we'll need some
+        * global sync for the callers, both for lookup and keeping rdt valid. */
+       rdt = rdtvecno[apic_vector];
+       if (!rdt) {
+               printk("vector %d has no RDT! (did you enable it?)", apic_vector);
+               return 0;
+       }
+       return rdt;
+}
+
+/* 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)
+{
+       struct Rdt *rdt = ioapic_vector2rdt(apic_vector);
+       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;
+       }
+       rdt->hi = hw_coreid << 24;
+       rdt->lo |= Pm | MTf;
+       rtblput(rdt->apic, rdt->intin, rdt->hi, rdt->lo);
+       spin_unlock(&rdt->apic->lock);
 }
-#endif
 
+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;
+       spin_lock(&rdt->apic->lock);
+       /* don't allow shared vectors to be masked.  whatever. */
+       if (rdt->enabled > 1) {
+               spin_unlock(&rdt->apic->lock);
+               return;
+       }
+       rdt->lo |= Im;
+       rtblput(rdt->apic, rdt->intin, rdt->hi, rdt->lo);
+       spin_unlock(&rdt->apic->lock);
+}
 
-/* Attempts to enable a bus interrupt, initializes irq_h, and returns the IDT
- * vector to use (-1 on error).
+static void ioapic_unmask_irq(struct irq_handler *unused, int apic_vector)
+{
+       struct Rdt *rdt = ioapic_vector2rdt(apic_vector);
+       if (!rdt)
+               return;
+       spin_lock(&rdt->apic->lock);
+       rdt->lo &= ~Im;
+       rtblput(rdt->apic, rdt->intin, rdt->hi, rdt->lo);
+       spin_unlock(&rdt->apic->lock);
+}
+
+/* Attempts to init a bus interrupt, initializes irq_h, and returns the IDT
+ * vector to use (-1 on error).  If routable, the IRQ will route to core 0.  The
+ * IRQ will be masked, if possible.  Call irq_h->unmask() when you're ready.
  *
  * This will determine the type of bus the device is on (LAPIC, IOAPIC, PIC,
  * etc), and set the appropriate fields in isr_h.  If applicable, it'll also
@@ -438,25 +521,34 @@ int disablemsi(Vctl *, Pcidev * p)
  * 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(). */
-int bus_irq_enable(struct irq_handler *irq_h)
+ * 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
+ * did was dec enabled and mask the entry. */
+int bus_irq_setup(struct irq_handler *irq_h)
 {
        struct Rbus *rbus;
        struct Rdt *rdt;
-       uint32_t hi, lo;
-       int busno = 0, devno, vecno;
-       struct pci_device pcidev;
-
-       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->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:
@@ -466,94 +558,73 @@ int bus_irq_enable(struct irq_handler *irq_h)
                         * method was 0.  we're not doing that (unless we have to). */
                        irq_h->check_spurious = lapic_check_spurious;
                        irq_h->eoi = lapic_send_eoi;
-                       irq_h->mask = 0;
-                       irq_h->unmask = 0;
+                       irq_h->mask = lapic_mask_irq;
+                       irq_h->unmask = lapic_unmask_irq;
+                       irq_h->route_irq = 0;
                        irq_h->type = "lapic";
                        /* For the LAPIC, irq == vector */
                        return irq_h->dev_irq;
+               case BusIPI:
+                       /* similar to LAPIC, but we don't actually have LVT entries */
+                       irq_h->check_spurious = lapic_check_spurious;
+                       irq_h->eoi = lapic_send_eoi;
+                       irq_h->mask = 0;
+                       irq_h->unmask = 0;
+                       irq_h->route_irq = 0;
+                       irq_h->type = "IPI";
+                       return irq_h->dev_irq;
                case BusISA:
-                       /* TODO: handle when we have ACPI, but no MP tables */
                        if (mpisabusno == -1)
-                               panic("no ISA bus allocated");
+                               panic("No ISA bus allocated");
                        busno = mpisabusno;
                        /* need to track the irq in devno in PCI interrupt assignment entry
                         * format (see mp.c or MP spec D.3). */
                        devno = irq_h->dev_irq << 2;
                        break;
                case BusPCI:
-                       /* we'll assume it's there. */
-#if 0
-                       Pcidev *pcidev;
-
-                       busno = BUSBNO(irq_h->tbdf);
-                       if ((pcidev = pcimatchtbdf(irq_h->tbdf)) == NULL)
-                               panic("no PCI dev for tbdf %p", irq_h->tbdf);
-                       if ((vecno = intrenablemsi(irq_h, pcidev)) != -1)
+                       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;
-                       disablemsi(irq_h, pcidev);
-#endif
-                       explode_tbdf(irq_h->tbdf);
-                       devno = pcidev_read8(&pcidev, PciINTP);
-                       printk("INTP is %d\n", devno);
+                       busno = BUSBNO(irq_h->tbdf);
+                       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);
-                       printk("devno is %08lx\n", devno);
-                       printk("bus_irq_enable: tbdf %p busno %d devno %d\n",
-                                  irq_h->tbdf, busno, devno);
                        break;
                default:
                        panic("Unknown bus type, TBDF %p", irq_h->tbdf);
        }
-
-       rdt = NULL;
-       for (rbus = rdtbus[busno]; rbus != NULL; rbus = rbus->next) {
-               printk("Check rbus->devno %p devno %p\n", rbus->devno, devno);
-               if (rbus->devno == devno) {
-                       rdt = rbus->rdt;
-                       break;
-               }
+       /* busno and devno are set, regardless of the bustype, enough to find rdt.
+        * these may differ from the values in tbdf. */
+       rdt = rbus_get_rdt(busno, devno);
+       if (!rdt) {
+               /* second chance.  if we didn't find the item the first time, then (if
+                * it exists at all), it wasn't in the MP tables (or we had no tables).
+                * So maybe we can figure it out via ACPI. */
+               acpi_make_rdt(irq_h->tbdf, irq_h->dev_irq, busno, devno);
+               rdt = rbus_get_rdt(busno, devno);
        }
-       if (rdt == NULL) {
-               // install it? Who knows?
-               int ioapic_route_irq(int irq, int apicno, int devno);
-               ioapic_route_irq(irq_h->dev_irq, 0, devno);
-               printk("rdt is NULLLLLLLLLLLLLLLLLLLLLL\n");
-
-               /*
-                * First crack in the smooth exterior of the new code:
-                * some BIOS make an MPS table where the PCI devices
-                * are just defaulted to ISA.  Rewrite this to be
-                * cleaner.
-                * no MPS table in akaros.
-                if((busno = mpisabusno) == -1)
-                return -1;
-
-                devno = irq_h->dev_irq<<2;
-                */
-               for (rbus = rdtbus[busno]; rbus != NULL; rbus = rbus->next)
-                       if (rbus->devno == devno) {
-                               printk("rbus->devno = %p, devno %p\n", rbus->devno, devno);
-                               rdt = rbus->rdt;
-                               break;
-                       }
-               printk("isa: tbdf %p busno %d devno %d %#p\n",
-                          irq_h->tbdf, busno, devno, rdt);
-       }
-       if (rdt == NULL) {
-               printk("RDT Is STILL NULL!\n");
+       if (!rdt) {
+               printk("Unable to build IOAPIC route for irq %d\n", irq_h->dev_irq);
                return -1;
        }
-
-       printk("Second crack\n");
        /*
-        * Second crack:
         * what to do about devices that intrenable/intrdisable frequently?
         * 1) there is no ioapicdisable yet;
         * 2) it would be good to reuse freed vectors.
         * Oh bugger.
+        * brho: plus the diff btw mask/unmask and enable/disable is unclear
         */
        /*
         * This is a low-frequency event so just lock
@@ -561,63 +632,29 @@ int bus_irq_enable(struct irq_handler *irq_h)
         * rather than putting a Lock in each entry.
         */
        spin_lock(&rdt->apic->lock);
-       printk("%p: %ld/%d/%d (%d)\n", irq_h->tbdf, rdt->apic - xioapic, rbus->devno,
-                  rdt->intin, devno);
+       /* if a destination has already been picked, we store it in the lo.  this
+        * stays around regardless of enabled/disabled, since we don't reap vectors
+        * yet.  nor do we really mess with enabled... */
        if ((rdt->lo & 0xff) == 0) {
                vecno = nextvec();
                rdt->lo |= vecno;
                rdtvecno[vecno] = rdt;
-       } else
-               printk("%p: mutiple irq bus %d dev %d\n", irq_h->tbdf, busno, devno);
-
+       } else {
+               printd("%p: mutiple irq bus %d dev %d\n", irq_h->tbdf, busno, devno);
+       }
        rdt->enabled++;
-       lo = (rdt->lo & ~Im);
-       ioapicintrdd(&hi, &lo); // XXX picks a destination and sets phys/fixed
-       rtblput(rdt->apic, rdt->intin, hi, lo);
-       vecno = lo & 0xff;
+       rdt->hi = 0;                    /* route to 0 by default */
+       rdt->lo |= Pm | MTf;
+       rtblput(rdt->apic, rdt->intin, rdt->hi, rdt->lo);
+       vecno = rdt->lo & 0xff;
        spin_unlock(&rdt->apic->lock);
 
-       printk("busno %d devno %d hi %p lo %p vecno %d\n",
-                  busno, devno, hi, lo, vecno);
-
        irq_h->check_spurious = lapic_check_spurious;
        irq_h->eoi = lapic_send_eoi;
-       irq_h->mask = 0;
-       irq_h->unmask = 0;
+       irq_h->mask = ioapic_mask_irq;
+       irq_h->unmask = ioapic_unmask_irq;
+       irq_h->route_irq = ioapic_route_irq;
        irq_h->type = "ioapic";
 
        return vecno;
 }
-
-int ioapicintrdisable(int vecno)
-{
-       struct Rdt *rdt;
-
-       /*
-        * FOV. Oh dear. This isn't very good.
-        * Fortunately rdtvecno[vecno] is static
-        * once assigned.
-        * Must do better.
-        *
-        * What about any pending interrupts?
-        */
-       if (vecno < 0 || vecno > MaxIdtIOAPIC) {
-               panic("ioapicintrdisable: vecno %d out of range", vecno);
-               return -1;
-       }
-       if ((rdt = rdtvecno[vecno]) == NULL) {
-               // XXX what if they asked for a LAPIC vector?
-               panic("ioapicintrdisable: vecno %d has no rdt", vecno);
-               return -1;
-       }
-
-       spin_lock(&rdt->apic->lock);
-       rdt->enabled--;
-       /* XXX looks like they think rdt->lo is supposed to have Im set/masked?  and
-        * they also blow away their 'hi' routing decision from earlier */
-       if (rdt->enabled == 0)
-               rtblput(rdt->apic, rdt->intin, 0, rdt->lo);
-       spin_unlock(&rdt->apic->lock);
-
-       return 0;
-}