xref: /minix3/minix/kernel/arch/i386/arch_smp.c (revision 9624407e7addfd8b88486acfe3a0e056e2b92ee3)
1*433d6423SLionel Sambuc /* This file contains essentially the MP handling code of the Minix kernel.
2*433d6423SLionel Sambuc  *
3*433d6423SLionel Sambuc  * Changes:
4*433d6423SLionel Sambuc  *   Apr 1,  2008     Added SMP support.
5*433d6423SLionel Sambuc  */
6*433d6423SLionel Sambuc 
7*433d6423SLionel Sambuc #define _SMP
8*433d6423SLionel Sambuc 
9*433d6423SLionel Sambuc #include <unistd.h>
10*433d6423SLionel Sambuc #include <assert.h>
11*433d6423SLionel Sambuc #include <stdlib.h>
12*433d6423SLionel Sambuc #include <string.h>
13*433d6423SLionel Sambuc #include <machine/cmos.h>
14*433d6423SLionel Sambuc #include <machine/bios.h>
15*433d6423SLionel Sambuc 
16*433d6423SLionel Sambuc #include "kernel/spinlock.h"
17*433d6423SLionel Sambuc #include "kernel/smp.h"
18*433d6423SLionel Sambuc #include "apic.h"
19*433d6423SLionel Sambuc #include "acpi.h"
20*433d6423SLionel Sambuc #include "kernel/clock.h"
21*433d6423SLionel Sambuc 
22*433d6423SLionel Sambuc #include "glo.h"
23*433d6423SLionel Sambuc 
24*433d6423SLionel Sambuc void trampoline(void);
25*433d6423SLionel Sambuc 
26*433d6423SLionel Sambuc /*
27*433d6423SLionel Sambuc  * arguments for trampoline. We need to pass the logical cpu id, gdt and idt.
28*433d6423SLionel Sambuc  * They have to be in location which is reachable using absolute addressing in
29*433d6423SLionel Sambuc  * 16-bit mode
30*433d6423SLionel Sambuc  */
31*433d6423SLionel Sambuc extern volatile u32_t __ap_id, __ap_pt;
32*433d6423SLionel Sambuc extern volatile struct desctableptr_s __ap_gdt, __ap_idt;
33*433d6423SLionel Sambuc extern u32_t __ap_gdt_tab, __ap_idt_tab;
34*433d6423SLionel Sambuc extern void * __trampoline_end;
35*433d6423SLionel Sambuc 
36*433d6423SLionel Sambuc extern u32_t busclock[CONFIG_MAX_CPUS];
37*433d6423SLionel Sambuc extern int panicking;
38*433d6423SLionel Sambuc 
39*433d6423SLionel Sambuc static int volatile ap_cpu_ready;
40*433d6423SLionel Sambuc static int volatile cpu_down;
41*433d6423SLionel Sambuc 
42*433d6423SLionel Sambuc /* there can be at most 255 local APIC ids, each fits in 8 bits */
43*433d6423SLionel Sambuc static unsigned char apicid2cpuid[255];
44*433d6423SLionel Sambuc unsigned char cpuid2apicid[CONFIG_MAX_CPUS];
45*433d6423SLionel Sambuc 
46*433d6423SLionel Sambuc SPINLOCK_DEFINE(smp_cpu_lock)
47*433d6423SLionel Sambuc SPINLOCK_DEFINE(dispq_lock)
48*433d6423SLionel Sambuc 
49*433d6423SLionel Sambuc static void smp_reinit_vars(void);
50*433d6423SLionel Sambuc 
51*433d6423SLionel Sambuc /* These are initialized in protect.c */
52*433d6423SLionel Sambuc extern struct segdesc_s gdt[GDT_SIZE];
53*433d6423SLionel Sambuc extern struct gatedesc_s idt[IDT_SIZE];
54*433d6423SLionel Sambuc extern struct tss_s tss[CONFIG_MAX_CPUS];
55*433d6423SLionel Sambuc extern int prot_init_done;	/* Indicates they are ready */
56*433d6423SLionel Sambuc 
57*433d6423SLionel Sambuc static phys_bytes trampoline_base;
58*433d6423SLionel Sambuc 
ap_lin_addr(void * vaddr)59*433d6423SLionel Sambuc static u32_t ap_lin_addr(void *vaddr)
60*433d6423SLionel Sambuc {
61*433d6423SLionel Sambuc 	assert(trampoline_base);
62*433d6423SLionel Sambuc 	return (u32_t) vaddr - (u32_t) &trampoline + trampoline_base;
63*433d6423SLionel Sambuc }
64*433d6423SLionel Sambuc 
65*433d6423SLionel Sambuc /*
66*433d6423SLionel Sambuc  * copies the 16-bit AP trampoline code to the first 1M of memory
67*433d6423SLionel Sambuc  */
copy_trampoline(void)68*433d6423SLionel Sambuc void copy_trampoline(void)
69*433d6423SLionel Sambuc {
70*433d6423SLionel Sambuc 	unsigned tramp_size, tramp_start = (unsigned)&trampoline;;
71*433d6423SLionel Sambuc 
72*433d6423SLionel Sambuc 	/* The trampoline code/data is made to be page-aligned. */
73*433d6423SLionel Sambuc 	assert(!(tramp_start % I386_PAGE_SIZE));
74*433d6423SLionel Sambuc 
75*433d6423SLionel Sambuc 	tramp_size = (unsigned) &__trampoline_end - tramp_start;
76*433d6423SLionel Sambuc 	trampoline_base = alloc_lowest(&kinfo, tramp_size);
77*433d6423SLionel Sambuc 
78*433d6423SLionel Sambuc 	/* The memory allocator finds the lowest available memory..
79*433d6423SLionel Sambuc 	 * Verify it's low enough
80*433d6423SLionel Sambuc 	 */
81*433d6423SLionel Sambuc 	assert(trampoline_base + tramp_size < (1 << 20));
82*433d6423SLionel Sambuc 
83*433d6423SLionel Sambuc 	/* prepare gdt and idt for the new cpus; make copies
84*433d6423SLionel Sambuc 	 * of both the tables and the descriptors of them
85*433d6423SLionel Sambuc 	 * in their boot addressing environment.
86*433d6423SLionel Sambuc 	 */
87*433d6423SLionel Sambuc 	assert(prot_init_done);
88*433d6423SLionel Sambuc 	memcpy(&__ap_gdt_tab, gdt, sizeof(gdt));
89*433d6423SLionel Sambuc 	memcpy(&__ap_idt_tab, gdt, sizeof(idt));
90*433d6423SLionel Sambuc 	__ap_gdt.base = ap_lin_addr(&__ap_gdt_tab);
91*433d6423SLionel Sambuc 	__ap_gdt.limit = sizeof(gdt)-1;
92*433d6423SLionel Sambuc 	__ap_idt.base = ap_lin_addr(&__ap_idt_tab);
93*433d6423SLionel Sambuc 	__ap_idt.limit = sizeof(idt)-1;
94*433d6423SLionel Sambuc 
95*433d6423SLionel Sambuc 	phys_copy((phys_bytes) trampoline, trampoline_base, tramp_size);
96*433d6423SLionel Sambuc }
97*433d6423SLionel Sambuc 
98*433d6423SLionel Sambuc extern int booting_cpu;	/* tell protect.c what to do */
99*433d6423SLionel Sambuc 
smp_start_aps(void)100*433d6423SLionel Sambuc static void smp_start_aps(void)
101*433d6423SLionel Sambuc {
102*433d6423SLionel Sambuc 	unsigned cpu;
103*433d6423SLionel Sambuc 	u32_t biosresetvector;
104*433d6423SLionel Sambuc 	phys_bytes __ap_id_phys;
105*433d6423SLionel Sambuc 	struct proc *bootstrap_pt = get_cpulocal_var(ptproc);
106*433d6423SLionel Sambuc 
107*433d6423SLionel Sambuc 	/* TODO hack around the alignment problem */
108*433d6423SLionel Sambuc 
109*433d6423SLionel Sambuc 	phys_copy(0x467, (phys_bytes) &biosresetvector, sizeof(u32_t));
110*433d6423SLionel Sambuc 
111*433d6423SLionel Sambuc 	/* set the bios shutdown code to 0xA */
112*433d6423SLionel Sambuc 	outb(RTC_INDEX, 0xF);
113*433d6423SLionel Sambuc 	outb(RTC_IO, 0xA);
114*433d6423SLionel Sambuc 
115*433d6423SLionel Sambuc 	assert(bootstrap_pt);
116*433d6423SLionel Sambuc 	assert(bootstrap_pt->p_seg.p_cr3);
117*433d6423SLionel Sambuc 	__ap_pt  = bootstrap_pt->p_seg.p_cr3;
118*433d6423SLionel Sambuc 	assert(__ap_pt);
119*433d6423SLionel Sambuc 
120*433d6423SLionel Sambuc 	copy_trampoline();
121*433d6423SLionel Sambuc 
122*433d6423SLionel Sambuc 	/* New locations for cpu id, pagetable root */
123*433d6423SLionel Sambuc 	__ap_id_phys = trampoline_base +
124*433d6423SLionel Sambuc 		(phys_bytes) &__ap_id - (phys_bytes)&trampoline;
125*433d6423SLionel Sambuc 
126*433d6423SLionel Sambuc 	/* setup the warm reset vector */
127*433d6423SLionel Sambuc 	phys_copy((phys_bytes) &trampoline_base, 0x467, sizeof(u32_t));
128*433d6423SLionel Sambuc 
129*433d6423SLionel Sambuc 	/* okay, we're ready to go.  boot all of the ap's now.  we loop through
130*433d6423SLionel Sambuc 	 * using the processor's apic id values.
131*433d6423SLionel Sambuc 	 */
132*433d6423SLionel Sambuc 	for (cpu = 0; cpu < ncpus; cpu++) {
133*433d6423SLionel Sambuc 		ap_cpu_ready = -1;
134*433d6423SLionel Sambuc 		/* Don't send INIT/SIPI to boot cpu.  */
135*433d6423SLionel Sambuc 		if((apicid() == cpuid2apicid[cpu]) &&
136*433d6423SLionel Sambuc 				(apicid() == bsp_lapic_id)) {
137*433d6423SLionel Sambuc 			continue;
138*433d6423SLionel Sambuc 		}
139*433d6423SLionel Sambuc 
140*433d6423SLionel Sambuc 		__ap_id = booting_cpu = cpu;
141*433d6423SLionel Sambuc 		phys_copy((phys_bytes) &__ap_id, __ap_id_phys, sizeof(__ap_id));
142*433d6423SLionel Sambuc 		mfence();
143*433d6423SLionel Sambuc 		if (apic_send_init_ipi(cpu, trampoline_base) ||
144*433d6423SLionel Sambuc 				apic_send_startup_ipi(cpu, trampoline_base)) {
145*433d6423SLionel Sambuc 			printf("WARNING cannot boot cpu %d\n", cpu);
146*433d6423SLionel Sambuc 			continue;
147*433d6423SLionel Sambuc 		}
148*433d6423SLionel Sambuc 
149*433d6423SLionel Sambuc 		/* wait for 5 secs for the processors to boot */
150*433d6423SLionel Sambuc 		lapic_set_timer_one_shot(5000000);
151*433d6423SLionel Sambuc 
152*433d6423SLionel Sambuc 		while (lapic_read(LAPIC_TIMER_CCR)) {
153*433d6423SLionel Sambuc 			if (ap_cpu_ready == cpu) {
154*433d6423SLionel Sambuc 				cpu_set_flag(cpu, CPU_IS_READY);
155*433d6423SLionel Sambuc 				break;
156*433d6423SLionel Sambuc 			}
157*433d6423SLionel Sambuc 		}
158*433d6423SLionel Sambuc 		if (ap_cpu_ready == -1) {
159*433d6423SLionel Sambuc 			printf("WARNING : CPU %d didn't boot\n", cpu);
160*433d6423SLionel Sambuc 		}
161*433d6423SLionel Sambuc 	}
162*433d6423SLionel Sambuc 
163*433d6423SLionel Sambuc 	phys_copy((phys_bytes) &biosresetvector, 0x467, sizeof(u32_t));
164*433d6423SLionel Sambuc 
165*433d6423SLionel Sambuc 	outb(RTC_INDEX, 0xF);
166*433d6423SLionel Sambuc 	outb(RTC_IO, 0);
167*433d6423SLionel Sambuc 
168*433d6423SLionel Sambuc 	bsp_finish_booting();
169*433d6423SLionel Sambuc 	NOT_REACHABLE;
170*433d6423SLionel Sambuc }
171*433d6423SLionel Sambuc 
smp_halt_cpu(void)172*433d6423SLionel Sambuc void smp_halt_cpu (void)
173*433d6423SLionel Sambuc {
174*433d6423SLionel Sambuc 	NOT_IMPLEMENTED;
175*433d6423SLionel Sambuc }
176*433d6423SLionel Sambuc 
smp_shutdown_aps(void)177*433d6423SLionel Sambuc void smp_shutdown_aps(void)
178*433d6423SLionel Sambuc {
179*433d6423SLionel Sambuc 	unsigned cpu;
180*433d6423SLionel Sambuc 
181*433d6423SLionel Sambuc 	if (ncpus == 1)
182*433d6423SLionel Sambuc 		goto exit_shutdown_aps;
183*433d6423SLionel Sambuc 
184*433d6423SLionel Sambuc 	/* we must let the other cpus enter the kernel mode */
185*433d6423SLionel Sambuc 	BKL_UNLOCK();
186*433d6423SLionel Sambuc 
187*433d6423SLionel Sambuc 	for (cpu = 0; cpu < ncpus; cpu++) {
188*433d6423SLionel Sambuc 		if (cpu == cpuid)
189*433d6423SLionel Sambuc 			continue;
190*433d6423SLionel Sambuc 		if (!cpu_test_flag(cpu, CPU_IS_READY)) {
191*433d6423SLionel Sambuc 			printf("CPU %d didn't boot\n", cpu);
192*433d6423SLionel Sambuc 			continue;
193*433d6423SLionel Sambuc 		}
194*433d6423SLionel Sambuc 
195*433d6423SLionel Sambuc 		cpu_down = -1;
196*433d6423SLionel Sambuc 		barrier();
197*433d6423SLionel Sambuc 		apic_send_ipi(APIC_SMP_CPU_HALT_VECTOR, cpu, APIC_IPI_DEST);
198*433d6423SLionel Sambuc 		/* wait for the cpu to be down */
199*433d6423SLionel Sambuc 		while (cpu_down != cpu);
200*433d6423SLionel Sambuc 		printf("CPU %d is down\n", cpu);
201*433d6423SLionel Sambuc 		cpu_clear_flag(cpu, CPU_IS_READY);
202*433d6423SLionel Sambuc 	}
203*433d6423SLionel Sambuc 
204*433d6423SLionel Sambuc exit_shutdown_aps:
205*433d6423SLionel Sambuc 	ioapic_disable_all();
206*433d6423SLionel Sambuc 
207*433d6423SLionel Sambuc 	lapic_disable();
208*433d6423SLionel Sambuc 
209*433d6423SLionel Sambuc 	ncpus = 1; /* hopefully !!! */
210*433d6423SLionel Sambuc 	lapic_addr = lapic_eoi_addr = 0;
211*433d6423SLionel Sambuc 	return;
212*433d6423SLionel Sambuc }
213*433d6423SLionel Sambuc 
ap_finish_booting(void)214*433d6423SLionel Sambuc static void ap_finish_booting(void)
215*433d6423SLionel Sambuc {
216*433d6423SLionel Sambuc 	unsigned cpu = cpuid;
217*433d6423SLionel Sambuc 
218*433d6423SLionel Sambuc 	/* inform the world of our presence. */
219*433d6423SLionel Sambuc 	ap_cpu_ready = cpu;
220*433d6423SLionel Sambuc 
221*433d6423SLionel Sambuc 	/*
222*433d6423SLionel Sambuc 	 * Finish processor initialisation.  CPUs must be excluded from running.
223*433d6423SLionel Sambuc 	 * lapic timer calibration locks and unlocks the BKL because of the
224*433d6423SLionel Sambuc 	 * nested interrupts used for calibration. Therefore BKL is not good
225*433d6423SLionel Sambuc 	 * enough, the boot_lock must be held.
226*433d6423SLionel Sambuc 	 */
227*433d6423SLionel Sambuc 	spinlock_lock(&boot_lock);
228*433d6423SLionel Sambuc 	BKL_LOCK();
229*433d6423SLionel Sambuc 
230*433d6423SLionel Sambuc 	printf("CPU %d is up\n", cpu);
231*433d6423SLionel Sambuc 
232*433d6423SLionel Sambuc 	cpu_identify();
233*433d6423SLionel Sambuc 
234*433d6423SLionel Sambuc 	lapic_enable(cpu);
235*433d6423SLionel Sambuc 	fpu_init();
236*433d6423SLionel Sambuc 
237*433d6423SLionel Sambuc 	if (app_cpu_init_timer(system_hz)) {
238*433d6423SLionel Sambuc 		panic("FATAL : failed to initialize timer interrupts CPU %d, "
239*433d6423SLionel Sambuc 				"cannot continue without any clock source!", cpu);
240*433d6423SLionel Sambuc 	}
241*433d6423SLionel Sambuc 
242*433d6423SLionel Sambuc 	/* FIXME assign CPU local idle structure */
243*433d6423SLionel Sambuc 	get_cpulocal_var(proc_ptr) = get_cpulocal_var_ptr(idle_proc);
244*433d6423SLionel Sambuc 	get_cpulocal_var(bill_ptr) = get_cpulocal_var_ptr(idle_proc);
245*433d6423SLionel Sambuc 
246*433d6423SLionel Sambuc 	ap_boot_finished(cpu);
247*433d6423SLionel Sambuc 	spinlock_unlock(&boot_lock);
248*433d6423SLionel Sambuc 
249*433d6423SLionel Sambuc 	switch_to_user();
250*433d6423SLionel Sambuc 	NOT_REACHABLE;
251*433d6423SLionel Sambuc }
252*433d6423SLionel Sambuc 
smp_ap_boot(void)253*433d6423SLionel Sambuc void smp_ap_boot(void)
254*433d6423SLionel Sambuc {
255*433d6423SLionel Sambuc 	switch_k_stack((char *)get_k_stack_top(__ap_id) -
256*433d6423SLionel Sambuc 			X86_STACK_TOP_RESERVED, ap_finish_booting);
257*433d6423SLionel Sambuc }
258*433d6423SLionel Sambuc 
smp_reinit_vars(void)259*433d6423SLionel Sambuc static void smp_reinit_vars(void)
260*433d6423SLionel Sambuc {
261*433d6423SLionel Sambuc 	lapic_addr = lapic_eoi_addr = 0;
262*433d6423SLionel Sambuc 	ioapic_enabled = 0;
263*433d6423SLionel Sambuc 
264*433d6423SLionel Sambuc 	ncpus = 1;
265*433d6423SLionel Sambuc }
266*433d6423SLionel Sambuc 
tss_init_all(void)267*433d6423SLionel Sambuc static void tss_init_all(void)
268*433d6423SLionel Sambuc {
269*433d6423SLionel Sambuc 	unsigned cpu;
270*433d6423SLionel Sambuc 
271*433d6423SLionel Sambuc 	for(cpu = 0; cpu < ncpus ; cpu++)
272*433d6423SLionel Sambuc 		tss_init(cpu, get_k_stack_top(cpu));
273*433d6423SLionel Sambuc }
274*433d6423SLionel Sambuc 
discover_cpus(void)275*433d6423SLionel Sambuc static int discover_cpus(void)
276*433d6423SLionel Sambuc {
277*433d6423SLionel Sambuc 	struct acpi_madt_lapic * cpu;
278*433d6423SLionel Sambuc 
279*433d6423SLionel Sambuc 	while (ncpus < CONFIG_MAX_CPUS && (cpu = acpi_get_lapic_next())) {
280*433d6423SLionel Sambuc 		apicid2cpuid[cpu->apic_id] = ncpus;
281*433d6423SLionel Sambuc 		cpuid2apicid[ncpus] = cpu->apic_id;
282*433d6423SLionel Sambuc 		printf("CPU %3d local APIC id %3d\n", ncpus, cpu->apic_id);
283*433d6423SLionel Sambuc 		ncpus++;
284*433d6423SLionel Sambuc 	}
285*433d6423SLionel Sambuc 
286*433d6423SLionel Sambuc 	return ncpus;
287*433d6423SLionel Sambuc }
288*433d6423SLionel Sambuc 
smp_init(void)289*433d6423SLionel Sambuc void smp_init (void)
290*433d6423SLionel Sambuc {
291*433d6423SLionel Sambuc 	/* read the MP configuration */
292*433d6423SLionel Sambuc 	if (!discover_cpus()) {
293*433d6423SLionel Sambuc 		ncpus = 1;
294*433d6423SLionel Sambuc 		goto uniproc_fallback;
295*433d6423SLionel Sambuc 	}
296*433d6423SLionel Sambuc 
297*433d6423SLionel Sambuc 	lapic_addr = LOCAL_APIC_DEF_ADDR;
298*433d6423SLionel Sambuc 	ioapic_enabled = 0;
299*433d6423SLionel Sambuc 
300*433d6423SLionel Sambuc 	tss_init_all();
301*433d6423SLionel Sambuc 
302*433d6423SLionel Sambuc 	/*
303*433d6423SLionel Sambuc 	 * we still run on the boot stack and we cannot use cpuid as its value
304*433d6423SLionel Sambuc 	 * wasn't set yet. apicid2cpuid initialized in mps_init()
305*433d6423SLionel Sambuc 	 */
306*433d6423SLionel Sambuc 	bsp_cpu_id = apicid2cpuid[apicid()];
307*433d6423SLionel Sambuc 
308*433d6423SLionel Sambuc 	if (!lapic_enable(bsp_cpu_id)) {
309*433d6423SLionel Sambuc 		printf("ERROR : failed to initialize BSP Local APIC\n");
310*433d6423SLionel Sambuc 		goto uniproc_fallback;
311*433d6423SLionel Sambuc 	}
312*433d6423SLionel Sambuc 
313*433d6423SLionel Sambuc 	bsp_lapic_id = apicid();
314*433d6423SLionel Sambuc 
315*433d6423SLionel Sambuc 	acpi_init();
316*433d6423SLionel Sambuc 
317*433d6423SLionel Sambuc 	if (!detect_ioapics()) {
318*433d6423SLionel Sambuc 		lapic_disable();
319*433d6423SLionel Sambuc 		lapic_addr = 0x0;
320*433d6423SLionel Sambuc 		goto uniproc_fallback;
321*433d6423SLionel Sambuc 	}
322*433d6423SLionel Sambuc 
323*433d6423SLionel Sambuc 	ioapic_enable_all();
324*433d6423SLionel Sambuc 
325*433d6423SLionel Sambuc 	if (ioapic_enabled)
326*433d6423SLionel Sambuc 		machine.apic_enabled = 1;
327*433d6423SLionel Sambuc 
328*433d6423SLionel Sambuc 	/* set smp idt entries. */
329*433d6423SLionel Sambuc 	apic_idt_init(0); /* Not a reset ! */
330*433d6423SLionel Sambuc 	idt_reload();
331*433d6423SLionel Sambuc 
332*433d6423SLionel Sambuc 	BOOT_VERBOSE(printf("SMP initialized\n"));
333*433d6423SLionel Sambuc 
334*433d6423SLionel Sambuc 	switch_k_stack((char *)get_k_stack_top(bsp_cpu_id) -
335*433d6423SLionel Sambuc 			X86_STACK_TOP_RESERVED, smp_start_aps);
336*433d6423SLionel Sambuc 
337*433d6423SLionel Sambuc 	return;
338*433d6423SLionel Sambuc 
339*433d6423SLionel Sambuc uniproc_fallback:
340*433d6423SLionel Sambuc 	apic_idt_init(1); /* Reset to PIC idt ! */
341*433d6423SLionel Sambuc 	idt_reload();
342*433d6423SLionel Sambuc 	smp_reinit_vars (); /* revert to a single proc system. */
343*433d6423SLionel Sambuc 	intr_init(0); /* no auto eoi */
344*433d6423SLionel Sambuc 	printf("WARNING : SMP initialization failed\n");
345*433d6423SLionel Sambuc }
346*433d6423SLionel Sambuc 
arch_smp_halt_cpu(void)347*433d6423SLionel Sambuc void arch_smp_halt_cpu(void)
348*433d6423SLionel Sambuc {
349*433d6423SLionel Sambuc 	/* say that we are down */
350*433d6423SLionel Sambuc 	cpu_down = cpuid;
351*433d6423SLionel Sambuc 	barrier();
352*433d6423SLionel Sambuc 	/* unlock the BKL and don't continue */
353*433d6423SLionel Sambuc 	BKL_UNLOCK();
354*433d6423SLionel Sambuc 	for(;;);
355*433d6423SLionel Sambuc }
356*433d6423SLionel Sambuc 
arch_send_smp_schedule_ipi(unsigned cpu)357*433d6423SLionel Sambuc void arch_send_smp_schedule_ipi(unsigned cpu)
358*433d6423SLionel Sambuc {
359*433d6423SLionel Sambuc 	apic_send_ipi(APIC_SMP_SCHED_PROC_VECTOR, cpu, APIC_IPI_DEST);
360*433d6423SLionel Sambuc }
361