xref: /haiku/src/system/kernel/arch/x86/apic.cpp (revision 02354704729d38c3b078c696adc1bbbd33cbcf72)
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
29 apic_available()
30 {
31 	return sLocalAPIC != NULL || sX2APIC;
32 }
33 
34 
35 bool
36 x2apic_available()
37 {
38 	return sX2APIC;
39 }
40 
41 
42 uint32
43 apic_read(uint32 offset)
44 {
45 	return *(volatile uint32 *)((char *)sLocalAPIC + offset);
46 }
47 
48 
49 void
50 apic_write(uint32 offset, uint32 data)
51 {
52 	*(volatile uint32 *)((char *)sLocalAPIC + offset) = data;
53 }
54 
55 
56 uint32
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
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
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
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
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
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
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
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
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
151 apic_set_interrupt_command(uint32 destination, uint32 mode)
152 {
153 	if (sX2APIC) {
154 		uint64 command = x86_read_msr(IA32_MSR_APIC_INTR_COMMAND);
155 		command &= APIC_INTR_COMMAND_1_MASK;
156 		command |= (uint64)destination << 32;
157 		command |= mode;
158 		x86_write_msr(IA32_MSR_APIC_INTR_COMMAND, command);
159 	} else {
160 		uint32 command2 = apic_read(APIC_INTR_COMMAND_2)
161 				& APIC_INTR_COMMAND_2_MASK;
162 		command2 |= destination << 24;
163 		apic_write(APIC_INTR_COMMAND_2, command2);
164 
165 		uint32 command1 = apic_read(APIC_INTR_COMMAND_1)
166 				& APIC_INTR_COMMAND_1_MASK;
167 		command1 |= mode;
168 		apic_write(APIC_INTR_COMMAND_1, command1);
169 	}
170 }
171 
172 
173 bool
174 apic_interrupt_delivered(void)
175 {
176 	if (sX2APIC)
177 		return true;
178 	else
179 		return (apic_read(APIC_INTR_COMMAND_1) & APIC_DELIVERY_STATUS) == 0;
180 }
181 
182 
183 uint32
184 apic_lvt_timer()
185 {
186 	if (sX2APIC)
187 		return x86_read_msr(IA32_MSR_APIC_LVT_TIMER);
188 	else
189 		return apic_read(APIC_LVT_TIMER);
190 }
191 
192 
193 void
194 apic_set_lvt_timer(uint32 config)
195 {
196 	if (sX2APIC)
197 		x86_write_msr(IA32_MSR_APIC_LVT_TIMER, config);
198 	else
199 		apic_write(APIC_LVT_TIMER, config);
200 }
201 
202 
203 uint32
204 apic_lvt_error()
205 {
206 	if (sX2APIC)
207 		return x86_read_msr(IA32_MSR_APIC_LVT_ERROR);
208 	else
209 		return apic_read(APIC_LVT_ERROR);
210 }
211 
212 
213 void
214 apic_set_lvt_error(uint32 config)
215 {
216 	if (sX2APIC)
217 		x86_write_msr(IA32_MSR_APIC_LVT_ERROR, config);
218 	else
219 		apic_write(APIC_LVT_ERROR, config);
220 }
221 
222 
223 uint32
224 apic_lvt_initial_timer_count()
225 {
226 	if (sX2APIC)
227 		return x86_read_msr(IA32_MSR_APIC_INITIAL_TIMER_COUNT);
228 	else
229 		return apic_read(APIC_INITIAL_TIMER_COUNT);
230 }
231 
232 
233 void
234 apic_set_lvt_initial_timer_count(uint32 config)
235 {
236 	if (sX2APIC)
237 		x86_write_msr(IA32_MSR_APIC_INITIAL_TIMER_COUNT, config);
238 	else
239 		apic_write(APIC_INITIAL_TIMER_COUNT, config);
240 }
241 
242 
243 uint32
244 apic_lvt_timer_divide_config()
245 {
246 	if (sX2APIC)
247 		return x86_read_msr(IA32_MSR_APIC_TIMER_DIVIDE_CONFIG);
248 	else
249 		return apic_read(APIC_TIMER_DIVIDE_CONFIG);
250 }
251 
252 
253 void
254 apic_set_lvt_timer_divide_config(uint32 config)
255 {
256 	if (sX2APIC)
257 		x86_write_msr(IA32_MSR_APIC_TIMER_DIVIDE_CONFIG, config);
258 	else
259 		apic_write(APIC_TIMER_DIVIDE_CONFIG, config);
260 }
261 
262 
263 status_t
264 apic_init(kernel_args *args)
265 {
266 	if (args->arch_args.apic == NULL)
267 		return B_NO_INIT;
268 
269 	uint64 apic_base = x86_read_msr(IA32_MSR_APIC_BASE);
270 
271 	if (x86_check_feature(IA32_FEATURE_EXT_X2APIC, FEATURE_EXT)
272 		&& (x86_check_feature(IA32_FEATURE_EXT_HYPERVISOR, FEATURE_EXT)
273 			|| ((apic_base & IA32_MSR_APIC_BASE_X2APIC) != 0))) {
274 		dprintf("found x2apic\n");
275 
276 		if (get_safemode_boolean(B_SAFEMODE_DISABLE_X2APIC, false)) {
277 			dprintf("x2apic disabled per safemode setting\n");
278 		} else {
279 			sX2APIC = true;
280 			return B_OK;
281 		}
282 	}
283 
284 	sLocalAPIC = args->arch_args.apic;
285 	dprintf("mapping local apic at %p\n", sLocalAPIC);
286 	if (vm_map_physical_memory(B_SYSTEM_TEAM, "local apic", &sLocalAPIC,
287 			B_EXACT_ADDRESS, B_PAGE_SIZE,
288 			B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA,
289 			args->arch_args.apic_phys, true) < 0) {
290 		panic("mapping the local apic failed");
291 		return B_ERROR;
292 	}
293 
294 	return B_OK;
295 }
296 
297 
298 status_t
299 apic_per_cpu_init(kernel_args *args, int32 cpu)
300 {
301 	if (sX2APIC) {
302 		uint64 apic_base = x86_read_msr(IA32_MSR_APIC_BASE);
303 		if ((apic_base & IA32_MSR_APIC_BASE_X2APIC) == 0) {
304 			x86_write_msr(IA32_MSR_APIC_BASE, apic_base
305 				| IA32_MSR_APIC_BASE_X2APIC);
306 		}
307 	}
308 
309 	dprintf("setting up %sapic for CPU %" B_PRId32 ": apic id %" B_PRIu32 ", "
310 		"version %" B_PRIu32 "\n", sX2APIC ? "x2" : "", cpu, apic_local_id(),
311 		apic_version());
312 
313 	if (!sX2APIC && cpu < 8) {
314 		apic_write(APIC_DEST_FORMAT, uint32(-1));
315 
316 		uint8 logical_apic_id = 1 << cpu;
317 		uint32 value = apic_read(APIC_LOGICAL_DEST);
318 		value &= 0xffffff;
319 		apic_write(APIC_LOGICAL_DEST, value | (logical_apic_id << 24));
320 	}
321 
322 	// get logical APIC ID
323 	gCPU[cpu].arch.logical_apic_id = apic_logical_apic_id();
324 	if (!sX2APIC)
325 		gCPU[cpu].arch.logical_apic_id >>= 24;
326 	dprintf("CPU %" B_PRId32 ": logical apic id: %#" B_PRIx32 "\n", cpu,
327 		gCPU[cpu].arch.logical_apic_id);
328 
329 	/* set spurious interrupt vector to 0xff */
330 	uint32 config = apic_spurious_intr_vector() & 0xffffff00;
331 	config |= APIC_ENABLE | 0xff;
332 	apic_set_spurious_intr_vector(config);
333 
334 	// don't touch the LINT0/1 configuration in virtual wire mode
335 	// ToDo: implement support for other modes...
336 #if 0
337 	if (cpu == 0) {
338 		/* setup LINT0 as ExtINT */
339 		config = (apic_read(APIC_LINT0) & 0xffff00ff);
340 		config |= APIC_LVT_DM_ExtINT | APIC_LVT_IIPP | APIC_LVT_TM;
341 		apic_write(APIC_LINT0, config);
342 
343 		/* setup LINT1 as NMI */
344 		config = (apic_read(APIC_LINT1) & 0xffff00ff);
345 		config |= APIC_LVT_DM_NMI | APIC_LVT_IIPP;
346 		apic_write(APIC_LINT1, config);
347 	}
348 	if (cpu > 0) {
349 		dprintf("LINT0: %p\n", (void *)apic_read(APIC_LINT0));
350 		dprintf("LINT1: %p\n", (void *)apic_read(APIC_LINT1));
351 
352 		/* disable LINT0/1 */
353 		config = apic_read(APIC_LINT0);
354 		apic_write(APIC_LINT0, config | APIC_LVT_MASKED);
355 
356 		config = apic_read(APIC_LINT1);
357 		apic_write(APIC_LINT1, config | APIC_LVT_MASKED);
358 	} else {
359 		dprintf("0: LINT0: %p\n", (void *)apic_read(APIC_LINT0));
360 		dprintf("0: LINT1: %p\n", (void *)apic_read(APIC_LINT1));
361 	}
362 #endif
363 
364 	apic_timer_per_cpu_init(args, cpu);
365 
366 	/* setup error vector to 0xfe */
367 	config = (apic_lvt_error() & 0xffffff00) | 0xfe;
368 	apic_set_lvt_error(config);
369 
370 	/* accept all interrupts */
371 	config = apic_task_priority() & 0xffffff00;
372 	apic_set_task_priority(config);
373 
374 	config = apic_spurious_intr_vector();
375 	apic_end_of_interrupt();
376 
377 	return B_OK;
378 }
379