xref: /kvm-unit-tests/lib/riscv/setup.c (revision 12e0faac079872cb66fd71cec6a86354907aee9f)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Initialize machine setup information and I/O.
4  *
5  * Copyright (C) 2023, Ventana Micro Systems Inc., Andrew Jones <ajones@ventanamicro.com>
6  */
7 #include <libcflat.h>
8 #include <alloc.h>
9 #include <alloc_page.h>
10 #include <alloc_phys.h>
11 #include <argv.h>
12 #include <auxinfo.h>
13 #include <cpumask.h>
14 #include <devicetree.h>
15 #include <memregions.h>
16 #include <on-cpus.h>
17 #include <vmalloc.h>
18 #include <asm/csr.h>
19 #include <asm/mmu.h>
20 #include <asm/page.h>
21 #include <asm/processor.h>
22 #include <asm/setup.h>
23 #include <asm/timer.h>
24 
25 #define VA_BASE			((phys_addr_t)3 * SZ_1G)
26 #if __riscv_xlen == 64
27 #define VA_TOP			((phys_addr_t)4 * SZ_1G)
28 #else
29 #define VA_TOP			((phys_addr_t)0)
30 #endif
31 
32 #define MAX_DT_MEM_REGIONS	16
33 #define NR_MEM_REGIONS		(MAX_DT_MEM_REGIONS + 16)
34 
35 extern unsigned long _etext;
36 
37 char *initrd;
38 u32 initrd_size;
39 
40 struct thread_info cpus[NR_CPUS];
41 int nr_cpus;
42 uint64_t timebase_frequency;
43 
44 static struct mem_region riscv_mem_regions[NR_MEM_REGIONS + 1];
45 
46 int hartid_to_cpu(unsigned long hartid)
47 {
48 	int cpu;
49 
50 	for_each_present_cpu(cpu)
51 		if (cpus[cpu].hartid == hartid)
52 			return cpu;
53 	return -1;
54 }
55 
56 static void cpu_set_fdt(int fdtnode __unused, u64 regval, void *info __unused)
57 {
58 	int cpu = nr_cpus++;
59 
60 	assert_msg(cpu < NR_CPUS, "Number cpus exceeds maximum supported (%d).", NR_CPUS);
61 
62 	cpus[cpu].cpu = cpu;
63 	cpus[cpu].hartid = regval;
64 	set_cpu_present(cpu, true);
65 }
66 
67 static void cpu_init_acpi(void)
68 {
69 	assert_msg(false, "ACPI not available");
70 }
71 
72 static void cpu_init(void)
73 {
74 	int ret;
75 
76 	nr_cpus = 0;
77 	if (dt_available()) {
78 		ret = dt_for_each_cpu_node(cpu_set_fdt, NULL);
79 		assert(ret == 0);
80 	} else {
81 		cpu_init_acpi();
82 	}
83 
84 	set_cpu_online(hartid_to_cpu(csr_read(CSR_SSCRATCH)), true);
85 	cpu0_calls_idle = true;
86 }
87 
88 static void mem_allocator_init(struct mem_region *freemem, phys_addr_t freemem_start)
89 {
90 	phys_addr_t freemem_end = freemem->end;
91 	phys_addr_t base, top;
92 
93 	freemem_start = PAGE_ALIGN(freemem_start);
94 	freemem_end &= PAGE_MASK;
95 
96 	/*
97 	 * The assert below is mostly checking that the free memory doesn't
98 	 * start in the 3G-4G range, which is reserved for virtual addresses,
99 	 * but it also confirms that there is some free memory (the amount
100 	 * is arbitrarily selected, but should be sufficient for a unit test)
101 	 *
102 	 * TODO: Allow the VA range to shrink and move.
103 	 */
104 	if (freemem_end > VA_BASE) {
105 		struct mem_region *curr, *rest;
106 		freemem_end = VA_BASE;
107 		memregions_split(VA_BASE, &curr, &rest);
108 		assert(curr == freemem);
109 		if (rest)
110 			rest->flags = MR_F_UNUSED;
111 	}
112 	assert(freemem_end - freemem_start >= SZ_1M * 16);
113 
114 	init_alloc_vpage(__va(VA_TOP));
115 
116 	/*
117 	 * TODO: Remove the need for this phys allocator dance, since, as we
118 	 * can see with the assert, we could have gone straight to the page
119 	 * allocator.
120 	 */
121 	phys_alloc_init(freemem_start, freemem_end - freemem_start);
122 	phys_alloc_set_minimum_alignment(PAGE_SIZE);
123 	phys_alloc_get_unused(&base, &top);
124 	assert(base == freemem_start && top == freemem_end);
125 
126 	page_alloc_init_area(0, freemem_start >> PAGE_SHIFT, freemem_end >> PAGE_SHIFT);
127 	page_alloc_ops_enable();
128 }
129 
130 static void mem_init(phys_addr_t freemem_start)
131 {
132 	struct mem_region *freemem, *code, *data;
133 
134 	memregions_init(riscv_mem_regions, NR_MEM_REGIONS);
135 	memregions_add_dt_regions(MAX_DT_MEM_REGIONS);
136 
137 	/* Split the region with the code into two regions; code and data */
138 	memregions_split((unsigned long)&_etext, &code, &data);
139 	assert(code);
140 	code->flags |= MR_F_CODE;
141 
142 	freemem = memregions_find(freemem_start);
143 	assert(freemem && !(freemem->flags & (MR_F_IO | MR_F_CODE)));
144 
145 	mem_allocator_init(freemem, freemem_start);
146 }
147 
148 static void freemem_push_fdt(void **freemem, const void *fdt)
149 {
150 	u32 fdt_size;
151 	int ret;
152 
153 	fdt_size = fdt_totalsize(fdt);
154 	ret = fdt_move(fdt, *freemem, fdt_size);
155 	assert(ret == 0);
156 	ret = dt_init(*freemem);
157 	assert(ret == 0);
158 	*freemem += fdt_size;
159 }
160 
161 static void freemem_push_dt_initrd(void **freemem)
162 {
163 	const char *tmp;
164 	int ret;
165 
166 	ret = dt_get_initrd(&tmp, &initrd_size);
167 	assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
168 	if (ret == 0) {
169 		initrd = *freemem;
170 		memmove(initrd, tmp, initrd_size);
171 		*freemem += initrd_size;
172 	}
173 }
174 
175 static void initrd_setup(void)
176 {
177 	char *env;
178 
179 	if (!initrd)
180 		return;
181 
182 	/* environ is currently the only file in the initrd */
183 	env = malloc(initrd_size);
184 	memcpy(env, initrd, initrd_size);
185 	setup_env(env, initrd_size);
186 }
187 
188 static void banner(void)
189 {
190 	puts("\n");
191 	puts("##########################################################################\n");
192 	puts("#    kvm-unit-tests\n");
193 	puts("##########################################################################\n");
194 	puts("\n");
195 }
196 
197 void setup(const void *fdt, phys_addr_t freemem_start)
198 {
199 	void *freemem;
200 	const char *bootargs;
201 	int ret;
202 
203 	assert(freemem_start < VA_BASE);
204 	freemem = __va(freemem_start);
205 
206 	freemem_push_fdt(&freemem, fdt);
207 	freemem_push_dt_initrd(&freemem);
208 
209 	mem_init(PAGE_ALIGN(__pa(freemem)));
210 	cpu_init();
211 	timer_get_frequency();
212 	thread_info_init();
213 	io_init();
214 
215 	ret = dt_get_bootargs(&bootargs);
216 	assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
217 	setup_args_progname(bootargs);
218 
219 	initrd_setup();
220 
221 	if (!(auxinfo.flags & AUXINFO_MMU_OFF))
222 		setup_vm();
223 
224 	banner();
225 }
226 
227 #ifdef CONFIG_EFI
228 #include <efi.h>
229 
230 extern unsigned long exception_vectors;
231 extern unsigned long boot_hartid;
232 
233 static efi_status_t efi_mem_init(efi_bootinfo_t *efi_bootinfo)
234 {
235 	struct mem_region *freemem_mr = NULL, *code, *data;
236 	void *freemem;
237 
238 	memregions_init(riscv_mem_regions, NR_MEM_REGIONS);
239 
240 	memregions_efi_init(&efi_bootinfo->mem_map, &freemem_mr);
241 	if (!freemem_mr)
242 		return EFI_OUT_OF_RESOURCES;
243 
244 	memregions_split((unsigned long)&_etext, &code, &data);
245 	assert(code && (code->flags & MR_F_CODE));
246 	if (data)
247 		data->flags &= ~MR_F_CODE;
248 
249 	for (struct mem_region *m = mem_regions; m->end; ++m)
250 		assert(m == code || !(m->flags & MR_F_CODE));
251 
252 	freemem = (void *)PAGE_ALIGN(freemem_mr->start);
253 
254 	if (efi_bootinfo->fdt)
255 		freemem_push_fdt(&freemem, efi_bootinfo->fdt);
256 
257 	mmu_disable();
258 	mem_allocator_init(freemem_mr, (unsigned long)freemem);
259 
260 	return EFI_SUCCESS;
261 }
262 
263 efi_status_t setup_efi(efi_bootinfo_t *efi_bootinfo)
264 {
265 	efi_status_t status;
266 
267 	csr_write(CSR_STVEC, (unsigned long)&exception_vectors);
268 	csr_write(CSR_SSCRATCH, boot_hartid);
269 
270 	status = efi_mem_init(efi_bootinfo);
271 	if (status != EFI_SUCCESS) {
272 		printf("Failed to initialize memory\n");
273 		return status;
274 	}
275 
276 	cpu_init();
277 	timer_get_frequency();
278 	thread_info_init();
279 	io_init();
280 	initrd_setup();
281 
282 	if (!(auxinfo.flags & AUXINFO_MMU_OFF))
283 		setup_vm();
284 
285 	banner();
286 
287 	return EFI_SUCCESS;
288 }
289 #endif /* CONFIG_EFI */
290