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