akaros/kern/arch/x86/ioapic.c
<<
>>
Prefs
   1/*
   2 * This file is part of the UCB release of Plan 9. It is subject to the license
   3 * terms in the LICENSE file found in the top-level directory of this
   4 * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
   5 * part of the UCB release of Plan 9, including this file, may be copied,
   6 * modified, propagated, or distributed except according to the terms contained
   7 * in the LICENSE file.
   8 */
   9
  10#include <slab.h>
  11#include <kmalloc.h>
  12#include <kref.h>
  13#include <string.h>
  14#include <stdio.h>
  15#include <assert.h>
  16#include <error.h>
  17#include <cpio.h>
  18#include <pmap.h>
  19#include <smp.h>
  20#include <net/ip.h>
  21#include <arch/io.h>
  22#include <acpi.h>
  23#include <trap.h>
  24
  25/* Rbus chains, one for each device bus: each rbus matches a device to an rdt */
  26struct Rbus {
  27        struct Rbus *next;
  28        int devno;
  29        struct Rdt *rdt;
  30};
  31
  32/* Each rdt describes an ioapic input pin (intin, from the bus/device) */
  33struct Rdt {
  34        struct apic *apic;
  35        int intin;
  36        uint32_t lo;            /* matches the lo in the intin, incl Im */
  37        uint32_t hi;            /* matches the hi in the intin, incl routing */
  38
  39        int ref;                /* could map to multiple busses */
  40        int enabled;            /* times enabled */
  41};
  42
  43enum {                          /* IOAPIC registers */
  44        Ioregsel = 0x00,        /* indirect register address */
  45        Iowin = 0x10,           /* indirect register data */
  46        Ioipa = 0x08,           /* IRQ Pin Assertion */
  47        Ioeoi = 0x10,           /* EOI */
  48
  49        Ioapicid = 0x00,        /* Identification */
  50        Ioapicver = 0x01,       /* Version */
  51        Ioapicarb = 0x02,       /* Arbitration */
  52        Ioabcfg = 0x03,         /* Boot Coniguration */
  53        Ioredtbl = 0x10,        /* Redirection Table */
  54};
  55
  56static struct Rdt rdtarray[Nrdt];
  57static int nrdtarray;
  58static struct Rbus *rdtbus[Nbus];
  59/* reverse mapping of IDT vector to the RDT/IOAPIC entry triggering vector */
  60static struct Rdt *rdtvecno[IdtMAX + 1];
  61
  62static spinlock_t idtnolock;
  63static int idtno = IdtIOAPIC;
  64
  65struct apic xioapic[Napic];
  66
  67static bool ioapic_exists(void)
  68{
  69        /* not foolproof, if we called this before parsing */
  70        for (int i = 0; i < Napic; i++)
  71                if (xioapic[i].useable)
  72                        return TRUE;
  73        return FALSE;
  74}
  75
  76static void rtblget(struct apic *apic, int sel, uint32_t * hi, uint32_t * lo)
  77{
  78        sel = Ioredtbl + 2 * sel;
  79
  80        write_mmreg32(apic->addr + Ioregsel, sel + 1);
  81        *hi = read_mmreg32(apic->addr + Iowin);
  82        write_mmreg32(apic->addr + Ioregsel, sel);
  83        *lo = read_mmreg32(apic->addr + Iowin);
  84}
  85
  86static void rtblput(struct apic *apic, int sel, uint32_t hi, uint32_t lo)
  87{
  88        sel = Ioredtbl + 2 * sel;
  89
  90        write_mmreg32(apic->addr + Ioregsel, sel + 1);
  91        write_mmreg32(apic->addr + Iowin, hi);
  92        write_mmreg32(apic->addr + Ioregsel, sel);
  93        write_mmreg32(apic->addr + Iowin, lo);
  94}
  95
  96struct Rdt *rdtlookup(struct apic *apic, int intin)
  97{
  98        int i;
  99        struct Rdt *r;
 100
 101        for (i = 0; i < nrdtarray; i++) {
 102                r = rdtarray + i;
 103                if (apic == r->apic && intin == r->intin)
 104                        return r;
 105        }
 106        return NULL;
 107}
 108
 109struct Rdt *rbus_get_rdt(int busno, int devno)
 110{
 111        struct Rbus *rbus;
 112
 113        for (rbus = rdtbus[busno]; rbus != NULL; rbus = rbus->next) {
 114                if (rbus->devno == devno)
 115                        return rbus->rdt;
 116        }
 117        return 0;
 118}
 119
 120/* builds RDT and Rbus entries, given the wiring of bus:dev to ioapicno:intin.
 121 * - busno is the source bus
 122 * - devno is the device number in the style of a PCI Interrupt Assignment
 123 * Entry.  Which is the irq << 2 (check MP spec D.3).
 124 * - ioapic is the ioapic the device is connected to
 125 * - intin is the INTIN pin on the ioapic
 126 * - lo is the lower part of the IOAPIC apic-message, which has the polarity and
 127 * trigger mode flags. */
 128void ioapicintrinit(int busno, int ioapicno, int intin, int devno, int lo)
 129{
 130        struct Rbus *rbus;
 131        struct Rdt *rdt;
 132        struct apic *ioapic;
 133
 134        if (busno >= Nbus || ioapicno >= Napic || nrdtarray >= Nrdt) {
 135                printk("Bad bus %d ioapic %d or nrdtarray %d too big\n", busno,
 136                       ioapicno, nrdtarray);
 137                return;
 138        }
 139        ioapic = &xioapic[ioapicno];
 140        if (!ioapic->useable || intin >= ioapic->nrdt) {
 141                printk("IOAPIC unusable (%d) or not enough nrdt (%d) for %d\n",
 142                       ioapic->useable, ioapic->nrdt, intin);
 143                return;
 144        }
 145
 146        rdt = rdtlookup(ioapic, intin);
 147        if (rdt == NULL) {
 148                rdt = &rdtarray[nrdtarray++];
 149                rdt->apic = ioapic;
 150                rdt->intin = intin;
 151                rdt->lo = lo;
 152                rdt->hi = 0;
 153        } else {
 154                /* Polarity/trigger check.  Stored lo also has the vector in
 155                 * 0xff */
 156                if (lo != (rdt->lo & ~0xff)) {
 157                        printk("multi-irq botch bus %d %d/%d/%d lo %d vs %d\n",
 158                               busno, ioapicno, intin, devno, lo, rdt->lo);
 159                        return;
 160                }
 161        }
 162        /* TODO: this shit is racy.  (refcnt, linked list addition) */
 163        rdt->ref++;
 164        rbus = kzmalloc(sizeof *rbus, 0);
 165        rbus->rdt = rdt;
 166        rbus->devno = devno;
 167        rbus->next = rdtbus[busno];
 168        rdtbus[busno] = rbus;
 169}
 170
 171static int map_polarity[4] = {
 172        -1, IPhigh, -1, IPlow
 173};
 174
 175static int map_edge_level[4] = {
 176        -1, TMedge, -1, TMlevel
 177};
 178
 179static int acpi_irq2ioapic(int irq)
 180{
 181        int ioapic_idx = 0;
 182        struct apic *ioapic;
 183        /* with acpi, the ioapics map a global interrupt space.  each covers a
 184         * window of the space from [ibase, ibase + nrdt). */
 185        for (ioapic = xioapic; ioapic < &xioapic[Napic]; ioapic++, ioapic_idx++)
 186        {
 187                /* addr check is just for sanity */
 188                if (!ioapic->useable || !ioapic->addr)
 189                        continue;
 190                if ((ioapic->ibase <= irq) &&
 191                    (irq < ioapic->ibase + ioapic->nrdt))
 192                        return ioapic_idx;
 193        }
 194        return -1;
 195}
 196
 197/* Build an RDT route, like we would have had from the MP tables had they been
 198 * parsed, via ACPI.
 199 *
 200 * This only really deals with the ISA IRQs and maybe PCI ones that happen to
 201 * have an override.  FWIW, on qemu the PCI NIC shows up as an ACPI intovr.
 202 *
 203 * From Brendan http://f.osdev.org/viewtopic.php?f=1&t=25951:
 204 *
 205 *      Before parsing the MADT you should begin by assuming that redirection
 206 *      entries 0 to 15 are used for ISA IRQs 0 to 15. The MADT's "Interrupt
 207 *      Source Override Structures" will tell you when this initial/default
 208 *      assumption is wrong. For example, the MADT might tell you that ISA IRQ 9
 209 *      is connected to IO APIC 44 and is level triggered; and (in this case)
 210 *      it'd be silly to assume that ISA IRQ 9 is also connected to IO APIC
 211 *      input 9 just because IO APIC input 9 is not listed.
 212 *
 213 *      For PCI IRQs, the MADT tells you nothing and you can't assume anything
 214 *      at all. Sadly, you have to interpret the ACPI AML to determine how PCI
 215 *      IRQs are connected to IO APIC inputs (or find some other work-around;
 216 *      like implementing a motherboard driver for each different motherboard,
 217 *      or some complex auto-detection scheme, or just configure PCI devices to
 218 *      use MSI instead). */
 219static int acpi_make_rdt(int tbdf, int irq, int busno, int devno)
 220{
 221        struct Atable *at;
 222        struct Apicst *st, *lst;
 223        uint32_t lo;
 224        int pol, edge_level, ioapic_nr, gsi_irq;
 225
 226        at = apics;
 227        st = NULL;
 228        for (int i = 0; i < at->nchildren; i++) {
 229                lst = at->children[i]->tbl;
 230                if (lst->type == ASintovr) {
 231                        if (lst->intovr.irq == irq) {
 232                                st = lst;
 233                                break;
 234                        }
 235                }
 236        }
 237        if (st) {
 238                pol = map_polarity[st->intovr.flags & AFpmask];
 239                if (pol < 0) {
 240                        printk("ACPI override had bad polarity\n");
 241                        return -1;
 242                }
 243                edge_level = map_edge_level[(st->intovr.flags & AFlevel) >> 2];
 244                if (edge_level < 0) {
 245                        printk("ACPI override had bad edge/level\n");
 246                        return -1;
 247                }
 248                lo = pol | edge_level;
 249                gsi_irq = st->intovr.intr;
 250        } else {
 251                if (BUSTYPE(tbdf) == BusISA) {
 252                        lo = IPhigh | TMedge;
 253                        gsi_irq = irq;
 254                } else {
 255                        /* Need to query ACPI at some point to handle this */
 256                        printk("Non-ISA IRQ %d not found in MADT, aborting\n",
 257                               irq);
 258                        return -1;
 259                }
 260        }
 261        ioapic_nr = acpi_irq2ioapic(gsi_irq);
 262        if (ioapic_nr < 0) {
 263                printk("Could not find an IOAPIC for global irq %d!\n",
 264                       gsi_irq);
 265                return -1;
 266        }
 267        ioapicintrinit(busno, ioapic_nr, gsi_irq - xioapic[ioapic_nr].ibase,
 268                       devno, lo);
 269        return 0;
 270}
 271
 272void ioapicinit(int id, int ibase, uintptr_t pa)
 273{
 274        struct apic *apic;
 275        static int base;
 276
 277        assert((IOAPIC_PBASE <= pa) &&
 278               (pa + PGSIZE <= IOAPIC_PBASE + APIC_SIZE));
 279        /*
 280         * Mark the IOAPIC useable if it has a good ID
 281         * and the registers can be mapped.
 282         */
 283        if (id >= Napic)
 284                return;
 285
 286        apic = &xioapic[id];
 287        apic->addr = IOAPIC_BASE + (pa - IOAPIC_PBASE);
 288        if (apic->useable)
 289                return;
 290        apic->useable = 1;
 291        apic->paddr = pa;
 292
 293        /*
 294         * Initialise the I/O APIC.
 295         * The MultiProcessor Specification says it is the
 296         * responsibility of the O/S to set the APIC ID.
 297         */
 298        spin_lock(&apic->lock);
 299        write_mmreg32(apic->addr + Ioregsel, Ioapicver);
 300        apic->nrdt = ((read_mmreg32(apic->addr + Iowin) >> 16) & 0xff) + 1;
 301        /* the ibase is the global system interrupt base, told to us by ACPI.
 302         * if it's -1, we're called from mpparse, and just guess/make up our own
 303         * assignments. */
 304        if (ibase != -1)
 305                apic->ibase = ibase;
 306        else {
 307                apic->ibase = base;
 308                base += apic->nrdt;
 309        }
 310        write_mmreg32(apic->addr + Ioregsel, Ioapicid);
 311        write_mmreg32(apic->addr + Iowin, id << 24);
 312        spin_unlock(&apic->lock);
 313        printk("IOAPIC initialized at %p, nrdt %d, ibase %d\n", apic->addr,
 314               apic->nrdt, apic->ibase);
 315}
 316
 317char *ioapicdump(char *start, char *end)
 318{
 319        int i, n;
 320        struct Rbus *rbus;
 321        struct Rdt *rdt;
 322        struct apic *apic;
 323        uint32_t hi, lo;
 324
 325        if (!2)
 326                return start;
 327        for (i = 0; i < Napic; i++) {
 328                apic = &xioapic[i];
 329                if (!apic->useable || apic->addr == 0)
 330                        continue;
 331                start = seprintf(start, end,
 332                                 "ioapic %d addr %p nrdt %d ibase %d\n",
 333                                 i, apic->addr, apic->nrdt, apic->ibase);
 334                for (n = 0; n < apic->nrdt; n++) {
 335                        spin_lock(&apic->lock);
 336                        rtblget(apic, n, &hi, &lo);
 337                        spin_unlock(&apic->lock);
 338                        start = seprintf(start, end, " rdt %2.2d %p %p\n",
 339                                         n, hi, lo);
 340                }
 341        }
 342        for (i = 0; i < Nbus; i++) {
 343                if ((rbus = rdtbus[i]) == NULL)
 344                        continue;
 345                start = seprintf(start, end, "iointr bus %d:\n", i);
 346                for (; rbus != NULL; rbus = rbus->next) {
 347                        rdt = rbus->rdt;
 348                        start = seprintf(start, end,
 349                                         " apic %ld devno %p(%d %d) intin %d hi %p lo %p\n",
 350                                         rdt->apic - xioapic, rbus->devno,
 351                                         rbus->devno >> 2, rbus->devno & 0x03,
 352                                         rdt->intin, rdt->hi, rdt->lo);
 353                }
 354        }
 355        return start;
 356}
 357
 358/* Zeros and masks every redirect entry in every IOAPIC */
 359void ioapiconline(void)
 360{
 361        int i;
 362        struct apic *apic;
 363
 364        for (apic = xioapic; apic < &xioapic[Napic]; apic++) {
 365                if (!apic->useable || !apic->addr)
 366                        continue;
 367                for (i = 0; i < apic->nrdt; i++) {
 368                        spin_lock(&apic->lock);
 369                        rtblput(apic, i, 0, Im);
 370                        spin_unlock(&apic->lock);
 371                }
 372        }
 373}
 374
 375int nextvec(void)
 376{
 377        unsigned int vecno;
 378
 379        /* TODO: half-way decent integer service (vmem) */
 380        spin_lock(&idtnolock);
 381        vecno = idtno;
 382        idtno = (idtno + 1) % IdtMAX;
 383        if (idtno < IdtIOAPIC)
 384                idtno += IdtIOAPIC;
 385        spin_unlock(&idtnolock);
 386
 387        return vecno;
 388}
 389
 390static void msi_mask_irq(struct irq_handler *irq_h, int apic_vector)
 391{
 392        pci_msi_mask(irq_h->dev_private);
 393}
 394
 395static void msi_unmask_irq(struct irq_handler *irq_h, int apic_vector)
 396{
 397        pci_msi_unmask(irq_h->dev_private);
 398}
 399
 400static void msi_route_irq(struct irq_handler *irq_h, int apic_vector, int dest)
 401{
 402        pci_msi_route(irq_h->dev_private, dest);
 403}
 404
 405static void msix_mask_irq(struct irq_handler *irq_h, int apic_vector)
 406{
 407        pci_msix_mask_vector(irq_h->dev_private);
 408}
 409
 410static void msix_unmask_irq(struct irq_handler *irq_h, int apic_vector)
 411{
 412        pci_msix_unmask_vector(irq_h->dev_private);
 413}
 414
 415static void msix_route_irq(struct irq_handler *irq_h, int apic_vector, int dest)
 416{
 417        pci_msix_route_vector(irq_h->dev_private, dest);
 418}
 419
 420static int msi_irq_enable(struct irq_handler *irq_h, struct pci_device *p)
 421{
 422        unsigned int vno, lo, hi = 0;
 423        uint64_t msivec;
 424        struct msix_irq_vector *linkage;
 425
 426        vno = nextvec();
 427
 428        /* routing the IRQ to core 0 (hi = 0) in physical mode (Pm) */
 429        lo = IPlow | TMedge | Pm | vno;
 430
 431        msivec = (uint64_t) hi << 32 | lo;
 432        irq_h->dev_private = pci_msix_enable(p, msivec);
 433        if (!irq_h->dev_private) {
 434                if (pci_msi_enable(p, msivec) == -1) {
 435                        /* TODO: should free vno here */
 436                        return -1;
 437                }
 438                irq_h->dev_private = p;
 439                irq_h->check_spurious = lapic_check_spurious;
 440                irq_h->eoi = lapic_send_eoi;
 441                irq_h->mask = msi_mask_irq;
 442                irq_h->unmask = msi_unmask_irq;
 443                irq_h->route_irq = msi_route_irq;
 444                irq_h->type = "msi";
 445                printk("MSI irq: (%x,%x,%x): enabling %p %s vno %d\n",
 446                           p->bus, p->dev, p->func, msivec, irq_h->name, vno);
 447                return vno;
 448        }
 449        irq_h->check_spurious = lapic_check_spurious;
 450        irq_h->eoi = lapic_send_eoi;
 451        irq_h->mask = msix_mask_irq;
 452        irq_h->unmask = msix_unmask_irq;
 453        irq_h->route_irq = msix_route_irq;
 454        irq_h->type = "msi-x";
 455        printk("MSI-X irq: (%x,%x,%x): enabling %p %s vno %d\n",
 456               p->bus, p->dev, p->func, msivec, irq_h->name, vno);
 457        return vno;
 458}
 459
 460static struct Rdt *ioapic_vector2rdt(int apic_vector)
 461{
 462        struct Rdt *rdt;
 463
 464        if (apic_vector < IdtIOAPIC || apic_vector > MaxIdtIOAPIC) {
 465                warn("ioapic vector %d out of range", apic_vector);
 466                return 0;
 467        }
 468        /* Fortunately rdtvecno[vecno] is static once assigned. o/w, we'll need
 469         * some global sync for the callers, both for lookup and keeping rdt
 470         * valid. */
 471        rdt = rdtvecno[apic_vector];
 472        if (!rdt) {
 473                warn("vector %d has no RDT! (did you enable it?)", apic_vector);
 474                return 0;
 475        }
 476        return rdt;
 477}
 478
 479/* Routes the IRQ to the hw_coreid.  Will take effect immediately.  Route
 480 * masking from rdt->lo will take effect.  The early return cases are probably
 481 * bugs in IOAPIC irq_h setup. */
 482static void ioapic_route_irq(struct irq_handler *unused, int apic_vector,
 483                             int hw_coreid)
 484{
 485        struct Rdt *rdt = ioapic_vector2rdt(apic_vector);
 486
 487        if (!rdt) {
 488                printk("Missing IOAPIC route for vector!\n", apic_vector);
 489                return;
 490        }
 491        spin_lock(&rdt->apic->lock);
 492        /* this bit gets set in apicinit, only if we found it via MP or ACPI */
 493        if (!xlapic[hw_coreid].useable) {
 494                printk("Can't route to uninitialized LAPIC %d!\n", hw_coreid);
 495                spin_unlock(&rdt->apic->lock);
 496                return;
 497        }
 498        rdt->hi = hw_coreid << 24;
 499        rdt->lo |= Pm | MTf;
 500        rtblput(rdt->apic, rdt->intin, rdt->hi, rdt->lo);
 501        spin_unlock(&rdt->apic->lock);
 502}
 503
 504static void ioapic_mask_irq(struct irq_handler *unused, int apic_vector)
 505{
 506        /* could store the rdt in the irq_h */
 507        struct Rdt *rdt = ioapic_vector2rdt(apic_vector);
 508
 509        if (!rdt)
 510                return;
 511        spin_lock(&rdt->apic->lock);
 512        /* don't allow shared vectors to be masked.  whatever. */
 513        if (rdt->enabled > 1) {
 514                spin_unlock(&rdt->apic->lock);
 515                return;
 516        }
 517        rdt->lo |= Im;
 518        rtblput(rdt->apic, rdt->intin, rdt->hi, rdt->lo);
 519        spin_unlock(&rdt->apic->lock);
 520}
 521
 522static void ioapic_unmask_irq(struct irq_handler *unused, int apic_vector)
 523{
 524        struct Rdt *rdt = ioapic_vector2rdt(apic_vector);
 525        if (!rdt)
 526                return;
 527        spin_lock(&rdt->apic->lock);
 528        rdt->lo &= ~Im;
 529        rtblput(rdt->apic, rdt->intin, rdt->hi, rdt->lo);
 530        spin_unlock(&rdt->apic->lock);
 531}
 532
 533/* Attempts to init a bus interrupt, initializes irq_h, and returns the IDT
 534 * vector to use (-1 on error).  If routable, the IRQ will route to core 0.  The
 535 * IRQ will be masked, if possible.  Call irq_h->unmask() when you're ready.
 536 *
 537 * This will determine the type of bus the device is on (LAPIC, IOAPIC, PIC,
 538 * etc), and set the appropriate fields in isr_h.  If applicable, it'll also
 539 * allocate an IDT vector, such as for an IOAPIC, and route the IOAPIC entries
 540 * appropriately.
 541 *
 542 * Callers init irq_h->dev_irq and ->tbdf.  tbdf encodes the bus type and the
 543 * classic PCI bus:dev:func.  dev_irq may be ignored based on the bus type (e.g.
 544 * PCI, esp MSI).
 545 *
 546 * In plan9, this was ioapicintrenable(), which also unmasked.  We don't have a
 547 * deinit/disable method that would tear down the route yet.  All the plan9 one
 548 * did was dec enabled and mask the entry. */
 549int bus_irq_setup(struct irq_handler *irq_h)
 550{
 551        struct Rbus *rbus;
 552        struct Rdt *rdt;
 553        int busno, devno, vecno;
 554        struct pci_device *pcidev;
 555
 556        if (!ioapic_exists()) {
 557                switch (BUSTYPE(irq_h->tbdf)) {
 558                        case BusLAPIC:
 559                        case BusIPI:
 560                                break;
 561                        default:
 562                                irq_h->check_spurious = pic_check_spurious;
 563                                irq_h->eoi = pic_send_eoi;
 564                                irq_h->mask = pic_mask_irq;
 565                                irq_h->unmask = pic_unmask_irq;
 566                                irq_h->route_irq = 0;
 567                                irq_h->type = "pic";
 568                                /* PIC devices have vector = irq + 32 */
 569                                return irq_h->dev_irq + IdtPIC;
 570                }
 571        }
 572        switch (BUSTYPE(irq_h->tbdf)) {
 573        case BusLAPIC:
 574                /* nxm used to set the initial 'isr' method (i think equiv to
 575                 * our check_spurious) to apiceoi for non-spurious lapic
 576                 * vectors.  in effect, i think they were sending the EOI early,
 577                 * and their eoi method was 0.  we're not doing that (unless we
 578                 * have to). */
 579                irq_h->check_spurious = lapic_check_spurious;
 580                irq_h->eoi = lapic_send_eoi;
 581                irq_h->mask = lapic_mask_irq;
 582                irq_h->unmask = lapic_unmask_irq;
 583                irq_h->route_irq = 0;
 584                irq_h->type = "lapic";
 585                /* For the LAPIC, irq == vector */
 586                return irq_h->dev_irq;
 587        case BusIPI:
 588                /* similar to LAPIC, but we don't actually have LVT entries */
 589                irq_h->check_spurious = lapic_check_spurious;
 590                irq_h->eoi = lapic_send_eoi;
 591                irq_h->mask = 0;
 592                irq_h->unmask = 0;
 593                irq_h->route_irq = 0;
 594                irq_h->type = "IPI";
 595                return irq_h->dev_irq;
 596        case BusISA:
 597                if (mpisabusno == -1)
 598                        panic("No ISA bus allocated");
 599                busno = mpisabusno;
 600                /* need to track the irq in devno in PCI interrupt assignment
 601                 * entry format (see mp.c or MP spec D.3). */
 602                devno = irq_h->dev_irq << 2;
 603                break;
 604        case BusPCI:
 605                pcidev = pci_match_tbdf(irq_h->tbdf);
 606                if (!pcidev) {
 607                        warn("No PCI dev for tbdf %p!", irq_h->tbdf);
 608                        return -1;
 609                }
 610                if ((vecno = msi_irq_enable(irq_h, pcidev)) != -1)
 611                        return vecno;
 612                busno = BUSBNO(irq_h->tbdf);
 613                assert(busno == pcidev->bus);
 614                devno = pcidev_read8(pcidev, PciINTP);
 615
 616                /* this might not be a big deal - some PCI devices have no INTP.
 617                 * if so, change our devno - 1 below. */
 618                if (devno == 0)
 619                        panic("no INTP for tbdf %p", irq_h->tbdf);
 620                /* remember, devno is the device shifted with irq pin in bits
 621                 * 0-1.  we subtract 1, since the PCI intp maps 1 -> INTA, 2 ->
 622                 * INTB, etc, and the MP spec uses 0 -> INTA, 1 -> INTB, etc. */
 623                devno = BUSDNO(irq_h->tbdf) << 2 | (devno - 1);
 624                break;
 625        default:
 626                panic("Unknown bus type, TBDF %p", irq_h->tbdf);
 627        }
 628        /* busno and devno are set, regardless of the bustype, enough to find
 629         * rdt.  these may differ from the values in tbdf. */
 630        rdt = rbus_get_rdt(busno, devno);
 631        if (!rdt) {
 632                /* second chance.  if we didn't find the item the first time,
 633                 * then (if it exists at all), it wasn't in the MP tables (or we
 634                 * had no tables).  So maybe we can figure it out via ACPI. */
 635                acpi_make_rdt(irq_h->tbdf, irq_h->dev_irq, busno, devno);
 636                rdt = rbus_get_rdt(busno, devno);
 637        }
 638        if (!rdt) {
 639                printk("Unable to build IOAPIC route for irq %d\n",
 640                       irq_h->dev_irq);
 641                return -1;
 642        }
 643        /*
 644         * what to do about devices that intrenable/intrdisable frequently?
 645         * 1) there is no ioapicdisable yet;
 646         * 2) it would be good to reuse freed vectors.
 647         * Oh bugger.
 648         * brho: plus the diff btw mask/unmask and enable/disable is unclear
 649         */
 650        /*
 651         * This is a low-frequency event so just lock
 652         * the whole IOAPIC to initialise the RDT entry
 653         * rather than putting a Lock in each entry.
 654         */
 655        spin_lock(&rdt->apic->lock);
 656        /* if a destination has already been picked, we store it in the lo.
 657         * this stays around regardless of enabled/disabled, since we don't reap
 658         * vectors yet.  nor do we really mess with enabled... */
 659        if ((rdt->lo & 0xff) == 0) {
 660                vecno = nextvec();
 661                rdt->lo |= vecno;
 662                rdtvecno[vecno] = rdt;
 663        } else {
 664                printd("%p: mutiple irq bus %d dev %d\n", irq_h->tbdf, busno,
 665                       devno);
 666        }
 667        rdt->enabled++;
 668        rdt->hi = 0;                    /* route to 0 by default */
 669        rdt->lo |= Pm | MTf;
 670        rtblput(rdt->apic, rdt->intin, rdt->hi, rdt->lo);
 671        vecno = rdt->lo & 0xff;
 672        spin_unlock(&rdt->apic->lock);
 673
 674        irq_h->check_spurious = lapic_check_spurious;
 675        irq_h->eoi = lapic_send_eoi;
 676        irq_h->mask = ioapic_mask_irq;
 677        irq_h->unmask = ioapic_unmask_irq;
 678        irq_h->route_irq = ioapic_route_irq;
 679        irq_h->type = "ioapic";
 680
 681        return vecno;
 682}
 683