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