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