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