xref: /kvm-unit-tests/lib/riscv/setup.c (revision f35fe0ee486a61b68826f16a1d7ba7dd435a7881)
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 #ifdef CONFIG_EFI
35 #define NR_MEM_REGIONS		(MAX_DT_MEM_REGIONS + 128)
36 #else
37 #define NR_MEM_REGIONS		(MAX_DT_MEM_REGIONS + 16)
38 #endif
39 
40 extern unsigned long _etext;
41 
42 char *initrd;
43 u32 initrd_size;
44 
45 struct thread_info cpus[NR_CPUS];
46 int nr_cpus;
47 uint64_t timebase_frequency;
48 
49 static struct mem_region riscv_mem_regions[NR_MEM_REGIONS + 1];
50 
cpu_set_fdt(int fdtnode __unused,u64 regval,void * info __unused)51 static void cpu_set_fdt(int fdtnode __unused, u64 regval, void *info __unused)
52 {
53 	int cpu = nr_cpus++;
54 
55 	assert_msg(cpu < NR_CPUS, "Number cpus exceeds maximum supported (%d).", NR_CPUS);
56 
57 	cpus[cpu].cpu = cpu;
58 	cpus[cpu].hartid = regval;
59 
60 	if (!sbi_hart_get_status(cpus[cpu].hartid).error)
61 		set_cpu_present(cpu, true);
62 }
63 
cpu_init_acpi(void)64 static void cpu_init_acpi(void)
65 {
66 	assert_msg(false, "ACPI not available");
67 }
68 
cpu_init(void)69 static void cpu_init(void)
70 {
71 	int ret, me;
72 
73 	nr_cpus = 0;
74 	if (dt_available()) {
75 		ret = dt_for_each_cpu_node(cpu_set_fdt, NULL);
76 		assert(ret == 0);
77 	} else {
78 		cpu_init_acpi();
79 	}
80 
81 	me = hartid_to_cpu(csr_read(CSR_SSCRATCH));
82 	assert(cpu_present(me));
83 	set_cpu_online(me, true);
84 	cpu0_calls_idle = true;
85 }
86 
mem_allocator_init(struct mem_region * freemem,phys_addr_t freemem_start)87 static void mem_allocator_init(struct mem_region *freemem, phys_addr_t freemem_start)
88 {
89 	phys_addr_t freemem_end = freemem->end;
90 	phys_addr_t base, top;
91 
92 	freemem_start = PAGE_ALIGN(freemem_start);
93 	freemem_end &= PHYS_PAGE_MASK;
94 
95 	/*
96 	 * The assert below is mostly checking that the free memory doesn't
97 	 * start in the 3G-4G range, which is reserved for virtual addresses,
98 	 * but it also confirms that there is some free memory (the amount
99 	 * is arbitrarily selected, but should be sufficient for a unit test)
100 	 *
101 	 * TODO: Allow the VA range to shrink and move.
102 	 */
103 	if (freemem_end > VA_BASE) {
104 		struct mem_region *curr, *rest;
105 		freemem_end = VA_BASE;
106 		memregions_split(VA_BASE, &curr, &rest);
107 		assert(curr == freemem);
108 		if (rest)
109 			rest->flags = MR_F_UNUSED;
110 	}
111 	assert(freemem_end - freemem_start >= SZ_1M * 16);
112 
113 	init_alloc_vpage(__va(VA_TOP));
114 
115 	/*
116 	 * TODO: Remove the need for this phys allocator dance, since, as we
117 	 * can see with the assert, we could have gone straight to the page
118 	 * allocator.
119 	 */
120 	phys_alloc_init(freemem_start, freemem_end - freemem_start);
121 	phys_alloc_set_minimum_alignment(PAGE_SIZE);
122 	phys_alloc_get_unused(&base, &top);
123 	assert(base == freemem_start && top == freemem_end);
124 
125 	page_alloc_init_area(0, freemem_start >> PAGE_SHIFT, freemem_end >> PAGE_SHIFT);
126 	page_alloc_ops_enable();
127 }
128 
mem_init(phys_addr_t freemem_start)129 static void mem_init(phys_addr_t freemem_start)
130 {
131 	struct mem_region *freemem, *code, *data;
132 
133 	memregions_init(riscv_mem_regions, NR_MEM_REGIONS);
134 	memregions_add_dt_regions(MAX_DT_MEM_REGIONS);
135 
136 	/* Split the region with the code into two regions; code and data */
137 	memregions_split((unsigned long)&_etext, &code, &data);
138 	assert(code);
139 	code->flags |= MR_F_CODE;
140 
141 	freemem = memregions_find(freemem_start);
142 	assert(freemem && !(freemem->flags & (MR_F_IO | MR_F_CODE)));
143 
144 	mem_allocator_init(freemem, freemem_start);
145 }
146 
freemem_push_fdt(void ** freemem,const void * fdt)147 static void freemem_push_fdt(void **freemem, const void *fdt)
148 {
149 	u32 fdt_size;
150 	int ret;
151 
152 	fdt_size = fdt_totalsize(fdt);
153 	ret = fdt_move(fdt, *freemem, fdt_size);
154 	assert(ret == 0);
155 	ret = dt_init(*freemem);
156 	assert(ret == 0);
157 	*freemem += fdt_size;
158 }
159 
freemem_push_dt_initrd(void ** freemem)160 static void freemem_push_dt_initrd(void **freemem)
161 {
162 	const char *tmp;
163 	int ret;
164 
165 	ret = dt_get_initrd(&tmp, &initrd_size);
166 	assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
167 	if (ret == 0) {
168 		initrd = *freemem;
169 		memmove(initrd, tmp, initrd_size);
170 		*freemem += initrd_size;
171 	}
172 }
173 
initrd_setup(void)174 static void initrd_setup(void)
175 {
176 	char *env;
177 
178 	if (!initrd)
179 		return;
180 
181 	/* environ is currently the only file in the initrd */
182 	env = malloc(initrd_size);
183 	memcpy(env, initrd, initrd_size);
184 	setup_env(env, initrd_size);
185 }
186 
banner(void)187 static void banner(void)
188 {
189 	puts("\n");
190 	puts("##########################################################################\n");
191 	puts("#    kvm-unit-tests\n");
192 	puts("##########################################################################\n");
193 	puts("\n");
194 }
195 
setup(const void * fdt,phys_addr_t freemem_start)196 void setup(const void *fdt, phys_addr_t freemem_start)
197 {
198 	void *freemem;
199 	const char *bootargs;
200 	int ret;
201 
202 	assert(freemem_start < VA_BASE);
203 	freemem = __va(freemem_start);
204 
205 	freemem_push_fdt(&freemem, fdt);
206 	freemem_push_dt_initrd(&freemem);
207 
208 	mem_init(PAGE_ALIGN(__pa(freemem)));
209 	cpu_init();
210 	timer_get_frequency();
211 	thread_info_init();
212 	local_hart_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 
efi_mem_init(efi_bootinfo_t * efi_bootinfo)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 
setup_efi(efi_bootinfo_t * efi_bootinfo)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 	local_hart_init();
280 	io_init();
281 	initrd_setup();
282 
283 	if (!(auxinfo.flags & AUXINFO_MMU_OFF))
284 		setup_vm();
285 
286 	banner();
287 
288 	return EFI_SUCCESS;
289 }
290 #endif /* CONFIG_EFI */
291