1 /* This file contains essentially the MP handling code of the Minix kernel.
4 * Apr 1, 2008 Added SMP support.
13 #include <machine/cmos.h>
14 #include <machine/bios.h>
16 #include "kernel/spinlock.h"
17 #include "kernel/smp.h"
20 #include "kernel/clock.h"
24 void trampoline(void);
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
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
;
36 extern u32_t busclock
[CONFIG_MAX_CPUS
];
39 static int volatile ap_cpu_ready
;
40 static int volatile cpu_down
;
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
];
46 SPINLOCK_DEFINE(smp_cpu_lock
)
47 SPINLOCK_DEFINE(dispq_lock
)
49 static void smp_reinit_vars(void);
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 */
57 static phys_bytes trampoline_base
;
59 static u32_t
ap_lin_addr(void *vaddr
)
61 assert(trampoline_base
);
62 return (u32_t
) vaddr
- (u32_t
) &trampoline
+ trampoline_base
;
66 * copies the 16-bit AP trampoline code to the first 1M of memory
68 void copy_trampoline(void)
70 unsigned tramp_size
, tramp_start
= (unsigned)&trampoline
;;
72 /* The trampoline code/data is made to be page-aligned. */
73 assert(!(tramp_start
% I386_PAGE_SIZE
));
75 tramp_size
= (unsigned) &__trampoline_end
- tramp_start
;
76 trampoline_base
= alloc_lowest(&kinfo
, tramp_size
);
78 /* The memory allocator finds the lowest available memory..
79 * Verify it's low enough
81 assert(trampoline_base
+ tramp_size
< (1 << 20));
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.
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;
95 phys_copy((phys_bytes
) trampoline
, trampoline_base
, tramp_size
);
98 extern int booting_cpu
; /* tell protect.c what to do */
100 static void smp_start_aps(void)
103 u32_t biosresetvector
;
104 phys_bytes __ap_id_phys
;
105 struct proc
*bootstrap_pt
= get_cpulocal_var(ptproc
);
107 /* TODO hack around the alignment problem */
109 phys_copy(0x467, (phys_bytes
) &biosresetvector
, sizeof(u32_t
));
111 /* set the bios shutdown code to 0xA */
112 outb(RTC_INDEX
, 0xF);
115 assert(bootstrap_pt
);
116 assert(bootstrap_pt
->p_seg
.p_cr3
);
117 __ap_pt
= bootstrap_pt
->p_seg
.p_cr3
;
122 /* New locations for cpu id, pagetable root */
123 __ap_id_phys
= trampoline_base
+
124 (phys_bytes
) &__ap_id
- (phys_bytes
)&trampoline
;
126 /* setup the warm reset vector */
127 phys_copy((phys_bytes
) &trampoline_base
, 0x467, sizeof(u32_t
));
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.
132 for (cpu
= 0; cpu
< ncpus
; cpu
++) {
134 /* Don't send INIT/SIPI to boot cpu. */
135 if((apicid() == cpuid2apicid
[cpu
]) &&
136 (apicid() == bsp_lapic_id
)) {
140 __ap_id
= booting_cpu
= cpu
;
141 phys_copy((phys_bytes
) &__ap_id
, __ap_id_phys
, sizeof(__ap_id
));
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
);
149 /* wait for 5 secs for the processors to boot */
150 lapic_set_timer_one_shot(5000000);
152 while (lapic_read(LAPIC_TIMER_CCR
)) {
153 if (ap_cpu_ready
== cpu
) {
154 cpu_set_flag(cpu
, CPU_IS_READY
);
158 if (ap_cpu_ready
== -1) {
159 printf("WARNING : CPU %d didn't boot\n", cpu
);
163 phys_copy((phys_bytes
) &biosresetvector
, 0x467, sizeof(u32_t
));
165 outb(RTC_INDEX
, 0xF);
168 bsp_finish_booting();
172 void smp_halt_cpu (void)
177 void smp_shutdown_aps(void)
182 goto exit_shutdown_aps
;
184 /* we must let the other cpus enter the kernel mode */
187 for (cpu
= 0; cpu
< ncpus
; cpu
++) {
190 if (!cpu_test_flag(cpu
, CPU_IS_READY
)) {
191 printf("CPU %d didn't boot\n", cpu
);
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
);
205 ioapic_disable_all();
209 ncpus
= 1; /* hopefully !!! */
210 lapic_addr
= lapic_eoi_addr
= 0;
214 static void ap_finish_booting(void)
216 unsigned cpu
= cpuid
;
218 /* inform the world of our presence. */
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.
227 spinlock_lock(&boot_lock
);
230 printf("CPU %d is up\n", cpu
);
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
);
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
);
246 ap_boot_finished(cpu
);
247 spinlock_unlock(&boot_lock
);
253 void smp_ap_boot(void)
255 switch_k_stack((char *)get_k_stack_top(__ap_id
) -
256 X86_STACK_TOP_RESERVED
, ap_finish_booting
);
259 static void smp_reinit_vars(void)
261 lapic_addr
= lapic_eoi_addr
= 0;
267 static void tss_init_all(void)
271 for(cpu
= 0; cpu
< ncpus
; cpu
++)
272 tss_init(cpu
, get_k_stack_top(cpu
));
275 static int discover_cpus(void)
277 struct acpi_madt_lapic
* cpu
;
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
);
291 /* read the MP configuration */
292 if (!discover_cpus()) {
294 goto uniproc_fallback
;
297 lapic_addr
= LOCAL_APIC_DEF_ADDR
;
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()
306 bsp_cpu_id
= apicid2cpuid
[apicid()];
308 if (!lapic_enable(bsp_cpu_id
)) {
309 printf("ERROR : failed to initialize BSP Local APIC\n");
310 goto uniproc_fallback
;
313 bsp_lapic_id
= apicid();
317 if (!detect_ioapics()) {
320 goto uniproc_fallback
;
326 machine
.apic_enabled
= 1;
328 /* set smp idt entries. */
329 apic_idt_init(0); /* Not a reset ! */
332 BOOT_VERBOSE(printf("SMP initialized\n"));
334 switch_k_stack((char *)get_k_stack_top(bsp_cpu_id
) -
335 X86_STACK_TOP_RESERVED
, smp_start_aps
);
340 apic_idt_init(1); /* Reset to PIC idt ! */
342 smp_reinit_vars (); /* revert to a single proc system. */
343 intr_init(0); /* no auto eoi */
344 printf("WARNING : SMP initialization failed\n");
347 void arch_smp_halt_cpu(void)
349 /* say that we are down */
352 /* unlock the BKL and don't continue */
357 void arch_send_smp_schedule_ipi(unsigned cpu
)
359 apic_send_ipi(APIC_SMP_SCHED_PROC_VECTOR
, cpu
, APIC_IPI_DEST
);