xref: /kvm-unit-tests/lib/arm/setup.c (revision c1cd1a2bed6907efd4699638d11c3407768cc158)
1 /*
2  * Initialize machine setup information and I/O.
3  *
4  * After running setup() unit tests may query how many cpus they have
5  * (nr_cpus), how much memory they have (PHYS_END - PHYS_OFFSET), may
6  * use dynamic memory allocation (malloc, etc.), printf, and exit.
7  * Finally, argc and argv are also ready to be passed to main().
8  *
9  * Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com>
10  *
11  * This work is licensed under the terms of the GNU LGPL, version 2.
12  */
13 #include <libcflat.h>
14 #include <libfdt/libfdt.h>
15 #include <devicetree.h>
16 #include <alloc.h>
17 #include <alloc_phys.h>
18 #include <alloc_page.h>
19 #include <vmalloc.h>
20 #include <auxinfo.h>
21 #include <argv.h>
22 #include <asm/thread_info.h>
23 #include <asm/setup.h>
24 #include <asm/page.h>
25 #include <asm/processor.h>
26 #include <asm/smp.h>
27 #include <asm/timer.h>
28 
29 #include "io.h"
30 
31 #define NR_INITIAL_MEM_REGIONS 16
32 
33 extern unsigned long stacktop;
34 extern unsigned long etext;
35 
36 struct timer_state __timer_state;
37 
38 char *initrd;
39 u32 initrd_size;
40 
41 u64 cpus[NR_CPUS] = { [0 ... NR_CPUS-1] = (u64)~0 };
42 int nr_cpus;
43 
44 static struct mem_region __initial_mem_regions[NR_INITIAL_MEM_REGIONS + 1];
45 struct mem_region *mem_regions = __initial_mem_regions;
46 phys_addr_t __phys_offset, __phys_end;
47 
48 int mpidr_to_cpu(uint64_t mpidr)
49 {
50 	int i;
51 
52 	for (i = 0; i < nr_cpus; ++i)
53 		if (cpus[i] == (mpidr & MPIDR_HWID_BITMASK))
54 			return i;
55 	return -1;
56 }
57 
58 static void cpu_set(int fdtnode __unused, u64 regval, void *info __unused)
59 {
60 	int cpu = nr_cpus++;
61 
62 	assert_msg(cpu < NR_CPUS, "Number cpus exceeds maximum supported (%d).", NR_CPUS);
63 
64 	cpus[cpu] = regval;
65 	set_cpu_present(cpu, true);
66 }
67 
68 static void cpu_init(void)
69 {
70 	int ret;
71 
72 	nr_cpus = 0;
73 	ret = dt_for_each_cpu_node(cpu_set, NULL);
74 	assert(ret == 0);
75 	set_cpu_online(0, true);
76 }
77 
78 unsigned int mem_region_get_flags(phys_addr_t paddr)
79 {
80 	struct mem_region *r;
81 
82 	for (r = mem_regions; r->end; ++r) {
83 		if (paddr >= r->start && paddr < r->end)
84 			return r->flags;
85 	}
86 
87 	return MR_F_UNKNOWN;
88 }
89 
90 static void mem_init(phys_addr_t freemem_start)
91 {
92 	phys_addr_t code_end = (phys_addr_t)(unsigned long)&etext;
93 	struct dt_pbus_reg regs[NR_INITIAL_MEM_REGIONS];
94 	struct mem_region mem = {
95 		.start = (phys_addr_t)-1,
96 	};
97 	struct mem_region *primary = NULL;
98 	phys_addr_t base, top;
99 	int nr_regs, nr_io = 0, i;
100 
101 	/*
102 	 * mach-virt I/O regions:
103 	 *   - The first 1G (arm/arm64)
104 	 *   - 512M at 256G (arm64, arm uses highmem=off)
105 	 *   - 512G at 512G (arm64, arm uses highmem=off)
106 	 */
107 	mem_regions[nr_io++] = (struct mem_region){ 0, (1ul << 30), MR_F_IO };
108 #ifdef __aarch64__
109 	mem_regions[nr_io++] = (struct mem_region){ (1ul << 38), (1ul << 38) | (1ul << 29), MR_F_IO };
110 	mem_regions[nr_io++] = (struct mem_region){ (1ul << 39), (1ul << 40), MR_F_IO };
111 #endif
112 
113 	nr_regs = dt_get_memory_params(regs, NR_INITIAL_MEM_REGIONS - nr_io);
114 	assert(nr_regs > 0);
115 
116 	for (i = 0; i < nr_regs; ++i) {
117 		struct mem_region *r = &mem_regions[nr_io + i];
118 
119 		r->start = regs[i].addr;
120 		r->end = regs[i].addr + regs[i].size;
121 
122 		/*
123 		 * pick the region we're in for our primary region
124 		 */
125 		if (freemem_start >= r->start && freemem_start < r->end) {
126 			r->flags |= MR_F_PRIMARY;
127 			primary = r;
128 		}
129 
130 		/*
131 		 * set the lowest and highest addresses found,
132 		 * ignoring potential gaps
133 		 */
134 		if (r->start < mem.start)
135 			mem.start = r->start;
136 		if (r->end > mem.end)
137 			mem.end = r->end;
138 	}
139 	assert(primary);
140 	assert(!(mem.start & ~PHYS_MASK) && !((mem.end - 1) & ~PHYS_MASK));
141 
142 	__phys_offset = primary->start;	/* PHYS_OFFSET */
143 	__phys_end = primary->end;	/* PHYS_END */
144 
145 	/* Split the primary region into two regions; code and data */
146 	mem_regions[nr_io + i] = (struct mem_region){
147 		.start = code_end,
148 		.end = primary->end,
149 		.flags = MR_F_PRIMARY,
150 	};
151 	*primary = (struct mem_region){
152 		.start = primary->start,
153 		.end = code_end,
154 		.flags = MR_F_PRIMARY | MR_F_CODE,
155 	};
156 
157 	phys_alloc_init(freemem_start, __phys_end - freemem_start);
158 	phys_alloc_set_minimum_alignment(SMP_CACHE_BYTES);
159 
160 	phys_alloc_get_unused(&base, &top);
161 	base = PAGE_ALIGN(base);
162 	top = top & PAGE_MASK;
163 	assert(sizeof(long) == 8 || !(base >> 32));
164 	if (sizeof(long) != 8 && (top >> 32) != 0)
165 		top = ((uint64_t)1 << 32);
166 	page_alloc_init_area(0, base >> PAGE_SHIFT, top >> PAGE_SHIFT);
167 	page_alloc_ops_enable();
168 }
169 
170 static void timer_save_state(void)
171 {
172 	const struct fdt_property *prop;
173 	const void *fdt = dt_fdt();
174 	int node, len;
175 	u32 *data;
176 
177 	node = fdt_node_offset_by_compatible(fdt, -1, "arm,armv8-timer");
178 	assert(node >= 0 || node == -FDT_ERR_NOTFOUND);
179 
180 	if (node == -FDT_ERR_NOTFOUND) {
181 		__timer_state.ptimer.irq = -1;
182 		__timer_state.vtimer.irq = -1;
183 		return;
184 	}
185 
186 	/*
187 	 * From Linux devicetree timer binding documentation
188 	 *
189 	 * interrupts <type irq flags>:
190 	 *	secure timer irq
191 	 *	non-secure timer irq		(ptimer)
192 	 *	virtual timer irq		(vtimer)
193 	 *	hypervisor timer irq
194 	 */
195 	prop = fdt_get_property(fdt, node, "interrupts", &len);
196 	assert(prop && len == (4 * 3 * sizeof(u32)));
197 
198 	data = (u32 *)prop->data;
199 	assert(fdt32_to_cpu(data[3]) == 1 /* PPI */);
200 	__timer_state.ptimer.irq = fdt32_to_cpu(data[4]);
201 	__timer_state.ptimer.irq_flags = fdt32_to_cpu(data[5]);
202 	assert(fdt32_to_cpu(data[6]) == 1 /* PPI */);
203 	__timer_state.vtimer.irq = fdt32_to_cpu(data[7]);
204 	__timer_state.vtimer.irq_flags = fdt32_to_cpu(data[8]);
205 }
206 
207 void setup(const void *fdt)
208 {
209 	void *freemem = &stacktop;
210 	const char *bootargs, *tmp;
211 	u32 fdt_size;
212 	int ret;
213 
214 	/*
215 	 * Before calling mem_init we need to move the fdt and initrd
216 	 * to safe locations. We move them to construct the memory
217 	 * map illustrated below:
218 	 *
219 	 *    +----------------------+   <-- top of physical memory
220 	 *    |                      |
221 	 *    ~                      ~
222 	 *    |                      |
223 	 *    +----------------------+   <-- top of initrd
224 	 *    |                      |
225 	 *    +----------------------+   <-- top of FDT
226 	 *    |                      |
227 	 *    +----------------------+   <-- top of cpu0's stack
228 	 *    |                      |
229 	 *    +----------------------+   <-- top of text/data/bss sections,
230 	 *    |                      |       see arm/flat.lds
231 	 *    |                      |
232 	 *    +----------------------+   <-- load address
233 	 *    |                      |
234 	 *    +----------------------+
235 	 */
236 	fdt_size = fdt_totalsize(fdt);
237 	ret = fdt_move(fdt, freemem, fdt_size);
238 	assert(ret == 0);
239 	ret = dt_init(freemem);
240 	assert(ret == 0);
241 	freemem += fdt_size;
242 
243 	ret = dt_get_initrd(&tmp, &initrd_size);
244 	assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
245 	if (ret == 0) {
246 		initrd = freemem;
247 		memmove(initrd, tmp, initrd_size);
248 		freemem += initrd_size;
249 	}
250 
251 	mem_init(PAGE_ALIGN((unsigned long)freemem));
252 	cpu_init();
253 
254 	/* cpu_init must be called before thread_info_init */
255 	thread_info_init(current_thread_info(), 0);
256 
257 	/* mem_init must be called before io_init */
258 	io_init();
259 
260 	timer_save_state();
261 
262 	ret = dt_get_bootargs(&bootargs);
263 	assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
264 	setup_args_progname(bootargs);
265 
266 	if (initrd) {
267 		/* environ is currently the only file in the initrd */
268 		char *env = malloc(initrd_size);
269 		memcpy(env, initrd, initrd_size);
270 		setup_env(env, initrd_size);
271 	}
272 
273 	if (!(auxinfo.flags & AUXINFO_MMU_OFF))
274 		setup_vm();
275 }
276