1 /* 2 * Copyright 2008, Dustin Howett, dustin.howett@gmail.com. All rights reserved. 3 * Copyright 2004-2009, Axel Dörfler, axeld@pinc-software.de. 4 * Distributed under the terms of the MIT License. 5 * 6 * Copyright 2001, Travis Geiselbrecht. All rights reserved. 7 * Distributed under the terms of the NewOS License. 8 */ 9 10 11 #include "smp.h" 12 13 #include <string.h> 14 15 #include <KernelExport.h> 16 17 #include <kernel.h> 18 #include <safemode.h> 19 #include <boot/stage2.h> 20 #include <boot/menu.h> 21 #include <arch/x86/apic.h> 22 #include <arch/x86/arch_acpi.h> 23 #include <arch/x86/arch_smp.h> 24 #include <arch/x86/arch_system_info.h> 25 26 #include "mmu.h" 27 #include "acpi.h" 28 #include "hpet.h" 29 30 31 #define NO_SMP 0 32 33 #define TRACE_SMP 34 #ifdef TRACE_SMP 35 # define TRACE(x) dprintf x 36 #else 37 # define TRACE(x) ; 38 #endif 39 40 struct gdt_idt_descr { 41 uint16 a; 42 uint32 *b; 43 } _PACKED; 44 45 static struct scan_spots_struct smp_scan_spots[] = { 46 { 0x9fc00, 0xa0000, 0xa0000 - 0x9fc00 }, 47 { 0xf0000, 0x100000, 0x100000 - 0xf0000 }, 48 { 0, 0, 0 } 49 }; 50 51 extern "C" void execute_n_instructions(int count); 52 53 extern "C" void smp_trampoline(void); 54 extern "C" void smp_trampoline_end(void); 55 56 57 static int smp_get_current_cpu(void); 58 59 60 static uint32 61 apic_read(uint32 offset) 62 { 63 return *(uint32 *)((uint32)gKernelArgs.arch_args.apic + offset); 64 } 65 66 67 static void 68 apic_write(uint32 offset, uint32 data) 69 { 70 uint32 *addr = (uint32 *)((uint32)gKernelArgs.arch_args.apic + offset); 71 *addr = data; 72 } 73 74 75 static int 76 smp_get_current_cpu(void) 77 { 78 if (gKernelArgs.arch_args.apic == NULL) 79 return 0; 80 81 uint8_t apic_id = (apic_read(APIC_ID) & 0xffffffff) >> 24; 82 for (int i = 0; i < gKernelArgs.num_cpus; i++) { 83 if (gKernelArgs.arch_args.cpu_apic_id[i] == apic_id) 84 return i; 85 } 86 87 return 0; 88 } 89 90 91 static mp_floating_struct * 92 smp_mp_probe(uint32 base, uint32 limit) 93 { 94 TRACE(("smp_mp_probe: entry base 0x%lx, limit 0x%lx\n", base, limit)); 95 for (uint32 *pointer = (uint32 *)base; (uint32)pointer < limit; pointer++) { 96 if (*pointer == MP_FLOATING_SIGNATURE) { 97 TRACE(("smp_mp_probe: found floating pointer structure at %p\n", 98 pointer)); 99 return (mp_floating_struct *)pointer; 100 } 101 } 102 103 return NULL; 104 } 105 106 107 static status_t 108 smp_do_mp_config(mp_floating_struct *floatingStruct) 109 { 110 TRACE(("smp: intel mp version %s, %s", 111 (floatingStruct->spec_revision == 1) ? "1.1" : "1.4", 112 (floatingStruct->mp_feature_2 & 0x80) 113 ? "imcr and pic compatibility mode.\n" 114 : "virtual wire compatibility mode.\n")); 115 116 if (floatingStruct->config_table == NULL) { 117 #if 1 118 // TODO: need to implement 119 TRACE(("smp: standard configuration %d unimplemented\n", 120 floatingStruct->mp_feature_1)); 121 gKernelArgs.num_cpus = 1; 122 return B_OK; 123 #else 124 // this system conforms to one of the default configurations 125 TRACE(("smp: standard configuration %d\n", floatingStruct->mp_feature_1)); 126 gKernelArgs.num_cpus = 2; 127 gKernelArgs.cpu_apic_id[0] = 0; 128 gKernelArgs.cpu_apic_id[1] = 1; 129 apic_phys = (unsigned int *)0xfee00000; 130 ioapic_phys = (unsigned int *)0xfec00000; 131 dprintf("smp: WARNING: standard configuration code is untested"); 132 return B_OK; 133 #endif 134 } 135 136 // We are not running in standard configuration, so we have to look through 137 // all of the mp configuration table crap to figure out how many processors 138 // we have, where our apics are, etc. 139 140 mp_config_table *config = floatingStruct->config_table; 141 gKernelArgs.num_cpus = 0; 142 143 // print our new found configuration. 144 TRACE(("smp: oem id: %.8s product id: %.12s\n", config->oem, 145 config->product)); 146 TRACE(("smp: base table has %d entries, extended section %d bytes\n", 147 config->num_base_entries, config->ext_length)); 148 149 gKernelArgs.arch_args.apic_phys = (uint32)config->apic; 150 151 char *pointer = (char *)((uint32)config + sizeof(struct mp_config_table)); 152 for (int32 i = 0; i < config->num_base_entries; i++) { 153 switch (*pointer) { 154 case MP_BASE_PROCESSOR: 155 { 156 struct mp_base_processor *processor 157 = (struct mp_base_processor *)pointer; 158 pointer += sizeof(struct mp_base_processor); 159 160 if (gKernelArgs.num_cpus == MAX_BOOT_CPUS) { 161 TRACE(("smp: already reached maximum boot CPUs (%d)\n", 162 MAX_BOOT_CPUS)); 163 continue; 164 } 165 166 // skip if the processor is not enabled. 167 if (!(processor->cpu_flags & 0x1)) { 168 TRACE(("smp: skip apic id %d: disabled\n", 169 processor->apic_id)); 170 continue; 171 } 172 173 gKernelArgs.arch_args.cpu_apic_id[gKernelArgs.num_cpus] 174 = processor->apic_id; 175 gKernelArgs.arch_args.cpu_apic_version[gKernelArgs.num_cpus] 176 = processor->apic_version; 177 178 #ifdef TRACE_SMP 179 const char *cpuFamily[] = { "", "", "", "", "Intel 486", 180 "Intel Pentium", "Intel Pentium Pro", "Intel Pentium II" }; 181 #endif 182 TRACE(("smp: cpu#%ld: %s, apic id %d, version %d%s\n", 183 gKernelArgs.num_cpus, 184 cpuFamily[(processor->signature & 0xf00) >> 8], 185 processor->apic_id, processor->apic_version, 186 (processor->cpu_flags & 0x2) ? ", BSP" : "")); 187 188 gKernelArgs.num_cpus++; 189 break; 190 } 191 case MP_BASE_BUS: 192 { 193 struct mp_base_bus *bus = (struct mp_base_bus *)pointer; 194 pointer += sizeof(struct mp_base_bus); 195 196 TRACE(("smp: bus %d: %c%c%c%c%c%c\n", bus->bus_id, 197 bus->name[0], bus->name[1], bus->name[2], bus->name[3], 198 bus->name[4], bus->name[5])); 199 200 break; 201 } 202 case MP_BASE_IO_APIC: 203 { 204 struct mp_base_ioapic *io = (struct mp_base_ioapic *)pointer; 205 pointer += sizeof(struct mp_base_ioapic); 206 207 gKernelArgs.arch_args.ioapic_phys = (uint32)io->addr; 208 209 TRACE(("smp: found io apic with apic id %d, version %d\n", 210 io->ioapic_id, io->ioapic_version)); 211 212 break; 213 } 214 case MP_BASE_IO_INTR: 215 case MP_BASE_LOCAL_INTR: 216 { 217 struct mp_base_interrupt *interrupt 218 = (struct mp_base_interrupt *)pointer; 219 pointer += sizeof(struct mp_base_interrupt); 220 221 dprintf("smp: %s int: type %d, source bus %d, irq %3d, dest " 222 "apic %d, int %3d, polarity %d, trigger mode %d\n", 223 interrupt->type == MP_BASE_IO_INTR ? "I/O" : "local", 224 interrupt->interrupt_type, interrupt->source_bus_id, 225 interrupt->source_bus_irq, interrupt->dest_apic_id, 226 interrupt->dest_apic_int, interrupt->polarity, 227 interrupt->trigger_mode); 228 break; 229 } 230 } 231 } 232 233 dprintf("smp: apic @ %p, i/o apic @ %p, total %ld processors detected\n", 234 (void *)gKernelArgs.arch_args.apic_phys, 235 (void *)gKernelArgs.arch_args.ioapic_phys, 236 gKernelArgs.num_cpus); 237 238 return gKernelArgs.num_cpus > 0 ? B_OK : B_ERROR; 239 } 240 241 242 static status_t 243 smp_do_acpi_config(void) 244 { 245 TRACE(("smp: using ACPI to detect MP configuration\n")); 246 247 // reset CPU count 248 gKernelArgs.num_cpus = 0; 249 250 acpi_madt *madt = (acpi_madt *)acpi_find_table(ACPI_MADT_SIGNATURE); 251 252 if (madt == NULL) { 253 TRACE(("smp: Failed to find MADT!\n")); 254 return B_ERROR; 255 } 256 257 gKernelArgs.arch_args.apic_phys = madt->local_apic_address; 258 TRACE(("smp: local apic address is 0x%lx\n", madt->local_apic_address)); 259 260 acpi_apic *apic = (acpi_apic *)((uint8 *)madt + sizeof(acpi_madt)); 261 acpi_apic *end = (acpi_apic *)((uint8 *)madt + madt->header.length); 262 while (apic < end) { 263 switch (apic->type) { 264 case ACPI_MADT_LOCAL_APIC: 265 { 266 if (gKernelArgs.num_cpus == MAX_BOOT_CPUS) { 267 TRACE(("smp: already reached maximum boot CPUs (%d)\n", 268 MAX_BOOT_CPUS)); 269 break; 270 } 271 272 acpi_local_apic *localApic = (acpi_local_apic *)apic; 273 TRACE(("smp: found local APIC with id %u\n", 274 localApic->apic_id)); 275 if ((localApic->flags & ACPI_LOCAL_APIC_ENABLED) == 0) { 276 TRACE(("smp: APIC is disabled and will not be used\n")); 277 break; 278 } 279 280 gKernelArgs.arch_args.cpu_apic_id[gKernelArgs.num_cpus] 281 = localApic->apic_id; 282 // TODO: how to find out? putting 0x10 in to indicate a local apic 283 gKernelArgs.arch_args.cpu_apic_version[gKernelArgs.num_cpus] 284 = 0x10; 285 gKernelArgs.num_cpus++; 286 break; 287 } 288 289 case ACPI_MADT_IO_APIC: { 290 acpi_io_apic *ioApic = (acpi_io_apic *)apic; 291 TRACE(("smp: found io APIC with id %u and address 0x%lx\n", 292 ioApic->io_apic_id, ioApic->io_apic_address)); 293 gKernelArgs.arch_args.ioapic_phys = ioApic->io_apic_address; 294 break; 295 } 296 } 297 298 apic = (acpi_apic *)((uint8 *)apic + apic->length); 299 } 300 301 return gKernelArgs.num_cpus > 0 ? B_OK : B_ERROR; 302 } 303 304 305 /*! Target function of the trampoline code. 306 The trampoline code should have the pgdir and a gdt set up for us, 307 along with us being on the final stack for this processor. We need 308 to set up the local APIC and load the global idt and gdt. When we're 309 done, we'll jump into the kernel with the cpu number as an argument. 310 */ 311 static int 312 smp_cpu_ready(void) 313 { 314 uint32 curr_cpu = smp_get_current_cpu(); 315 struct gdt_idt_descr idt_descr; 316 struct gdt_idt_descr gdt_descr; 317 318 //TRACE(("smp_cpu_ready: entry cpu %ld\n", curr_cpu)); 319 320 // Important. Make sure supervisor threads can fault on read only pages... 321 asm("movl %%eax, %%cr0" : : "a" ((1 << 31) | (1 << 16) | (1 << 5) | 1)); 322 asm("cld"); 323 asm("fninit"); 324 325 // Set up the final idt 326 idt_descr.a = IDT_LIMIT - 1; 327 idt_descr.b = (uint32 *)gKernelArgs.arch_args.vir_idt; 328 329 asm("lidt %0;" 330 : : "m" (idt_descr)); 331 332 // Set up the final gdt 333 gdt_descr.a = GDT_LIMIT - 1; 334 gdt_descr.b = (uint32 *)gKernelArgs.arch_args.vir_gdt; 335 336 asm("lgdt %0;" 337 : : "m" (gdt_descr)); 338 339 asm("pushl %0; " // push the cpu number 340 "pushl %1; " // kernel args 341 "pushl $0x0;" // dummy retval for call to main 342 "pushl %2; " // this is the start address 343 "ret; " // jump. 344 : : "g" (curr_cpu), "g" (&gKernelArgs), 345 "g" (gKernelArgs.kernel_image.elf_header.e_entry)); 346 347 // no where to return to 348 return 0; 349 } 350 351 352 static void 353 calculate_apic_timer_conversion_factor(void) 354 { 355 int64 t1, t2; 356 uint32 config; 357 uint32 count; 358 359 // setup the timer 360 config = apic_read(APIC_LVT_TIMER); 361 config = (config & APIC_LVT_TIMER_MASK) + APIC_LVT_MASKED; 362 // timer masked, vector 0 363 apic_write(APIC_LVT_TIMER, config); 364 365 config = (apic_read(APIC_TIMER_DIVIDE_CONFIG) & ~0x0000000f); 366 apic_write(APIC_TIMER_DIVIDE_CONFIG, config | APIC_TIMER_DIVIDE_CONFIG_1); 367 // divide clock by one 368 369 t1 = system_time(); 370 apic_write(APIC_INITIAL_TIMER_COUNT, 0xffffffff); // start the counter 371 372 execute_n_instructions(128 * 20000); 373 374 count = apic_read(APIC_CURRENT_TIMER_COUNT); 375 t2 = system_time(); 376 377 count = 0xffffffff - count; 378 379 gKernelArgs.arch_args.apic_time_cv_factor 380 = (uint32)((1000000.0/(t2 - t1)) * count); 381 382 TRACE(("APIC ticks/sec = %ld\n", 383 gKernelArgs.arch_args.apic_time_cv_factor)); 384 } 385 386 387 // #pragma mark - 388 389 390 void 391 smp_init_other_cpus(void) 392 { 393 if (get_safemode_boolean(B_SAFEMODE_DISABLE_SMP, false)) { 394 // SMP has been disabled! 395 TRACE(("smp disabled per safemode setting\n")); 396 gKernelArgs.num_cpus = 1; 397 } 398 399 if (get_safemode_boolean(B_SAFEMODE_DISABLE_APIC, false)) { 400 TRACE(("local apic disabled per safemode setting\n")); 401 gKernelArgs.arch_args.apic_phys = 0; 402 } 403 404 if (gKernelArgs.arch_args.apic_phys == 0) 405 return; 406 407 TRACE(("smp: found %ld cpu%s\n", gKernelArgs.num_cpus, 408 gKernelArgs.num_cpus != 1 ? "s" : "")); 409 TRACE(("smp: apic_phys = %p\n", (void *)gKernelArgs.arch_args.apic_phys)); 410 TRACE(("smp: ioapic_phys = %p\n", 411 (void *)gKernelArgs.arch_args.ioapic_phys)); 412 413 // map in the apic & ioapic (if available) 414 gKernelArgs.arch_args.apic = (uint32 *)mmu_map_physical_memory( 415 gKernelArgs.arch_args.apic_phys, B_PAGE_SIZE, kDefaultPageFlags); 416 if (gKernelArgs.arch_args.ioapic_phys != 0) { 417 gKernelArgs.arch_args.ioapic = (uint32 *)mmu_map_physical_memory( 418 gKernelArgs.arch_args.ioapic_phys, B_PAGE_SIZE, kDefaultPageFlags); 419 } 420 421 TRACE(("smp: apic = %p\n", gKernelArgs.arch_args.apic)); 422 TRACE(("smp: ioapic = %p\n", gKernelArgs.arch_args.ioapic)); 423 424 // calculate how fast the apic timer is 425 calculate_apic_timer_conversion_factor(); 426 427 if (gKernelArgs.num_cpus < 2) 428 return; 429 430 for (uint32 i = 1; i < gKernelArgs.num_cpus; i++) { 431 // create a final stack the trampoline code will put the ap processor on 432 gKernelArgs.cpu_kstack[i].start = (addr_t)mmu_allocate(NULL, 433 KERNEL_STACK_SIZE + KERNEL_STACK_GUARD_PAGES * B_PAGE_SIZE); 434 gKernelArgs.cpu_kstack[i].size = KERNEL_STACK_SIZE 435 + KERNEL_STACK_GUARD_PAGES * B_PAGE_SIZE; 436 } 437 } 438 439 440 void 441 smp_boot_other_cpus(void) 442 { 443 if (gKernelArgs.num_cpus < 2) 444 return; 445 446 TRACE(("trampolining other cpus\n")); 447 448 // The first 8 MB are identity mapped, either 0x9e000-0x9ffff is reserved 449 // for this, or when PXE services are used 0x8b000-0x8cfff. 450 451 // allocate a stack and a code area for the smp trampoline 452 // (these have to be < 1M physical, 0xa0000-0xfffff is reserved by the BIOS, 453 // and when PXE services are used, the 0x8d000-0x9ffff is also reserved) 454 #ifdef _PXE_ENV 455 uint32 trampolineCode = 0x8b000; 456 uint32 trampolineStack = 0x8c000; 457 #else 458 uint32 trampolineCode = 0x9f000; 459 uint32 trampolineStack = 0x9e000; 460 #endif 461 462 // copy the trampoline code over 463 memcpy((char *)trampolineCode, (const void*)&smp_trampoline, 464 (uint32)&smp_trampoline_end - (uint32)&smp_trampoline); 465 466 // boot the cpus 467 for (uint32 i = 1; i < gKernelArgs.num_cpus; i++) { 468 uint32 *finalStack; 469 uint32 *tempStack; 470 uint32 config; 471 uint32 numStartups; 472 uint32 j; 473 474 // set this stack up 475 finalStack = (uint32 *)gKernelArgs.cpu_kstack[i].start; 476 memset((uint8*)finalStack + KERNEL_STACK_GUARD_PAGES * B_PAGE_SIZE, 0, 477 KERNEL_STACK_SIZE); 478 tempStack = (finalStack 479 + (KERNEL_STACK_SIZE + KERNEL_STACK_GUARD_PAGES * B_PAGE_SIZE) 480 / sizeof(uint32)) - 1; 481 *tempStack = (uint32)&smp_cpu_ready; 482 483 // set the trampoline stack up 484 tempStack = (uint32 *)(trampolineStack + B_PAGE_SIZE - 4); 485 // final location of the stack 486 *tempStack = ((uint32)finalStack) + KERNEL_STACK_SIZE 487 + KERNEL_STACK_GUARD_PAGES * B_PAGE_SIZE - sizeof(uint32); 488 tempStack--; 489 // page dir 490 *tempStack = gKernelArgs.arch_args.phys_pgdir; 491 492 // put a gdt descriptor at the bottom of the stack 493 *((uint16 *)trampolineStack) = 0x18 - 1; // LIMIT 494 *((uint32 *)(trampolineStack + 2)) = trampolineStack + 8; 495 496 // put the gdt at the bottom 497 memcpy(&((uint32 *)trampolineStack)[2], 498 (void *)gKernelArgs.arch_args.vir_gdt, 6 * 4); 499 500 /* clear apic errors */ 501 if (gKernelArgs.arch_args.cpu_apic_version[i] & 0xf0) { 502 apic_write(APIC_ERROR_STATUS, 0); 503 apic_read(APIC_ERROR_STATUS); 504 } 505 506 //dprintf("assert INIT\n"); 507 /* send (aka assert) INIT IPI */ 508 config = (apic_read(APIC_INTR_COMMAND_2) & APIC_INTR_COMMAND_2_MASK) 509 | (gKernelArgs.arch_args.cpu_apic_id[i] << 24); 510 apic_write(APIC_INTR_COMMAND_2, config); /* set target pe */ 511 config = (apic_read(APIC_INTR_COMMAND_1) & 0xfff00000) 512 | APIC_TRIGGER_MODE_LEVEL | APIC_INTR_COMMAND_1_ASSERT 513 | APIC_DELIVERY_MODE_INIT; 514 apic_write(APIC_INTR_COMMAND_1, config); 515 516 dprintf("wait for delivery\n"); 517 // wait for pending to end 518 while ((apic_read(APIC_INTR_COMMAND_1) & APIC_DELIVERY_STATUS) != 0) 519 asm volatile ("pause;"); 520 521 dprintf("deassert INIT\n"); 522 /* deassert INIT */ 523 config = (apic_read(APIC_INTR_COMMAND_2) & APIC_INTR_COMMAND_2_MASK) 524 | (gKernelArgs.arch_args.cpu_apic_id[i] << 24); 525 apic_write(APIC_INTR_COMMAND_2, config); 526 config = (apic_read(APIC_INTR_COMMAND_1) & 0xfff00000) 527 | APIC_TRIGGER_MODE_LEVEL | APIC_DELIVERY_MODE_INIT; 528 apic_write(APIC_INTR_COMMAND_1, config); 529 530 dprintf("wait for delivery\n"); 531 // wait for pending to end 532 while ((apic_read(APIC_INTR_COMMAND_1) & APIC_DELIVERY_STATUS) != 0) 533 asm volatile ("pause;"); 534 535 /* wait 10ms */ 536 spin(10000); 537 538 /* is this a local apic or an 82489dx ? */ 539 numStartups = (gKernelArgs.arch_args.cpu_apic_version[i] & 0xf0) 540 ? 2 : 0; 541 dprintf("num startups = %ld\n", numStartups); 542 for (j = 0; j < numStartups; j++) { 543 /* it's a local apic, so send STARTUP IPIs */ 544 dprintf("send STARTUP\n"); 545 apic_write(APIC_ERROR_STATUS, 0); 546 547 /* set target pe */ 548 config = (apic_read(APIC_INTR_COMMAND_2) & APIC_INTR_COMMAND_2_MASK) 549 | (gKernelArgs.arch_args.cpu_apic_id[i] << 24); 550 apic_write(APIC_INTR_COMMAND_2, config); 551 552 /* send the IPI */ 553 config = (apic_read(APIC_INTR_COMMAND_1) & 0xfff0f800) 554 | APIC_DELIVERY_MODE_STARTUP | (trampolineCode >> 12); 555 apic_write(APIC_INTR_COMMAND_1, config); 556 557 /* wait */ 558 spin(200); 559 560 dprintf("wait for delivery\n"); 561 while ((apic_read(APIC_INTR_COMMAND_1) & APIC_DELIVERY_STATUS) != 0) 562 asm volatile ("pause;"); 563 } 564 565 // Wait for the trampoline code to clear the final stack location. 566 // This serves as a notification for us that it has loaded the address 567 // and it is safe for us to overwrite it to trampoline the next CPU. 568 tempStack++; 569 while (*tempStack != 0) 570 spin(1000); 571 } 572 573 TRACE(("done trampolining\n")); 574 } 575 576 577 void 578 smp_add_safemode_menus(Menu *menu) 579 { 580 MenuItem *item; 581 582 if (gKernelArgs.arch_args.ioapic_phys != 0) { 583 menu->AddItem(item = new(nothrow) MenuItem("Disable IO-APIC")); 584 item->SetType(MENU_ITEM_MARKABLE); 585 item->SetMarked(true); 586 // TODO: disabled by default for now 587 item->SetData(B_SAFEMODE_DISABLE_IOAPIC); 588 item->SetHelpText("Disables using the IO APIC for interrupt handling, " 589 "forcing instead the use of the PIC."); 590 } 591 592 if (gKernelArgs.arch_args.apic_phys != 0) { 593 menu->AddItem(item = new(nothrow) MenuItem("Disable LOCAL APIC")); 594 item->SetType(MENU_ITEM_MARKABLE); 595 item->SetData(B_SAFEMODE_DISABLE_APIC); 596 item->SetHelpText("Disables using the LOCAL APIC for timekeeping."); 597 } 598 599 if (gKernelArgs.num_cpus < 2) 600 return; 601 602 item = new(nothrow) MenuItem("Disable SMP"); 603 menu->AddItem(item); 604 item->SetData(B_SAFEMODE_DISABLE_SMP); 605 item->SetType(MENU_ITEM_MARKABLE); 606 item->SetHelpText("Disables all but one CPU core."); 607 } 608 609 610 void 611 smp_init(void) 612 { 613 #if NO_SMP 614 gKernelArgs.num_cpus = 1; 615 return; 616 #endif 617 618 // first try to find ACPI tables to get MP configuration as it handles 619 // physical as well as logical MP configurations as in multiple cpus, 620 // multiple cores or hyper threading. 621 if (smp_do_acpi_config() == B_OK) 622 return; 623 624 // then try to find MPS tables and do configuration based on them 625 for (int32 i = 0; smp_scan_spots[i].length > 0; i++) { 626 mp_floating_struct *floatingStruct = smp_mp_probe( 627 smp_scan_spots[i].start, smp_scan_spots[i].stop); 628 if (floatingStruct != NULL && smp_do_mp_config(floatingStruct) == B_OK) 629 return; 630 } 631 632 // everything failed or we are not running an SMP system 633 gKernelArgs.num_cpus = 1; 634 } 635