|  | /* | 
|  | * This file is part of the UCB release of Plan 9. It is subject to the license | 
|  | * terms in the LICENSE file found in the top-level directory of this | 
|  | * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No | 
|  | * part of the UCB release of Plan 9, including this file, may be copied, | 
|  | * modified, propagated, or distributed except according to the terms contained | 
|  | * in the LICENSE file. | 
|  | */ | 
|  |  | 
|  | #include <slab.h> | 
|  | #include <kmalloc.h> | 
|  | #include <kref.h> | 
|  | #include <string.h> | 
|  | #include <stdio.h> | 
|  | #include <assert.h> | 
|  | #include <error.h> | 
|  | #include <cpio.h> | 
|  | #include <pmap.h> | 
|  | #include <smp.h> | 
|  | #include <net/ip.h> | 
|  | #include <arch/io.h> | 
|  | #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;		/* 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 */ | 
|  | }; | 
|  |  | 
|  | enum {				/* IOAPIC registers */ | 
|  | Ioregsel = 0x00,	/* indirect register address */ | 
|  | Iowin = 0x10,		/* indirect register data */ | 
|  | Ioipa = 0x08,		/* IRQ Pin Assertion */ | 
|  | Ioeoi = 0x10,		/* EOI */ | 
|  |  | 
|  | Ioapicid = 0x00,	/* Identification */ | 
|  | Ioapicver = 0x01,	/* Version */ | 
|  | Ioapicarb = 0x02,	/* Arbitration */ | 
|  | Ioabcfg = 0x03,		/* Boot Coniguration */ | 
|  | Ioredtbl = 0x10,	/* Redirection Table */ | 
|  | }; | 
|  |  | 
|  | static struct Rdt rdtarray[Nrdt]; | 
|  | static int nrdtarray; | 
|  | static struct Rbus *rdtbus[Nbus]; | 
|  | /* reverse mapping of IDT vector to the RDT/IOAPIC entry triggering vector */ | 
|  | static struct Rdt *rdtvecno[IdtMAX + 1]; | 
|  |  | 
|  | struct apic xioapic[Napic]; | 
|  |  | 
|  | static bool ioapic_exists(void) | 
|  | { | 
|  | /* not foolproof, if we called this before parsing */ | 
|  | 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) | 
|  | { | 
|  | sel = Ioredtbl + 2 * sel; | 
|  |  | 
|  | write_mmreg32(apic->addr + Ioregsel, sel + 1); | 
|  | *hi = read_mmreg32(apic->addr + Iowin); | 
|  | write_mmreg32(apic->addr + Ioregsel, sel); | 
|  | *lo = read_mmreg32(apic->addr + Iowin); | 
|  | } | 
|  |  | 
|  | static void rtblput(struct apic *apic, int sel, uint32_t hi, uint32_t lo) | 
|  | { | 
|  | sel = Ioredtbl + 2 * sel; | 
|  |  | 
|  | write_mmreg32(apic->addr + Ioregsel, sel + 1); | 
|  | write_mmreg32(apic->addr + Iowin, hi); | 
|  | write_mmreg32(apic->addr + Ioregsel, sel); | 
|  | write_mmreg32(apic->addr + Iowin, lo); | 
|  | } | 
|  |  | 
|  | struct Rdt *rdtlookup(struct apic *apic, int intin) | 
|  | { | 
|  | int i; | 
|  | struct Rdt *r; | 
|  |  | 
|  | for (i = 0; i < nrdtarray; i++) { | 
|  | r = rdtarray + i; | 
|  | if (apic == r->apic && intin == r->intin) | 
|  | return r; | 
|  | } | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | 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 *ioapic; | 
|  |  | 
|  | if (busno >= Nbus || ioapicno >= Napic || nrdtarray >= Nrdt) { | 
|  | printk("Bad bus %d ioapic %d or nrdtarray %d too big\n", busno, | 
|  | ioapicno, nrdtarray); | 
|  | return; | 
|  | } | 
|  | 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(ioapic, intin); | 
|  | if (rdt == NULL) { | 
|  | rdt = &rdtarray[nrdtarray++]; | 
|  | rdt->apic = ioapic; | 
|  | rdt->intin = intin; | 
|  | rdt->lo = lo; | 
|  | rdt->hi = 0; | 
|  | } else { | 
|  | /* Polarity/trigger check.  Stored lo also has the vector in | 
|  | * 0xff */ | 
|  | if (lo != (rdt->lo & ~0xff)) { | 
|  | printk("multi-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; | 
|  | rbus->devno = devno; | 
|  | rbus->next = rdtbus[busno]; | 
|  | rdtbus[busno] = rbus; | 
|  | } | 
|  |  | 
|  | static int map_polarity[4] = { | 
|  | -1, IPhigh, -1, IPlow | 
|  | }; | 
|  |  | 
|  | static int map_edge_level[4] = { | 
|  | -1, TMedge, -1, TMlevel | 
|  | }; | 
|  |  | 
|  | 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) | 
|  | { | 
|  | struct Atable *at; | 
|  | struct Apicst *st, *lst; | 
|  | uint32_t lo; | 
|  | int pol, edge_level, ioapic_nr, gsi_irq; | 
|  |  | 
|  | at = apics; | 
|  | st = NULL; | 
|  | for (int i = 0; i < at->nchildren; i++) { | 
|  | lst = at->children[i]->tbl; | 
|  | if (lst->type == ASintovr) { | 
|  | if (lst->intovr.irq == irq) { | 
|  | st = lst; | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | 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; | 
|  | } | 
|  | } | 
|  | 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; | 
|  | } | 
|  | ioapicintrinit(busno, ioapic_nr, gsi_irq - xioapic[ioapic_nr].ibase, | 
|  | devno, lo); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void ioapicinit(int id, int ibase, uintptr_t pa) | 
|  | { | 
|  | struct apic *apic; | 
|  | static int base; | 
|  |  | 
|  | assert((IOAPIC_PBASE <= pa) && | 
|  | (pa + PGSIZE <= IOAPIC_PBASE + APIC_SIZE)); | 
|  | /* | 
|  | * Mark the IOAPIC useable if it has a good ID | 
|  | * and the registers can be mapped. | 
|  | */ | 
|  | if (id >= Napic) | 
|  | return; | 
|  |  | 
|  | apic = &xioapic[id]; | 
|  | apic->addr = IOAPIC_BASE + (pa - IOAPIC_PBASE); | 
|  | if (apic->useable) | 
|  | return; | 
|  | apic->useable = 1; | 
|  | apic->paddr = pa; | 
|  |  | 
|  | /* | 
|  | * Initialise the I/O APIC. | 
|  | * The MultiProcessor Specification says it is the | 
|  | * responsibility of the O/S to set the APIC ID. | 
|  | */ | 
|  | 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 { | 
|  | apic->ibase = base; | 
|  | base += apic->nrdt; | 
|  | } | 
|  | write_mmreg32(apic->addr + Ioregsel, Ioapicid); | 
|  | write_mmreg32(apic->addr + Iowin, id << 24); | 
|  | spin_unlock(&apic->lock); | 
|  | printk("IOAPIC initialized at %p, nrdt %d, ibase %d\n", apic->addr, | 
|  | apic->nrdt, apic->ibase); | 
|  | } | 
|  |  | 
|  | char *ioapicdump(char *start, char *end) | 
|  | { | 
|  | int i, n; | 
|  | struct Rbus *rbus; | 
|  | struct Rdt *rdt; | 
|  | struct apic *apic; | 
|  | uint32_t hi, lo; | 
|  |  | 
|  | if (!2) | 
|  | return start; | 
|  | for (i = 0; i < Napic; i++) { | 
|  | apic = &xioapic[i]; | 
|  | if (!apic->useable || apic->addr == 0) | 
|  | continue; | 
|  | 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); | 
|  | rtblget(apic, n, &hi, &lo); | 
|  | spin_unlock(&apic->lock); | 
|  | start = seprintf(start, end, " rdt %2.2d %p %p\n", | 
|  | n, hi, lo); | 
|  | } | 
|  | } | 
|  | for (i = 0; i < Nbus; i++) { | 
|  | if ((rbus = rdtbus[i]) == NULL) | 
|  | continue; | 
|  | start = seprintf(start, end, "iointr bus %d:\n", i); | 
|  | for (; rbus != NULL; rbus = rbus->next) { | 
|  | rdt = rbus->rdt; | 
|  | start = seprintf(start, end, | 
|  | " 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->hi, rdt->lo); | 
|  | } | 
|  | } | 
|  | return start; | 
|  | } | 
|  |  | 
|  | /* Zeros and masks every redirect entry in every IOAPIC */ | 
|  | void ioapiconline(void) | 
|  | { | 
|  | int i; | 
|  | struct apic *apic; | 
|  |  | 
|  | for (apic = xioapic; apic < &xioapic[Napic]; apic++) { | 
|  | if (!apic->useable || !apic->addr) | 
|  | continue; | 
|  | for (i = 0; i < apic->nrdt; i++) { | 
|  | spin_lock(&apic->lock); | 
|  | rtblput(apic, i, 0, Im); | 
|  | spin_unlock(&apic->lock); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | static void msi_mask_irq(struct irq_handler *irq_h, int apic_vector) | 
|  | { | 
|  | pci_msi_mask(irq_h->dev_private); | 
|  | } | 
|  |  | 
|  | static void msi_unmask_irq(struct irq_handler *irq_h, int apic_vector) | 
|  | { | 
|  | pci_msi_unmask(irq_h->dev_private); | 
|  | } | 
|  |  | 
|  | static void msi_route_irq(struct irq_handler *irq_h, int apic_vector, int dest) | 
|  | { | 
|  | pci_msi_route(irq_h->dev_private, dest); | 
|  | } | 
|  |  | 
|  | static void msi_cleanup_irq(struct irq_handler *irq_h) | 
|  | { | 
|  | pci_msi_reset_vector(irq_h->dev_private); | 
|  | put_irq_vector(irq_h->apic_vector); | 
|  | } | 
|  |  | 
|  | 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 void msix_cleanup_irq(struct irq_handler *irq_h) | 
|  | { | 
|  | pci_msix_reset_vector(irq_h->dev_private); | 
|  | kfree(irq_h->dev_private); | 
|  | put_irq_vector(irq_h->apic_vector); | 
|  | } | 
|  |  | 
|  | 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 = get_irq_vector(); | 
|  | if (!vno) { | 
|  | printk("[kernel] Unable to get a vector for MSI(X)!\n"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | /* routing the IRQ to core 0 (hi = 0) in physical mode (Pm) */ | 
|  | lo = IPlow | TMedge | Pm | vno; | 
|  |  | 
|  | msivec = (uint64_t) hi << 32 | lo; | 
|  | irq_h->dev_private = pci_msix_enable(p, msivec); | 
|  | if (!irq_h->dev_private) { | 
|  | if (pci_msi_enable(p, msivec) == -1) { | 
|  | put_irq_vector(vno); | 
|  | 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->cleanup = msi_cleanup_irq; | 
|  | irq_h->type = "msi"; | 
|  | printk("MSI irq: (%02x:%02x.%x): %s vector %d\n", | 
|  | p->bus, p->dev, p->func, 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->cleanup = msix_cleanup_irq; | 
|  | irq_h->type = "msi-x"; | 
|  | printk("MSI-X irq: (%02x,%02x,%x): %s vector %d\n", | 
|  | p->bus, p->dev, p->func, irq_h->name, vno); | 
|  | return vno; | 
|  | } | 
|  |  | 
|  | static struct Rdt *ioapic_vector2rdt(int apic_vector) | 
|  | { | 
|  | struct Rdt *rdt; | 
|  |  | 
|  | if (apic_vector < IdtIOAPIC || apic_vector > MaxIdtIOAPIC) { | 
|  | warn("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) { | 
|  | warn("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); | 
|  | } | 
|  |  | 
|  | 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); | 
|  | } | 
|  |  | 
|  | 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 | 
|  | * allocate an IDT vector, such as for an IOAPIC, and route the IOAPIC entries | 
|  | * appropriately. | 
|  | * | 
|  | * Callers init irq_h->dev_irq and ->tbdf.  tbdf encodes the bus type and the | 
|  | * 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 | 
|  | * did was dec enabled and mask the entry. */ | 
|  | int bus_irq_setup(struct irq_handler *irq_h) | 
|  | { | 
|  | struct Rbus *rbus; | 
|  | struct Rdt *rdt; | 
|  | 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: | 
|  | /* nxm used to set the initial 'isr' method (i think equiv to | 
|  | * our check_spurious) to apiceoi for non-spurious lapic | 
|  | * vectors.  in effect, i think they were sending the EOI early, | 
|  | * and their eoi 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 = 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: | 
|  | if (mpisabusno == -1) | 
|  | 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: | 
|  | pcidev = pci_match_tbdf(irq_h->tbdf); | 
|  | if (!pcidev) { | 
|  | warn("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); | 
|  | 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.  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: | 
|  | panic("Unknown bus type, TBDF %p", irq_h->tbdf); | 
|  | } | 
|  | /* 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) { | 
|  | printk("Unable to build IOAPIC route for irq %d\n", | 
|  | irq_h->dev_irq); | 
|  | return -1; | 
|  | } | 
|  | /* | 
|  | * 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 | 
|  | * the whole IOAPIC to initialise the RDT entry | 
|  | * rather than putting a Lock in each entry. | 
|  | */ | 
|  | spin_lock(&rdt->apic->lock); | 
|  | /* 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 = get_irq_vector(); | 
|  | if (!vecno) { | 
|  | printk("[kernel] unable to get an IOAPIC vector\n"); | 
|  | spin_unlock(&rdt->apic->lock); | 
|  | return -1; | 
|  | } | 
|  | rdt->lo |= vecno; | 
|  | rdtvecno[vecno] = rdt; | 
|  | } else { | 
|  | printd("%p: mutiple irq bus %d dev %d\n", irq_h->tbdf, busno, | 
|  | devno); | 
|  | } | 
|  | rdt->enabled++; | 
|  | 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); | 
|  |  | 
|  | irq_h->check_spurious = lapic_check_spurious; | 
|  | irq_h->eoi = lapic_send_eoi; | 
|  | 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; | 
|  | } |