xref: /haiku/src/system/boot/platform/bios_ia32/smp.cpp (revision d3d8b26997fac34a84981e6d2b649521de2cc45a)
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 	trampolineCode = 0x9f000;
400 	trampolineStack = 0x9e000;
401 
402 	// copy the trampoline code over
403 	memcpy((char *)trampolineCode, (const void*)&smp_trampoline,
404 		(uint32)&smp_trampoline_end - (uint32)&smp_trampoline);
405 
406 	// boot the cpus
407 	for (i = 1; i < gKernelArgs.num_cpus; i++) {
408 		uint32 *finalStack;
409 		uint32 *tempStack;
410 		uint32 config;
411 		uint32 numStartups;
412 		uint32 j;
413 
414 		// set this stack up
415 		finalStack = (uint32 *)gKernelArgs.cpu_kstack[i].start;
416 		memset(finalStack, 0, KERNEL_STACK_SIZE);
417 		tempStack = (finalStack + KERNEL_STACK_SIZE / sizeof(uint32)) - 1;
418 		*tempStack = (uint32)&smp_cpu_ready;
419 
420 		// set the trampoline stack up
421 		tempStack = (uint32 *)(trampolineStack + B_PAGE_SIZE - 4);
422 		// final location of the stack
423 		*tempStack = ((uint32)finalStack) + KERNEL_STACK_SIZE - sizeof(uint32);
424 		tempStack--;
425 		// page dir
426 		*tempStack = gKernelArgs.arch_args.phys_pgdir;
427 
428 		// put a gdt descriptor at the bottom of the stack
429 		*((uint16 *)trampolineStack) = 0x18 - 1; // LIMIT
430 		*((uint32 *)(trampolineStack + 2)) = trampolineStack + 8;
431 
432 		// put the gdt at the bottom
433 		memcpy(&((uint32 *)trampolineStack)[2], (void *)gKernelArgs.arch_args.vir_gdt, 6*4);
434 
435 		/* clear apic errors */
436 		if (gKernelArgs.arch_args.cpu_apic_version[i] & 0xf0) {
437 			apic_write(APIC_ERROR_STATUS, 0);
438 			apic_read(APIC_ERROR_STATUS);
439 		}
440 
441 //dprintf("assert INIT\n");
442 		/* send (aka assert) INIT IPI */
443 		config = (apic_read(APIC_INTR_COMMAND_2) & APIC_INTR_COMMAND_2_MASK)
444 			| (gKernelArgs.arch_args.cpu_apic_id[i] << 24);
445 		apic_write(APIC_INTR_COMMAND_2, config); /* set target pe */
446 		config = (apic_read(APIC_INTR_COMMAND_1) & 0xfff00000)
447 			| APIC_TRIGGER_MODE_LEVEL | APIC_INTR_COMMAND_1_ASSERT | APIC_DELIVERY_MODE_INIT;
448 		apic_write(APIC_INTR_COMMAND_1, config);
449 
450 dprintf("wait for delivery\n");
451 		// wait for pending to end
452 		while ((apic_read(APIC_INTR_COMMAND_1) & APIC_DELIVERY_STATUS) != 0)
453 			;
454 
455 dprintf("deassert INIT\n");
456 		/* deassert INIT */
457 		config = (apic_read(APIC_INTR_COMMAND_2) & APIC_INTR_COMMAND_2_MASK)
458 			| (gKernelArgs.arch_args.cpu_apic_id[i] << 24);
459 		apic_write(APIC_INTR_COMMAND_2, config);
460 		config = (apic_read(APIC_INTR_COMMAND_1) & 0xfff00000)
461 			| APIC_TRIGGER_MODE_LEVEL | APIC_DELIVERY_MODE_INIT;
462 		apic_write(APIC_INTR_COMMAND_1, config);
463 
464 dprintf("wait for delivery\n");
465 		// wait for pending to end
466 		while ((apic_read(APIC_INTR_COMMAND_1) & APIC_DELIVERY_STATUS) != 0)
467 			;
468 
469 		/* wait 10ms */
470 		spin(10000);
471 
472 		/* is this a local apic or an 82489dx ? */
473 		numStartups = (gKernelArgs.arch_args.cpu_apic_version[i] & 0xf0) ? 2 : 0;
474 dprintf("num startups = %ld\n", numStartups);
475 		for (j = 0; j < numStartups; j++) {
476 			/* it's a local apic, so send STARTUP IPIs */
477 dprintf("send STARTUP\n");
478 			apic_write(APIC_ERROR_STATUS, 0);
479 
480 			/* set target pe */
481 			config = (apic_read(APIC_INTR_COMMAND_2) & APIC_INTR_COMMAND_2_MASK)
482 				| (gKernelArgs.arch_args.cpu_apic_id[i] << 24);
483 			apic_write(APIC_INTR_COMMAND_2, config);
484 
485 			/* send the IPI */
486 			config = (apic_read(APIC_INTR_COMMAND_1) & 0xfff0f800)
487 				| APIC_DELIVERY_MODE_STARTUP | (trampolineCode >> 12);
488 			apic_write(APIC_INTR_COMMAND_1, config);
489 
490 			/* wait */
491 			spin(200);
492 
493 dprintf("wait for delivery\n");
494 			while ((apic_read(APIC_INTR_COMMAND_1) & APIC_DELIVERY_STATUS) != 0)
495 				;
496 		}
497 	}
498 
499 	TRACE(("done trampolining\n"));
500 }
501 
502 
503 void
504 smp_add_safemode_menus(Menu *menu)
505 {
506 	MenuItem *item;
507 
508 	if (gKernelArgs.num_cpus < 2)
509 		return;
510 
511 	// ToDo: this should work with dual CPUs with HT as well!
512 	if (gKernelArgs.num_cpus > 2 || !supports_hyper_threading()) {
513 		menu->AddItem(item = new MenuItem("Disable SMP"));
514 		item->SetData(B_SAFEMODE_DISABLE_SMP);
515 		item->SetType(MENU_ITEM_MARKABLE);
516 	}
517 
518 	if (supports_hyper_threading()) {
519 		menu->AddItem(item = new MenuItem("Disable Hyper-Threading"));
520 		item->SetData(B_SAFEMODE_DISABLE_HYPER_THREADING);
521 		item->SetType(MENU_ITEM_MARKABLE);
522 	}
523 }
524 
525 
526 void
527 smp_init(void)
528 {
529 	if (smp_find_mp_config() > 1) {
530 		uint32 i;
531 
532 		TRACE(("smp_boot: had found > 1 cpus\n"));
533 		TRACE(("post config:\n"));
534 		TRACE(("num_cpus = %ld\n", gKernelArgs.num_cpus));
535 		TRACE(("apic_phys = %p\n", (void *)gKernelArgs.arch_args.apic_phys));
536 		TRACE(("ioapic_phys = %p\n", (void *)gKernelArgs.arch_args.ioapic_phys));
537 
538 		// map in the apic & ioapic
539 		gKernelArgs.arch_args.apic = (uint32 *)mmu_map_physical_memory(
540 			gKernelArgs.arch_args.apic_phys, B_PAGE_SIZE, kDefaultPageFlags);
541 		gKernelArgs.arch_args.ioapic = (uint32 *)mmu_map_physical_memory(
542 			gKernelArgs.arch_args.ioapic_phys, B_PAGE_SIZE, kDefaultPageFlags);
543 
544 		TRACE(("apic = %p\n", gKernelArgs.arch_args.apic));
545 		TRACE(("ioapic = %p\n", gKernelArgs.arch_args.ioapic));
546 
547 		// calculate how fast the apic timer is
548 		calculate_apic_timer_conversion_factor();
549 
550 		for (i = 1; i < gKernelArgs.num_cpus; i++) {
551 			// create a final stack the trampoline code will put the ap processor on
552 			gKernelArgs.cpu_kstack[i].start = (addr_t)mmu_allocate(NULL, KERNEL_STACK_SIZE);
553 			gKernelArgs.cpu_kstack[i].size = KERNEL_STACK_SIZE;
554 		}
555 	}
556 }
557 
558 
559