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