x86: irq handler func pointers
[akaros.git] / kern / arch / x86 / apic9.c
1 #include <vfs.h>
2 #include <kfs.h>
3 #include <slab.h>
4 #include <kmalloc.h>
5 #include <kref.h>
6 #include <string.h>
7 #include <stdio.h>
8 #include <assert.h>
9 #include <error.h>
10 #include <cpio.h>
11 #include <pmap.h>
12 #include <smp.h>
13 #include <ip.h>
14 #include <arch/io.h>
15
16 enum {                                                  /* Local APIC registers */
17         Id = 0x0020,                            /* Identification */
18         Ver = 0x0030,   /* Version */
19         Tp = 0x0080,    /* Task Priority */
20         Ap = 0x0090,    /* Arbitration Priority */
21         Pp = 0x00a0,    /* Processor Priority */
22         Eoi = 0x00b0,   /* EOI */
23         Ld = 0x00d0,    /* Logical Destination */
24         Df = 0x00e0,    /* Destination Format */
25         Siv = 0x00f0,   /* Spurious Interrupt Vector */
26         Is = 0x0100,    /* Interrupt Status (8) */
27         Tm = 0x0180,    /* Trigger Mode (8) */
28         Ir = 0x0200,    /* Interrupt Request (8) */
29         Es = 0x0280,    /* Error Status */
30         Iclo = 0x0300,  /* Interrupt Command */
31         Ichi = 0x0310,  /* Interrupt Command [63:32] */
32         Lvt0 = 0x0320,  /* Local Vector Table 0 */
33         Lvt5 = 0x0330,  /* Local Vector Table 5 */
34         Lvt4 = 0x0340,  /* Local Vector Table 4 */
35         Lvt1 = 0x0350,  /* Local Vector Table 1 */
36         Lvt2 = 0x0360,  /* Local Vector Table 2 */
37         Lvt3 = 0x0370,  /* Local Vector Table 3 */
38         Tic = 0x0380,   /* Timer Initial Count */
39         Tcc = 0x0390,   /* Timer Current Count */
40         Tdc = 0x03e0,   /* Timer Divide Configuration */
41
42         Tlvt = Lvt0,    /* Timer */
43         Lint0 = Lvt1,   /* Local Interrupt 0 */
44         Lint1 = Lvt2,   /* Local Interrupt 1 */
45         Elvt = Lvt3,    /* Error */
46         Pclvt = Lvt4,   /* Performance Counter */
47         Tslvt = Lvt5,   /* Thermal Sensor */
48 };
49
50 static char *apicregnames[] = {
51         [Id] "Identification",
52         [Ver] "Version",
53         [Tp] "Task Priority",
54         [Ap] "Arbitration Priority",
55         [Pp] "Processor Priority",
56         [Eoi] "EOI",
57         [Ld] "Logical Destination",
58         [Df] "Destination Format",
59         [Siv] "Spurious Interrupt Vector",
60         [Is] "Interrupt Status (8)",
61         [Tm] "Trigger Mode (8)",
62         [Ir] "Interrupt Request (8)",
63         [Es] "Error Status",
64         [Iclo] "Interrupt Command",
65         [Ichi] "Interrupt Command [63:32]",
66         [Lvt0] "Local Vector Table 0",
67         [Lvt5] "Local Vector Table 5",
68         [Lvt4] "Local Vector Table 4",
69         [Lvt1] "Local Vector Table 1",
70         [Lvt2] "Local Vector Table 2",
71         [Lvt3] "Local Vector Table 3",
72         [Tic] "Timer Initial Count",
73         [Tcc] "Timer Current Count",
74         [Tdc] "Timer Divide Configuration",
75
76         [Tlvt] "Timer",
77         [Lint0] "Local Interrupt 0",
78         [Lint1] "Local Interrupt 1",
79         [Elvt] "Error",
80         [Pclvt] "Performance Counter",
81         [Tslvt] "Thermal Sensor",
82 };
83
84 enum {                                                  /* Siv */
85         Swen = 0x00000100,                      /* Software Enable */
86         Fdis = 0x00000200,      /* Focus Disable */
87 };
88
89 enum {                                                  /* Iclo */
90         Lassert = 0x00004000,           /* Assert level */
91
92         DSnone = 0x00000000,    /* Use Destination Field */
93         DSself = 0x00040000,    /* Self is only destination */
94         DSallinc = 0x00080000,  /* All including self */
95         DSallexc = 0x000c0000,  /* All Excluding self */
96 };
97
98 enum {                                                  /* Tlvt */
99         Periodic = 0x00020000,          /* Periodic Timer Mode */
100 };
101
102 enum {                                                  /* Tdc */
103         DivX2 = 0x00000000,                     /* Divide by 2 */
104         DivX4 = 0x00000001,     /* Divide by 4 */
105         DivX8 = 0x00000002,     /* Divide by 8 */
106         DivX16 = 0x00000003,    /* Divide by 16 */
107         DivX32 = 0x00000008,    /* Divide by 32 */
108         DivX64 = 0x00000009,    /* Divide by 64 */
109         DivX128 = 0x0000000a,   /* Divide by 128 */
110         DivX1 = 0x0000000b,     /* Divide by 1 */
111 };
112
113 static uintptr_t apicbase;
114 static int apmachno = 1;
115
116 struct apic xlapic[Napic];
117
118 static uint32_t apicrget(int r)
119 {
120         uint32_t val;
121         if (!apicbase)
122                 panic("apicrget: no apic");
123         val = read_mmreg32(apicbase + r);
124         printk("apicrget: %s returns %p\n", apicregnames[r], val);
125         return *((uint32_t *) (apicbase + r));
126         return val;
127 }
128
129 static void apicrput(int r, uint32_t data)
130 {
131         if (!apicbase)
132                 panic("apicrput: no apic");
133         printk("apicrput: %s = %p\n", apicregnames[r], data);
134         write_mmreg32(apicbase + r, data);
135 }
136
137 int apiceoi(int vecno)
138 {
139         apicrput(Eoi, 0);
140
141         return vecno;
142 }
143
144 int apicisr(int vecno)
145 {
146         int isr;
147
148         isr = apicrget(Is + (vecno / 32) * 16);
149
150         return isr & (1 << (vecno % 32));
151 }
152
153 void apicinit(int apicno, uintptr_t pa, int isbp)
154 {
155         struct apic *apic;
156
157         /*
158          * Mark the APIC useable if it has a good ID
159          * and the registers can be mapped.
160          * The APIC Extended Broadcast and ID bits in the HyperTransport
161          * Transaction Control register determine whether 4 or 8 bits
162          * are used for the APIC ID. There is also xAPIC and x2APIC
163          * to be dealt with sometime.
164          */
165         printk("apicinit: apicno %d pa %#p isbp %d\n", apicno, pa, isbp);
166         if (apicno >= Napic) {
167                 printd("apicinit%d: out of range\n", apicno);
168                 return;
169         }
170         if ((apic = &xlapic[apicno])->useable) {
171                 printd("apicinit%d: already initialised\n", apicno);
172                 return;
173         }
174         assert(pa == LAPIC_PBASE);
175         apicbase = LAPIC_BASE;
176         printk("apicinit%d: apicbase %#p -> %#p\n", apicno, pa, apicbase);
177         apic->useable = 1;
178         printk("\t\tapicinit%d: it's useable\n", apicno);
179
180         /*
181          * Assign a machno to the processor associated with this
182          * APIC, it may not be an identity map.
183          * Machno 0 is always the bootstrap processor.
184          */
185
186         if (isbp) {
187                 apic->machno = 0;
188 #warning "where in pcpui do we put the apicno?"
189                 //m->apicno = apicno; // acpino is the hw_coreid
190         } else
191                 apic->machno = apmachno++;
192                 // ^^ machno is the os_coreid
193 }
194
195 static char *apicdump0(char *start, char *end, struct apic *apic, int i)
196 {
197         if (!apic->useable || apic->addr != 0)
198                 return start;
199         start =
200                 seprintf(start, end, "apic%d: machno %d lint0 %#8.8p lint1 %#8.8p\n", i,
201                                  apic->machno, apic->lvt[0], apic->lvt[1]);
202         start =
203                 seprintf(start, end, " tslvt %#8.8p pclvt %#8.8p elvt %#8.8p\n",
204                                  apicrget(Tslvt), apicrget(Pclvt), apicrget(Elvt));
205         start =
206                 seprintf(start, end,
207                                  " tlvt %#8.8p lint0 %#8.8p lint1 %#8.8p siv %#8.8p\n",
208                                  apicrget(Tlvt), apicrget(Lint0), apicrget(Lint1),
209                                  apicrget(Siv));
210         return start;
211 }
212
213 char *apicdump(char *start, char *end)
214 {
215         int i;
216
217         if (!2)
218                 return start;
219
220         start =
221                 seprintf(start, end, "apicbase %#p apmachno %d\n", apicbase, apmachno);
222         for (i = 0; i < Napic; i++)
223                 start = apicdump0(start, end, xlapic + i, i);
224         /* endxioapic?
225            for(i = 0; i < Napic; i++)
226            start = apicdump0(start, endxioapic + i, i);
227          */
228         return start;
229 }
230
231 #if 0
232 static void apictimer(Ureg * ureg, void *)
233 {
234         timerintr(ureg, 0);
235 }
236
237 #endif
238 int apiconline(void)
239 {
240         struct apic *apic;
241         uint64_t tsc;
242         uint32_t dfr, ver;
243         int apicno, nlvt;
244
245         if (!apicbase)
246                 return 0;
247         if ((apicno = ((apicrget(Id) >> 24) & 0xff)) >= Napic)
248                 return 0;
249         apic = &xlapic[apicno];
250         if (!apic->useable || apic->addr)
251                 return 0;
252         /*
253          * Things that can only be done when on the processor
254          * owning the APIC, apicinit above runs on the bootstrap
255          * processor.
256          */
257         ver = apicrget(Ver);
258         nlvt = ((ver >> 16) & 0xff) + 1;
259         if (nlvt > ARRAY_SIZE(apic->lvt)) {
260                 printk("apiconline%d: nlvt %d > max (%d)\n",
261                            apicno, nlvt, ARRAY_SIZE(apic->lvt));
262                 nlvt = ARRAY_SIZE(apic->lvt);
263         }
264         apic->nlvt = nlvt;
265         apic->ver = ver & 0xff;
266 #warning "fix me for AMD apic"
267         /*
268          * These don't really matter in Physical mode;
269          * set the defaults anyway.
270          if(memcmp(m->cpuinfo, "AuthenticAMD", 12) == 0)
271          dfr = 0xf0000000;
272          else
273          */
274         dfr = 0xffffffff;
275         apicrput(Df, dfr);
276         apicrput(Ld, 0x00000000);
277
278         /*
279          * Disable interrupts until ready by setting the Task Priority
280          * register to 0xff.
281          */
282         apicrput(Tp, 0xff);
283
284         /*
285          * Software-enable the APIC in the Spurious Interrupt Vector
286          * register and set the vector number. The vector number must have
287          * bits 3-0 0x0f unless the Extended Spurious Vector Enable bit
288          * is set in the HyperTransport Transaction Control register.
289          */
290         apicrput(Siv, Swen | IdtSPURIOUS);
291
292         /*
293          * Acknowledge any outstanding interrupts.
294          */
295         apicrput(Eoi, 0);
296
297
298
299 // TODO: sort this shit out with the system timing stuff
300         /*
301          * Use the TSC to determine the APIC timer frequency.
302          * It might be possible to snarf this from a chipset
303          * register instead.
304          */
305         apicrput(Tdc, DivX1);
306         apicrput(Tlvt, Im);
307         // system_timing.tsc_freq? is that valid yet?
308         tsc = read_tsc() + 2 * 1024 * (1048576 / 10) /*m->cpuhz/10 */ ;
309         apicrput(Tic, 0xffffffff);
310
311         while (read_tsc() < tsc) ;
312 #define HZ 60
313         apic->hz = (0xffffffff - apicrget(Tcc)) * 10;
314         apic->max = apic->hz / HZ;
315         apic->min = apic->hz / (100 * HZ);
316         apic->div =
317                 ((2ULL * 1024 * 1048576 /*m->cpuhz */  / apic->max) + HZ / 2) / HZ;
318
319         if ( /*m->machno == 0 || */ 2) {
320                 printk("apic%d: hz %lld max %lld min %lld div %lld\n", apicno,
321                            apic->hz, apic->max, apic->min, apic->div);
322         }
323
324
325
326
327         /*
328          * Mask interrupts on Performance Counter overflow and
329          * Thermal Sensor if implemented, and on Lintr0 (Legacy INTR),
330          * and Lintr1 (Legacy NMI).
331          * Clear any Error Status (write followed by read) and enable
332          * the Error interrupt.
333          */
334         switch (apic->nlvt) {
335                 case 6:
336                         apicrput(Tslvt, Im);
337                  /*FALLTHROUGH*/ case 5:
338                         apicrput(Pclvt, Im);
339                  /*FALLTHROUGH*/ default:
340                         break;
341         }
342         apicrput(Lint1, apic->lvt[1] | Im | IdtLINT1);
343         apicrput(Lint0, apic->lvt[0] | Im | IdtLINT0);
344
345         apicrput(Es, 0);
346         apicrget(Es);
347         apicrput(Elvt, IdtERROR);
348
349         /*
350          * Issue an INIT Level De-Assert to synchronise arbitration ID's.
351          * (Necessary in this implementation? - not if Pentium 4 or Xeon
352          * (APIC Version >= 0x14), or AMD).
353          apicrput(Ichi, 0);
354          apicrput(Iclo, DSallinc|Lassert|MTir);
355          while(apicrget(Iclo) & Ds)
356          ;
357          */
358
359 #warning "not reloading the timer"
360         /*
361          * Reload the timer to de-synchronise the processors,
362          * then lower the task priority to allow interrupts to be
363          * accepted by the APIC.
364          microdelay((TK2MS(1)*1000/apmachno) * m->machno);
365          */
366
367 #if 0
368         if (apic->machno == 0) {
369                 apicrput(Tic, apic->max);
370                 intrenable(IdtTIMER, apictimer, 0, -1, "APIC timer");
371                 apicrput(Tlvt, Periodic | IrqTIMER);
372         }
373 #endif
374         if (node_id() /*m->machno */  == 0)
375                 apicrput(Tp, 0);
376
377 #warning "map from apicno to cpu info not installed"
378 /*
379         maps from apic to per-cpu info
380         xlapicmachptr[apicno] = m;
381 */
382
383         return 1;
384 }
385
386 #if 0
387 /* To start timers on TCs as part of the boot process. */
388 void apictimerenab(void)
389 {
390         struct apic *apic;
391
392         apic = &xlapic[(apicrget(Id) >> 24) & 0xff];
393
394         apiceoi(IdtTIMER);
395         apicrput(Tic, apic->max);
396         apicrput(Tlvt, Periodic | IrqTIMER);
397
398 }
399
400 void apictimerset(uint64_t next)
401 {
402         Mpl pl;
403         struct apic *apic;
404         int64_t period;
405
406         apic = &xlapic[(apicrget(Id) >> 24) & 0xff];
407
408         pl = splhi();
409         spin_lock(&(&m->apictimerlock)->lock);
410
411         period = apic->max;
412         if (next != 0) {
413                 period = next - fastticks(NULL);        /* fastticks is just rdtsc() */
414                 period /= apic->div;
415
416                 if (period < apic->min)
417                         period = apic->min;
418                 else if (period > apic->max - apic->min)
419                         period = apic->max;
420         }
421         apicrput(Tic, period);
422
423         spin_unlock(&(&m->apictimerlock)->lock);
424         splx(pl);
425 }
426
427 void apicsipi(int apicno, uintptr_t pa)
428 {
429         int i;
430         uint32_t crhi, crlo;
431
432         /*
433          * SIPI - Start-up IPI.
434          * To do: checks on apic validity.
435          */
436         crhi = apicno << 24;
437         apicrput(Ichi, crhi);
438         apicrput(Iclo, DSnone | TMlevel | Lassert | MTir);
439         microdelay(200);
440         apicrput(Iclo, DSnone | TMlevel | MTir);
441         millidelay(10);
442
443         crlo = DSnone | TMedge | MTsipi | ((uint32_t) pa / (4 * KiB));
444         for (i = 0; i < 2; i++) {
445                 apicrput(Ichi, crhi);
446                 apicrput(Iclo, crlo);
447                 microdelay(200);
448         }
449 }
450
451 void apicipi(int apicno)
452 {
453         apicrput(Ichi, apicno << 24);
454         apicrput(Iclo, DSnone | TMedge | Lassert | MTf | IdtIPI);
455         while (apicrget(Iclo) & Ds) ;
456 }
457
458 void apicpri(int pri)
459 {
460         apicrput(Tp, pri);
461 }
462 #endif