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