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