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 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 */ 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 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 172 void smp_halt_cpu (void) 173 { 174 NOT_IMPLEMENTED; 175 } 176 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 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 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 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 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 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 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 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 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