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