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