Various APIC debugging and IOAPIC IRQ routing
[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
26 struct Rbus {
27         struct Rbus *next;
28         int devno;
29         struct Rdt *rdt;
30 };
31
32 struct Rdt {
33         struct apic *apic;
34         int intin;
35         uint32_t lo;
36
37         int ref;                                        /* could map to multiple busses */
38         int enabled;                            /* times enabled */
39 };
40
41 enum {                                                  /* IOAPIC registers */
42         Ioregsel = 0x00,                        /* indirect register address */
43         Iowin = 0x04,   /* indirect register data */
44         Ioipa = 0x08,   /* IRQ Pin Assertion */
45         Ioeoi = 0x10,   /* EOI */
46
47         Ioapicid = 0x00,        /* Identification */
48         Ioapicver = 0x01,       /* Version */
49         Ioapicarb = 0x02,       /* Arbitration */
50         Ioabcfg = 0x03, /* Boot Coniguration */
51         Ioredtbl = 0x10,        /* Redirection Table */
52 };
53
54 static struct Rdt rdtarray[Nrdt];
55 static int nrdtarray;
56 static struct Rbus *rdtbus[Nbus];
57 static struct Rdt *rdtvecno[IdtMAX + 1];
58
59 static spinlock_t idtnolock;
60 static int idtno = IdtIOAPIC;
61
62 struct apic xioapic[Napic];
63 int apiceoi(int);
64 int apicisr(int);
65 static void rtblget(struct apic *apic, int sel, uint32_t * hi, uint32_t * lo)
66 {
67         sel = Ioredtbl + 2 * sel;
68
69         *(apic->addr + Ioregsel) = sel + 1;
70         *hi = *(apic->addr + Iowin);
71         *(apic->addr + Ioregsel) = sel;
72         *lo = *(apic->addr + Iowin);
73 }
74
75 static void rtblput(struct apic *apic, int sel, uint32_t hi, uint32_t lo)
76 {
77         sel = Ioredtbl + 2 * sel;
78
79         *(apic->addr + Ioregsel) = sel + 1;
80         *(apic->addr + Iowin) = hi;
81         *(apic->addr + Ioregsel) = sel;
82         *(apic->addr + Iowin) = lo;
83 }
84
85 struct Rdt *rdtlookup(struct apic *apic, int intin)
86 {
87         int i;
88         struct Rdt *r;
89
90         for (i = 0; i < nrdtarray; i++) {
91                 r = rdtarray + i;
92                 if (apic == r->apic && intin == r->intin)
93                         return r;
94         }
95         return NULL;
96 }
97
98 /* busno is the source bus
99  * apic is the destination apic
100  * intin is the INTIN pin on the destination apic
101  * devno is the device number in the style of a PCI Interrupt
102  * Assignment Entry. Which is devno << 2? 
103  * lo is the vector table entry. We need to figure out how
104  * to compute this from acpi. We used to get it from the
105  * mptable but we would like to avoid that.
106  */
107 void ioapicintrinit(int busno, int apicno, int intin, int devno, int lo)
108 {
109         struct Rbus *rbus;
110         struct Rdt *rdt;
111         struct apic *apic;
112         printk("%s: busno %d apicno %d intin %d devno %p lo %p\n", __func__,
113                    busno, apicno, intin, devno, lo);
114
115         if (busno >= Nbus || apicno >= Napic || nrdtarray >= Nrdt) {
116                 printk("FAIL 1\n");
117                 return;
118         }
119         apic = &xioapic[apicno];
120         if (!apic->useable || intin >= apic->nrdt) {
121                 printk("apic->usable %d intin %d apic->nrdt %d OOR\n", apic->useable,
122                            intin, apic->nrdt);
123                 printk("apicno %d, apic %p\n", apicno, apic);
124                 monitor(0);
125                 return;
126         }
127
128         rdt = rdtlookup(apic, intin);
129         if (rdt == NULL) {
130                 printk("NO RDT, install it for apic %d intin %d lo %p\n", apicno, intin,
131                            lo);
132                 rdt = &rdtarray[nrdtarray++];
133                 rdt->apic = apic;
134                 rdt->intin = intin;
135                 rdt->lo = lo;
136         } else {
137                 if (lo != rdt->lo) {
138                         printd("mutiple irq botch bus %d %d/%d/%d lo %d vs %d\n",
139                                    busno, apicno, intin, devno, lo, rdt->lo);
140                         return;
141                 }
142                 printk("dup rdt %d %d %d %d %.8p\n", busno, apicno, intin, devno, lo);
143         }
144         rdt->ref++;
145         rbus = kzmalloc(sizeof *rbus, 0);
146         rbus->rdt = rdt;
147         rbus->devno = devno;
148         rbus->next = rdtbus[busno];
149         rdtbus[busno] = rbus;
150 }
151
152 static int map_polarity[4] = {
153         -1, IPhigh, -1, IPlow
154 };
155
156 static int map_edge_level[4] = {
157         -1, TMedge, -1, TMlevel
158 };
159
160 int ioapic_route_irq(int irq, int apicno, int devno)
161 {
162         extern struct Madt *apics;
163         struct Madt *a = apics;
164         struct Apicst *st;
165         uint32_t lo;
166         int pol, edge_level;
167         printk("%s(%d,%d);\n", __func__, irq, apicno);
168         /* find it. */
169         for (st = apics->st; st != NULL; st = st->next) {
170                 printk("Check %d, ", st->type);
171                 if (st->type == ASintovr) {
172                         printk("irq of st is %d\n", st->intovr.irq);
173                         if (st->intovr.irq == irq)
174                                 break;
175                 }
176         }
177         if (!st) {
178                 printk("IRQ %d not found in MADT\n", irq);
179                 return -1;
180         }
181
182         pol = map_polarity[st->intovr.flags & AFpmask];
183         if (pol < 0) {
184                 printk("BAD POLARITY\n");
185                 return -1;
186         }
187
188         edge_level = map_edge_level[(st->intovr.flags & AFlevel) >> 2];
189         if (edge_level < 0) {
190                 printk("BAD edge/level\n");
191                 return -1;
192         }
193         lo = pol | edge_level;
194         ioapicintrinit(0, 8, 0 /*st->intovr.intr */ , devno, lo);
195         printk("FOUND the MADT for %d\n", irq);
196         return 0;
197 }
198
199 void ioapicinit(int id, int ibase, uintptr_t pa)
200 {
201         struct apic *apic;
202         static int base;
203
204         assert(pa == IOAPIC_PBASE);
205         /*
206          * Mark the IOAPIC useable if it has a good ID
207          * and the registers can be mapped.
208          */
209         if (id >= Napic)
210                 return;
211
212         apic = &xioapic[id];
213         apic->addr = IOAPIC_BASE;
214         if (apic->useable)
215                 return;
216         apic->useable = 1;
217         printk("\t\tioapicinit %d: it's useable, apic %p\n", id, apic);
218         apic->paddr = pa;
219
220         /*
221          * Initialise the I/O APIC.
222          * The MultiProcessor Specification says it is the
223          * responsibility of the O/S to set the APIC ID.
224          */
225         spin_lock(&apic->lock);
226         *(apic->addr + Ioregsel) = Ioapicver;
227         apic->nrdt = ((*(apic->addr + Iowin) >> 16) & 0xff) + 1;
228         if (ibase != -1)
229                 apic->ibase = ibase;
230         else {
231                 apic->ibase = base;
232                 base += apic->nrdt;
233         }
234         *(apic->addr + Ioregsel) = Ioapicid;
235         *(apic->addr + Iowin) = id << 24;
236         spin_unlock(&apic->lock);
237 }
238
239 char *ioapicdump(char *start, char *end)
240 {
241         int i, n;
242         struct Rbus *rbus;
243         struct Rdt *rdt;
244         struct apic *apic;
245         uint32_t hi, lo;
246
247         if (!2)
248                 return start;
249         for (i = 0; i < Napic; i++) {
250                 apic = &xioapic[i];
251                 if (!apic->useable || apic->addr == 0)
252                         continue;
253                 start = seprintf(start, end, "ioapic %d addr %#p nrdt %d ibase %d\n",
254                                                  i, apic->addr, apic->nrdt, apic->ibase);
255                 for (n = 0; n < apic->nrdt; n++) {
256                         spin_lock(&apic->lock);
257                         rtblget(apic, n, &hi, &lo);
258                         spin_unlock(&apic->lock);
259                         start = seprintf(start, end, " rdt %2.2d %p %p\n", n, hi, lo);
260                 }
261         }
262         for (i = 0; i < Nbus; i++) {
263                 if ((rbus = rdtbus[i]) == NULL)
264                         continue;
265                 start = seprintf(start, end, "iointr bus %d:\n", i);
266                 for (; rbus != NULL; rbus = rbus->next) {
267                         rdt = rbus->rdt;
268                         start = seprintf(start, end,
269                                                          " apic %ld devno %#p (%d %d) intin %d lo %#p ref %d\n",
270                                                          rdt->apic - xioapic, rbus->devno, rbus->devno >> 2,
271                                                          rbus->devno & 0x03, rdt->intin, rdt->lo, rdt->ref);
272                 }
273         }
274         return start;
275 }
276
277 void ioapiconline(void)
278 {
279         int i;
280         struct apic *apic;
281
282         for (apic = xioapic; apic < &xioapic[Napic]; apic++) {
283                 if (!apic->useable || apic->addr == NULL)
284                         continue;
285                 for (i = 0; i < apic->nrdt; i++) {
286                         spin_lock(&apic->lock);
287                         rtblput(apic, i, 0, Im);
288                         spin_unlock(&apic->lock);
289                 }
290         }
291 }
292
293 static int dfpolicy = 0;
294
295 static void ioapicintrdd(uint32_t * hi, uint32_t * lo)
296 {
297         int i;
298         static int df;
299         static spinlock_t dflock;
300
301         /*
302          * Set delivery mode (lo) and destination field (hi),
303          * according to interrupt routing policy.
304          */
305         /*
306          * The bulk of this code was written ~1995, when there was
307          * one architecture and one generation of hardware, the number
308          * of CPUs was up to 4(8) and the choices for interrupt routing
309          * were physical, or flat logical (optionally with lowest
310          * priority interrupt). Logical mode hasn't scaled well with
311          * the increasing number of packages/cores/threads, so the
312          * fall-back is to physical mode, which works across all processor
313          * generations, both AMD and Intel, using the APIC and xAPIC.
314          *
315          * Interrupt routing policy can be set here.
316          */
317         switch (dfpolicy) {
318                 default:        /* noise core 0 */
319 #warning "sys->machptr[0]->apicno --- what is this in Akaros?"
320                         *hi = 0;        //sys->machptr[0]->apicno<<24;
321                         break;
322                 case 1: /* round-robin */
323                         /*
324                          * Assign each interrupt to a different CPU on a round-robin
325                          * Some idea of the packages/cores/thread topology would be
326                          * useful here, e.g. to not assign interrupts to more than one
327                          * thread in a core. But, as usual, Intel make that an onerous
328                          * task.
329                          */
330                         spin_lock(&dflock);
331                         for (;;) {
332 #if 0
333                                 i = df++;
334                                 if (df >= sys->nmach + 1)
335                                         df = 0;
336                                 if (sys->machptr[i] == NULL || !sys->machptr[i]->online)
337                                         continue;
338                                 i = sys->machptr[i]->apicno;
339 #endif
340 #warning "always picking acpino 0"
341                                 i = 0;
342                                 if (xlapic[i].useable && xlapic[i].addr == 0)
343                                         break;
344                         }
345                         spin_unlock(&dflock);
346
347                         *hi = i << 24;
348                         break;
349         }
350         *lo |= Pm | MTf;
351 }
352
353 int nextvec(void)
354 {
355         unsigned int vecno;
356
357         spin_lock(&idtnolock);
358         vecno = idtno;
359         idtno = (idtno + 8) % IdtMAX;
360         if (idtno < IdtIOAPIC)
361                 idtno += IdtIOAPIC;
362         spin_unlock(&idtnolock);
363
364         return vecno;
365 }
366
367 #warning "no msi mask yet"
368 static int msimask(struct Vkey *v, int mask)
369 {
370 #if 0
371         Pcidev *p;
372
373         p = pcimatchtbdf(v->tbdf);
374         if (p == NULL)
375                 return -1;
376         return pcimsimask(p, mask);
377 #else
378         return -1;
379 #endif
380 }
381
382 #warning "No msi yet"
383 #if 0
384 static int intrenablemsi(struct vctl *v, Pcidev * p)
385 {
386         unsigned int vno, lo, hi;
387         uint64_t msivec;
388
389         vno = nextvec();
390
391         lo = IPlow | TMedge | vno;
392         ioapicintrdd(&hi, &lo);
393
394         if (lo & Lm)
395                 lo |= MTlp;
396
397         msivec = (uint64_t) hi << 32 | lo;
398         if (pcimsienable(p, msivec) == -1)
399                 return -1;
400         v->isr = apicisr;
401         v->eoi = apiceoi;
402         v->vno = vno;
403         v->type = "msi";
404         v->mask = msimask;
405
406         printk("msiirq: %T: enabling %.16llp %s irq %d vno %d\n", p->tbdf, msivec,
407                    v->name, v->irq, vno);
408         return vno;
409 }
410 #endif
411 #warning "no disable msi yet"
412 #if 0
413 int disablemsi(Vctl *, Pcidev * p)
414 {
415         if (p == NULL)
416                 return -1;
417         return pcimsimask(p, 1);
418 }
419 #endif
420 int ioapicintrenable(Vctl * v)
421 {
422         struct Rbus *rbus;
423         struct Rdt *rdt;
424         uint32_t hi, lo;
425         int busno = 0, devno, vecno;
426
427 /*
428  * Bridge between old and unspecified new scheme,
429  * the work in progress...
430  */
431         if (v->tbdf == BUSUNKNOWN) {
432                 printk("%s; BUSUNKNOWN\n", __func__);
433                 if (v->irq >= IrqLINT0 && v->irq <= MaxIrqLAPIC) {
434                         if (v->irq != IrqSPURIOUS)
435                                 v->isr = apiceoi;
436                         v->type = "lapic";
437                         return v->irq;
438                 } else {
439                         printk("%s; legacy isa\n", __func__);
440
441                         /*
442                          * Legacy ISA.
443                          * Make a busno and devno using the
444                          * ISA bus number and the irq.
445                          */
446 #if 0
447                         extern int mpisabusno;
448
449                         if (mpisabusno == -1)
450                                 panic("no ISA bus allocated");
451                         busno = mpisabusno;
452 #endif
453                         busno = 0;
454                         devno = v->irq << 2;
455                 }
456         } else if (BUSTYPE(v->tbdf) == BusPCI) {
457                 printk("%s; BusPCI \n", __func__);
458                 /*
459                  * PCI.
460                  * Make a devno from BUSDNO(tbdf) and pcidev->intp.
461                  */
462                 /* we'll assume it's there. */
463 #if 0
464                 Pcidev *pcidev;
465
466                 busno = BUSBNO(v->tbdf);
467                 if ((pcidev = pcimatchtbdf(v->tbdf)) == NULL)
468                         panic("no PCI dev for tbdf %p", v->tbdf);
469                 if ((vecno = intrenablemsi(v, pcidev)) != -1)
470                         return vecno;
471                 disablemsi(v, pcidev);
472 #endif
473
474                 struct pci_device pcidev;
475
476                 explode_tbdf(v->tbdf);
477                 devno = pcidev_read8(&pcidev, PciINTP);
478                 printk("INTP is %d\n", devno);
479
480                 if (devno == 0)
481                         panic("no INTP for tbdf %p", v->tbdf);
482                 devno = BUSDNO(v->tbdf) << 2 | (devno - 1);
483                 printk("devno is %08lx\n", devno);
484                 printk("ioapicintrenable: tbdf %p busno %d devno %d\n",
485                            v->tbdf, busno, devno);
486         } else {
487                 //SET(busno, devno);
488                 busno = devno = 0;
489                 panic("unknown tbdf %px", v->tbdf);
490         }
491
492         rdt = NULL;
493         for (rbus = rdtbus[busno]; rbus != NULL; rbus = rbus->next) {
494                 printk("Check rbus->devno %p devno %p\n", rbus->devno, devno);
495                 if (rbus->devno == devno) {
496                         rdt = rbus->rdt;
497                         break;
498                 }
499         }
500         if (rdt == NULL) {
501                 // install it? Who knows?
502                 int ioapic_route_irq(int irq, int apicno, int devno);
503                 ioapic_route_irq(v->irq, 0, devno);
504                 extern int mpisabusno;
505                 printk("rdt is NULLLLLLLLLLLLLLLLLLLLLL\n");
506
507                 /*
508                  * First crack in the smooth exterior of the new code:
509                  * some BIOS make an MPS table where the PCI devices
510                  * are just defaulted to ISA.  Rewrite this to be
511                  * cleaner.
512                  * no MPS table in akaros.
513                  if((busno = mpisabusno) == -1)
514                  return -1;
515
516                  devno = v->irq<<2;
517                  */
518                 for (rbus = rdtbus[busno]; rbus != NULL; rbus = rbus->next)
519                         if (rbus->devno == devno) {
520                                 printk("rbus->devno = %p, devno %p\n", rbus->devno, devno);
521                                 rdt = rbus->rdt;
522                                 break;
523                         }
524                 printk("isa: tbdf %p busno %d devno %d %#p\n",
525                            v->tbdf, busno, devno, rdt);
526         }
527         if (rdt == NULL) {
528                 printk("RDT Is STILL NULL!\n");
529                 return -1;
530         }
531
532         printk("Second crack\n");
533         /*
534          * Second crack:
535          * what to do about devices that intrenable/intrdisable frequently?
536          * 1) there is no ioapicdisable yet;
537          * 2) it would be good to reuse freed vectors.
538          * Oh bugger.
539          */
540         /*
541          * This is a low-frequency event so just lock
542          * the whole IOAPIC to initialise the RDT entry
543          * rather than putting a Lock in each entry.
544          */
545         spin_lock(&rdt->apic->lock);
546         printk("%p: %ld/%d/%d (%d)\n", v->tbdf, rdt->apic - xioapic, rbus->devno,
547                    rdt->intin, devno);
548         if ((rdt->lo & 0xff) == 0) {
549                 vecno = nextvec();
550                 rdt->lo |= vecno;
551                 rdtvecno[vecno] = rdt;
552         } else
553                 printk("%p: mutiple irq bus %d dev %d\n", v->tbdf, busno, devno);
554
555         rdt->enabled++;
556         lo = (rdt->lo & ~Im);
557         ioapicintrdd(&hi, &lo);
558         rtblput(rdt->apic, rdt->intin, hi, lo);
559         vecno = lo & 0xff;
560         spin_unlock(&rdt->apic->lock);
561
562         printk("busno %d devno %d hi %p lo %p vecno %d\n",
563                    busno, devno, hi, lo, vecno);
564         v->isr = apicisr;
565         v->eoi = apiceoi;
566         v->vno = vecno;
567         v->type = "ioapic";
568
569         return vecno;
570 }
571
572 int ioapicintrdisable(int vecno)
573 {
574         struct Rdt *rdt;
575
576         /*
577          * FOV. Oh dear. This isn't very good.
578          * Fortunately rdtvecno[vecno] is static
579          * once assigned.
580          * Must do better.
581          *
582          * What about any pending interrupts?
583          */
584         if (vecno < 0 || vecno > MaxVectorAPIC) {
585                 panic("ioapicintrdisable: vecno %d out of range", vecno);
586                 return -1;
587         }
588         if ((rdt = rdtvecno[vecno]) == NULL) {
589                 panic("ioapicintrdisable: vecno %d has no rdt", vecno);
590                 return -1;
591         }
592
593         spin_lock(&rdt->apic->lock);
594         rdt->enabled--;
595         if (rdt->enabled == 0)
596                 rtblput(rdt->apic, rdt->intin, 0, rdt->lo);
597         spin_unlock(&rdt->apic->lock);
598
599         return 0;
600 }
601
602 spinlock_t vctllock;
603
604 int intrenable(int irq, void (*f) (void *, void *), void *a, int tbdf)
605 {
606         int vno;
607         Vctl *v;
608         extern int ioapicintrenable(Vctl *);
609
610         if (f == NULL) {
611                 printk("intrenable: nil handler for %d, tbdf %p\n", irq, tbdf);
612                 return NULL;
613         }
614
615         v = kzmalloc(sizeof(Vctl), KMALLOC_WAIT);
616         v->isintr = 1;
617         v->irq = irq;
618         v->tbdf = tbdf;
619         v->f = f;
620         v->a = a;
621
622         //spilock(&vctllock);
623         vno = ioapicintrenable(v);
624         printk("INTRENABLE, vno is %d\n", vno);
625         if (vno == -1) {
626                 //iunlock(&vctllock);
627                 printk("intrenable: couldn't enable irq %d, tbdf %p for %s\n",
628                            irq, tbdf, v->name);
629                 kfree(v);
630                 return NULL;
631         }
632 #if 0
633         if (vctl[vno]) {
634                 if (vctl[v->vno]->isr != v->isr || vctl[v->vno]->eoi != v->eoi)
635                         panic("intrenable: handler: %s %s %#p %#p %#p %#p",
636                                   vctl[v->vno]->name, v->name,
637                                   vctl[v->vno]->isr, v->isr, vctl[v->vno]->eoi, v->eoi);
638         }
639
640         v->vno = vno;
641         v->next = vctl[vno];
642         vctl[vno] = v;
643 #endif
644         //iunlock(&vctllock);
645
646         if (v->mask)
647                 v->mask(v, 0);
648
649         /*
650          * Return the assigned vector so intrdisable can find
651          * the handler; the IRQ is useless in the wonderful world
652          * of the IOAPIC.
653          */
654         printk("INTRNABLE returns %p\n", v);
655         printk("INTRNABLE returns %d\n", v->vno);
656         return v->vno;
657 }