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