VMX: change msr emulation to pass in the vm trapframe
[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 static char *apicregnames[] = {
18         [MSR_LAPIC_ID] "Identification",
19         [MSR_LAPIC_VERSION] "Version",
20         [MSR_LAPIC_TPR] "Task Priority",
21 //      [Ap] "Arbitration Priority",
22         [MSR_LAPIC_PPR] "Processor Priority",
23         [MSR_LAPIC_EOI] "EOI",
24         [MSR_LAPIC_LDR] "Logical Destination",
25         [MSR_LAPIC_SPURIOUS] "Spurious Interrupt Vector",
26         [MSR_LAPIC_ISR_START] "Interrupt Status (8)",
27         [MSR_LAPIC_TMR_START] "Trigger Mode (8)",
28         [MSR_LAPIC_IRR_START] "Interrupt Request (8)",
29         [MSR_LAPIC_ESR] "Error Status",
30         [MSR_LAPIC_ICR] "Interrupt Command",
31         [MSR_LAPIC_INITIAL_COUNT] "Timer Initial Count",
32         [MSR_LAPIC_CURRENT_COUNT] "Timer Current Count",
33         [MSR_LAPIC_DIVIDE_CONFIG_REG] "Timer Divide Configuration",
34
35         [MSR_LAPIC_LVT_TIMER] "Timer",
36         [MSR_LAPIC_LVT_LINT0] "Local Interrupt 0",
37         [MSR_LAPIC_LVT_LINT1] "Local Interrupt 1",
38         [MSR_LAPIC_LVT_ERROR_REG] "Error",
39         [MSR_LAPIC_LVT_PERFMON] "Performance Counter",
40         [MSR_LAPIC_LVT_THERMAL] "Thermal Sensor",
41 };
42
43 enum {                                                  /* Siv */
44         Swen = 0x00000100,                      /* Software Enable */
45         Fdis = 0x00000200,      /* Focus Disable */
46 };
47
48 enum {                                                  /* Iclo */
49         Lassert = 0x00004000,           /* Assert level */
50
51         DSnone = 0x00000000,    /* Use Destination Field */
52         DSself = 0x00040000,    /* Self is only destination */
53         DSallinc = 0x00080000,  /* All including self */
54         DSallexc = 0x000c0000,  /* All Excluding self */
55 };
56
57 enum {                                                  /* Tlvt */
58         Periodic = 0x00020000,          /* Periodic Timer Mode */
59 };
60
61 enum {                                                  /* Tdc */
62         DivX2 = 0x00000000,                     /* Divide by 2 */
63         DivX4 = 0x00000001,     /* Divide by 4 */
64         DivX8 = 0x00000002,     /* Divide by 8 */
65         DivX16 = 0x00000003,    /* Divide by 16 */
66         DivX32 = 0x00000008,    /* Divide by 32 */
67         DivX64 = 0x00000009,    /* Divide by 64 */
68         DivX128 = 0x0000000a,   /* Divide by 128 */
69         DivX1 = 0x0000000b,     /* Divide by 1 */
70 };
71
72 static uintptr_t apicbase;
73 static int apmachno = 1;
74 static uint32_t apicr310;
75
76 struct apic xlapic[Napic];
77
78 static void __apic_ir_dump(uint64_t r);
79
80 static void __apic_ir_dump(uint64_t r)
81 {
82         int i;
83         uint32_t val;
84
85         if (r != MSR_LAPIC_ISR_START && r != MSR_LAPIC_IRR_START &&
86             r != MSR_LAPIC_TMR_START)
87                 panic("Invalid register dump offset!");
88
89         for (i = 7; i >= 0; i--) {
90                 val = apicrget(r+i);
91                 if (val) {
92                         printk("Register at range (%d,%d]: 0x%08x\n", ((i+1)*32),
93                                i*32, val);
94                 }
95         }
96 }
97 void apic_isr_dump(void)
98 {
99         printk("ISR DUMP\n");
100         __apic_ir_dump(MSR_LAPIC_ISR_START);
101 }
102 void apic_irr_dump(void)
103 {
104         printk("IRR DUMP\n");
105         __apic_ir_dump(MSR_LAPIC_IRR_START);
106 }
107
108 uint32_t apicrget(uint64_t r)
109 {
110         uint32_t val;
111
112         if (r >= MSR_LAPIC_END)
113                 panic("%s: OUT OF BOUNDS: register 0x%x\n", __func__, r);
114         if (r != MSR_LAPIC_SPURIOUS && r != MSR_LAPIC_DIVIDE_CONFIG_REG)
115                 printd("%s: Reading from register 0x%llx\n",
116                        __func__, r);
117
118         val = read_msr(r);
119         printd("apicrget: %s returns %p\n", apicregnames[r], val);
120         if (r == MSR_LAPIC_ID) {
121                 printd("APIC ID: 0x%lx\n", val);
122                 printd("APIC LOGICAL ID: 0x%lx\n",
123                        apicrget(MSR_LAPIC_LDR));
124         }
125         return val;
126 }
127
128 void apicrput(uint64_t r, uint32_t data)
129 {
130         uint64_t temp_data = 0;
131
132         if (r >= MSR_LAPIC_END)
133                 panic("%s: OUT OF BOUNDS: register 0x%x\n", __func__, r);
134         if (r != MSR_LAPIC_INITIAL_COUNT && r != MSR_LAPIC_LVT_TIMER &&
135             r != MSR_LAPIC_DIVIDE_CONFIG_REG && r != MSR_LAPIC_EOI)
136                 printd("%s: Writing to register 0x%llx, value 0x%lx\n",
137                        __func__, r, data);
138         if (r == MSR_LAPIC_ID)
139                 panic("ILLEGAL WRITE TO ID");
140         printd("apicrput: %s = %p\n", apicregnames[r], data);
141
142         temp_data |= data;
143
144         write_msr(r, temp_data);
145 }
146
147 void apicsendipi(uint64_t data)
148 {
149         printd("SENDING IPI: 0x%016lx\n", data);
150         write_msr(MSR_LAPIC_ICR, data);
151 }
152
153 void apicinit(int apicno, uintptr_t pa, int isbp)
154 {
155         struct apic *apic;
156         uint64_t msr_val;
157         /*
158          * Mark the APIC useable if it has a good ID
159          * and the registers can be mapped.
160          * The APIC Extended Broadcast and ID bits in the HyperTransport
161          * Transaction Control register determine whether 4 or 8 bits
162          * are used for the APIC ID. There is also xAPIC and x2APIC
163          * to be dealt with sometime.
164          */
165         printd("apicinit: apicno %d pa %#p isbp %d\n", apicno, pa, isbp);
166         if (apicno >= Napic) {
167                 printd("apicinit%d: out of range\n", apicno);
168                 return;
169         }
170         if ((apic = &xlapic[apicno])->useable) {
171                 printd("apicinit%d: already initialised\n", apicno);
172                 return;
173         }
174         assert(pa == LAPIC_PBASE);
175         apic->useable = 1;
176
177         /* plan 9 used to set up a mapping btw apic and pcpui like so:
178                 pcpui->apicno = apicno; // acpino is the hw_coreid
179                 apic->machno = apmachno++; // machno is the os_coreid
180          * akaros does its own remapping of hw <-> os coreid during smp_boot */
181
182         //X2APIC INIT
183         msr_val = read_msr(IA32_APIC_BASE);
184         write_msr(IA32_APIC_BASE, msr_val | (3<<10));
185 }
186
187 static char *apicdump0(char *start, char *end, struct apic *apic, int i)
188 {
189         if (!apic->useable || apic->addr != 0)
190                 return start;
191         start = seprintf(start, end,
192                          "apic%d: oscore %d lint0 %#8.8p lint1 %#8.8p\n", i,
193                          get_os_coreid(i), apic->lvt[0], apic->lvt[1]);
194         start = seprintf(start, end, " tslvt %#8.8p pclvt %#8.8p elvt %#8.8p\n",
195                          apicrget(MSR_LAPIC_LVT_THERMAL),
196                          apicrget(MSR_LAPIC_LVT_PERFMON),
197                          apicrget(MSR_LAPIC_LVT_ERROR_REG));
198         start = seprintf(start, end,
199                          " tlvt %#8.8p lint0 %#8.8p lint1 %#8.8p siv %#8.8p\n",
200                          apicrget(MSR_LAPIC_LVT_TIMER),
201                          apicrget(MSR_LAPIC_LVT_LINT0),
202                          apicrget(MSR_LAPIC_LVT_LINT1),
203                          apicrget(MSR_LAPIC_SPURIOUS));
204         return start;
205 }
206
207 char *apicdump(char *start, char *end)
208 {
209         int i;
210
211         if (!2)
212                 return start;
213
214         start =
215                 seprintf(start, end, "apicbase %#p apmachno %d\n", apicbase, apmachno);
216         for (i = 0; i < Napic; i++)
217                 start = apicdump0(start, end, xlapic + i, i);
218         for (i = 0; i < Napic; i++)
219                 start = apicdump0(start, end, xioapic + i, i);
220         return start;
221 }
222
223 void handle_lapic_error(struct hw_trapframe *hw_tf, void *data)
224 {
225         uint32_t err;
226         apicrput(MSR_LAPIC_ESR, 0);
227         err = apicrget(MSR_LAPIC_ESR);
228         /* i get a shitload of these on my nehalem, many with err == 0 */
229         printd("LAPIC error vector, got 0x%08x\n", err);
230 }
231
232 int apiconline(void)
233 {
234         struct apic *apic;
235         uint64_t tsc;
236         uint32_t dfr, ver;
237         int apicno, nlvt;
238         uint64_t msr_val;
239
240         //X2APIC INIT
241         msr_val = read_msr(IA32_APIC_BASE);
242         write_msr(IA32_APIC_BASE, msr_val | (3<<10));
243
244         apicno = lapic_get_id();
245         if (apicno >= Napic) {
246                 printk("Bad apicno %d on HW core %d!!\n", apicno, hw_core_id());
247                 return 0;
248         }
249         apic = &xlapic[apicno];
250         /* The addr check tells us if it is an IOAPIC or not... */
251         if (!apic->useable || apic->addr) {
252                 printk("Unsuitable apicno %d on HW core %d!!\n", apicno, hw_core_id());
253                 return 0;
254         }
255         /* Things that can only be done when on the processor owning the APIC,
256          * apicinit above runs on the bootstrap processor. */
257         ver = apicrget(MSR_LAPIC_VERSION);
258         nlvt = ((ver >> 16) & 0xff) + 1;
259         if (nlvt > ARRAY_SIZE(apic->lvt)) {
260                 printk("apiconline%d: nlvt %d > max (%d)\n",
261                            apicno, nlvt, ARRAY_SIZE(apic->lvt));
262                 nlvt = ARRAY_SIZE(apic->lvt);
263         }
264         apic->nlvt = nlvt;
265         apic->ver = ver & 0xff;
266
267         /* These don't really matter in Physical mode; set the defaults anyway.  If
268          * we have problems with logical IPIs on AMD, check this out: */
269         //if (memcmp(m->cpuinfo, "AuthenticAMD", 12) == 0)
270         //      dfr = 0xf0000000;
271         //else
272         //      dfr = 0xffffffff;
273         //apicrput(Df, dfr);
274         //apicrput(MSR_LAPIC_LDR, 0x00000000);
275
276         /* Disable interrupts until ready by setting the Task Priority register to
277          * 0xff. */
278         apicrput(MSR_LAPIC_TPR, 0xff);
279
280         /* Software-enable the APIC in the Spurious Interrupt Vector register and
281          * set the vector number. The vector number must have bits 3-0 0x0f unless
282          * the Extended Spurious Vector Enable bit is set in the HyperTransport
283          * Transaction Control register. */
284         apicrput(MSR_LAPIC_SPURIOUS, Swen | IdtLAPIC_SPURIOUS);
285
286         /* Acknowledge any outstanding interrupts. */
287         apicrput(MSR_LAPIC_EOI, 0);
288
289         /* Mask interrupts on Performance Counter overflow and Thermal Sensor if
290          * implemented, and on Lintr0 (Legacy INTR), Lintr1 (Legacy NMI), and the
291          * Timer.  Clear any Error Status (write followed by read) and enable the
292          * Error interrupt. */
293         switch (apic->nlvt) {
294                 case 6:
295                         apicrput(MSR_LAPIC_LVT_THERMAL, Im);
296                         /* fall-through */
297                 case 5:
298                         apicrput(MSR_LAPIC_LVT_PERFMON, Im);
299                         /* fall-through */
300                 default:
301                         break;
302         }
303         /* lvt[0] and [1] were set to 0 in the BSS */
304         apicrput(MSR_LAPIC_LVT_LINT1, apic->lvt[1] | Im | IdtLAPIC_LINT1);
305         apicrput(MSR_LAPIC_LVT_LINT0, apic->lvt[0] | Im | IdtLAPIC_LINT0);
306         apicrput(MSR_LAPIC_LVT_TIMER, Im);
307
308         apicrput(MSR_LAPIC_ESR, 0);
309         apicrget(MSR_LAPIC_ESR);
310         apicrput(MSR_LAPIC_LVT_ERROR_REG, IdtLAPIC_ERROR | Im);
311
312         /* Not sure we need this from plan 9, Akaros never did:
313          *
314          * Issue an INIT Level De-Assert to synchronise arbitration ID's.
315          * (Necessary in this implementation? - not if Pentium 4 or Xeon (APIC
316          * Version >= 0x14), or AMD). */
317         //apicrput(Ichi, 0);
318         //apicrput(Iclo, DSallinc | Lassert | MTir);
319         //while (apicrget(Iclo) & Ds)
320         //      cpu_relax();
321
322         /* this is to enable the APIC interrupts.  we did a SW lapic_enable()
323          * earlier.  if we ever have issues where the lapic seems offline, check
324          * here. */
325         apicrput(MSR_LAPIC_TPR, 0);
326         return 1;
327 }