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