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