xref: /haiku/src/system/boot/platform/efi/arch/riscv64/arch_smp.cpp (revision e673363df19a9479e524b26b518b576bec44c915)
1 /*
2  * Copyright 2019-2020, Haiku, Inc. All rights reserved.
3  * Released under the terms of the MIT License.
4 */
5 
6 
7 #include "arch_smp.h"
8 
9 #include <algorithm>
10 #include <string.h>
11 
12 #include <KernelExport.h>
13 
14 #include <kernel.h>
15 #include <safemode.h>
16 #include <boot/platform.h>
17 #include <boot/stage2.h>
18 #include <boot/menu.h>
19 #include <platform/sbi/sbi_syscalls.h>
20 
21 #include "mmu.h"
22 
23 
24 //#define TRACE_SMP
25 #ifdef TRACE_SMP
26 #	define TRACE(x) dprintf x
27 #else
28 #	define TRACE(x) ;
29 #endif
30 
31 
32 typedef status_t (*KernelEntry) (kernel_args *bootKernelArgs, int currentCPU);
33 
34 
35 struct CpuEntryInfo {
36 	uint64 satp;		//  0
37 	uint64 stackBase;	//  8
38 	uint64 stackSize;	// 16
39 	KernelEntry kernelEntry;// 24
40 };
41 
42 
43 static platform_cpu_info sCpus[SMP_MAX_CPUS];
44 uint32 sCpuCount = 0;
45 
46 
47 static void
48 arch_cpu_dump_hart_status(uint64 status)
49 {
50 	switch (status) {
51 		case SBI_HART_STATE_STARTED:
52 			dprintf("started");
53 			break;
54 		case SBI_HART_STATE_STOPPED:
55 			dprintf("stopped");
56 			break;
57 		case SBI_HART_STATE_START_PENDING:
58 			dprintf("startPending");
59 			break;
60 		case SBI_HART_STATE_STOP_PENDING:
61 			dprintf("stopPending");
62 			break;
63 		case SBI_HART_STATE_SUSPENDED:
64 			dprintf("suspended");
65 			break;
66 		case SBI_HART_STATE_SUSPEND_PENDING:
67 			dprintf("suspendPending");
68 			break;
69 		case SBI_HART_STATE_RESUME_PENDING:
70 			dprintf("resumePending");
71 			break;
72 		default:
73 			dprintf("?(%" B_PRIu64 ")", status);
74 	}
75 }
76 
77 
78 static void
79 arch_cpu_dump_hart()
80 {
81 	dprintf("  hart status:\n");
82 	for (uint32 i = 0; i < sCpuCount; i++) {
83 		dprintf("    hart %" B_PRIu32 ": ", i);
84 		sbiret res = sbi_hart_get_status(sCpus[i].id);
85 		if (res.error < 0)
86 			dprintf("error: %" B_PRIu64 , res.error);
87 		else {
88 			arch_cpu_dump_hart_status(res.value);
89 		}
90 		dprintf("\n");
91 	}
92 }
93 
94 
95 static void __attribute__((naked))
96 arch_cpu_entry(int hartId, CpuEntryInfo* info)
97 {
98 	// enable MMU
99 	asm("ld t0, 0(a1)");   // CpuEntryInfo::satp
100 	asm("csrw satp, t0");
101 	asm("sfence.vma");
102 
103 	// setup stack
104 	asm("ld sp, 8(a1)");   // CpuEntryInfo::stackBase
105 	asm("ld t0, 16(a1)");  // CpuEntryInfo::stackSize
106 	asm("add sp, sp, t0");
107 	asm("li fp, 0");
108 
109 	asm("tail arch_cpu_entry2");
110 }
111 
112 
113 extern "C" void
114 arch_cpu_entry2(int hartId, CpuEntryInfo* info)
115 {
116 	dprintf("%s(%d)\n", __func__, hartId);
117 
118 	uint32 cpu = 0;
119 	while (cpu < sCpuCount && !(sCpus[cpu].id == (uint32)hartId))
120 		cpu++;
121 
122 	if (!(cpu < sCpuCount))
123 		panic("CPU for hart id %d not found\n", hartId);
124 
125 	info->kernelEntry(&gKernelArgs, cpu);
126 	for (;;) {}
127 }
128 
129 
130 void
131 arch_smp_register_cpu(platform_cpu_info** cpu)
132 {
133 	dprintf("arch_smp_register_cpu()\n");
134 	uint32 newCount = sCpuCount + 1;
135 	if (newCount > SMP_MAX_CPUS) {
136 		*cpu = NULL;
137 		return;
138 	}
139 	*cpu = &sCpus[sCpuCount];
140 	sCpuCount = newCount;
141 }
142 
143 
144 platform_cpu_info*
145 arch_smp_find_cpu(uint32 phandle)
146 {
147 	for (uint32 i = 0; i < sCpuCount; i++) {
148 		if (sCpus[i].phandle == phandle)
149 			return &sCpus[i];
150 	}
151 	return NULL;
152 }
153 
154 
155 int
156 arch_smp_get_current_cpu(void)
157 {
158 	return Mhartid();
159 }
160 
161 
162 void
163 arch_smp_init_other_cpus(void)
164 {
165 	gKernelArgs.num_cpus = sCpuCount;
166 
167 	// make boot CPU first as expected by kernel
168 	for (uint32 i = 1; i < sCpuCount; i++) {
169 		if (sCpus[i].id == gBootHart)
170 			std::swap(sCpus[i], sCpus[0]);
171 	}
172 
173 	for (uint32 i = 0; i < sCpuCount; i++) {
174 		gKernelArgs.arch_args.hartIds[i] = sCpus[i].id;
175 		gKernelArgs.arch_args.plicContexts[i] = sCpus[i].plicContext;
176 	}
177 
178 	if (get_safemode_boolean(B_SAFEMODE_DISABLE_SMP, false)) {
179 		// SMP has been disabled!
180 		TRACE(("smp disabled per safemode setting\n"));
181 		gKernelArgs.num_cpus = 1;
182 	}
183 
184 	if (gKernelArgs.num_cpus < 2)
185 		return;
186 
187 	for (uint32 i = 1; i < gKernelArgs.num_cpus; i++) {
188 		// create a final stack the trampoline code will put the ap processor on
189 		void * stack = NULL;
190 		const size_t size = KERNEL_STACK_SIZE
191 			+ KERNEL_STACK_GUARD_PAGES * B_PAGE_SIZE;
192 		if (platform_allocate_region(&stack, size, 0, false) != B_OK) {
193 			panic("Unable to allocate AP stack");
194 		}
195 		memset(stack, 0, size);
196 		gKernelArgs.cpu_kstack[i].start = fix_address((uint64_t)stack);
197 		gKernelArgs.cpu_kstack[i].size = size;
198 	}
199 }
200 
201 
202 void
203 arch_smp_boot_other_cpus(uint64 satp, uint64 kernel_entry)
204 {
205 	dprintf("arch_smp_boot_other_cpus(%p, %p)\n", (void*)satp, (void*)kernel_entry);
206 
207 	arch_cpu_dump_hart();
208 	for (uint32 i = 0; i < sCpuCount; i++) {
209 		if (sCpus[i].id != gBootHart) {
210 			sbiret res;
211 			dprintf("  starting CPU %" B_PRIu32 "\n", sCpus[i].id);
212 
213 			dprintf("  stack: %#" B_PRIx64 " - %#" B_PRIx64 "\n",
214 				gKernelArgs.cpu_kstack[i].start, gKernelArgs.cpu_kstack[i].start
215 				+ gKernelArgs.cpu_kstack[i].size - 1);
216 
217 			CpuEntryInfo* info = new(std::nothrow) CpuEntryInfo{
218 				.satp = satp,
219 				.stackBase = gKernelArgs.cpu_kstack[i].start,
220 				.stackSize = gKernelArgs.cpu_kstack[i].size,
221 				.kernelEntry = (KernelEntry)kernel_entry
222 			};
223 			res = sbi_hart_start(sCpus[i].id, (addr_t)&arch_cpu_entry, (addr_t)info);
224 
225 			for (;;) {
226 				res = sbi_hart_get_status(sCpus[i].id);
227 				if (res.error < 0 || res.value == SBI_HART_STATE_STARTED)
228 					break;
229 			}
230 		}
231 	}
232 	arch_cpu_dump_hart();
233 }
234 
235 
236 void
237 arch_smp_add_safemode_menus(Menu *menu)
238 {
239 	MenuItem *item;
240 
241 	if (gKernelArgs.num_cpus < 2)
242 		return;
243 
244 	item = new(nothrow) MenuItem("Disable SMP");
245 	menu->AddItem(item);
246 	item->SetData(B_SAFEMODE_DISABLE_SMP);
247 	item->SetType(MENU_ITEM_MARKABLE);
248 	item->SetHelpText("Disables all but one CPU core.");
249 }
250 
251 
252 void
253 arch_smp_init(void)
254 {
255 }
256