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