1 /* This file contains essentially the MP handling code of the Minix kernel.
4 * Apr 1, 2008 Added SMP support.
9 #include "kernel/kernel.h"
10 #include "arch_proto.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>
21 #include "kernel/spinlock.h"
22 #include "kernel/smp.h"
25 #include "kernel/clock.h"
29 void trampoline(void);
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
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
;
41 extern u32_t busclock
[CONFIG_MAX_CPUS
];
44 static int volatile ap_cpu_ready
;
45 static int volatile cpu_down
;
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
];
51 SPINLOCK_DEFINE(smp_cpu_lock
)
52 SPINLOCK_DEFINE(dispq_lock
)
54 static void smp_reinit_vars(void);
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 */
62 static phys_bytes trampoline_base
;
64 static u32_t
ap_lin_addr(void *vaddr
)
66 assert(trampoline_base
);
67 return (u32_t
) vaddr
- (u32_t
) &trampoline
+ trampoline_base
;
71 * copies the 16-bit AP trampoline code to the first 1M of memory
73 void copy_trampoline(void)
75 unsigned tramp_size
, tramp_start
= (unsigned)&trampoline
;;
77 /* The trampoline code/data is made to be page-aligned. */
78 assert(!(tramp_start
% I386_PAGE_SIZE
));
80 tramp_size
= (unsigned) &__trampoline_end
- tramp_start
;
81 trampoline_base
= alloc_lowest(&kinfo
, tramp_size
);
83 /* The memory allocator finds the lowest available memory..
84 * Verify it's low enough
86 assert(trampoline_base
+ tramp_size
< (1 << 20));
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.
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;
100 phys_copy((phys_bytes
) trampoline
, trampoline_base
, tramp_size
);
103 extern int booting_cpu
; /* tell protect.c what to do */
105 static void smp_start_aps(void)
108 u32_t biosresetvector
;
109 phys_bytes __ap_id_phys
;
110 struct proc
*bootstrap_pt
= get_cpulocal_var(ptproc
);
112 /* TODO hack around the alignment problem */
114 phys_copy(0x467, (phys_bytes
) &biosresetvector
, sizeof(u32_t
));
116 /* set the bios shutdown code to 0xA */
117 outb(RTC_INDEX
, 0xF);
120 assert(bootstrap_pt
);
121 assert(bootstrap_pt
->p_seg
.p_cr3
);
122 __ap_pt
= bootstrap_pt
->p_seg
.p_cr3
;
127 /* New locations for cpu id, pagetable root */
128 __ap_id_phys
= trampoline_base
+
129 (phys_bytes
) &__ap_id
- (phys_bytes
)&trampoline
;
131 /* setup the warm reset vector */
132 phys_copy((phys_bytes
) &trampoline_base
, 0x467, sizeof(u32_t
));
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.
137 for (cpu
= 0; cpu
< ncpus
; cpu
++) {
139 /* Don't send INIT/SIPI to boot cpu. */
140 if((apicid() == cpuid2apicid
[cpu
]) &&
141 (apicid() == bsp_lapic_id
)) {
145 __ap_id
= booting_cpu
= cpu
;
146 phys_copy((phys_bytes
) &__ap_id
, __ap_id_phys
, sizeof(__ap_id
));
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
);
154 /* wait for 5 secs for the processors to boot */
155 lapic_set_timer_one_shot(5000000);
157 while (lapic_read(LAPIC_TIMER_CCR
)) {
158 if (ap_cpu_ready
== cpu
) {
159 cpu_set_flag(cpu
, CPU_IS_READY
);
163 if (ap_cpu_ready
== -1) {
164 printf("WARNING : CPU %d didn't boot\n", cpu
);
168 phys_copy((phys_bytes
) &biosresetvector
, 0x467, sizeof(u32_t
));
170 outb(RTC_INDEX
, 0xF);
173 bsp_finish_booting();
177 void smp_halt_cpu (void)
182 void smp_shutdown_aps(void)
187 goto exit_shutdown_aps
;
189 /* we must let the other cpus enter the kernel mode */
192 for (cpu
= 0; cpu
< ncpus
; cpu
++) {
195 if (!cpu_test_flag(cpu
, CPU_IS_READY
)) {
196 printf("CPU %d didn't boot\n", cpu
);
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
);
210 ioapic_disable_all();
214 ncpus
= 1; /* hopefully !!! */
215 lapic_addr
= lapic_eoi_addr
= 0;
219 static void ap_finish_booting(void)
221 unsigned cpu
= cpuid
;
223 /* inform the world of our presence. */
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.
232 spinlock_lock(&boot_lock
);
235 printf("CPU %d is up\n", cpu
);
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
);
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
);
251 ap_boot_finished(cpu
);
252 spinlock_unlock(&boot_lock
);
258 void smp_ap_boot(void)
260 switch_k_stack((char *)get_k_stack_top(__ap_id
) -
261 X86_STACK_TOP_RESERVED
, ap_finish_booting
);
264 static void smp_reinit_vars(void)
266 lapic_addr
= lapic_eoi_addr
= 0;
272 static void tss_init_all(void)
276 for(cpu
= 0; cpu
< ncpus
; cpu
++)
277 tss_init(cpu
, get_k_stack_top(cpu
));
280 static int discover_cpus(void)
282 struct acpi_madt_lapic
* cpu
;
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
);
296 /* read the MP configuration */
297 if (!discover_cpus()) {
299 goto uniproc_fallback
;
302 lapic_addr
= LOCAL_APIC_DEF_ADDR
;
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()
311 bsp_cpu_id
= apicid2cpuid
[apicid()];
313 if (!lapic_enable(bsp_cpu_id
)) {
314 printf("ERROR : failed to initialize BSP Local APIC\n");
315 goto uniproc_fallback
;
318 bsp_lapic_id
= apicid();
322 if (!detect_ioapics()) {
325 goto uniproc_fallback
;
331 machine
.apic_enabled
= 1;
333 /* set smp idt entries. */
334 apic_idt_init(0); /* Not a reset ! */
337 BOOT_VERBOSE(printf("SMP initialized\n"));
339 switch_k_stack((char *)get_k_stack_top(bsp_cpu_id
) -
340 X86_STACK_TOP_RESERVED
, smp_start_aps
);
345 apic_idt_init(1); /* Reset to PIC idt ! */
347 smp_reinit_vars (); /* revert to a single proc system. */
348 intr_init(0); /* no auto eoi */
349 printf("WARNING : SMP initialization failed\n");
352 void arch_smp_halt_cpu(void)
354 /* say that we are down */
357 /* unlock the BKL and don't continue */
362 void arch_send_smp_schedule_ipi(unsigned cpu
)
364 apic_send_ipi(APIC_SMP_SCHED_PROC_VECTOR
, cpu
, APIC_IPI_DEST
);