xref: /haiku/src/system/boot/platform/bios_ia32/smp.cpp (revision 55b40aa53a835472ec7952b138ae4256203d02e4)
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_apic.h>
20 #include <arch/x86/arch_system_info.h>
21 
22 #include <string.h>
23 
24 #define NO_SMP 0
25 
26 #define TRACE_SMP
27 #ifdef TRACE_SMP
28 #	define TRACE(x) dprintf x
29 #else
30 #	define TRACE(x) ;
31 #endif
32 
33 struct gdt_idt_descr {
34 	uint16 a;
35 	uint32 *b;
36 } _PACKED;
37 
38 struct smp_scan_spots_struct {
39 	uint32 start;
40 	uint32 stop;
41 	uint32 length;
42 };
43 
44 static struct smp_scan_spots_struct smp_scan_spots[] = {
45 	{ 0x9fc00, 0xa0000, 0xa0000 - 0x9fc00 },
46 	{ 0xf0000, 0x100000, 0x100000 - 0xf0000 },
47 	{ 0, 0, 0 }
48 };
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 struct mp_floating_struct *sFloatingStruct = NULL;
58 
59 static int smp_get_current_cpu(void);
60 
61 
62 static uint32
63 apic_read(uint32 offset)
64 {
65 	return *(uint32 *)((uint32)gKernelArgs.arch_args.apic + offset);
66 }
67 
68 
69 static void
70 apic_write(uint32 offset, uint32 data)
71 {
72 	uint32 *addr = (uint32 *)((uint32)gKernelArgs.arch_args.apic + offset);
73 	*addr = data;
74 }
75 
76 
77 static bool
78 supports_hyper_threading(void)
79 {
80 	cpuid_info info;
81 	if (get_current_cpuid(&info, 0) == B_OK
82 		&& !strncmp(info.eax_0.vendor_id, "GenuineIntel", 12)
83 		&& info.eax_0.max_eax > 0) {
84 		if (get_current_cpuid(&info, 1) == B_OK)
85 			return (info.eax_1.features & (1 << 28)) != 0;
86 	}
87 
88 	return false;
89 }
90 
91 
92 static int
93 smp_get_current_cpu(void)
94 {
95 	if (gKernelArgs.arch_args.apic == NULL)
96 		return 0;
97 
98 	return gKernelArgs.arch_args.cpu_os_id[(apic_read(APIC_ID) & 0xffffffff) >> 24];
99 }
100 
101 
102 static uint32 *
103 smp_probe(uint32 base, uint32 limit)
104 {
105 	uint32 *ptr;
106 
107 	TRACE(("smp_probe: entry base 0x%lx, limit 0x%lx\n", base, limit));
108 
109 	for (ptr = (uint32 *) base; (uint32)ptr < limit; ptr++) {
110 		if (*ptr == MP_FLOATING_SIGNATURE) {
111 			TRACE(("smp_probe: found floating pointer structure at %p\n", ptr));
112 			return ptr;
113 		}
114 	}
115 	return NULL;
116 }
117 
118 
119 static void
120 smp_do_config(void)
121 {
122 	struct mp_config_table *config;
123 	char *ptr;
124 	int i;
125 #ifdef TRACE_SMP
126 	const char *cpu_family[] = { "", "", "", "", "Intel 486",
127 		"Intel Pentium", "Intel Pentium Pro", "Intel Pentium II" };
128 #endif
129 
130 	/*
131 	 * we are not running in standard configuration, so we have to look through
132 	 * all of the mp configuration table crap to figure out how many processors
133 	 * we have, where our apics are, etc.
134 	 */
135 	gKernelArgs.num_cpus = 0;
136 
137 	config = sFloatingStruct->config_table;
138 
139 	/* print out our new found configuration. */
140 
141 	ptr = (char *)&(config->oem[0]);
142 	TRACE(("smp: oem id: %c%c%c%c%c%c%c%c product id: "
143 		"%c%c%c%c%c%c%c%c%c%c%c%c\n", ptr[0], ptr[1], ptr[2], ptr[3], ptr[4],
144 		ptr[5], ptr[6], ptr[7], ptr[8], ptr[9], ptr[10], ptr[11], ptr[12],
145 		ptr[13], ptr[14], ptr[15], ptr[16], ptr[17], ptr[18], ptr[19]));
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 	ptr = (char *)((uint32)config + sizeof(struct mp_config_table));
152 	for (i = 0; i < config->num_base_entries; i++) {
153 		switch (*ptr) {
154 			case MP_BASE_PROCESSOR:
155 			{
156 				struct mp_base_processor *processor = (struct mp_base_processor *)ptr;
157 
158 				gKernelArgs.arch_args.cpu_apic_id[gKernelArgs.num_cpus] = processor->apic_id;
159 				gKernelArgs.arch_args.cpu_os_id[processor->apic_id] = gKernelArgs.num_cpus;
160 				gKernelArgs.arch_args.cpu_apic_version[gKernelArgs.num_cpus] = processor->apic_version;
161 
162 				TRACE(("smp: cpu#%ld: %s, apic id %d, version %d%s\n",
163 					gKernelArgs.num_cpus, cpu_family[(processor->signature & 0xf00) >> 8],
164 					processor->apic_id, processor->apic_version, (processor->cpu_flags & 0x2) ?
165 					", BSP" : ""));
166 
167 				gKernelArgs.num_cpus++;
168 				ptr += sizeof(struct mp_base_processor);
169 				break;
170 			}
171 			case MP_BASE_BUS:
172 			{
173 				struct mp_base_bus *bus = (struct mp_base_bus *)ptr;
174 
175 				TRACE(("smp: bus %d: %c%c%c%c%c%c\n", bus->bus_id,
176 					bus->name[0], bus->name[1], bus->name[2], bus->name[3],
177 					bus->name[4], bus->name[5]));
178 
179 				ptr += sizeof(struct mp_base_bus);
180 				break;
181 			}
182 			case MP_BASE_IO_APIC:
183 			{
184 				struct mp_base_ioapic *io = (struct mp_base_ioapic *) ptr;
185 				gKernelArgs.arch_args.ioapic_phys = (uint32)io->addr;
186 
187 				TRACE(("smp: found io apic with apic id %d, version %d\n",
188 					io->ioapic_id, io->ioapic_version));
189 
190 				ptr += sizeof(struct mp_base_ioapic);
191 				break;
192 			}
193 			case MP_BASE_IO_INTR:
194 			case MP_BASE_LOCAL_INTR:
195 			{
196 				struct mp_base_interrupt *interrupt = (struct mp_base_interrupt *)ptr;
197 
198 				dprintf("smp: %s int: type %d, source bus %d, irq %d, dest apic %d, int %d, polarity %d, trigger mode %d\n",
199 					interrupt->type == MP_BASE_IO_INTR ? "I/O" : "local",
200 					interrupt->interrupt_type, interrupt->source_bus_id,
201 					interrupt->source_bus_irq, interrupt->dest_apic_id,
202 					interrupt->dest_apic_int, interrupt->polarity,
203 					interrupt->trigger_mode);
204 				ptr += sizeof(struct mp_base_interrupt);
205 				break;
206 			}
207 		}
208 	}
209 
210 	dprintf("smp: apic @ %p, i/o apic @ %p, total %ld processors detected\n",
211 		(void *)gKernelArgs.arch_args.apic_phys,
212 		(void *)gKernelArgs.arch_args.ioapic_phys,
213 		gKernelArgs.num_cpus);
214 
215 	// Try to detect single CPU hyper threading setup
216 	// ToDo: this should be done using the ACPI APIC table
217 	// ToDo: this only works with a single HT enabled CPU anyway
218 
219 	if (gKernelArgs.num_cpus == 1 && supports_hyper_threading()) {
220 		cpuid_info info;
221 		get_current_cpuid(&info, 1);
222 
223 		dprintf("CPU supports Hyper-Threading, %ld processors in package\n",
224 			info.eax_1.logical_cpus);
225 
226 		// enable the second logical processor
227 /*
228 		gKernelArgs.num_cpus = 2;
229 		gKernelArgs.arch_args.cpu_apic_id[1] = gKernelArgs.arch_args.cpu_apic_id[0] + 1;
230 		gKernelArgs.arch_args.cpu_os_id[gKernelArgs.arch_args.cpu_apic_id[1]] = 1;
231 		gKernelArgs.arch_args.cpu_apic_version[1] = gKernelArgs.arch_args.cpu_apic_version[0];;
232 */
233 	}
234 
235 	// this BIOS looks broken, because it didn't report any cpus (VMWare)
236 	if (gKernelArgs.num_cpus == 0)
237 		gKernelArgs.num_cpus = 1;
238 }
239 
240 
241 static int
242 smp_find_mp_config(void)
243 {
244 	int i;
245 
246 #if NO_SMP
247 	if (1)
248 		return gKernelArgs.num_cpus = 1;
249 #endif
250 
251 	for (i = 0; smp_scan_spots[i].length > 0; i++) {
252 		sFloatingStruct = (struct mp_floating_struct *)smp_probe(smp_scan_spots[i].start,
253 			smp_scan_spots[i].stop);
254 		if (sFloatingStruct != NULL)
255 			break;
256 	}
257 
258 	if (sFloatingStruct != NULL) {
259 		TRACE(("smp_boot: intel mp version %s, %s",
260 			(sFloatingStruct->spec_revision == 1) ? "1.1" : "1.4",
261 			(sFloatingStruct->mp_feature_2 & 0x80)
262 				? "imcr and pic compatibility mode.\n"
263 				: "virtual wire compatibility mode.\n"));
264 
265 		if (sFloatingStruct->config_table == NULL) {
266 			// XXX need to implement
267 #if 1
268 			gKernelArgs.num_cpus = 1;
269 			return 1;
270 #else
271 			/* this system conforms to one of the default configurations */
272 //			mp_num_def_config = sFloatingStruct->mp_feature_1;
273 			TRACE(("smp: standard configuration %d\n", sFloatingStruct->mp_feature_1));
274 /*			num_cpus = 2;
275 			gKernelArgs.cpu_apic_id[0] = 0;
276 			gKernelArgs.cpu_apic_id[1] = 1;
277 			apic_phys = (unsigned int *) 0xfee00000;
278 			ioapic_phys = (unsigned int *) 0xfec00000;
279 			kprintf ("smp: WARNING: standard configuration code is untested");
280 */
281 #endif
282 		} else {
283 			smp_do_config();
284 		}
285 		return gKernelArgs.num_cpus;
286 	}
287 
288 	return gKernelArgs.num_cpus = 1;
289 }
290 
291 
292 /** Target function of the trampoline code.
293  *	The trampoline code should have the pgdir and a gdt set up for us,
294  *	along with us being on the final stack for this processor. We need
295  *	to set up the local APIC and load the global idt and gdt. When we're
296  *	done, we'll jump into the kernel with the cpu number as an argument.
297  */
298 
299 static int
300 smp_cpu_ready(void)
301 {
302 	uint32 curr_cpu = smp_get_current_cpu();
303 	struct gdt_idt_descr idt_descr;
304 	struct gdt_idt_descr gdt_descr;
305 
306 	//TRACE(("smp_cpu_ready: entry cpu %ld\n", curr_cpu));
307 
308 	// Important.  Make sure supervisor threads can fault on read only pages...
309 	asm("movl %%eax, %%cr0" : : "a" ((1 << 31) | (1 << 16) | (1 << 5) | 1));
310 	asm("cld");
311 	asm("fninit");
312 
313 	// Set up the final idt
314 	idt_descr.a = IDT_LIMIT - 1;
315 	idt_descr.b = (uint32 *)gKernelArgs.arch_args.vir_idt;
316 
317 	asm("lidt	%0;"
318 		: : "m" (idt_descr));
319 
320 	// Set up the final gdt
321 	gdt_descr.a = GDT_LIMIT - 1;
322 	gdt_descr.b = (uint32 *)gKernelArgs.arch_args.vir_gdt;
323 
324 	asm("lgdt	%0;"
325 		: : "m" (gdt_descr));
326 
327 	asm("pushl  %0; "					// push the cpu number
328 		"pushl 	%1;	"					// kernel args
329 		"pushl 	$0x0;"					// dummy retval for call to main
330 		"pushl 	%2;	"					// this is the start address
331 		"ret;		"					// jump.
332 		: : "g" (curr_cpu), "g" (&gKernelArgs), "g" (gKernelArgs.kernel_image.elf_header.e_entry));
333 
334 	// no where to return to
335 	return 0;
336 }
337 
338 
339 static void
340 calculate_apic_timer_conversion_factor(void)
341 {
342 	int64 t1, t2;
343 	uint32 config;
344 	uint32 count;
345 
346 	// setup the timer
347 	config = apic_read(APIC_LVT_TIMER);
348 	config = (config & APIC_LVT_TIMER_MASK) + APIC_LVT_MASKED; // timer masked, vector 0
349 	apic_write(APIC_LVT_TIMER, config);
350 
351 	config = (apic_read(APIC_TIMER_DIVIDE_CONFIG) & ~0x0000000f);
352 	apic_write(APIC_TIMER_DIVIDE_CONFIG, config | APIC_TIMER_DIVIDE_CONFIG_1);
353 		// divide clock by one
354 
355 	t1 = system_time();
356 	apic_write(APIC_INITIAL_TIMER_COUNT, 0xffffffff); // start the counter
357 
358 	execute_n_instructions(128*20000);
359 
360 	count = apic_read(APIC_CURRENT_TIMER_COUNT);
361 	t2 = system_time();
362 
363 	count = 0xffffffff - count;
364 
365 	gKernelArgs.arch_args.apic_time_cv_factor = (uint32)((1000000.0/(t2 - t1)) * count);
366 
367 	TRACE(("APIC ticks/sec = %ld\n", gKernelArgs.arch_args.apic_time_cv_factor));
368 }
369 
370 
371 //	#pragma mark -
372 
373 
374 void
375 smp_boot_other_cpus(void)
376 {
377 	uint32 trampolineCode;
378 	uint32 trampolineStack;
379 	uint32 i;
380 
381 	void *handle = load_driver_settings(B_SAFEMODE_DRIVER_SETTINGS);
382 	if (handle != NULL) {
383 		if (get_driver_boolean_parameter(handle, B_SAFEMODE_DISABLE_SMP, false, false)) {
384 			// SMP has been disabled!
385 			gKernelArgs.num_cpus = 1;
386 		}
387 		unload_driver_settings(handle);
388 	}
389 
390 	if (gKernelArgs.num_cpus < 2)
391 		return;
392 
393 	TRACE(("trampolining other cpus\n"));
394 
395 	// The first 8 MB are identity mapped, 0x9e000-0x9ffff is reserved for this
396 
397 	// allocate a stack and a code area for the smp trampoline
398 	// (these have to be < 1M physical, 0xa0000-0xfffff is reserved by the BIOS)
399 #ifdef _PXE_ENV
400 	trampolineCode = 0x8b000;
401 	trampolineStack = 0x8c000;
402 #else
403 	trampolineCode = 0x9f000;
404 	trampolineStack = 0x9e000;
405 #endif
406 
407 	// copy the trampoline code over
408 	memcpy((char *)trampolineCode, (const void*)&smp_trampoline,
409 		(uint32)&smp_trampoline_end - (uint32)&smp_trampoline);
410 
411 	// boot the cpus
412 	for (i = 1; i < gKernelArgs.num_cpus; i++) {
413 		uint32 *finalStack;
414 		uint32 *tempStack;
415 		uint32 config;
416 		uint32 numStartups;
417 		uint32 j;
418 
419 		// set this stack up
420 		finalStack = (uint32 *)gKernelArgs.cpu_kstack[i].start;
421 		memset(finalStack, 0, KERNEL_STACK_SIZE);
422 		tempStack = (finalStack + KERNEL_STACK_SIZE / sizeof(uint32)) - 1;
423 		*tempStack = (uint32)&smp_cpu_ready;
424 
425 		// set the trampoline stack up
426 		tempStack = (uint32 *)(trampolineStack + B_PAGE_SIZE - 4);
427 		// final location of the stack
428 		*tempStack = ((uint32)finalStack) + KERNEL_STACK_SIZE - sizeof(uint32);
429 		tempStack--;
430 		// page dir
431 		*tempStack = gKernelArgs.arch_args.phys_pgdir;
432 
433 		// put a gdt descriptor at the bottom of the stack
434 		*((uint16 *)trampolineStack) = 0x18 - 1; // LIMIT
435 		*((uint32 *)(trampolineStack + 2)) = trampolineStack + 8;
436 
437 		// put the gdt at the bottom
438 		memcpy(&((uint32 *)trampolineStack)[2], (void *)gKernelArgs.arch_args.vir_gdt, 6*4);
439 
440 		/* clear apic errors */
441 		if (gKernelArgs.arch_args.cpu_apic_version[i] & 0xf0) {
442 			apic_write(APIC_ERROR_STATUS, 0);
443 			apic_read(APIC_ERROR_STATUS);
444 		}
445 
446 //dprintf("assert INIT\n");
447 		/* send (aka assert) INIT IPI */
448 		config = (apic_read(APIC_INTR_COMMAND_2) & APIC_INTR_COMMAND_2_MASK)
449 			| (gKernelArgs.arch_args.cpu_apic_id[i] << 24);
450 		apic_write(APIC_INTR_COMMAND_2, config); /* set target pe */
451 		config = (apic_read(APIC_INTR_COMMAND_1) & 0xfff00000)
452 			| APIC_TRIGGER_MODE_LEVEL | APIC_INTR_COMMAND_1_ASSERT | APIC_DELIVERY_MODE_INIT;
453 		apic_write(APIC_INTR_COMMAND_1, config);
454 
455 dprintf("wait for delivery\n");
456 		// wait for pending to end
457 		while ((apic_read(APIC_INTR_COMMAND_1) & APIC_DELIVERY_STATUS) != 0)
458 			;
459 
460 dprintf("deassert INIT\n");
461 		/* deassert INIT */
462 		config = (apic_read(APIC_INTR_COMMAND_2) & APIC_INTR_COMMAND_2_MASK)
463 			| (gKernelArgs.arch_args.cpu_apic_id[i] << 24);
464 		apic_write(APIC_INTR_COMMAND_2, config);
465 		config = (apic_read(APIC_INTR_COMMAND_1) & 0xfff00000)
466 			| APIC_TRIGGER_MODE_LEVEL | APIC_DELIVERY_MODE_INIT;
467 		apic_write(APIC_INTR_COMMAND_1, config);
468 
469 dprintf("wait for delivery\n");
470 		// wait for pending to end
471 		while ((apic_read(APIC_INTR_COMMAND_1) & APIC_DELIVERY_STATUS) != 0)
472 			;
473 
474 		/* wait 10ms */
475 		spin(10000);
476 
477 		/* is this a local apic or an 82489dx ? */
478 		numStartups = (gKernelArgs.arch_args.cpu_apic_version[i] & 0xf0) ? 2 : 0;
479 dprintf("num startups = %ld\n", numStartups);
480 		for (j = 0; j < numStartups; j++) {
481 			/* it's a local apic, so send STARTUP IPIs */
482 dprintf("send STARTUP\n");
483 			apic_write(APIC_ERROR_STATUS, 0);
484 
485 			/* set target pe */
486 			config = (apic_read(APIC_INTR_COMMAND_2) & APIC_INTR_COMMAND_2_MASK)
487 				| (gKernelArgs.arch_args.cpu_apic_id[i] << 24);
488 			apic_write(APIC_INTR_COMMAND_2, config);
489 
490 			/* send the IPI */
491 			config = (apic_read(APIC_INTR_COMMAND_1) & 0xfff0f800)
492 				| APIC_DELIVERY_MODE_STARTUP | (trampolineCode >> 12);
493 			apic_write(APIC_INTR_COMMAND_1, config);
494 
495 			/* wait */
496 			spin(200);
497 
498 dprintf("wait for delivery\n");
499 			while ((apic_read(APIC_INTR_COMMAND_1) & APIC_DELIVERY_STATUS) != 0)
500 				;
501 		}
502 	}
503 
504 	TRACE(("done trampolining\n"));
505 }
506 
507 
508 void
509 smp_add_safemode_menus(Menu *menu)
510 {
511 	MenuItem *item;
512 
513 	if (gKernelArgs.num_cpus < 2)
514 		return;
515 
516 	// ToDo: this should work with dual CPUs with HT as well!
517 	if (gKernelArgs.num_cpus > 2 || !supports_hyper_threading()) {
518 		menu->AddItem(item = new(nothrow) MenuItem("Disable SMP"));
519 		item->SetData(B_SAFEMODE_DISABLE_SMP);
520 		item->SetType(MENU_ITEM_MARKABLE);
521 	}
522 
523 	if (supports_hyper_threading()) {
524 		menu->AddItem(item = new(nothrow) MenuItem("Disable Hyper-Threading"));
525 		item->SetData(B_SAFEMODE_DISABLE_HYPER_THREADING);
526 		item->SetType(MENU_ITEM_MARKABLE);
527 	}
528 }
529 
530 
531 void
532 smp_init(void)
533 {
534 	if (smp_find_mp_config() > 1) {
535 		uint32 i;
536 
537 		TRACE(("smp_boot: had found > 1 cpus\n"));
538 		TRACE(("post config:\n"));
539 		TRACE(("num_cpus = %ld\n", gKernelArgs.num_cpus));
540 		TRACE(("apic_phys = %p\n", (void *)gKernelArgs.arch_args.apic_phys));
541 		TRACE(("ioapic_phys = %p\n", (void *)gKernelArgs.arch_args.ioapic_phys));
542 
543 		// map in the apic & ioapic
544 		gKernelArgs.arch_args.apic = (uint32 *)mmu_map_physical_memory(
545 			gKernelArgs.arch_args.apic_phys, B_PAGE_SIZE, kDefaultPageFlags);
546 		gKernelArgs.arch_args.ioapic = (uint32 *)mmu_map_physical_memory(
547 			gKernelArgs.arch_args.ioapic_phys, B_PAGE_SIZE, kDefaultPageFlags);
548 
549 		TRACE(("apic = %p\n", gKernelArgs.arch_args.apic));
550 		TRACE(("ioapic = %p\n", gKernelArgs.arch_args.ioapic));
551 
552 		// calculate how fast the apic timer is
553 		calculate_apic_timer_conversion_factor();
554 
555 		for (i = 1; i < gKernelArgs.num_cpus; i++) {
556 			// create a final stack the trampoline code will put the ap processor on
557 			gKernelArgs.cpu_kstack[i].start = (addr_t)mmu_allocate(NULL, KERNEL_STACK_SIZE);
558 			gKernelArgs.cpu_kstack[i].size = KERNEL_STACK_SIZE;
559 		}
560 	}
561 }
562 
563 
564