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