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