xref: /haiku/src/system/kernel/arch/x86/apic.cpp (revision ba0223da5d79c5cd27496ee0e5712921cebb7642)
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 static uint32
43 apic_read(uint32 offset)
44 {
45 	return *(volatile uint32 *)((char *)sLocalAPIC + offset);
46 }
47 
48 
49 static 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 = 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
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
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
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
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
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
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
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
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
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
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
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