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