x86: can use the IOAPIC even without MP tables
[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;                            /* should match the lo in the intin */
39                                                                 /* don't have a hi for some reason */
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
69 static bool ioapic_exists(void)
70 {
71         /* not foolproof, if we called this before parsing */
72         return xioapic[0].useable ? TRUE : FALSE;
73 }
74
75 /* TODO: put these in a header */
76 int apiceoi(int);
77 int apicisr(int);
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         } else {
155                 if (lo != rdt->lo) {
156                         printk("mutiple irq botch bus %d %d/%d/%d lo %d vs %d\n",
157                                    busno, ioapicno, intin, devno, lo, rdt->lo);
158                         return;
159                 }
160         }
161         rdt->ref++;
162         rbus = kzmalloc(sizeof *rbus, 0);
163         rbus->rdt = rdt;
164         rbus->devno = devno;
165         rbus->next = rdtbus[busno];
166         rdtbus[busno] = rbus;
167 }
168
169 static int map_polarity[4] = {
170         -1, IPhigh, -1, IPlow
171 };
172
173 static int map_edge_level[4] = {
174         -1, TMedge, -1, TMlevel
175 };
176
177 static int acpi_irq2ioapic(int irq)
178 {
179         int ioapic_idx = 0;
180         struct apic *ioapic;
181         /* with acpi, the ioapics map a global interrupt space.  each covers a
182          * window of the space from [ibase, ibase + nrdt). */
183         for (ioapic = xioapic; ioapic < &xioapic[Napic]; ioapic++, ioapic_idx++) {
184                 /* addr check is just for sanity */
185                 if (!ioapic->useable || !ioapic->addr)
186                         continue;
187                 if ((ioapic->ibase <= irq) && (irq < ioapic->ibase + ioapic->nrdt))
188                         return ioapic_idx;
189         }
190         return -1;
191 }
192
193 /* Build an RDT route, like we would have had from the MP tables had they been
194  * parsed, via ACPI.
195  *
196  * This only really deals with the ISA IRQs and maybe PCI ones that happen to
197  * have an override.  FWIW, on qemu the PCI NIC shows up as an ACPI intovr.
198  *
199  * From Brendan http://f.osdev.org/viewtopic.php?f=1&t=25951:
200  *
201  *              Before parsing the MADT you should begin by assuming that redirection
202  *              entries 0 to 15 are used for ISA IRQs 0 to 15. The MADT's "Interrupt
203  *              Source Override Structures" will tell you when this initial/default
204  *              assumption is wrong. For example, the MADT might tell you that ISA IRQ 9
205  *              is connected to IO APIC 44 and is level triggered; and (in this case)
206  *              it'd be silly to assume that ISA IRQ 9 is also connected to IO APIC
207  *              input 9 just because IO APIC input 9 is not listed.
208  *
209  *              For PCI IRQs, the MADT tells you nothing and you can't assume anything
210  *              at all. Sadly, you have to interpret the ACPI AML to determine how PCI
211  *              IRQs are connected to IO APIC inputs (or find some other work-around;
212  *              like implementing a motherboard driver for each different motherboard,
213  *              or some complex auto-detection scheme, or just configure PCI devices to
214  *              use MSI instead). */
215 static int acpi_make_rdt(int tbdf, int irq, int busno, int devno)
216 {
217         struct Apicst *st;
218         uint32_t lo;
219         int pol, edge_level, ioapic_nr, gsi_irq;
220
221         for (st = apics->st; st != NULL; st = st->next) {
222                 if (st->type == ASintovr) {
223                         if (st->intovr.irq == irq)
224                                 break;
225                 }
226         }
227         if (st) {
228                 pol = map_polarity[st->intovr.flags & AFpmask];
229                 if (pol < 0) {
230                         printk("ACPI override had bad polarity\n");
231                         return -1;
232                 }
233                 edge_level = map_edge_level[(st->intovr.flags & AFlevel) >> 2];
234                 if (edge_level < 0) {
235                         printk("ACPI override had bad edge/level\n");
236                         return -1;
237                 }
238                 lo = pol | edge_level;
239                 gsi_irq = st->intovr.intr;
240         } else {
241                 if (BUSTYPE(tbdf) == BusISA) {
242                         lo = IPhigh | TMedge;
243                         gsi_irq = irq;
244                 } else {
245                         /* Need to query ACPI at some point to handle this */
246                         printk("Non-ISA IRQ %d not found in MADT", irq);
247                         if (BUSTYPE(tbdf) != BusPCI) {
248                                 printk(", aborting...\n");
249                                 return -1;
250                         }
251                         /* Going to just guess some values for PCI */
252                         printk(", guessing...\n");
253                         lo = IPlow | TMlevel;
254                         gsi_irq = irq;
255                 }
256         }
257         ioapic_nr = acpi_irq2ioapic(gsi_irq);
258         if (ioapic_nr < 0) {
259                 printk("Could not find an IOAPIC for global irq %d!\n", gsi_irq);
260                 return -1;
261         }
262         ioapicintrinit(busno, ioapic_nr, gsi_irq - xioapic[ioapic_nr].ibase,
263                        devno, lo);
264         return 0;
265 }
266
267 void ioapicinit(int id, int ibase, uintptr_t pa)
268 {
269         struct apic *apic;
270         static int base;
271
272         assert((IOAPIC_PBASE <= pa) && (pa + PGSIZE <= IOAPIC_PBASE + APIC_SIZE));
273         /*
274          * Mark the IOAPIC useable if it has a good ID
275          * and the registers can be mapped.
276          */
277         if (id >= Napic)
278                 return;
279
280         apic = &xioapic[id];
281         apic->addr = IOAPIC_BASE + (pa - IOAPIC_PBASE);
282         if (apic->useable)
283                 return;
284         apic->useable = 1;
285         printk("\t\tioapicinit %d: it's useable, apic %p\n", id, apic);
286         apic->paddr = pa;
287
288         /*
289          * Initialise the I/O APIC.
290          * The MultiProcessor Specification says it is the
291          * responsibility of the O/S to set the APIC ID.
292          */
293         spin_lock(&apic->lock);
294         write_mmreg32(apic->addr + Ioregsel, Ioapicver);
295         apic->nrdt = ((read_mmreg32(apic->addr + Iowin) >> 16) & 0xff) + 1;
296         if (ibase != -1)
297                 apic->ibase = ibase;
298         else {
299                 apic->ibase = base;
300                 base += apic->nrdt;
301         }
302         write_mmreg32(apic->addr + Ioregsel, Ioapicid);
303         write_mmreg32(apic->addr + Iowin, id << 24);
304         spin_unlock(&apic->lock);
305 }
306
307 char *ioapicdump(char *start, char *end)
308 {
309         int i, n;
310         struct Rbus *rbus;
311         struct Rdt *rdt;
312         struct apic *apic;
313         uint32_t hi, lo;
314
315         if (!2)
316                 return start;
317         for (i = 0; i < Napic; i++) {
318                 apic = &xioapic[i];
319                 if (!apic->useable || apic->addr == 0)
320                         continue;
321                 start = seprintf(start, end, "ioapic %d addr %#p nrdt %d ibase %d\n",
322                                                  i, apic->addr, apic->nrdt, apic->ibase);
323                 for (n = 0; n < apic->nrdt; n++) {
324                         spin_lock(&apic->lock);
325                         rtblget(apic, n, &hi, &lo);
326                         spin_unlock(&apic->lock);
327                         start = seprintf(start, end, " rdt %2.2d %p %p\n", n, hi, lo);
328                 }
329         }
330         for (i = 0; i < Nbus; i++) {
331                 if ((rbus = rdtbus[i]) == NULL)
332                         continue;
333                 start = seprintf(start, end, "iointr bus %d:\n", i);
334                 for (; rbus != NULL; rbus = rbus->next) {
335                         rdt = rbus->rdt;
336                         start = seprintf(start, end,
337                                                          " apic %ld devno %#p (%d %d) intin %d lo %#p ref %d\n",
338                                                          rdt->apic - xioapic, rbus->devno, rbus->devno >> 2,
339                                                          rbus->devno & 0x03, rdt->intin, rdt->lo, rdt->ref);
340                 }
341         }
342         return start;
343 }
344
345 /* Zeros and masks every redirect entry in every IOAPIC */
346 void ioapiconline(void)
347 {
348         int i;
349         struct apic *apic;
350
351         for (apic = xioapic; apic < &xioapic[Napic]; apic++) {
352                 if (!apic->useable || !apic->addr)
353                         continue;
354                 for (i = 0; i < apic->nrdt; i++) {
355                         spin_lock(&apic->lock);
356                         rtblput(apic, i, 0, Im);
357                         spin_unlock(&apic->lock);
358                 }
359         }
360 }
361
362 static int dfpolicy = 0;
363
364 static void ioapicintrdd(uint32_t * hi, uint32_t * lo)
365 {
366         int i;
367         static int df;
368         static spinlock_t dflock;
369
370         /*
371          * Set delivery mode (lo) and destination field (hi),
372          * according to interrupt routing policy.
373          */
374         /*
375          * The bulk of this code was written ~1995, when there was
376          * one architecture and one generation of hardware, the number
377          * of CPUs was up to 4(8) and the choices for interrupt routing
378          * were physical, or flat logical (optionally with lowest
379          * priority interrupt). Logical mode hasn't scaled well with
380          * the increasing number of packages/cores/threads, so the
381          * fall-back is to physical mode, which works across all processor
382          * generations, both AMD and Intel, using the APIC and xAPIC.
383          *
384          * Interrupt routing policy can be set here.
385          */
386         switch (dfpolicy) {
387                 default:        /* noise core 0 */
388 #warning "sys->machptr[0]->apicno --- what is this in Akaros?"
389                         *hi = 0;        //sys->machptr[0]->apicno<<24;
390                         break;
391                 case 1: /* round-robin */
392                         /*
393                          * Assign each interrupt to a different CPU on a round-robin
394                          * Some idea of the packages/cores/thread topology would be
395                          * useful here, e.g. to not assign interrupts to more than one
396                          * thread in a core. But, as usual, Intel make that an onerous
397                          * task.
398                          */
399                         spin_lock(&dflock);
400                         for (;;) {
401 #if 0
402                                 i = df++;
403                                 if (df >= sys->nmach + 1)
404                                         df = 0;
405                                 if (sys->machptr[i] == NULL || !sys->machptr[i]->online)
406                                         continue;
407                                 i = sys->machptr[i]->apicno;
408 #endif
409 #warning "always picking acpino 0"
410                                 i = 0;
411                                 if (xlapic[i].useable && xlapic[i].addr == 0)
412                                         break;
413                         }
414                         spin_unlock(&dflock);
415
416                         *hi = i << 24;
417                         break;
418         }
419         *lo |= Pm | MTf;
420 }
421
422 int nextvec(void)
423 {
424         unsigned int vecno;
425
426         /* TODO: half-way decent integer service (vmem) */
427         spin_lock(&idtnolock);
428         vecno = idtno;
429         idtno = (idtno + 1) % IdtMAX;
430         if (idtno < IdtIOAPIC)
431                 idtno += IdtIOAPIC;
432         spin_unlock(&idtnolock);
433
434         return vecno;
435 }
436
437 #warning "no msi mask yet"
438 #if 0
439 static int msimask(struct Vkey *v, int mask)
440 {
441         Pcidev *p;
442
443         p = pcimatchtbdf(v->tbdf);
444         if (p == NULL)
445                 return -1;
446         return pcimsimask(p, mask);
447 }
448 #endif
449
450 #warning "No msi yet"
451 #if 0
452 static int intrenablemsi(struct vctl *v, Pcidev * p)
453 {
454         unsigned int vno, lo, hi;
455         uint64_t msivec;
456
457         vno = nextvec();
458
459         lo = IPlow | TMedge | vno;
460         ioapicintrdd(&hi, &lo);
461
462         if (lo & Lm)
463                 lo |= MTlp;
464
465         msivec = (uint64_t) hi << 32 | lo;
466         if (pcimsienable(p, msivec) == -1)
467                 return -1;
468         v->isr = apicisr;
469         v->eoi = apiceoi;
470         v->vno = vno;
471         v->type = "msi";
472         v->mask = msimask;
473
474         printk("msiirq: %T: enabling %.16llp %s irq %d vno %d\n", p->tbdf, msivec,
475                    v->name, v->irq, vno);
476         return vno;
477 }
478 #endif
479 #warning "no disable msi yet"
480 #if 0
481 int disablemsi(Vctl *, Pcidev * p)
482 {
483         if (p == NULL)
484                 return -1;
485         return pcimsimask(p, 1);
486 }
487 #endif
488
489
490 /* Attempts to enable a bus interrupt, initializes irq_h, and returns the IDT
491  * vector to use (-1 on error).
492  *
493  * This will determine the type of bus the device is on (LAPIC, IOAPIC, PIC,
494  * etc), and set the appropriate fields in isr_h.  If applicable, it'll also
495  * allocate an IDT vector, such as for an IOAPIC, and route the IOAPIC entries
496  * appropriately.
497  *
498  * Callers init irq_h->dev_irq and ->tbdf.  tbdf encodes the bus type and the
499  * classic PCI bus:dev:func.
500  *
501  * In plan9, this was ioapicintrenable(). */
502 int bus_irq_enable(struct irq_handler *irq_h)
503 {
504         struct Rbus *rbus;
505         struct Rdt *rdt;
506         uint32_t hi, lo;
507         int busno = 0, devno, vecno;
508         struct pci_device pcidev;
509
510         if (!ioapic_exists() && (BUSTYPE(irq_h->tbdf) != BusLAPIC)) {
511                 irq_h->check_spurious = pic_check_spurious;
512                 irq_h->eoi = pic_send_eoi;
513                 irq_h->mask = pic_mask_irq;
514                 irq_h->unmask = pic_unmask_irq;
515                 irq_h->type = "pic";
516                 /* PIC devices have vector = irq + 32 */
517                 return irq_h->dev_irq + IdtPIC;
518         }
519         switch (BUSTYPE(irq_h->tbdf)) {
520                 case BusLAPIC:
521                         /* nxm used to set the initial 'isr' method (i think equiv to our
522                          * check_spurious) to apiceoi for non-spurious lapic vectors.  in
523                          * effect, i think they were sending the EOI early, and their eoi
524                          * method was 0.  we're not doing that (unless we have to). */
525                         irq_h->check_spurious = lapic_check_spurious;
526                         irq_h->eoi = lapic_send_eoi;
527                         irq_h->mask = 0;
528                         irq_h->unmask = 0;
529                         irq_h->type = "lapic";
530                         /* For the LAPIC, irq == vector */
531                         return irq_h->dev_irq;
532                 case BusISA:
533                         if (mpisabusno == -1)
534                                 panic("No ISA bus allocated");
535                         busno = mpisabusno;
536                         /* need to track the irq in devno in PCI interrupt assignment entry
537                          * format (see mp.c or MP spec D.3). */
538                         devno = irq_h->dev_irq << 2;
539                         break;
540                 case BusPCI:
541                         /* we'll assume it's there. */
542 #if 0
543                         Pcidev *pcidev;
544
545                         busno = BUSBNO(irq_h->tbdf);
546                         if ((pcidev = pcimatchtbdf(irq_h->tbdf)) == NULL)
547                                 panic("no PCI dev for tbdf %p", irq_h->tbdf);
548                         if ((vecno = intrenablemsi(irq_h, pcidev)) != -1)
549                                 return vecno;
550                         disablemsi(irq_h, pcidev);
551 #endif
552                         explode_tbdf(irq_h->tbdf);
553                         devno = pcidev_read8(&pcidev, PciINTP);
554                         printk("INTP is %d\n", devno);
555
556                         if (devno == 0)
557                                 panic("no INTP for tbdf %p", irq_h->tbdf);
558                         /* remember, devno is the device shifted with irq pin in bits 0-1 */
559                         devno = BUSDNO(irq_h->tbdf) << 2 | (devno - 1);
560                         printk("devno is %08lx\n", devno);
561                         printk("bus_irq_enable: tbdf %p busno %d devno %d\n",
562                                    irq_h->tbdf, busno, devno);
563                         break;
564                 default:
565                         panic("Unknown bus type, TBDF %p", irq_h->tbdf);
566         }
567         /* busno and devno are set, regardless of the bustype, enough to find rdt.
568          * these may differ from the values in tbdf. */
569         rdt = rbus_get_rdt(busno, devno);
570         if (!rdt) {
571                 /* second chance.  if we didn't find the item the first time, then (if
572                  * it exists at all), it wasn't in the MP tables (or we had no tables).
573                  * So maybe we can figure it out via ACPI. */
574                 acpi_make_rdt(irq_h->tbdf, irq_h->dev_irq, busno, devno);
575                 rdt = rbus_get_rdt(busno, devno);
576         }
577         if (!rdt) {
578                 printk("Unable to build IOAPIC route for irq %d\n", irq_h->dev_irq);
579                 return -1;
580         }
581
582         /*
583          * what to do about devices that intrenable/intrdisable frequently?
584          * 1) there is no ioapicdisable yet;
585          * 2) it would be good to reuse freed vectors.
586          * Oh bugger.
587          */
588         /*
589          * This is a low-frequency event so just lock
590          * the whole IOAPIC to initialise the RDT entry
591          * rather than putting a Lock in each entry.
592          */
593         spin_lock(&rdt->apic->lock);
594         if ((rdt->lo & 0xff) == 0) {
595                 vecno = nextvec();
596                 rdt->lo |= vecno;
597                 rdtvecno[vecno] = rdt;
598         } else {
599                 printd("%p: mutiple irq bus %d dev %d\n", irq_h->tbdf, busno, devno);
600         }
601
602         rdt->enabled++;
603         lo = (rdt->lo & ~Im);
604         ioapicintrdd(&hi, &lo); // XXX picks a destination and sets phys/fixed
605         rtblput(rdt->apic, rdt->intin, hi, lo);
606         vecno = lo & 0xff;
607         spin_unlock(&rdt->apic->lock);
608
609         irq_h->check_spurious = lapic_check_spurious;
610         irq_h->eoi = lapic_send_eoi;
611         irq_h->mask = 0;        /* TODO */
612         irq_h->unmask = 0;
613         irq_h->type = "ioapic";
614
615         return vecno;
616 }
617
618 int ioapicintrdisable(int vecno)
619 {
620         struct Rdt *rdt;
621
622         /*
623          * FOV. Oh dear. This isn't very good.
624          * Fortunately rdtvecno[vecno] is static
625          * once assigned.
626          * Must do better.
627          *
628          * What about any pending interrupts?
629          */
630         if (vecno < 0 || vecno > MaxIdtIOAPIC) {
631                 panic("ioapicintrdisable: vecno %d out of range", vecno);
632                 return -1;
633         }
634         if ((rdt = rdtvecno[vecno]) == NULL) {
635                 // XXX what if they asked for a LAPIC vector?
636                 panic("ioapicintrdisable: vecno %d has no rdt", vecno);
637                 return -1;
638         }
639
640         spin_lock(&rdt->apic->lock);
641         rdt->enabled--;
642         /* XXX looks like they think rdt->lo is supposed to have Im set/masked?  and
643          * they also blow away their 'hi' routing decision from earlier */
644         if (rdt->enabled == 0)
645                 rtblput(rdt->apic, rdt->intin, 0, rdt->lo);
646         spin_unlock(&rdt->apic->lock);
647
648         return 0;
649 }