xref: /haiku/src/system/kernel/arch/x86/arch_smp.cpp (revision 9760dcae2038d47442f4658c2575844c6cf92c40)
1 /*
2  * Copyright 2002-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
6  * Distributed under the terms of the NewOS License.
7  */
8 
9 
10 #include <boot/kernel_args.h>
11 #include <vm/vm.h>
12 #include <int.h>
13 #include <smp.h>
14 #include <smp_priv.h>
15 
16 #include <arch/cpu.h>
17 #include <arch/vm.h>
18 #include <arch/smp.h>
19 
20 #include <timer.h>
21 
22 #include <arch/x86/smp_priv.h>
23 #include <arch/x86/arch_apic.h>
24 #include <arch/x86/timer.h>
25 
26 #include <string.h>
27 #include <stdio.h>
28 
29 #include "timers/apic.h"
30 
31 
32 //#define TRACE_ARCH_SMP
33 #ifdef TRACE_ARCH_SMP
34 #	define TRACE(x) dprintf x
35 #else
36 #	define TRACE(x) ;
37 #endif
38 
39 static void *sLocalAPIC = NULL;
40 static uint32 sCPUAPICIds[B_MAX_CPU_COUNT];
41 static uint32 sCPUOSIds[B_MAX_CPU_COUNT];
42 static uint32 sAPICVersions[B_MAX_CPU_COUNT];
43 
44 extern bool gUsingIOAPIC;
45 extern "C" void init_sse(void);
46 
47 static uint32
48 apic_read(uint32 offset)
49 {
50 	return *(volatile uint32 *)((char *)sLocalAPIC + offset);
51 }
52 
53 
54 static void
55 apic_write(uint32 offset, uint32 data)
56 {
57 	*(volatile uint32 *)((char *)sLocalAPIC + offset) = data;
58 }
59 
60 
61 static status_t
62 setup_apic(kernel_args *args, int32 cpu)
63 {
64 	uint32 config;
65 
66 	TRACE(("setting up the APIC for CPU %ld...\n", cpu));
67 	TRACE(("	apic id %ld, version %ld\n", apic_read(APIC_ID), apic_read(APIC_VERSION)));
68 
69 	/* set spurious interrupt vector to 0xff */
70 	config = apic_read(APIC_SPURIOUS_INTR_VECTOR) & 0xffffff00;
71 	config |= APIC_ENABLE | 0xff;
72 	apic_write(APIC_SPURIOUS_INTR_VECTOR, config);
73 
74 	// don't touch the LINT0/1 configuration in virtual wire mode
75 	// ToDo: implement support for other modes...
76 #if 0
77 	if (cpu == 0) {
78 		/* setup LINT0 as ExtINT */
79 		config = (apic_read(APIC_LINT0) & 0xffff00ff);
80 		config |= APIC_LVT_DM_ExtINT | APIC_LVT_IIPP | APIC_LVT_TM;
81 		apic_write(APIC_LINT0, config);
82 
83 		/* setup LINT1 as NMI */
84 		config = (apic_read(APIC_LINT1) & 0xffff00ff);
85 		config |= APIC_LVT_DM_NMI | APIC_LVT_IIPP;
86 		apic_write(APIC_LINT1, config);
87 	}
88 	if (cpu > 0) {
89 		dprintf("LINT0: %p\n", (void *)apic_read(APIC_LINT0));
90 		dprintf("LINT1: %p\n", (void *)apic_read(APIC_LINT1));
91 
92 		/* disable LINT0/1 */
93 		config = apic_read(APIC_LINT0);
94 		apic_write(APIC_LINT0, config | APIC_LVT_MASKED);
95 
96 		config = apic_read(APIC_LINT1);
97 		apic_write(APIC_LINT1, config | APIC_LVT_MASKED);
98 	} else {
99 		dprintf("0: LINT0: %p\n", (void *)apic_read(APIC_LINT0));
100 		dprintf("0: LINT1: %p\n", (void *)apic_read(APIC_LINT1));
101 	}
102 #endif
103 
104 	apic_init_timer(args, cpu);
105 
106 	/* setup error vector to 0xfe */
107 	config = (apic_read(APIC_LVT_ERROR) & 0xffffff00) | 0xfe;
108 	apic_write(APIC_LVT_ERROR, config);
109 
110 	/* accept all interrupts */
111 	config = apic_read(APIC_TASK_PRIORITY) & 0xffffff00;
112 	apic_write(APIC_TASK_PRIORITY, config);
113 
114 	config = apic_read(APIC_SPURIOUS_INTR_VECTOR);
115 	apic_write(APIC_EOI, 0);
116 
117 	return B_OK;
118 }
119 
120 
121 static int32
122 i386_ici_interrupt(void *data)
123 {
124 	// genuine inter-cpu interrupt
125 	int cpu = smp_get_current_cpu();
126 	TRACE(("inter-cpu interrupt on cpu %d\n", cpu));
127 
128 	// if we are not using the IO APIC we need to acknowledge the
129 	// interrupt ourselfs
130 	if (!gUsingIOAPIC)
131 		apic_write(APIC_EOI, 0);
132 
133 	return smp_intercpu_int_handler(cpu);
134 }
135 
136 
137 static int32
138 i386_spurious_interrupt(void *data)
139 {
140 	// spurious interrupt
141 	TRACE(("spurious interrupt on cpu %ld\n", smp_get_current_cpu()));
142 
143 	// spurious interrupts must not be acknowledged as it does not expect
144 	// a end of interrupt - if we still do it we would loose the next best
145 	// interrupt
146 	return B_HANDLED_INTERRUPT;
147 }
148 
149 
150 static int32
151 i386_smp_error_interrupt(void *data)
152 {
153 	// smp error interrupt
154 	TRACE(("smp error interrupt on cpu %ld\n", smp_get_current_cpu()));
155 
156 	// if we are not using the IO APIC we need to acknowledge the
157 	// interrupt ourselfs
158 	if (!gUsingIOAPIC)
159 		apic_write(APIC_EOI, 0);
160 
161 	return B_HANDLED_INTERRUPT;
162 }
163 
164 
165 status_t
166 arch_smp_init(kernel_args *args)
167 {
168 	TRACE(("arch_smp_init: entry\n"));
169 
170 	if (args->arch_args.apic == NULL)
171 		return B_OK;
172 
173 	sLocalAPIC = args->arch_args.apic;
174 
175 	// setup some globals
176 	memcpy(sCPUAPICIds, args->arch_args.cpu_apic_id, sizeof(args->arch_args.cpu_apic_id));
177 	memcpy(sCPUOSIds, args->arch_args.cpu_os_id, sizeof(args->arch_args.cpu_os_id));
178 	memcpy(sAPICVersions, args->arch_args.cpu_apic_version, sizeof(args->arch_args.cpu_apic_version));
179 
180 	// set up the local apic on the boot cpu
181 	arch_smp_per_cpu_init(args, 0);
182 
183 	if (args->num_cpus > 1) {
184 		// I/O interrupts start at ARCH_INTERRUPT_BASE, so all interrupts are shifted
185 		install_io_interrupt_handler(0xfd - ARCH_INTERRUPT_BASE, &i386_ici_interrupt, NULL, B_NO_LOCK_VECTOR);
186 		install_io_interrupt_handler(0xfe - ARCH_INTERRUPT_BASE, &i386_smp_error_interrupt, NULL, B_NO_LOCK_VECTOR);
187 		install_io_interrupt_handler(0xff - ARCH_INTERRUPT_BASE, &i386_spurious_interrupt, NULL, B_NO_LOCK_VECTOR);
188 	}
189 
190 	return B_OK;
191 }
192 
193 
194 status_t
195 arch_smp_per_cpu_init(kernel_args *args, int32 cpu)
196 {
197 	// set up the local apic on the current cpu
198 	TRACE(("arch_smp_init_percpu: setting up the apic on cpu %ld\n", cpu));
199 	setup_apic(args, cpu);
200 
201 	init_sse();
202 
203 	return B_OK;
204 }
205 
206 
207 void
208 arch_smp_send_broadcast_ici(void)
209 {
210 	uint32 config;
211 	cpu_status state = disable_interrupts();
212 
213 	config = apic_read(APIC_INTR_COMMAND_1) & APIC_INTR_COMMAND_1_MASK;
214 	apic_write(APIC_INTR_COMMAND_1, config | 0xfd | APIC_DELIVERY_MODE_FIXED
215 		| APIC_INTR_COMMAND_1_ASSERT
216 		| APIC_INTR_COMMAND_1_DEST_MODE_PHYSICAL
217 		| APIC_INTR_COMMAND_1_DEST_ALL_BUT_SELF);
218 
219 	restore_interrupts(state);
220 }
221 
222 
223 void
224 arch_smp_send_ici(int32 target_cpu)
225 {
226 	uint32 config;
227 	uint32 timeout;
228 	cpu_status state;
229 
230 	state = disable_interrupts();
231 
232 	config = apic_read(APIC_INTR_COMMAND_2) & APIC_INTR_COMMAND_2_MASK;
233 	apic_write(APIC_INTR_COMMAND_2, config | sCPUAPICIds[target_cpu] << 24);
234 
235 	config = apic_read(APIC_INTR_COMMAND_1) & APIC_INTR_COMMAND_1_MASK;
236 	apic_write(APIC_INTR_COMMAND_1, config | 0xfd | APIC_DELIVERY_MODE_FIXED
237 		| APIC_INTR_COMMAND_1_ASSERT
238 		| APIC_INTR_COMMAND_1_DEST_MODE_PHYSICAL
239 		| APIC_INTR_COMMAND_1_DEST_FIELD);
240 
241 	timeout = 100000000;
242 	// wait for message to be sent
243 	while ((apic_read(APIC_INTR_COMMAND_1) & APIC_DELIVERY_STATUS) != 0 && --timeout != 0)
244 		asm volatile ("pause;");
245 
246 	if (timeout == 0)
247 		panic("arch_smp_send_ici: timeout, target_cpu %ld", target_cpu);
248 
249 	restore_interrupts(state);
250 }
251