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
24static void *sLocalAPIC = NULL;
25static bool sX2APIC = false;
26
27
28bool
29apic_available()
30{
31	return sLocalAPIC != NULL || sX2APIC;
32}
33
34
35bool
36x2apic_available()
37{
38	return sX2APIC;
39}
40
41
42uint32
43apic_read(uint32 offset)
44{
45	return *(volatile uint32 *)((char *)sLocalAPIC + offset);
46}
47
48
49void
50apic_write(uint32 offset, uint32 data)
51{
52	*(volatile uint32 *)((char *)sLocalAPIC + offset) = data;
53}
54
55
56uint32
57apic_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
66uint32
67apic_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
76uint32
77apic_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
86void
87apic_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
96void
97apic_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
106uint32
107apic_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
116void
117apic_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
130uint32
131apic_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
140void
141apic_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
150void
151apic_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
173bool
174apic_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
183uint32
184apic_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
193void
194apic_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
203uint32
204apic_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
213void
214apic_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
223uint32
224apic_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
233void
234apic_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
243uint32
244apic_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
253void
254apic_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
263status_t
264apic_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
298status_t
299apic_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