Lindent on the new APIC/ACPI files
[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 uint8_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 = *((uint32_t *) (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         *((uint32_t *) (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         apicbase = LAPIC_BASE;
175         printk("apicinit%d: apicbase %#p -> %#p\n", apicno, pa, apicbase);
176         apic->useable = 1;
177         printk("apicinit%d: it's useable\n", apicno);
178
179         /*
180          * Assign a machno to the processor associated with this
181          * APIC, it may not be an identity map.
182          * Machno 0 is always the bootstrap processor.
183          */
184
185         if (isbp) {
186                 apic->machno = 0;
187 #warning "where in pcpui do we put the apicno?"
188                 //m->apicno = apicno;
189         } else
190                 apic->machno = apmachno++;
191 }
192
193 static char *apicdump0(char *start, char *end, struct apic *apic, int i)
194 {
195         if (!apic->useable || apic->addr != 0)
196                 return start;
197         start =
198                 seprintf(start, end, "apic%d: machno %d lint0 %#8.8p lint1 %#8.8p\n", i,
199                                  apic->machno, apic->lvt[0], apic->lvt[1]);
200         start =
201                 seprintf(start, end, " tslvt %#8.8p pclvt %#8.8p elvt %#8.8p\n",
202                                  apicrget(Tslvt), apicrget(Pclvt), apicrget(Elvt));
203         start =
204                 seprintf(start, end,
205                                  " tlvt %#8.8p lint0 %#8.8p lint1 %#8.8p siv %#8.8p\n",
206                                  apicrget(Tlvt), apicrget(Lint0), apicrget(Lint1),
207                                  apicrget(Siv));
208         return start;
209 }
210
211 char *apicdump(char *start, char *end)
212 {
213         int i;
214
215         if (!2)
216                 return start;
217
218         start =
219                 seprintf(start, end, "apicbase %#p apmachno %d\n", apicbase, apmachno);
220         for (i = 0; i < Napic; i++)
221                 start = apicdump0(start, end, xlapic + i, i);
222         /* endxioapic?
223            for(i = 0; i < Napic; i++)
224            start = apicdump0(start, endxioapic + i, i);
225          */
226         return start;
227 }
228
229 #if 0
230 static void apictimer(Ureg * ureg, void *)
231 {
232         timerintr(ureg, 0);
233 }
234
235 #endif
236 int apiconline(void)
237 {
238         struct apic *apic;
239         uint64_t tsc;
240         uint32_t dfr, ver;
241         int apicno, nlvt;
242
243         if (apicbase == NULL)
244                 return 0;
245         if ((apicno = ((apicrget(Id) >> 24) & 0xff)) >= Napic)
246                 return 0;
247         apic = &xlapic[apicno];
248         if (!apic->useable || apic->addr != NULL)
249                 return 0;
250
251         /*
252          * Things that can only be done when on the processor
253          * owning the APIC, apicinit above runs on the bootstrap
254          * processor.
255          */
256         ver = apicrget(Ver);
257         nlvt = ((ver >> 16) & 0xff) + 1;
258         if (nlvt > ARRAY_SIZE(apic->lvt)) {
259                 printk("apiconline%d: nlvt %d > max (%d)\n",
260                            apicno, nlvt, ARRAY_SIZE(apic->lvt));
261                 nlvt = ARRAY_SIZE(apic->lvt);
262         }
263         apic->nlvt = nlvt;
264         apic->ver = ver & 0xff;
265 #warning "fix me for AMD apic"
266         /*
267          * These don't really matter in Physical mode;
268          * set the defaults anyway.
269          if(memcmp(m->cpuinfo, "AuthenticAMD", 12) == 0)
270          dfr = 0xf0000000;
271          else
272          */
273         dfr = 0xffffffff;
274         apicrput(Df, dfr);
275         apicrput(Ld, 0x00000000);
276
277         /*
278          * Disable interrupts until ready by setting the Task Priority
279          * register to 0xff.
280          */
281         apicrput(Tp, 0xff);
282
283         /*
284          * Software-enable the APIC in the Spurious Interrupt Vector
285          * register and set the vector number. The vector number must have
286          * bits 3-0 0x0f unless the Extended Spurious Vector Enable bit
287          * is set in the HyperTransport Transaction Control register.
288          */
289         apicrput(Siv, Swen | IdtSPURIOUS);
290
291         /*
292          * Acknowledge any outstanding interrupts.
293          */
294         apicrput(Eoi, 0);
295
296         /*
297          * Use the TSC to determine the APIC timer frequency.
298          * It might be possible to snarf this from a chipset
299          * register instead.
300          */
301         apicrput(Tdc, DivX1);
302         apicrput(Tlvt, Im);
303         // system_timing.tsc_freq? is that valid yet?
304         tsc = read_tsc() + 2 * 1024 * (1048576 / 10) /*m->cpuhz/10 */ ;
305         apicrput(Tic, 0xffffffff);
306
307         while (read_tsc() < tsc) ;
308 #define HZ 60
309         apic->hz = (0xffffffff - apicrget(Tcc)) * 10;
310         apic->max = apic->hz / HZ;
311         apic->min = apic->hz / (100 * HZ);
312         apic->div =
313                 ((2ULL * 1024 * 1048576 /*m->cpuhz */  / apic->max) + HZ / 2) / HZ;
314
315         if ( /*m->machno == 0 || */ 2) {
316                 printk("apic%d: hz %lld max %lld min %lld div %lld\n", apicno,
317                            apic->hz, apic->max, apic->min, apic->div);
318         }
319
320         /*
321          * Mask interrupts on Performance Counter overflow and
322          * Thermal Sensor if implemented, and on Lintr0 (Legacy INTR),
323          * and Lintr1 (Legacy NMI).
324          * Clear any Error Status (write followed by read) and enable
325          * the Error interrupt.
326          */
327         switch (apic->nlvt) {
328                 case 6:
329                         apicrput(Tslvt, Im);
330                  /*FALLTHROUGH*/ case 5:
331                         apicrput(Pclvt, Im);
332                  /*FALLTHROUGH*/ default:
333                         break;
334         }
335         apicrput(Lint1, apic->lvt[1] | Im | IdtLINT1);
336         apicrput(Lint0, apic->lvt[0] | Im | IdtLINT0);
337
338         apicrput(Es, 0);
339         apicrget(Es);
340         apicrput(Elvt, IdtERROR);
341
342         /*
343          * Issue an INIT Level De-Assert to synchronise arbitration ID's.
344          * (Necessary in this implementation? - not if Pentium 4 or Xeon
345          * (APIC Version >= 0x14), or AMD).
346          apicrput(Ichi, 0);
347          apicrput(Iclo, DSallinc|Lassert|MTir);
348          while(apicrget(Iclo) & Ds)
349          ;
350          */
351
352 #warning "not reloading the timer"
353         /*
354          * Reload the timer to de-synchronise the processors,
355          * then lower the task priority to allow interrupts to be
356          * accepted by the APIC.
357          microdelay((TK2MS(1)*1000/apmachno) * m->machno);
358          */
359
360 #if 0
361         if (apic->machno == 0) {
362                 apicrput(Tic, apic->max);
363                 intrenable(IdtTIMER, apictimer, 0, -1, "APIC timer");
364                 apicrput(Tlvt, Periodic | IrqTIMER);
365         }
366 #endif
367         if (node_id() /*m->machno */  == 0)
368                 apicrput(Tp, 0);
369
370 #warning "map from apicno to cpu info not installed"
371 /*
372         maps from apic to per-cpu info
373         xlapicmachptr[apicno] = m;
374 */
375
376         return 1;
377 }
378
379 #if 0
380 /* To start timers on TCs as part of the boot process. */
381 void apictimerenab(void)
382 {
383         struct apic *apic;
384
385         apic = &xlapic[(apicrget(Id) >> 24) & 0xff];
386
387         apiceoi(IdtTIMER);
388         apicrput(Tic, apic->max);
389         apicrput(Tlvt, Periodic | IrqTIMER);
390
391 }
392
393 void apictimerset(uint64_t next)
394 {
395         Mpl pl;
396         struct apic *apic;
397         int64_t period;
398
399         apic = &xlapic[(apicrget(Id) >> 24) & 0xff];
400
401         pl = splhi();
402         spin_lock(&(&m->apictimerlock)->lock);
403
404         period = apic->max;
405         if (next != 0) {
406                 period = next - fastticks(NULL);        /* fastticks is just rdtsc() */
407                 period /= apic->div;
408
409                 if (period < apic->min)
410                         period = apic->min;
411                 else if (period > apic->max - apic->min)
412                         period = apic->max;
413         }
414         apicrput(Tic, period);
415
416         spin_unlock(&(&m->apictimerlock)->lock);
417         splx(pl);
418 }
419
420 void apicsipi(int apicno, uintptr_t pa)
421 {
422         int i;
423         uint32_t crhi, crlo;
424
425         /*
426          * SIPI - Start-up IPI.
427          * To do: checks on apic validity.
428          */
429         crhi = apicno << 24;
430         apicrput(Ichi, crhi);
431         apicrput(Iclo, DSnone | TMlevel | Lassert | MTir);
432         microdelay(200);
433         apicrput(Iclo, DSnone | TMlevel | MTir);
434         millidelay(10);
435
436         crlo = DSnone | TMedge | MTsipi | ((uint32_t) pa / (4 * KiB));
437         for (i = 0; i < 2; i++) {
438                 apicrput(Ichi, crhi);
439                 apicrput(Iclo, crlo);
440                 microdelay(200);
441         }
442 }
443
444 void apicipi(int apicno)
445 {
446         apicrput(Ichi, apicno << 24);
447         apicrput(Iclo, DSnone | TMedge | Lassert | MTf | IdtIPI);
448         while (apicrget(Iclo) & Ds) ;
449 }
450
451 void apicpri(int pri)
452 {
453         apicrput(Tp, pri);
454 }
455 #endif