Use vmap_pmem
[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", apic, 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 %.8ux\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         if(apic->useable || (apic->addr = vmap_pmem(pa, 1024)) == NULL)
214                 return;
215         apic->useable = 1;
216         apic->paddr = pa;
217
218         /*
219          * Initialise the I/O APIC.
220          * The MultiProcessor Specification says it is the
221          * responsibility of the O/S to set the APIC ID.
222          */
223         spin_lock(&apic->lock);
224         *(apic->addr+Ioregsel) = Ioapicver;
225         apic->nrdt = ((*(apic->addr+Iowin)>>16) & 0xff) + 1;
226         if(ibase != -1)
227                 apic->ibase = ibase;
228         else{
229                 apic->ibase = base;
230                 base += apic->nrdt;
231         }
232         *(apic->addr+Ioregsel) = Ioapicid;
233         *(apic->addr+Iowin) = id<<24;
234         spin_unlock(&apic->lock);
235 }
236
237 char *
238 ioapicdump(char *start, char *end)
239 {
240         int i, n;
241         struct Rbus *rbus;
242         struct Rdt *rdt;
243         struct apic *apic;
244         uint32_t hi, lo;
245
246         if(!2)
247                 return start;
248         for(i = 0; i < Napic; i++){
249                 apic = &xioapic[i];
250                 if(!apic->useable || apic->addr == 0)
251                         continue;
252                 start = seprintf(start, end, "ioapic %d addr %#p nrdt %d ibase %d\n",
253                                  i, apic->addr, apic->nrdt, apic->ibase);
254                 for(n = 0; n < apic->nrdt; n++){
255                         spin_lock(&apic->lock);
256                         rtblget(apic, n, &hi, &lo);
257                         spin_unlock(&apic->lock);
258                         start = seprintf(start, end, " rdt %2.2d %#8.8p %#8.8p\n", n, hi, lo);
259                 }
260         }
261         for(i = 0; i < Nbus; i++){
262                 if((rbus = rdtbus[i]) == NULL)
263                         continue;
264                 start = seprintf(start, end, "iointr bus %d:\n", i);
265                 for(; rbus != NULL; rbus = rbus->next){
266                         rdt = rbus->rdt;
267                         start = seprintf(start, end,
268                                          " apic %ld devno %#p (%d %d) intin %d lo %#p ref %d\n",
269                                          rdt->apic-xioapic, rbus->devno, rbus->devno>>2,
270                                          rbus->devno & 0x03, rdt->intin, rdt->lo, rdt->ref);
271                 }
272         }
273         return start;
274 }
275
276 void
277 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
296 ioapicintrdd(uint32_t* hi, uint32_t* lo)
297 {
298         int i;
299         static int df;
300         static spinlock_t dflock;
301
302         /*
303          * Set delivery mode (lo) and destination field (hi),
304          * according to interrupt routing policy.
305          */
306         /*
307          * The bulk of this code was written ~1995, when there was
308          * one architecture and one generation of hardware, the number
309          * of CPUs was up to 4(8) and the choices for interrupt routing
310          * were physical, or flat logical (optionally with lowest
311          * priority interrupt). Logical mode hasn't scaled well with
312          * the increasing number of packages/cores/threads, so the
313          * fall-back is to physical mode, which works across all processor
314          * generations, both AMD and Intel, using the APIC and xAPIC.
315          *
316          * Interrupt routing policy can be set here.
317          */
318         switch(dfpolicy){
319         default:                                /* noise core 0 */
320 #warning "sys->machptr[0]->apicno --- what is this in Akaros?"
321                 *hi = 0; //sys->machptr[0]->apicno<<24;
322                 break;
323         case 1:                                 /* round-robin */
324                 /*
325                  * Assign each interrupt to a different CPU on a round-robin
326                  * Some idea of the packages/cores/thread topology would be
327                  * useful here, e.g. to not assign interrupts to more than one
328                  * thread in a core. But, as usual, Intel make that an onerous
329                  * task.
330                  */
331                 spin_lock(&dflock);
332                 for(;;){
333 #if 0
334                         i = df++;
335                         if(df >= sys->nmach+1)
336                                 df = 0;
337                         if(sys->machptr[i] == NULL || !sys->machptr[i]->online)
338                                 continue;
339                         i = sys->machptr[i]->apicno;
340 #endif
341 #warning "always picking acpino 0"
342                         i = 0;
343                         if(xlapic[i].useable && xlapic[i].addr == 0)
344                                 break;
345                 }
346                 spin_unlock(&dflock);
347
348                 *hi = i<<24;
349                 break;
350         }
351         *lo |= Pm|MTf;
352 }
353
354 int
355 nextvec(void)
356 {
357         unsigned int vecno;
358
359         spin_lock(&idtnolock);
360         vecno = idtno;
361         idtno = (idtno+8) % IdtMAX;
362         if(idtno < IdtIOAPIC)
363                 idtno += IdtIOAPIC;
364         spin_unlock(&idtnolock);
365
366         return vecno;
367 }
368
369 #warning "no msi mask yet"
370 static int
371 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
388 intrenablemsi(struct vctl* v, Pcidev *p)
389 {
390         unsigned int vno, lo, hi;
391         uint64_t msivec;
392
393         vno = nextvec();
394
395         lo = IPlow | TMedge | vno;
396         ioapicintrdd(&hi, &lo);
397
398         if(lo & Lm)
399                 lo |= MTlp;
400
401         msivec = (uint64_t)hi<<32 | lo;
402         if(pcimsienable(p, msivec) == -1)
403                 return -1;
404         v->isr = apicisr;
405         v->eoi = apiceoi;
406         v->vno = vno;
407         v->type = "msi";
408         v->mask = msimask;
409
410         printk("msiirq: %T: enabling %.16llux %s irq %d vno %d\n", p->tbdf, msivec, v->name, v->irq, vno);
411         return vno;
412 }
413 #endif
414 #warning "no disable msi yet"
415 #if 0
416 int
417 disablemsi(Vctl*, Pcidev *p)
418 {
419         if(p == NULL)
420                 return -1;
421         return pcimsimask(p, 1);
422 }
423 #endif
424 int
425 ioapicintrenable(Vctl* v)
426 {
427         struct Rbus *rbus;
428         struct Rdt *rdt;
429         uint32_t hi, lo;
430         int busno = 0, devno, vecno;
431
432 /*
433  * Bridge between old and unspecified new scheme,
434  * the work in progress...
435  */
436         if(v->tbdf == BUSUNKNOWN){
437 printk("%s; BUSUNKNOWN\n", __func__);
438                 if(v->irq >= IrqLINT0 && v->irq <= MaxIrqLAPIC){
439                         if(v->irq != IrqSPURIOUS)
440                                 v->isr = apiceoi;
441                         v->type = "lapic";
442                         return v->irq;
443                 }
444                 else{
445 printk("%s; legacy isa\n", __func__);
446
447                         /*
448                          * Legacy ISA.
449                          * Make a busno and devno using the
450                          * ISA bus number and the irq.
451                          */
452 #if 0
453                         extern int mpisabusno;
454                         
455                         if(mpisabusno == -1)
456                                 panic("no ISA bus allocated");
457                         busno = mpisabusno;
458 #endif
459                         busno = 0;
460                         devno = v->irq<<2;
461                 }
462         }
463         else if(BUSTYPE(v->tbdf) == BusPCI){
464 printk("%s; BusPCI \n", __func__);
465                 /*
466                  * PCI.
467                  * Make a devno from BUSDNO(tbdf) and pcidev->intp.
468                  */
469                 /* we'll assume it's there. */
470 #if 0
471                 Pcidev *pcidev;
472                 
473                 busno = BUSBNO(v->tbdf);
474                 if((pcidev = pcimatchtbdf(v->tbdf)) == NULL)
475                         panic("no PCI dev for tbdf %#8.8ux", v->tbdf);
476                 if((vecno = intrenablemsi(v, pcidev)) != -1)
477                         return vecno;
478                 disablemsi(v, pcidev);
479 #endif
480
481                 struct pci_device pcidev;
482                 
483                 explode_tbdf(v->tbdf);
484                 devno = pcidev_read8(&pcidev, PciINTP);
485 printk("INTP is %d\n", devno);
486
487                 if(devno == 0)
488                         panic("no INTP for tbdf %#8.8ux", v->tbdf);
489                 devno = BUSDNO(v->tbdf)<<2|(devno-1);
490 printk("devno is %08lx\n", devno);
491                 printk("ioapicintrenable: tbdf %#8.8p busno %d devno %d\n",
492                        v->tbdf, busno, devno);
493         }
494         else{
495                 //SET(busno, devno);
496                 busno = devno = 0;
497                 panic("unknown tbdf %#8.8px", v->tbdf);
498         }
499         
500         rdt = NULL;
501         for(rbus = rdtbus[busno]; rbus != NULL; rbus = rbus->next){
502                 printk("Check rbus->devno %p devno %p\n", rbus->devno, devno);
503                 if(rbus->devno == devno){
504                         rdt = rbus->rdt;
505                         break;
506                 }
507         }
508         if(rdt == NULL){
509                 // install it? Who knows?
510 int ioapic_route_irq(int irq, int apicno, int devno);
511                 ioapic_route_irq(v->irq, 0, devno);
512                 extern int mpisabusno;
513 printk("rdt is NULLLLLLLLLLLLLLLLLLLLLL\n");
514                 
515                 /*
516                  * First crack in the smooth exterior of the new code:
517                  * some BIOS make an MPS table where the PCI devices
518                  * are just defaulted to ISA.  Rewrite this to be
519                  * cleaner.
520                  * no MPS table in akaros.
521                 if((busno = mpisabusno) == -1)
522                         return -1;
523
524                 devno = v->irq<<2;
525                  */
526                 for(rbus = rdtbus[busno]; rbus != NULL; rbus = rbus->next)
527                         if(rbus->devno == devno){
528 printk("rbus->devno = %p, devno %p\n", rbus->devno, devno);
529                                 rdt = rbus->rdt;
530                                 break;
531                         }
532                 printk("isa: tbdf %#8.8ux busno %d devno %d %#p\n",
533                        v->tbdf, busno, devno, rdt);
534         }
535         if(rdt == NULL){
536                 printk("RDT Is STILL NULL!\n");
537                 return -1;
538         }
539         
540 printk("Second crack\n");
541         /*
542          * Second crack:
543          * what to do about devices that intrenable/intrdisable frequently?
544          * 1) there is no ioapicdisable yet;
545          * 2) it would be good to reuse freed vectors.
546          * Oh bugger.
547          */
548         /*
549          * This is a low-frequency event so just lock
550          * the whole IOAPIC to initialise the RDT entry
551          * rather than putting a Lock in each entry.
552          */
553         spin_lock(&rdt->apic->lock);
554         printk("%p: %ld/%d/%d (%d)\n", v->tbdf, rdt->apic - xioapic, rbus->devno, rdt->intin, devno);
555         if((rdt->lo & 0xff) == 0){
556                 vecno = nextvec();
557                 rdt->lo |= vecno;
558                 rdtvecno[vecno] = rdt;
559         }else
560                 printk("%p: mutiple irq bus %d dev %d\n", v->tbdf, busno, devno);
561
562         rdt->enabled++;
563         lo = (rdt->lo & ~Im);
564         ioapicintrdd(&hi, &lo);
565         rtblput(rdt->apic, rdt->intin, hi, lo);
566         vecno = lo & 0xff;
567         spin_unlock(&rdt->apic->lock);
568
569         printk("busno %d devno %d hi %#8.8p lo %#8.8p vecno %d\n",
570                busno, devno, hi, lo, vecno);
571         v->isr = apicisr;
572         v->eoi = apiceoi;
573         v->vno = vecno;
574         v->type = "ioapic";
575
576         return vecno;
577 }
578
579 int
580 ioapicintrdisable(int vecno)
581 {
582         struct Rdt *rdt;
583
584         /*
585          * FOV. Oh dear. This isn't very good.
586          * Fortunately rdtvecno[vecno] is static
587          * once assigned.
588          * Must do better.
589          *
590          * What about any pending interrupts?
591          */
592         if(vecno < 0 || vecno > MaxVectorAPIC){
593                 panic("ioapicintrdisable: vecno %d out of range", vecno);
594                 return -1;
595         }
596         if((rdt = rdtvecno[vecno]) == NULL){
597                 panic("ioapicintrdisable: vecno %d has no rdt", vecno);
598                 return -1;
599         }
600         
601         spin_lock(&rdt->apic->lock);
602         rdt->enabled--;
603         if(rdt->enabled == 0)
604                 rtblput(rdt->apic, rdt->intin, 0, rdt->lo);
605         spin_unlock(&rdt->apic->lock);
606         
607         return 0;
608 }
609
610 spinlock_t vctllock;
611
612 void*
613 intrenable(int irq, void (*f)(void*, void*), void* a, int tbdf)
614 {
615         int vno;
616         Vctl *v;
617         extern int ioapicintrenable(Vctl*);
618
619         if(f == NULL){
620                 printk("intrenable: nil handler for %d, tbdf %p\n",
621                        irq, tbdf);
622                 return NULL;
623         }
624
625         v = kzmalloc(sizeof(Vctl), KMALLOC_WAIT);
626         v->isintr = 1;
627         v->irq = irq;
628         v->tbdf = tbdf;
629         v->f = f;
630         v->a = a;
631
632         //spilock(&vctllock);
633         vno = ioapicintrenable(v);
634         printk("INTRENABLE, vno is %d\n", vno);
635         if(vno == -1){
636                 //iunlock(&vctllock);
637                 printk("intrenable: couldn't enable irq %d, tbdf %p for %s\n",
638                         irq, tbdf, v->name);
639                 kfree(v);
640                 return NULL;
641         }
642 #if 0
643         if(vctl[vno]){
644                 if(vctl[v->vno]->isr != v->isr || vctl[v->vno]->eoi != v->eoi)
645                         panic("intrenable: handler: %s %s %#p %#p %#p %#p",
646                                 vctl[v->vno]->name, v->name,
647                                 vctl[v->vno]->isr, v->isr, vctl[v->vno]->eoi, v->eoi);
648         }
649
650         v->vno = vno;
651         v->next = vctl[vno];
652         vctl[vno] = v;
653 #endif
654         //iunlock(&vctllock);
655
656         if(v->mask)
657                 v->mask(v, 0);
658
659         /*
660          * Return the assigned vector so intrdisable can find
661          * the handler; the IRQ is useless in the wonderful world
662          * of the IOAPIC.
663          */
664         printk("INTRNABLE returns %d\n", v);
665         return v;
666 }