xref: /haiku/src/system/boot/platform/bios_ia32/smp.cpp (revision d374a27286b8a52974a97dba0d5966ea026a665d)
1 /*
2  * Copyright 2008, Dustin Howett, dustin.howett@gmail.com. All rights reserved.
3  * Copyright 2004-2010, 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 (uint32 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