BXE: min->MIN, plus an spatch
[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 #include <trap.h>
16
17 enum {                                                  /* Local APIC registers */
18         Id = 0x0020,                            /* Identification */
19         Ver = 0x0030,   /* Version */
20         Tp = 0x0080,    /* Task Priority */
21         Ap = 0x0090,    /* Arbitration Priority */
22         Pp = 0x00a0,    /* Processor Priority */
23         Eoi = 0x00b0,   /* EOI */
24         Ld = 0x00d0,    /* Logical Destination */
25         Df = 0x00e0,    /* Destination Format */
26         Siv = 0x00f0,   /* Spurious Interrupt Vector */
27         Is = 0x0100,    /* Interrupt Status (8) */
28         Tm = 0x0180,    /* Trigger Mode (8) */
29         Ir = 0x0200,    /* Interrupt Request (8) */
30         Es = 0x0280,    /* Error Status */
31         Iclo = 0x0300,  /* Interrupt Command */
32         Ichi = 0x0310,  /* Interrupt Command [63:32] */
33         Lvt0 = 0x0320,  /* Local Vector Table 0 */
34         Lvt5 = 0x0330,  /* Local Vector Table 5 */
35         Lvt4 = 0x0340,  /* Local Vector Table 4 */
36         Lvt1 = 0x0350,  /* Local Vector Table 1 */
37         Lvt2 = 0x0360,  /* Local Vector Table 2 */
38         Lvt3 = 0x0370,  /* Local Vector Table 3 */
39         Tic = 0x0380,   /* Timer Initial Count */
40         Tcc = 0x0390,   /* Timer Current Count */
41         Tdc = 0x03e0,   /* Timer Divide Configuration */
42
43         Tlvt = Lvt0,    /* Timer */
44         Lint0 = Lvt1,   /* Local Interrupt 0 */
45         Lint1 = Lvt2,   /* Local Interrupt 1 */
46         Elvt = Lvt3,    /* Error */
47         Pclvt = Lvt4,   /* Performance Counter */
48         Tslvt = Lvt5,   /* Thermal Sensor */
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 enum {                                                  /* Siv */
86         Swen = 0x00000100,                      /* Software Enable */
87         Fdis = 0x00000200,      /* Focus Disable */
88 };
89
90 enum {                                                  /* Iclo */
91         Lassert = 0x00004000,           /* Assert level */
92
93         DSnone = 0x00000000,    /* Use Destination Field */
94         DSself = 0x00040000,    /* Self is only destination */
95         DSallinc = 0x00080000,  /* All including self */
96         DSallexc = 0x000c0000,  /* All Excluding self */
97 };
98
99 enum {                                                  /* Tlvt */
100         Periodic = 0x00020000,          /* Periodic Timer Mode */
101 };
102
103 enum {                                                  /* Tdc */
104         DivX2 = 0x00000000,                     /* Divide by 2 */
105         DivX4 = 0x00000001,     /* Divide by 4 */
106         DivX8 = 0x00000002,     /* Divide by 8 */
107         DivX16 = 0x00000003,    /* Divide by 16 */
108         DivX32 = 0x00000008,    /* Divide by 32 */
109         DivX64 = 0x00000009,    /* Divide by 64 */
110         DivX128 = 0x0000000a,   /* Divide by 128 */
111         DivX1 = 0x0000000b,     /* Divide by 1 */
112 };
113
114 static uintptr_t apicbase;
115 static int apmachno = 1;
116
117 struct apic xlapic[Napic];
118
119 static uint32_t apicrget(int r)
120 {
121         uint32_t val;
122         if (!apicbase)
123                 panic("apicrget: no apic");
124         val = read_mmreg32(apicbase + r);
125         printd("apicrget: %s returns %p\n", apicregnames[r], val);
126         return val;
127 }
128
129 static void apicrput(int r, uint32_t data)
130 {
131         if (!apicbase)
132                 panic("apicrput: no apic");
133         printd("apicrput: %s = %p\n", apicregnames[r], data);
134         write_mmreg32(apicbase + r, data);
135 }
136
137 void apicinit(int apicno, uintptr_t pa, int isbp)
138 {
139         struct apic *apic;
140
141         /*
142          * Mark the APIC useable if it has a good ID
143          * and the registers can be mapped.
144          * The APIC Extended Broadcast and ID bits in the HyperTransport
145          * Transaction Control register determine whether 4 or 8 bits
146          * are used for the APIC ID. There is also xAPIC and x2APIC
147          * to be dealt with sometime.
148          */
149         printd("apicinit: apicno %d pa %#p isbp %d\n", apicno, pa, isbp);
150         if (apicno >= Napic) {
151                 printd("apicinit%d: out of range\n", apicno);
152                 return;
153         }
154         if ((apic = &xlapic[apicno])->useable) {
155                 printd("apicinit%d: already initialised\n", apicno);
156                 return;
157         }
158         assert(pa == LAPIC_PBASE);
159         apicbase = LAPIC_BASE;  /* was the plan to just clobber the global? */
160         apic->useable = 1;
161
162         /* plan 9 used to set up a mapping btw apic and pcpui like so:
163                 pcpui->apicno = apicno; // acpino is the hw_coreid
164                 apic->machno = apmachno++; // machno is the os_coreid
165          * akaros does its own remapping of hw <-> os coreid during smp_boot */
166 }
167
168 static char *apicdump0(char *start, char *end, struct apic *apic, int i)
169 {
170         if (!apic->useable || apic->addr != 0)
171                 return start;
172         start =
173                 seprintf(start, end, "apic%d: oscore %d lint0 %#8.8p lint1 %#8.8p\n", i,
174                                  get_os_coreid(i), apic->lvt[0], apic->lvt[1]);
175         start =
176                 seprintf(start, end, " tslvt %#8.8p pclvt %#8.8p elvt %#8.8p\n",
177                                  apicrget(Tslvt), apicrget(Pclvt), apicrget(Elvt));
178         start =
179                 seprintf(start, end,
180                                  " tlvt %#8.8p lint0 %#8.8p lint1 %#8.8p siv %#8.8p\n",
181                                  apicrget(Tlvt), apicrget(Lint0), apicrget(Lint1),
182                                  apicrget(Siv));
183         return start;
184 }
185
186 char *apicdump(char *start, char *end)
187 {
188         int i;
189
190         if (!2)
191                 return start;
192
193         start =
194                 seprintf(start, end, "apicbase %#p apmachno %d\n", apicbase, apmachno);
195         for (i = 0; i < Napic; i++)
196                 start = apicdump0(start, end, xlapic + i, i);
197         for (i = 0; i < Napic; i++)
198                 start = apicdump0(start, end, xioapic + i, i);
199         return start;
200 }
201
202 void handle_lapic_error(struct hw_trapframe *hw_tf, void *data)
203 {
204         uint32_t err;
205         apicrput(Es, 0);
206         err = apicrget(Es);
207         /* i get a shitload of these on my nehalem, many with err == 0 */
208         printd("LAPIC error vector, got 0x%08x\n", err);
209 }
210
211 int apiconline(void)
212 {
213         struct apic *apic;
214         uint64_t tsc;
215         uint32_t dfr, ver;
216         int apicno, nlvt;
217
218         if (!apicbase) {
219                 printk("No apicbase on HW core %d!!\n", hw_core_id());
220                 return 0;
221         }
222         if ((apicno = ((apicrget(Id) >> 24) & 0xff)) >= Napic) {
223                 printk("Bad apicno %d on HW core %d!!\n", apicno, hw_core_id());
224                 return 0;
225         }
226         apic = &xlapic[apicno];
227         /* The addr check tells us if it is an IOAPIC or not... */
228         if (!apic->useable || apic->addr) {
229                 printk("Unsuitable apicno %d on HW core %d!!\n", apicno, hw_core_id());
230                 return 0;
231         }
232         /* Things that can only be done when on the processor owning the APIC,
233          * apicinit above runs on the bootstrap processor. */
234         ver = apicrget(Ver);
235         nlvt = ((ver >> 16) & 0xff) + 1;
236         if (nlvt > ARRAY_SIZE(apic->lvt)) {
237                 printk("apiconline%d: nlvt %d > max (%d)\n",
238                            apicno, nlvt, ARRAY_SIZE(apic->lvt));
239                 nlvt = ARRAY_SIZE(apic->lvt);
240         }
241         apic->nlvt = nlvt;
242         apic->ver = ver & 0xff;
243
244         /* These don't really matter in Physical mode; set the defaults anyway.  If
245          * we have problems with logical IPIs on AMD, check this out: */
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         /* Disable interrupts until ready by setting the Task Priority register to
254          * 0xff. */
255         apicrput(Tp, 0xff);
256
257         /* Software-enable the APIC in the Spurious Interrupt Vector register and
258          * set the vector number. The vector number must have bits 3-0 0x0f unless
259          * the Extended Spurious Vector Enable bit is set in the HyperTransport
260          * Transaction Control register. */
261         apicrput(Siv, Swen | IdtLAPIC_SPURIOUS);
262
263         /* Acknowledge any outstanding interrupts. */
264         apicrput(Eoi, 0);
265
266         /* Mask interrupts on Performance Counter overflow and Thermal Sensor if
267          * implemented, and on Lintr0 (Legacy INTR), Lintr1 (Legacy NMI), and the
268          * Timer.  Clear any Error Status (write followed by read) and enable the
269          * Error interrupt. */
270         switch (apic->nlvt) {
271                 case 6:
272                         apicrput(Tslvt, Im);
273                         /* fall-through */
274                 case 5:
275                         apicrput(Pclvt, Im);
276                         /* fall-through */
277                 default:
278                         break;
279         }
280         /* lvt[0] and [1] were set to 0 in the BSS */
281         apicrput(Lint1, apic->lvt[1] | Im | IdtLAPIC_LINT1);
282         apicrput(Lint0, apic->lvt[0] | Im | IdtLAPIC_LINT0);
283         apicrput(Tlvt, Im);
284
285         apicrput(Es, 0);
286         apicrget(Es);
287         apicrput(Elvt, IdtLAPIC_ERROR | Im);
288
289         /* Not sure we need this from plan 9, Akaros never did:
290          *
291          * Issue an INIT Level De-Assert to synchronise arbitration ID's.
292          * (Necessary in this implementation? - not if Pentium 4 or Xeon (APIC
293          * Version >= 0x14), or AMD). */
294         //apicrput(Ichi, 0);
295         //apicrput(Iclo, DSallinc | Lassert | MTir);
296         //while (apicrget(Iclo) & Ds)
297         //      cpu_relax();
298
299         /* this is to enable the APIC interrupts.  we did a SW lapic_enable()
300          * earlier.  if we ever have issues where the lapic seems offline, check
301          * here. */
302         apicrput(Tp, 0);
303         return 1;
304 }