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