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