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