xref: /haiku/src/system/kernel/arch/x86/arch_smp.cpp (revision 46b84791737448e1e3668c31c9caa30fa707ad41)
1bd185b41SIngo Weinhold /*
2*46b84791SPuck Meerburg  * Copyright 2023, Puck Meerburg, puck@puckipedia.com.
3e3d001ffSPawel Dziepak  * Copyright 2013, Paweł Dziepak, pdziepak@quarnos.org.
4bd185b41SIngo Weinhold  * Copyright 2002-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
5bd185b41SIngo Weinhold  * Distributed under the terms of the MIT License.
6bd185b41SIngo Weinhold  *
7bd185b41SIngo Weinhold  * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
8bd185b41SIngo Weinhold  * Distributed under the terms of the NewOS License.
9bd185b41SIngo Weinhold  */
10bd185b41SIngo Weinhold 
11bd185b41SIngo Weinhold 
12bd185b41SIngo Weinhold #include <boot/kernel_args.h>
13e50cf876SIngo Weinhold #include <vm/vm.h>
149a6868d5SPawel Dziepak #include <cpu.h>
15bd185b41SIngo Weinhold #include <int.h>
16bd185b41SIngo Weinhold #include <smp.h>
17bd185b41SIngo Weinhold #include <smp_priv.h>
18bd185b41SIngo Weinhold 
198cf8e537SPawel Dziepak #include <arch/atomic.h>
20bd185b41SIngo Weinhold #include <arch/cpu.h>
21bd185b41SIngo Weinhold #include <arch/vm.h>
22bd185b41SIngo Weinhold #include <arch/smp.h>
23bd185b41SIngo Weinhold 
24655f3b41SMichael Lotz #include <arch/x86/apic.h>
25655f3b41SMichael Lotz #include <arch/x86/arch_smp.h>
26bd185b41SIngo Weinhold #include <arch/x86/smp_priv.h>
27bd185b41SIngo Weinhold #include <arch/x86/timer.h>
28bd185b41SIngo Weinhold 
29bd185b41SIngo Weinhold #include <string.h>
30bd185b41SIngo Weinhold #include <stdio.h>
31bd185b41SIngo Weinhold 
32e3d001ffSPawel Dziepak #include <algorithm>
33e3d001ffSPawel Dziepak 
34bd185b41SIngo Weinhold 
35bd185b41SIngo Weinhold //#define TRACE_ARCH_SMP
36bd185b41SIngo Weinhold #ifdef TRACE_ARCH_SMP
37bd185b41SIngo Weinhold #	define TRACE(x) dprintf x
38bd185b41SIngo Weinhold #else
39bd185b41SIngo Weinhold #	define TRACE(x) ;
40bd185b41SIngo Weinhold #endif
41bd185b41SIngo Weinhold 
423f1eed70SAlexander von Gluck IV 
43e3d001ffSPawel Dziepak #define	ICI_VECTOR		0xfd
44e3d001ffSPawel Dziepak 
45e3d001ffSPawel Dziepak 
46e6ea745eSPawel Dziepak static uint32 sCPUAPICIds[SMP_MAX_CPUS];
47e6ea745eSPawel Dziepak static uint32 sAPICVersions[SMP_MAX_CPUS];
48bd185b41SIngo Weinhold 
49bd185b41SIngo Weinhold 
50bd185b41SIngo Weinhold static int32
x86_ici_interrupt(void * data)515e9bb17dSAlex Smith x86_ici_interrupt(void *data)
52bd185b41SIngo Weinhold {
53bd185b41SIngo Weinhold 	// genuine inter-cpu interrupt
54bd185b41SIngo Weinhold 	int cpu = smp_get_current_cpu();
55bd185b41SIngo Weinhold 	TRACE(("inter-cpu interrupt on cpu %d\n", cpu));
56bd185b41SIngo Weinhold 	return smp_intercpu_int_handler(cpu);
57bd185b41SIngo Weinhold }
58bd185b41SIngo Weinhold 
59bd185b41SIngo Weinhold 
60bd185b41SIngo Weinhold static int32
x86_spurious_interrupt(void * data)615e9bb17dSAlex Smith x86_spurious_interrupt(void *data)
62bd185b41SIngo Weinhold {
63bd185b41SIngo Weinhold 	// spurious interrupt
6476a1175dSAlex Smith 	TRACE(("spurious interrupt on cpu %" B_PRId32 "\n", smp_get_current_cpu()));
65bd185b41SIngo Weinhold 
66bd185b41SIngo Weinhold 	// spurious interrupts must not be acknowledged as it does not expect
67bd185b41SIngo Weinhold 	// a end of interrupt - if we still do it we would loose the next best
68bd185b41SIngo Weinhold 	// interrupt
69bd185b41SIngo Weinhold 	return B_HANDLED_INTERRUPT;
70bd185b41SIngo Weinhold }
71bd185b41SIngo Weinhold 
72bd185b41SIngo Weinhold 
73bd185b41SIngo Weinhold static int32
x86_smp_error_interrupt(void * data)745e9bb17dSAlex Smith x86_smp_error_interrupt(void *data)
75bd185b41SIngo Weinhold {
76bd185b41SIngo Weinhold 	// smp error interrupt
7776a1175dSAlex Smith 	TRACE(("smp error interrupt on cpu %" B_PRId32 "\n", smp_get_current_cpu()));
78bd185b41SIngo Weinhold 	return B_HANDLED_INTERRUPT;
79bd185b41SIngo Weinhold }
80bd185b41SIngo Weinhold 
81bd185b41SIngo Weinhold 
82d897a478SPawel Dziepak uint32
x86_get_cpu_apic_id(int32 cpu)83d897a478SPawel Dziepak x86_get_cpu_apic_id(int32 cpu)
84d897a478SPawel Dziepak {
85bcfdf881SPawel Dziepak 	ASSERT(cpu >= 0 && cpu < SMP_MAX_CPUS);
86d897a478SPawel Dziepak 	return sCPUAPICIds[cpu];
87d897a478SPawel Dziepak }
88d897a478SPawel Dziepak 
89d897a478SPawel Dziepak 
90bd185b41SIngo Weinhold status_t
arch_smp_init(kernel_args * args)91bd185b41SIngo Weinhold arch_smp_init(kernel_args *args)
92bd185b41SIngo Weinhold {
93c1cd48b7SAlexander von Gluck IV 	TRACE(("%s: entry\n", __func__));
94bd185b41SIngo Weinhold 
95655f3b41SMichael Lotz 	if (!apic_available()) {
96655f3b41SMichael Lotz 		// if we don't have an apic we can't do smp
97c1cd48b7SAlexander von Gluck IV 		TRACE(("%s: apic not available for smp\n", __func__));
98bd185b41SIngo Weinhold 		return B_OK;
99655f3b41SMichael Lotz 	}
100bd185b41SIngo Weinhold 
101bd185b41SIngo Weinhold 	// setup some globals
102bd185b41SIngo Weinhold 	memcpy(sCPUAPICIds, args->arch_args.cpu_apic_id, sizeof(args->arch_args.cpu_apic_id));
103bd185b41SIngo Weinhold 	memcpy(sAPICVersions, args->arch_args.cpu_apic_version, sizeof(args->arch_args.cpu_apic_version));
104bd185b41SIngo Weinhold 
105bd185b41SIngo Weinhold 	// set up the local apic on the boot cpu
106bd185b41SIngo Weinhold 	arch_smp_per_cpu_init(args, 0);
107bd185b41SIngo Weinhold 
108bd185b41SIngo Weinhold 	if (args->num_cpus > 1) {
109bd185b41SIngo Weinhold 		// I/O interrupts start at ARCH_INTERRUPT_BASE, so all interrupts are shifted
1106a164daaSPawel Dziepak 		reserve_io_interrupt_vectors(3, 0xfd - ARCH_INTERRUPT_BASE,
1116a164daaSPawel Dziepak 			INTERRUPT_TYPE_ICI);
1125e9bb17dSAlex Smith 		install_io_interrupt_handler(0xfd - ARCH_INTERRUPT_BASE, &x86_ici_interrupt, NULL, B_NO_LOCK_VECTOR);
1135e9bb17dSAlex Smith 		install_io_interrupt_handler(0xfe - ARCH_INTERRUPT_BASE, &x86_smp_error_interrupt, NULL, B_NO_LOCK_VECTOR);
1145e9bb17dSAlex Smith 		install_io_interrupt_handler(0xff - ARCH_INTERRUPT_BASE, &x86_spurious_interrupt, NULL, B_NO_LOCK_VECTOR);
115bd185b41SIngo Weinhold 	}
116bd185b41SIngo Weinhold 
117bd185b41SIngo Weinhold 	return B_OK;
118bd185b41SIngo Weinhold }
119bd185b41SIngo Weinhold 
120bd185b41SIngo Weinhold 
121bd185b41SIngo Weinhold status_t
arch_smp_per_cpu_init(kernel_args * args,int32 cpu)122bd185b41SIngo Weinhold arch_smp_per_cpu_init(kernel_args *args, int32 cpu)
123bd185b41SIngo Weinhold {
124bd185b41SIngo Weinhold 	// set up the local apic on the current cpu
12576a1175dSAlex Smith 	TRACE(("arch_smp_init_percpu: setting up the apic on cpu %" B_PRId32 "\n",
12676a1175dSAlex Smith 		cpu));
127655f3b41SMichael Lotz 	apic_per_cpu_init(args, cpu);
128bd185b41SIngo Weinhold 
1298dd1e875SAlexander von Gluck IV 	// setup FPU and SSE if supported
1303f1eed70SAlexander von Gluck IV 	x86_init_fpu();
131bd185b41SIngo Weinhold 
132bd185b41SIngo Weinhold 	return B_OK;
133bd185b41SIngo Weinhold }
134bd185b41SIngo Weinhold 
135bd185b41SIngo Weinhold 
136*46b84791SPuck Meerburg static void
send_multicast_ici_physical(CPUSet & cpuSet)137*46b84791SPuck Meerburg send_multicast_ici_physical(CPUSet& cpuSet)
138bd185b41SIngo Weinhold {
139e3d001ffSPawel Dziepak 	int32 cpuCount = smp_get_num_cpus();
140*46b84791SPuck Meerburg 	int32 currentCpu = smp_get_current_cpu();
141e3d001ffSPawel Dziepak 
142*46b84791SPuck Meerburg 	for (int32 i = 0; i < cpuCount; i++) {
143*46b84791SPuck Meerburg 		if (cpuSet.GetBit(i) && i != currentCpu) {
144e3d001ffSPawel Dziepak 			uint32 destination = sCPUAPICIds[i];
145e3d001ffSPawel Dziepak 			uint32 mode = ICI_VECTOR | APIC_DELIVERY_MODE_FIXED
146bd185b41SIngo Weinhold 					| APIC_INTR_COMMAND_1_ASSERT
147bd185b41SIngo Weinhold 					| APIC_INTR_COMMAND_1_DEST_MODE_PHYSICAL
148e3d001ffSPawel Dziepak 					| APIC_INTR_COMMAND_1_DEST_FIELD;
149bd185b41SIngo Weinhold 
150e3d001ffSPawel Dziepak 			while (!apic_interrupt_delivered())
151e3d001ffSPawel Dziepak 				cpu_pause();
152e3d001ffSPawel Dziepak 			apic_set_interrupt_command(destination, mode);
153e3d001ffSPawel Dziepak 		}
154e3d001ffSPawel Dziepak 	}
155e3d001ffSPawel Dziepak }
156e3d001ffSPawel Dziepak 
157e3d001ffSPawel Dziepak 
158e3d001ffSPawel Dziepak void
arch_smp_send_multicast_ici(CPUSet & cpuSet)159*46b84791SPuck Meerburg arch_smp_send_multicast_ici(CPUSet& cpuSet)
160*46b84791SPuck Meerburg {
161*46b84791SPuck Meerburg #if KDEBUG
162*46b84791SPuck Meerburg 	if (are_interrupts_enabled())
163*46b84791SPuck Meerburg 		panic("arch_smp_send_multicast_ici: called with interrupts enabled");
164*46b84791SPuck Meerburg #endif
165*46b84791SPuck Meerburg 
166*46b84791SPuck Meerburg 	memory_write_barrier();
167*46b84791SPuck Meerburg 
168*46b84791SPuck Meerburg 	if (!x2apic_available()) {
169*46b84791SPuck Meerburg 		send_multicast_ici_physical(cpuSet);
170*46b84791SPuck Meerburg 		return;
171*46b84791SPuck Meerburg 	}
172*46b84791SPuck Meerburg 
173*46b84791SPuck Meerburg 	// WRMSR on the x2APIC MSRs is neither serializing, nor a load-store
174*46b84791SPuck Meerburg 	// operation, requiring both memory serialization *and* a load fence, which is
175*46b84791SPuck Meerburg 	// the only way to ensure the MSR doesn't get executed before the write
176*46b84791SPuck Meerburg 	// barrier.
177*46b84791SPuck Meerburg 	memory_read_barrier();
178*46b84791SPuck Meerburg 
179*46b84791SPuck Meerburg 	int32 cpuCount = smp_get_num_cpus();
180*46b84791SPuck Meerburg 	int32 currentCpu = smp_get_current_cpu();
181*46b84791SPuck Meerburg 
182*46b84791SPuck Meerburg 	uint32 mode = ICI_VECTOR | APIC_DELIVERY_MODE_FIXED
183*46b84791SPuck Meerburg 			| APIC_INTR_COMMAND_1_ASSERT
184*46b84791SPuck Meerburg 			| APIC_INTR_COMMAND_1_DEST_MODE_LOGICAL
185*46b84791SPuck Meerburg 			| APIC_INTR_COMMAND_1_DEST_FIELD;
186*46b84791SPuck Meerburg 
187*46b84791SPuck Meerburg 	for (int32 i = 0; i < cpuCount; i++) {
188*46b84791SPuck Meerburg 		if (!cpuSet.GetBit(i) || i == currentCpu)
189*46b84791SPuck Meerburg 			continue;
190*46b84791SPuck Meerburg 
191*46b84791SPuck Meerburg 		apic_set_interrupt_command(gCPU[i].arch.logical_apic_id, mode);
192*46b84791SPuck Meerburg 	}
193*46b84791SPuck Meerburg }
194*46b84791SPuck Meerburg 
195*46b84791SPuck Meerburg 
196*46b84791SPuck Meerburg void
arch_smp_send_broadcast_ici(void)197e3d001ffSPawel Dziepak arch_smp_send_broadcast_ici(void)
198e3d001ffSPawel Dziepak {
199e3d001ffSPawel Dziepak #if KDEBUG
200e3d001ffSPawel Dziepak 	if (are_interrupts_enabled())
201e3d001ffSPawel Dziepak 		panic("arch_smp_send_broadcast_ici: called with interrupts enabled");
202e3d001ffSPawel Dziepak #endif
203e3d001ffSPawel Dziepak 
2048cf8e537SPawel Dziepak 	memory_write_barrier();
205e3d001ffSPawel Dziepak 
206e3d001ffSPawel Dziepak 	uint32 mode = ICI_VECTOR | APIC_DELIVERY_MODE_FIXED
207e3d001ffSPawel Dziepak 			| APIC_INTR_COMMAND_1_ASSERT
208e3d001ffSPawel Dziepak 			| APIC_INTR_COMMAND_1_DEST_MODE_PHYSICAL
209e3d001ffSPawel Dziepak 			| APIC_INTR_COMMAND_1_DEST_ALL_BUT_SELF;
210e3d001ffSPawel Dziepak 
211e3d001ffSPawel Dziepak 	while (!apic_interrupt_delivered())
212e3d001ffSPawel Dziepak 		cpu_pause();
213e3d001ffSPawel Dziepak 	apic_set_interrupt_command(0, mode);
214bd185b41SIngo Weinhold }
215bd185b41SIngo Weinhold 
216bd185b41SIngo Weinhold 
217bd185b41SIngo Weinhold void
arch_smp_send_ici(int32 target_cpu)218bd185b41SIngo Weinhold arch_smp_send_ici(int32 target_cpu)
219bd185b41SIngo Weinhold {
220e3d001ffSPawel Dziepak #if KDEBUG
221e3d001ffSPawel Dziepak 	if (are_interrupts_enabled())
222e3d001ffSPawel Dziepak 		panic("arch_smp_send_ici: called with interrupts enabled");
223e3d001ffSPawel Dziepak #endif
224bd185b41SIngo Weinhold 
2258cf8e537SPawel Dziepak 	memory_write_barrier();
226bd185b41SIngo Weinhold 
227e3d001ffSPawel Dziepak 	uint32 destination = sCPUAPICIds[target_cpu];
228e3d001ffSPawel Dziepak 	uint32 mode = ICI_VECTOR | APIC_DELIVERY_MODE_FIXED
229bd185b41SIngo Weinhold 			| APIC_INTR_COMMAND_1_ASSERT
230bd185b41SIngo Weinhold 			| APIC_INTR_COMMAND_1_DEST_MODE_PHYSICAL
231e3d001ffSPawel Dziepak 			| APIC_INTR_COMMAND_1_DEST_FIELD;
232bd185b41SIngo Weinhold 
233e3d001ffSPawel Dziepak 	while (!apic_interrupt_delivered())
234e3d001ffSPawel Dziepak 		cpu_pause();
235e3d001ffSPawel Dziepak 	apic_set_interrupt_command(destination, mode);
236bd185b41SIngo Weinhold }
237e3d001ffSPawel Dziepak 
238