1/*
2 * Copyright 2021-2022 Haiku, Inc. All rights reserved.
3 * Released under the terms of the MIT License.
4 *
5 * Copyright 2008, Dustin Howett, dustin.howett@gmail.com. All rights reserved.
6 * Copyright 2004-2010, Axel D��rfler, axeld@pinc-software.de.
7 * Distributed under the terms of the MIT License.
8 *
9 * Copyright 2001, Travis Geiselbrecht. All rights reserved.
10 * Distributed under the terms of the NewOS License.
11 */
12
13
14#include "arch_smp.h"
15
16#include <kernel.h>
17#include <safemode.h>
18#include <boot/platform.h>
19#include <boot/stage2.h>
20#include <boot/menu.h>
21#include <arch/x86/apic.h>
22#include <arch/x86/arch_cpu.h>
23#include <arch/x86/arch_system_info.h>
24
25#include "mmu.h"
26#include "acpi.h"
27
28
29#define NO_SMP 0
30
31//#define TRACE_SMP
32#ifdef TRACE_SMP
33#	define TRACE(x...) dprintf(x)
34#else
35#	define TRACE(x...) ;
36#endif
37
38
39extern "C" void execute_n_instructions(int count);
40
41void copy_trampoline_code(uint64 trampolineCode, uint64 trampolineStack);
42void prepare_trampoline_args(uint64 trampolineCode, uint64 trampolineStack,
43	uint32 pagedir, uint64 kernelEntry, addr_t virtKernelArgs,
44	uint32 currentCpu);
45uint32 get_sentinel(uint64 trampolineStack);
46
47
48static uint32
49apic_read(uint32 offset)
50{
51	return *(volatile uint32 *)((addr_t)gKernelArgs.arch_args.apic_phys + offset);
52}
53
54
55static void
56apic_write(uint32 offset, uint32 data)
57{
58	*(volatile uint32 *)((addr_t)gKernelArgs.arch_args.apic_phys + offset) = data;
59}
60
61
62static status_t
63acpi_do_smp_config(void)
64{
65	TRACE("smp: using ACPI to detect MP configuration\n");
66
67	// reset CPU count
68	gKernelArgs.num_cpus = 0;
69
70	acpi_madt *madt = (acpi_madt *)acpi_find_table(ACPI_MADT_SIGNATURE);
71
72	if (madt == NULL) {
73		TRACE("smp: Failed to find MADT!\n");
74		return B_ERROR;
75	}
76
77	gKernelArgs.arch_args.apic_phys = madt->local_apic_address;
78	TRACE("smp: local apic address is 0x%" B_PRIx32 "\n", madt->local_apic_address);
79
80	acpi_apic *apic = (acpi_apic *)((uint8 *)madt + sizeof(acpi_madt));
81	acpi_apic *end = (acpi_apic *)((uint8 *)madt + madt->header.length);
82	while (apic < end) {
83		switch (apic->type) {
84			case ACPI_MADT_LOCAL_APIC:
85			{
86				if (gKernelArgs.num_cpus == SMP_MAX_CPUS) {
87					TRACE("smp: already reached maximum CPUs (%d)\n",
88						SMP_MAX_CPUS);
89					break;
90				}
91
92				acpi_local_apic *localApic = (acpi_local_apic *)apic;
93				TRACE("smp: found local APIC with id %u\n",
94					localApic->apic_id);
95				if ((localApic->flags & ACPI_LOCAL_APIC_ENABLED) == 0) {
96					TRACE("smp: APIC is disabled and will not be used\n");
97					break;
98				}
99
100				gKernelArgs.arch_args.cpu_apic_id[gKernelArgs.num_cpus]
101					= localApic->apic_id;
102				// TODO: how to find out? putting 0x10 in to indicate a local apic
103				gKernelArgs.arch_args.cpu_apic_version[gKernelArgs.num_cpus]
104					= 0x10;
105				gKernelArgs.num_cpus++;
106				break;
107			}
108
109			case ACPI_MADT_IO_APIC: {
110				acpi_io_apic *ioApic = (acpi_io_apic *)apic;
111				TRACE("smp: found io APIC with id %" B_PRIu32 " and address 0x%" B_PRIx32 "\n",
112					ioApic->io_apic_id, ioApic->io_apic_address);
113				if (gKernelArgs.arch_args.ioapic_phys == 0)
114					gKernelArgs.arch_args.ioapic_phys = ioApic->io_apic_address;
115				break;
116			}
117			default:
118				break;
119		}
120
121		apic = (acpi_apic *)((uint8 *)apic + apic->length);
122	}
123
124	return gKernelArgs.num_cpus > 0 ? B_OK : B_ERROR;
125}
126
127
128static void
129calculate_apic_timer_conversion_factor(void)
130{
131	int64 t1, t2;
132	uint32 config;
133	uint32 count;
134
135	TRACE("calculating apic timer conversion factor\n");
136
137	// setup the timer
138	config = apic_read(APIC_LVT_TIMER);
139	config = (config & APIC_LVT_TIMER_MASK) + APIC_LVT_MASKED;
140		// timer masked, vector 0
141	apic_write(APIC_LVT_TIMER, config);
142
143	config = (apic_read(APIC_TIMER_DIVIDE_CONFIG) & ~0x0000000f);
144	apic_write(APIC_TIMER_DIVIDE_CONFIG, config | APIC_TIMER_DIVIDE_CONFIG_1);
145		// divide clock by one
146
147	t1 = system_time();
148	apic_write(APIC_INITIAL_TIMER_COUNT, 0xffffffff); // start the counter
149
150	execute_n_instructions(128 * 20000);
151
152	count = apic_read(APIC_CURRENT_TIMER_COUNT);
153	t2 = system_time();
154
155	count = 0xffffffff - count;
156
157	gKernelArgs.arch_args.apic_time_cv_factor
158		= (uint32)((1000000.0/(t2 - t1)) * count);
159
160	TRACE("APIC ticks/sec = %" B_PRId32 "\n",
161		gKernelArgs.arch_args.apic_time_cv_factor);
162}
163
164
165//	#pragma mark -
166
167
168int
169arch_smp_get_current_cpu(void)
170{
171	if (gKernelArgs.arch_args.apic == NULL)
172		return 0;
173
174	uint8 apicID = apic_read(APIC_ID) >> 24;
175	for (uint32 i = 0; i < gKernelArgs.num_cpus; i++) {
176		if (gKernelArgs.arch_args.cpu_apic_id[i] == apicID)
177			return i;
178	}
179
180	return 0;
181}
182
183
184void
185arch_smp_init_other_cpus(void)
186{
187	if (get_safemode_boolean(B_SAFEMODE_DISABLE_SMP, false)) {
188		// SMP has been disabled!
189		TRACE("smp disabled per safemode setting\n");
190		gKernelArgs.num_cpus = 1;
191	}
192
193	if (get_safemode_boolean(B_SAFEMODE_DISABLE_APIC, false)) {
194		TRACE("local apic disabled per safemode setting, disabling smp\n");
195		gKernelArgs.arch_args.apic_phys = 0;
196		gKernelArgs.num_cpus = 1;
197	}
198
199	if (gKernelArgs.arch_args.apic_phys == 0)
200		return;
201
202	TRACE("smp: found %" B_PRId32 " cpu%s\n", gKernelArgs.num_cpus,
203		gKernelArgs.num_cpus != 1 ? "s" : "");
204	TRACE("smp: apic_phys = %lx\n", (addr_t)gKernelArgs.arch_args.apic_phys);
205	TRACE("smp: ioapic_phys = %lx\n",
206		(addr_t)gKernelArgs.arch_args.ioapic_phys);
207
208	// map in the apic
209	gKernelArgs.arch_args.apic = (void *)mmu_map_physical_memory(
210		gKernelArgs.arch_args.apic_phys, B_PAGE_SIZE, kDefaultPageFlags);
211
212	TRACE("smp: apic (mapped) = %lx\n", (addr_t)gKernelArgs.arch_args.apic.Pointer());
213
214	// calculate how fast the apic timer is
215	calculate_apic_timer_conversion_factor();
216
217	if (gKernelArgs.num_cpus < 2)
218		return;
219
220	for (uint32 i = 1; i < gKernelArgs.num_cpus; i++) {
221		// create a final stack the trampoline code will put the ap processor on
222		void * stack = NULL;
223		const size_t size = KERNEL_STACK_SIZE + KERNEL_STACK_GUARD_PAGES * B_PAGE_SIZE;
224		if (platform_allocate_region(&stack, size, 0, false) != B_OK) {
225			panic("Unable to allocate AP stack");
226		}
227		memset(stack, 0, size);
228		gKernelArgs.cpu_kstack[i].start = fix_address((uint64_t)stack);
229		gKernelArgs.cpu_kstack[i].size = size;
230	}
231}
232
233
234void
235arch_smp_boot_other_cpus(uint32 pagedir, uint64 kernelEntry, addr_t virtKernelArgs)
236{
237	TRACE("trampolining other cpus\n");
238
239	// allocate a stack and a code area for the smp trampoline
240	// (these have to be < 1M physical, 0xa0000-0xfffff is reserved by the BIOS)
241	uint64 trampolineCode = 0x9000;
242	uint64 trampolineStack = 0x8000;
243
244	// copy the trampoline code over
245	copy_trampoline_code(trampolineCode, trampolineStack);
246
247	// boot the cpus
248	TRACE("we have %" B_PRId32 " CPUs to boot...\n", gKernelArgs.num_cpus - 1);
249	for (uint32 i = 1; i < gKernelArgs.num_cpus; i++) {
250		TRACE("trampolining CPU %" B_PRId32 "\n", i);
251		uint32 config;
252		uint64 numStartups;
253		uint32 j;
254
255		prepare_trampoline_args(trampolineCode, trampolineStack,
256			pagedir, kernelEntry, virtKernelArgs, i);
257
258		/* clear apic errors */
259		if (gKernelArgs.arch_args.cpu_apic_version[i] & 0xf0) {
260			apic_write(APIC_ERROR_STATUS, 0);
261			apic_read(APIC_ERROR_STATUS);
262		}
263
264		/* send (aka assert) INIT IPI */
265		config = (apic_read(APIC_INTR_COMMAND_2) & APIC_INTR_COMMAND_2_MASK)
266			| (gKernelArgs.arch_args.cpu_apic_id[i] << 24);
267		apic_write(APIC_INTR_COMMAND_2, config); /* set target pe */
268		config = (apic_read(APIC_INTR_COMMAND_1) & 0xfff00000)
269			| APIC_TRIGGER_MODE_LEVEL | APIC_INTR_COMMAND_1_ASSERT
270			| APIC_DELIVERY_MODE_INIT;
271		apic_write(APIC_INTR_COMMAND_1, config);
272
273		// wait for pending to end
274		while ((apic_read(APIC_INTR_COMMAND_1) & APIC_DELIVERY_STATUS) != 0)
275			asm volatile ("pause;");
276
277		/* deassert INIT */
278		config = (apic_read(APIC_INTR_COMMAND_2) & APIC_INTR_COMMAND_2_MASK)
279			| (gKernelArgs.arch_args.cpu_apic_id[i] << 24);
280		apic_write(APIC_INTR_COMMAND_2, config);
281		config = (apic_read(APIC_INTR_COMMAND_1) & 0xfff00000)
282			| APIC_TRIGGER_MODE_LEVEL | APIC_DELIVERY_MODE_INIT;
283		apic_write(APIC_INTR_COMMAND_1, config);
284
285		// wait for pending to end
286		while ((apic_read(APIC_INTR_COMMAND_1) & APIC_DELIVERY_STATUS) != 0)
287			asm volatile ("pause;");
288
289		/* wait 10ms */
290		spin(10000);
291		/* is this a local apic or an 82489dx ? */
292		numStartups = (gKernelArgs.arch_args.cpu_apic_version[i] & 0xf0)
293			? 2 : 0;
294		for (j = 0; j < numStartups; j++) {
295			/* it's a local apic, so send STARTUP IPIs */
296			apic_write(APIC_ERROR_STATUS, 0);
297
298			/* set target pe */
299			config = (apic_read(APIC_INTR_COMMAND_2) & APIC_INTR_COMMAND_2_MASK)
300				| (gKernelArgs.arch_args.cpu_apic_id[i] << 24);
301			apic_write(APIC_INTR_COMMAND_2, config);
302
303			/* send the IPI */
304			config = (apic_read(APIC_INTR_COMMAND_1) & 0xfff0f800)
305				| APIC_DELIVERY_MODE_STARTUP | (trampolineCode >> 12);
306			apic_write(APIC_INTR_COMMAND_1, config);
307
308			/* wait */
309			spin(200);
310
311			while ((apic_read(APIC_INTR_COMMAND_1) & APIC_DELIVERY_STATUS) != 0)
312				asm volatile ("pause;");
313		}
314
315		// Wait for the trampoline code to clear the final stack location.
316		// This serves as a notification for us that it has loaded the address
317		// and it is safe for us to overwrite it to trampoline the next CPU.
318		while (get_sentinel(trampolineStack) != 0)
319			spin(1000);
320	}
321
322	TRACE("done trampolining\n");
323}
324
325
326void
327arch_smp_add_safemode_menus(Menu *menu)
328{
329	MenuItem *item;
330
331	if (gKernelArgs.arch_args.ioapic_phys != 0) {
332		menu->AddItem(item = new(nothrow) MenuItem("Disable IO-APIC"));
333		item->SetType(MENU_ITEM_MARKABLE);
334		item->SetData(B_SAFEMODE_DISABLE_IOAPIC);
335		item->SetHelpText("Disables using the IO APIC for interrupt routing, "
336			"forcing the use of the legacy PIC instead.");
337	}
338
339	if (gKernelArgs.arch_args.apic_phys != 0) {
340		menu->AddItem(item = new(nothrow) MenuItem("Disable local APIC"));
341		item->SetType(MENU_ITEM_MARKABLE);
342		item->SetData(B_SAFEMODE_DISABLE_APIC);
343		item->SetHelpText("Disables using the local APIC, also disables SMP.");
344
345		cpuid_info info;
346		if (get_current_cpuid(&info, 1, 0) == B_OK
347				&& (info.regs.ecx & IA32_FEATURE_EXT_X2APIC) != 0) {
348			menu->AddItem(item = new(nothrow) MenuItem("Disable X2APIC"));
349			item->SetType(MENU_ITEM_MARKABLE);
350			item->SetData(B_SAFEMODE_DISABLE_X2APIC);
351			item->SetHelpText("Disables using X2APIC.");
352		}
353
354		if (get_current_cpuid(&info, 7, 0) == B_OK
355				&& ((info.regs.ebx & (IA32_FEATURE_SMEP
356					| IA32_FEATURE_SMAP)) != 0)) {
357			menu->AddItem(item = new(nothrow) MenuItem(
358				"Disable SMEP and SMAP"));
359			item->SetType(MENU_ITEM_MARKABLE);
360			item->SetData(B_SAFEMODE_DISABLE_SMEP_SMAP);
361			item->SetHelpText("Disables using SMEP and SMAP.");
362		}
363	}
364
365	if (gKernelArgs.num_cpus < 2)
366		return;
367
368	item = new(nothrow) MenuItem("Disable SMP");
369	menu->AddItem(item);
370	item->SetData(B_SAFEMODE_DISABLE_SMP);
371	item->SetType(MENU_ITEM_MARKABLE);
372	item->SetHelpText("Disables all but one CPU core.");
373}
374
375
376void
377arch_smp_init(void)
378{
379	cpuid_info info;
380	if (get_current_cpuid(&info, 1, 0) != B_OK)
381		return;
382
383	if ((info.eax_1.features & IA32_FEATURE_APIC) == 0) {
384		// Local APICs aren't present; As they form the basis for all inter CPU
385		// communication and therefore SMP, we don't need to go any further.
386		TRACE("no local APIC present, not attempting SMP init\n");
387		return;
388	}
389
390	// first try to find ACPI tables to get MP configuration as it handles
391	// physical as well as logical MP configurations as in multiple cpus,
392	// multiple cores or hyper threading.
393	if (acpi_do_smp_config() == B_OK) {
394		TRACE("smp init success\n");
395		return;
396	}
397
398	// Everything failed or we are not running an SMP system, reset anything
399	// that might have been set through an incomplete configuration attempt.
400	gKernelArgs.arch_args.apic_phys = 0;
401	gKernelArgs.arch_args.ioapic_phys = 0;
402	gKernelArgs.num_cpus = 1;
403}
404