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