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