| /* |
| * 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]; |
| |
| static spinlock_t idtnolock; |
| static int idtno = IdtIOAPIC; |
| |
| 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); |
| } |
| } |
| } |
| |
| int nextvec(void) |
| { |
| unsigned int vecno; |
| |
| /* TODO: half-way decent integer service (vmem) */ |
| spin_lock(&idtnolock); |
| vecno = idtno; |
| idtno = (idtno + 1) % IdtMAX; |
| if (idtno < IdtIOAPIC) |
| idtno += IdtIOAPIC; |
| spin_unlock(&idtnolock); |
| |
| return vecno; |
| } |
| |
| 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 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(); |
| |
| /* 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) { |
| /* 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; |
| } |
| |
| 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 = nextvec(); |
| 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; |
| } |