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