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