1 /*
2 * Copyright 2010, Michael Lotz, mmlr@mlotz.ch. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Copyright 2002-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
6 * Distributed under the terms of the MIT License.
7 *
8 * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
9 * Distributed under the terms of the NewOS License.
10 */
11
12 #include <arch/x86/apic.h>
13 #include <arch/x86/msi.h>
14
15 #include <debug.h>
16 #include <kernel/cpu.h>
17 #include <safemode.h>
18 #include <vm/vm.h>
19 #include <util/AutoLock.h>
20
21 #include "timers/apic_timer.h"
22
23
24 static void *sLocalAPIC = NULL;
25 static bool sX2APIC = false;
26
27
28 bool
apic_available()29 apic_available()
30 {
31 return sLocalAPIC != NULL || sX2APIC;
32 }
33
34
35 bool
x2apic_available()36 x2apic_available()
37 {
38 return sX2APIC;
39 }
40
41
42 static uint32
apic_read(uint32 offset)43 apic_read(uint32 offset)
44 {
45 return *(volatile uint32 *)((char *)sLocalAPIC + offset);
46 }
47
48
49 static void
apic_write(uint32 offset,uint32 data)50 apic_write(uint32 offset, uint32 data)
51 {
52 *(volatile uint32 *)((char *)sLocalAPIC + offset) = data;
53 }
54
55
56 uint32
apic_local_id()57 apic_local_id()
58 {
59 if (sX2APIC)
60 return x86_read_msr(IA32_MSR_APIC_ID);
61 else
62 return (apic_read(APIC_ID) & 0xffffffff) >> 24;
63 }
64
65
66 uint32
apic_version()67 apic_version()
68 {
69 if (sX2APIC)
70 return x86_read_msr(IA32_MSR_APIC_VERSION);
71 else
72 return apic_read(APIC_VERSION);
73 }
74
75
76 uint32
apic_task_priority()77 apic_task_priority()
78 {
79 if (sX2APIC)
80 return x86_read_msr(IA32_MSR_APIC_TASK_PRIORITY);
81 else
82 return apic_read(APIC_TASK_PRIORITY);
83 }
84
85
86 void
apic_set_task_priority(uint32 config)87 apic_set_task_priority(uint32 config)
88 {
89 if (sX2APIC)
90 x86_write_msr(IA32_MSR_APIC_TASK_PRIORITY, config);
91 else
92 apic_write(APIC_TASK_PRIORITY, config);
93 }
94
95
96 void
apic_end_of_interrupt()97 apic_end_of_interrupt()
98 {
99 if (sX2APIC)
100 x86_write_msr(IA32_MSR_APIC_EOI, 0);
101 else
102 apic_write(APIC_EOI, 0);
103 }
104
105
106 uint32
apic_logical_apic_id()107 apic_logical_apic_id()
108 {
109 if (sX2APIC)
110 return x86_read_msr(IA32_MSR_APIC_LOGICAL_DEST);
111 else
112 return apic_read(APIC_LOGICAL_DEST);
113 }
114
115
116 void
apic_disable_local_ints()117 apic_disable_local_ints()
118 {
119 // just clear them out completely
120 if (sX2APIC) {
121 x86_write_msr(IA32_MSR_APIC_LVT_LINT0, APIC_LVT_MASKED);
122 x86_write_msr(IA32_MSR_APIC_LVT_LINT1, APIC_LVT_MASKED);
123 } else {
124 apic_write(APIC_LVT_LINT0, APIC_LVT_MASKED);
125 apic_write(APIC_LVT_LINT1, APIC_LVT_MASKED);
126 }
127 }
128
129
130 uint32
apic_spurious_intr_vector()131 apic_spurious_intr_vector()
132 {
133 if (sX2APIC)
134 return x86_read_msr(IA32_MSR_APIC_SPURIOUS_INTR_VECTOR);
135 else
136 return apic_read(APIC_SPURIOUS_INTR_VECTOR);
137 }
138
139
140 void
apic_set_spurious_intr_vector(uint32 config)141 apic_set_spurious_intr_vector(uint32 config)
142 {
143 if (sX2APIC)
144 x86_write_msr(IA32_MSR_APIC_SPURIOUS_INTR_VECTOR, config);
145 else
146 apic_write(APIC_SPURIOUS_INTR_VECTOR, config);
147 }
148
149
150 void
apic_set_interrupt_command(uint32 destination,uint32 mode)151 apic_set_interrupt_command(uint32 destination, uint32 mode)
152 {
153 if (sX2APIC) {
154 uint64 command = 0;
155 command |= (uint64)destination << 32;
156 command |= mode;
157 x86_write_msr(IA32_MSR_APIC_INTR_COMMAND, command);
158 } else {
159 uint32 command2 = 0;
160 command2 |= destination << 24;
161 apic_write(APIC_INTR_COMMAND_2, command2);
162
163 uint32 command1 = mode;
164 apic_write(APIC_INTR_COMMAND_1, command1);
165 }
166 }
167
168
169 bool
apic_interrupt_delivered(void)170 apic_interrupt_delivered(void)
171 {
172 if (sX2APIC)
173 return true;
174 else
175 return (apic_read(APIC_INTR_COMMAND_1) & APIC_DELIVERY_STATUS) == 0;
176 }
177
178
179 uint32
apic_lvt_timer()180 apic_lvt_timer()
181 {
182 if (sX2APIC)
183 return x86_read_msr(IA32_MSR_APIC_LVT_TIMER);
184 else
185 return apic_read(APIC_LVT_TIMER);
186 }
187
188
189 void
apic_set_lvt_timer(uint32 config)190 apic_set_lvt_timer(uint32 config)
191 {
192 if (sX2APIC)
193 x86_write_msr(IA32_MSR_APIC_LVT_TIMER, config);
194 else
195 apic_write(APIC_LVT_TIMER, config);
196 }
197
198
199 uint32
apic_lvt_error()200 apic_lvt_error()
201 {
202 if (sX2APIC)
203 return x86_read_msr(IA32_MSR_APIC_LVT_ERROR);
204 else
205 return apic_read(APIC_LVT_ERROR);
206 }
207
208
209 void
apic_set_lvt_error(uint32 config)210 apic_set_lvt_error(uint32 config)
211 {
212 if (sX2APIC)
213 x86_write_msr(IA32_MSR_APIC_LVT_ERROR, config);
214 else
215 apic_write(APIC_LVT_ERROR, config);
216 }
217
218
219 uint32
apic_lvt_initial_timer_count()220 apic_lvt_initial_timer_count()
221 {
222 if (sX2APIC)
223 return x86_read_msr(IA32_MSR_APIC_INITIAL_TIMER_COUNT);
224 else
225 return apic_read(APIC_INITIAL_TIMER_COUNT);
226 }
227
228
229 void
apic_set_lvt_initial_timer_count(uint32 config)230 apic_set_lvt_initial_timer_count(uint32 config)
231 {
232 if (sX2APIC)
233 x86_write_msr(IA32_MSR_APIC_INITIAL_TIMER_COUNT, config);
234 else
235 apic_write(APIC_INITIAL_TIMER_COUNT, config);
236 }
237
238
239 uint32
apic_lvt_timer_divide_config()240 apic_lvt_timer_divide_config()
241 {
242 if (sX2APIC)
243 return x86_read_msr(IA32_MSR_APIC_TIMER_DIVIDE_CONFIG);
244 else
245 return apic_read(APIC_TIMER_DIVIDE_CONFIG);
246 }
247
248
249 void
apic_set_lvt_timer_divide_config(uint32 config)250 apic_set_lvt_timer_divide_config(uint32 config)
251 {
252 if (sX2APIC)
253 x86_write_msr(IA32_MSR_APIC_TIMER_DIVIDE_CONFIG, config);
254 else
255 apic_write(APIC_TIMER_DIVIDE_CONFIG, config);
256 }
257
258
259 status_t
apic_init(kernel_args * args)260 apic_init(kernel_args *args)
261 {
262 if (args->arch_args.apic == NULL)
263 return B_NO_INIT;
264
265 uint64 apic_base = x86_read_msr(IA32_MSR_APIC_BASE);
266
267 if (x86_check_feature(IA32_FEATURE_EXT_X2APIC, FEATURE_EXT)
268 && (x86_check_feature(IA32_FEATURE_EXT_HYPERVISOR, FEATURE_EXT)
269 || ((apic_base & IA32_MSR_APIC_BASE_X2APIC) != 0))) {
270 dprintf("found x2apic\n");
271
272 if (get_safemode_boolean(B_SAFEMODE_DISABLE_X2APIC, false)) {
273 dprintf("x2apic disabled per safemode setting\n");
274 } else {
275 sX2APIC = true;
276 return B_OK;
277 }
278 }
279
280 sLocalAPIC = args->arch_args.apic;
281 dprintf("mapping local apic at %p\n", sLocalAPIC);
282 if (vm_map_physical_memory(B_SYSTEM_TEAM, "local apic", &sLocalAPIC,
283 B_EXACT_ADDRESS, B_PAGE_SIZE,
284 B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA,
285 args->arch_args.apic_phys, true) < 0) {
286 panic("mapping the local apic failed");
287 return B_ERROR;
288 }
289
290 return B_OK;
291 }
292
293
294 status_t
apic_per_cpu_init(kernel_args * args,int32 cpu)295 apic_per_cpu_init(kernel_args *args, int32 cpu)
296 {
297 if (sX2APIC) {
298 uint64 apic_base = x86_read_msr(IA32_MSR_APIC_BASE);
299 if ((apic_base & IA32_MSR_APIC_BASE_X2APIC) == 0) {
300 x86_write_msr(IA32_MSR_APIC_BASE, apic_base
301 | IA32_MSR_APIC_BASE_X2APIC);
302 }
303 }
304
305 dprintf("setting up %sapic for CPU %" B_PRId32 ": apic id %" B_PRIu32 ", "
306 "version %" B_PRIu32 "\n", sX2APIC ? "x2" : "", cpu, apic_local_id(),
307 apic_version());
308
309 if (!sX2APIC && cpu < 8) {
310 apic_write(APIC_DEST_FORMAT, uint32(-1));
311
312 uint8 logical_apic_id = 1 << cpu;
313 uint32 value = apic_read(APIC_LOGICAL_DEST);
314 value &= 0xffffff;
315 apic_write(APIC_LOGICAL_DEST, value | (logical_apic_id << 24));
316 }
317
318 // get logical APIC ID
319 gCPU[cpu].arch.logical_apic_id = apic_logical_apic_id();
320 if (!sX2APIC)
321 gCPU[cpu].arch.logical_apic_id >>= 24;
322 dprintf("CPU %" B_PRId32 ": logical apic id: %#" B_PRIx32 "\n", cpu,
323 gCPU[cpu].arch.logical_apic_id);
324
325 /* set spurious interrupt vector to 0xff */
326 uint32 config = apic_spurious_intr_vector() & 0xffffff00;
327 config |= APIC_ENABLE | 0xff;
328 apic_set_spurious_intr_vector(config);
329
330 // don't touch the LINT0/1 configuration in virtual wire mode
331 // ToDo: implement support for other modes...
332 #if 0
333 if (cpu == 0) {
334 /* setup LINT0 as ExtINT */
335 config = (apic_read(APIC_LINT0) & 0xffff00ff);
336 config |= APIC_LVT_DM_ExtINT | APIC_LVT_IIPP | APIC_LVT_TM;
337 apic_write(APIC_LINT0, config);
338
339 /* setup LINT1 as NMI */
340 config = (apic_read(APIC_LINT1) & 0xffff00ff);
341 config |= APIC_LVT_DM_NMI | APIC_LVT_IIPP;
342 apic_write(APIC_LINT1, config);
343 }
344 if (cpu > 0) {
345 dprintf("LINT0: %p\n", (void *)apic_read(APIC_LINT0));
346 dprintf("LINT1: %p\n", (void *)apic_read(APIC_LINT1));
347
348 /* disable LINT0/1 */
349 config = apic_read(APIC_LINT0);
350 apic_write(APIC_LINT0, config | APIC_LVT_MASKED);
351
352 config = apic_read(APIC_LINT1);
353 apic_write(APIC_LINT1, config | APIC_LVT_MASKED);
354 } else {
355 dprintf("0: LINT0: %p\n", (void *)apic_read(APIC_LINT0));
356 dprintf("0: LINT1: %p\n", (void *)apic_read(APIC_LINT1));
357 }
358 #endif
359
360 apic_timer_per_cpu_init(args, cpu);
361
362 /* setup error vector to 0xfe */
363 config = (apic_lvt_error() & 0xffffff00) | 0xfe;
364 apic_set_lvt_error(config);
365
366 /* accept all interrupts */
367 config = apic_task_priority() & 0xffffff00;
368 apic_set_task_priority(config);
369
370 config = apic_spurious_intr_vector();
371 apic_end_of_interrupt();
372
373 return B_OK;
374 }
375