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