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