xref: /kvm-unit-tests/lib/riscv/setup.c (revision 846737f068d95d5d4652a8bc17332cdfd1e8d74b)
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(phys_addr_t freemem_start, phys_addr_t freemem_end)
89 {
90 	phys_addr_t base, top;
91 
92 	freemem_start = PAGE_ALIGN(freemem_start);
93 	freemem_end &= 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 		freemem_end = VA_BASE;
105 	assert(freemem_end - freemem_start >= SZ_1M * 16);
106 
107 	init_alloc_vpage(__va(VA_TOP));
108 
109 	/*
110 	 * TODO: Remove the need for this phys allocator dance, since, as we
111 	 * can see with the assert, we could have gone straight to the page
112 	 * allocator.
113 	 */
114 	phys_alloc_init(freemem_start, freemem_end - freemem_start);
115 	phys_alloc_set_minimum_alignment(PAGE_SIZE);
116 	phys_alloc_get_unused(&base, &top);
117 	assert(base == freemem_start && top == freemem_end);
118 
119 	page_alloc_init_area(0, freemem_start >> PAGE_SHIFT, freemem_end >> PAGE_SHIFT);
120 	page_alloc_ops_enable();
121 }
122 
123 static void mem_init(phys_addr_t freemem_start)
124 {
125 	struct mem_region *freemem, *code, *data;
126 
127 	memregions_init(riscv_mem_regions, NR_MEM_REGIONS);
128 	memregions_add_dt_regions(MAX_DT_MEM_REGIONS);
129 
130 	/* Split the region with the code into two regions; code and data */
131 	memregions_split((unsigned long)&_etext, &code, &data);
132 	assert(code);
133 	code->flags |= MR_F_CODE;
134 
135 	freemem = memregions_find(freemem_start);
136 	assert(freemem && !(freemem->flags & (MR_F_IO | MR_F_CODE)));
137 
138 	mem_allocator_init(freemem_start, freemem->end);
139 }
140 
141 static void freemem_push_fdt(void **freemem, const void *fdt)
142 {
143 	u32 fdt_size;
144 	int ret;
145 
146 	fdt_size = fdt_totalsize(fdt);
147 	ret = fdt_move(fdt, *freemem, fdt_size);
148 	assert(ret == 0);
149 	ret = dt_init(*freemem);
150 	assert(ret == 0);
151 	*freemem += fdt_size;
152 }
153 
154 static void freemem_push_dt_initrd(void **freemem)
155 {
156 	const char *tmp;
157 	int ret;
158 
159 	ret = dt_get_initrd(&tmp, &initrd_size);
160 	assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
161 	if (ret == 0) {
162 		initrd = *freemem;
163 		memmove(initrd, tmp, initrd_size);
164 		*freemem += initrd_size;
165 	}
166 }
167 
168 static void initrd_setup(void)
169 {
170 	char *env;
171 
172 	if (!initrd)
173 		return;
174 
175 	/* environ is currently the only file in the initrd */
176 	env = malloc(initrd_size);
177 	memcpy(env, initrd, initrd_size);
178 	setup_env(env, initrd_size);
179 }
180 
181 static void banner(void)
182 {
183 	puts("\n");
184 	puts("##########################################################################\n");
185 	puts("#    kvm-unit-tests\n");
186 	puts("##########################################################################\n");
187 	puts("\n");
188 }
189 
190 void setup(const void *fdt, phys_addr_t freemem_start)
191 {
192 	void *freemem;
193 	const char *bootargs;
194 	int ret;
195 
196 	assert(sizeof(long) == 8 || freemem_start < VA_BASE);
197 	freemem = __va(freemem_start);
198 
199 	freemem_push_fdt(&freemem, fdt);
200 	freemem_push_dt_initrd(&freemem);
201 
202 	mem_init(PAGE_ALIGN(__pa(freemem)));
203 	cpu_init();
204 	timer_get_frequency();
205 	thread_info_init();
206 	io_init();
207 
208 	ret = dt_get_bootargs(&bootargs);
209 	assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
210 	setup_args_progname(bootargs);
211 
212 	initrd_setup();
213 
214 	if (!(auxinfo.flags & AUXINFO_MMU_OFF))
215 		setup_vm();
216 
217 	banner();
218 }
219 
220 #ifdef CONFIG_EFI
221 #include <efi.h>
222 
223 extern unsigned long exception_vectors;
224 extern unsigned long boot_hartid;
225 
226 static efi_status_t efi_mem_init(efi_bootinfo_t *efi_bootinfo)
227 {
228 	struct mem_region *freemem_mr = NULL, *code, *data;
229 	void *freemem;
230 
231 	memregions_init(riscv_mem_regions, NR_MEM_REGIONS);
232 
233 	memregions_efi_init(&efi_bootinfo->mem_map, &freemem_mr);
234 	if (!freemem_mr)
235 		return EFI_OUT_OF_RESOURCES;
236 
237 	memregions_split((unsigned long)&_etext, &code, &data);
238 	assert(code && (code->flags & MR_F_CODE));
239 	if (data)
240 		data->flags &= ~MR_F_CODE;
241 
242 	for (struct mem_region *m = mem_regions; m->end; ++m)
243 		assert(m == code || !(m->flags & MR_F_CODE));
244 
245 	freemem = (void *)PAGE_ALIGN(freemem_mr->start);
246 
247 	if (efi_bootinfo->fdt)
248 		freemem_push_fdt(&freemem, efi_bootinfo->fdt);
249 
250 	mmu_disable();
251 	mem_allocator_init((unsigned long)freemem, freemem_mr->end);
252 
253 	return EFI_SUCCESS;
254 }
255 
256 efi_status_t setup_efi(efi_bootinfo_t *efi_bootinfo)
257 {
258 	efi_status_t status;
259 
260 	csr_write(CSR_STVEC, (unsigned long)&exception_vectors);
261 	csr_write(CSR_SSCRATCH, boot_hartid);
262 
263 	status = efi_mem_init(efi_bootinfo);
264 	if (status != EFI_SUCCESS) {
265 		printf("Failed to initialize memory\n");
266 		return status;
267 	}
268 
269 	cpu_init();
270 	timer_get_frequency();
271 	thread_info_init();
272 	io_init();
273 	initrd_setup();
274 
275 	if (!(auxinfo.flags & AUXINFO_MMU_OFF))
276 		setup_vm();
277 
278 	banner();
279 
280 	return EFI_SUCCESS;
281 }
282 #endif /* CONFIG_EFI */
283