af82c189ef80011634f8f5f6994d8d2b04181fa1
[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
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
86 enum {                                          /* Siv */
87         Swen            = 0x00000100,           /* Software Enable */
88         Fdis            = 0x00000200,           /* Focus Disable */
89 };
90
91 enum {                                          /* Iclo */
92         Lassert         = 0x00004000,           /* Assert level */
93
94         DSnone          = 0x00000000,           /* Use Destination Field */
95         DSself          = 0x00040000,           /* Self is only destination */
96         DSallinc        = 0x00080000,           /* All including self */
97         DSallexc        = 0x000c0000,           /* All Excluding self */
98 };
99
100 enum {                                          /* Tlvt */
101         Periodic        = 0x00020000,           /* Periodic Timer Mode */
102 };
103
104 enum {                                          /* Tdc */
105         DivX2           = 0x00000000,           /* Divide by 2 */
106         DivX4           = 0x00000001,           /* Divide by 4 */
107         DivX8           = 0x00000002,           /* Divide by 8 */
108         DivX16          = 0x00000003,           /* Divide by 16 */
109         DivX32          = 0x00000008,           /* Divide by 32 */
110         DivX64          = 0x00000009,           /* Divide by 64 */
111         DivX128         = 0x0000000a,           /* Divide by 128 */
112         DivX1           = 0x0000000b,           /* Divide by 1 */
113 };
114
115 static uint8_t* apicbase;
116 static int apmachno = 1;
117
118 struct apic     xlapic[Napic];
119
120 static uint32_t
121 apicrget(int r)
122 {
123         uint32_t val;
124         if (! apicbase)
125                 panic("apicrget: no apic");
126         val = *((uint32_t*)(apicbase+r));
127         printk("apicrget: %s returns %p\n", apicregnames[r], val);
128         return *((uint32_t*)(apicbase+r));
129         return val;
130 }
131
132 static void
133 apicrput(int r, uint32_t data)
134 {
135         if (! apicbase)
136                 panic("apicrput: no apic");
137         printk("apicrput: %s = %p\n", apicregnames[r], data);
138         *((uint32_t*)(apicbase+r)) = data;
139 }
140
141 int
142 apiceoi(int vecno)
143 {
144         apicrput(Eoi, 0);
145
146         return vecno;
147 }
148
149 int
150 apicisr(int vecno)
151 {
152         int isr;
153
154         isr = apicrget(Is + (vecno/32)*16);
155
156         return isr & (1<<(vecno%32));
157 }
158
159 void
160 apicinit(int apicno, uintptr_t pa, int isbp)
161 {
162         struct apic *apic;
163
164         /*
165          * Mark the APIC useable if it has a good ID
166          * and the registers can be mapped.
167          * The APIC Extended Broadcast and ID bits in the HyperTransport
168          * Transaction Control register determine whether 4 or 8 bits
169          * are used for the APIC ID. There is also xAPIC and x2APIC
170          * to be dealt with sometime.
171          */
172         printk("apicinit: apicno %d pa %#p isbp %d\n", apicno, pa, isbp);
173         if(apicno >= Napic){
174                 printd("apicinit%d: out of range\n", apicno);
175                 return;
176         }
177         if((apic = &xlapic[apicno])->useable){
178                 printd("apicinit%d: already initialised\n", apicno);
179                 return;
180         }
181         apicbase = LAPIC_BASE;
182         printk("apicinit%d: apicbase %#p -> %#p\n", apicno, pa, apicbase);
183         apic->useable = 1;
184         printk("apicinit%d: it's useable\n", apicno);
185
186         /*
187          * Assign a machno to the processor associated with this
188          * APIC, it may not be an identity map.
189          * Machno 0 is always the bootstrap processor.
190          */
191
192         if(isbp){
193                 apic->machno = 0;
194 #warning "where in pcpui do we put the apicno?"
195                 //m->apicno = apicno;
196         }
197         else
198                 apic->machno = apmachno++;
199 }
200
201 static char *
202 apicdump0(char *start, char *end, struct apic *apic, int i)
203 {
204         if(!apic->useable || apic->addr != 0)
205                 return start;
206         start = seprintf(start, end, "apic%d: machno %d lint0 %#8.8p lint1 %#8.8p\n",
207                 i, apic->machno, apic->lvt[0], apic->lvt[1]);
208         start = seprintf(start, end, " tslvt %#8.8p pclvt %#8.8p elvt %#8.8p\n",
209                 apicrget(Tslvt), apicrget(Pclvt), apicrget(Elvt));
210         start = seprintf(start, end, " tlvt %#8.8p lint0 %#8.8p lint1 %#8.8p siv %#8.8p\n",
211                 apicrget(Tlvt), apicrget(Lint0),
212                 apicrget(Lint1), apicrget(Siv));
213         return start;
214 }
215
216 char *
217 apicdump(char *start, char *end)
218 {
219         int i;
220
221         if(!2)
222                 return start;
223
224         start = seprintf(start, end, "apicbase %#p apmachno %d\n", apicbase, apmachno);
225         for(i = 0; i < Napic; i++)
226                 start = apicdump0(start, end, xlapic + i, i);
227         /* endxioapic?
228         for(i = 0; i < Napic; i++)
229                 start = apicdump0(start, endxioapic + i, i);
230         */
231         return start;
232 }
233 #if 0
234 static void
235 apictimer(Ureg* ureg, void*)
236 {
237         timerintr(ureg, 0);
238 }
239
240 #endif
241 int
242 apiconline(void)
243 {
244         struct apic *apic;
245         uint64_t tsc;
246         uint32_t dfr, ver;
247         int apicno, nlvt;
248
249         if(apicbase == NULL)
250                 return 0;
251         if((apicno = ((apicrget(Id)>>24) & 0xff)) >= Napic)
252                 return 0;
253         apic = &xlapic[apicno];
254         if(!apic->useable || apic->addr != NULL)
255                 return 0;
256
257         /*
258          * Things that can only be done when on the processor
259          * owning the APIC, apicinit above runs on the bootstrap
260          * processor.
261          */
262         ver = apicrget(Ver);
263         nlvt = ((ver>>16) & 0xff) + 1;
264         if(nlvt > ARRAY_SIZE(apic->lvt)){
265                 printk("apiconline%d: nlvt %d > max (%d)\n",
266                         apicno, nlvt, ARRAY_SIZE(apic->lvt));
267                 nlvt = ARRAY_SIZE(apic->lvt);
268         }
269         apic->nlvt = nlvt;
270         apic->ver = ver & 0xff;
271 #warning "fix me for AMD apic"
272         /*
273          * These don't really matter in Physical mode;
274          * set the defaults anyway.
275         if(memcmp(m->cpuinfo, "AuthenticAMD", 12) == 0)
276                 dfr = 0xf0000000;
277         else
278          */
279                 dfr = 0xffffffff;
280         apicrput(Df, dfr);
281         apicrput(Ld, 0x00000000);
282
283         /*
284          * Disable interrupts until ready by setting the Task Priority
285          * register to 0xff.
286          */
287         apicrput(Tp, 0xff);
288
289         /*
290          * Software-enable the APIC in the Spurious Interrupt Vector
291          * register and set the vector number. The vector number must have
292          * bits 3-0 0x0f unless the Extended Spurious Vector Enable bit
293          * is set in the HyperTransport Transaction Control register.
294          */
295         apicrput(Siv, Swen|IdtSPURIOUS);
296
297         /*
298          * Acknowledge any outstanding interrupts.
299          */
300         apicrput(Eoi, 0);
301
302         /*
303          * Use the TSC to determine the APIC timer frequency.
304          * It might be possible to snarf this from a chipset
305          * register instead.
306          */
307         apicrput(Tdc, DivX1);
308         apicrput(Tlvt, Im);
309         // system_timing.tsc_freq? is that valid yet?
310         tsc = read_tsc() + 2*1024*(1048576/10) /*m->cpuhz/10*/ ;
311         apicrput(Tic, 0xffffffff);
312
313         while(read_tsc() < tsc)
314                 ;
315 #define HZ 60
316         apic->hz = (0xffffffff-apicrget(Tcc))*10;
317         apic->max = apic->hz/HZ;
318         apic->min = apic->hz/(100*HZ);
319         apic->div = ((2ULL*1024*1048576 /*m->cpuhz*/ /apic->max)+HZ/2)/HZ;
320
321         if(/*m->machno == 0 ||*/ 2){
322                 printk("apic%d: hz %lld max %lld min %lld div %lld\n", apicno,
323                         apic->hz, apic->max, apic->min, apic->div);
324         }
325
326         /*
327          * Mask interrupts on Performance Counter overflow and
328          * Thermal Sensor if implemented, and on Lintr0 (Legacy INTR),
329          * and Lintr1 (Legacy NMI).
330          * Clear any Error Status (write followed by read) and enable
331          * the Error interrupt.
332          */
333         switch(apic->nlvt){
334         case 6:
335                 apicrput(Tslvt, Im);
336                 /*FALLTHROUGH*/
337         case 5:
338                 apicrput(Pclvt, Im);
339                 /*FALLTHROUGH*/
340         default:
341                 break;
342         }
343         apicrput(Lint1, apic->lvt[1]|Im|IdtLINT1);
344         apicrput(Lint0, apic->lvt[0]|Im|IdtLINT0);
345
346         apicrput(Es, 0);
347         apicrget(Es);
348         apicrput(Elvt, IdtERROR);
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
375 #endif
376         if(node_id() /*m->machno*/ == 0)
377                 apicrput(Tp, 0);
378
379 #warning "map from apicno to cpu info not installed"
380 /*
381         maps from apic to per-cpu info
382         xlapicmachptr[apicno] = m;
383 */
384
385         return 1;
386 }
387
388 #if 0
389 /* To start timers on TCs as part of the boot process. */
390 void
391 apictimerenab(void)
392 {
393         struct apic *apic;
394
395         apic = &xlapic[(apicrget(Id)>>24) & 0xff];
396
397         apiceoi(IdtTIMER);
398         apicrput(Tic, apic->max);
399         apicrput(Tlvt, Periodic|IrqTIMER);
400
401 }
402
403 void
404 apictimerset(uint64_t next)
405 {
406         Mpl pl;
407         struct apic *apic;
408         int64_t period;
409
410         apic = &xlapic[(apicrget(Id)>>24) & 0xff];
411
412         pl = splhi();
413         spin_lock(&(&m->apictimerlock)->lock);
414
415         period = apic->max;
416         if(next != 0){
417                 period = next - fastticks(NULL);        /* fastticks is just rdtsc() */
418                 period /= apic->div;
419
420                 if(period < apic->min)
421                         period = apic->min;
422                 else if(period > apic->max - apic->min)
423                         period = apic->max;
424         }
425         apicrput(Tic, period);
426
427         spin_unlock(&(&m->apictimerlock)->lock);
428         splx(pl);
429 }
430
431 void
432 apicsipi(int apicno, uintptr_t pa)
433 {
434         int i;
435         uint32_t crhi, crlo;
436
437         /*
438          * SIPI - Start-up IPI.
439          * To do: checks on apic validity.
440          */
441         crhi = apicno<<24;
442         apicrput(Ichi, crhi);
443         apicrput(Iclo, DSnone|TMlevel|Lassert|MTir);
444         microdelay(200);
445         apicrput(Iclo, DSnone|TMlevel|MTir);
446         millidelay(10);
447
448         crlo = DSnone|TMedge|MTsipi|((uint32_t)pa/(4*KiB));
449         for(i = 0; i < 2; i++){
450                 apicrput(Ichi, crhi);
451                 apicrput(Iclo, crlo);
452                 microdelay(200);
453         }
454 }
455
456 void
457 apicipi(int apicno)
458 {
459         apicrput(Ichi, apicno<<24);
460         apicrput(Iclo, DSnone|TMedge|Lassert|MTf|IdtIPI);
461         while(apicrget(Iclo) & Ds)
462                 ;
463 }
464
465 void
466 apicpri(int pri)
467 {
468         apicrput(Tp, pri);
469 }
470 #endif