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