xref: /kvm-unit-tests/lib/arm/setup.c (revision 97b5f9553a48a15296462dc4c6170200b0dd70b8)
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 <argv.h>
20 #include <asm/thread_info.h>
21 #include <asm/setup.h>
22 #include <asm/page.h>
23 #include <asm/smp.h>
24 
25 #include "io.h"
26 
27 #define NR_INITIAL_MEM_REGIONS 16
28 
29 extern unsigned long stacktop;
30 
31 char *initrd;
32 u32 initrd_size;
33 
34 u64 cpus[NR_CPUS] = { [0 ... NR_CPUS-1] = (u64)~0 };
35 int nr_cpus;
36 
37 static struct mem_region __initial_mem_regions[NR_INITIAL_MEM_REGIONS + 1];
38 struct mem_region *mem_regions = __initial_mem_regions;
39 phys_addr_t __phys_offset, __phys_end;
40 
41 int mpidr_to_cpu(uint64_t mpidr)
42 {
43 	int i;
44 
45 	for (i = 0; i < nr_cpus; ++i)
46 		if (cpus[i] == (mpidr & MPIDR_HWID_BITMASK))
47 			return i;
48 	return -1;
49 }
50 
51 static void cpu_set(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] = regval;
58 	set_cpu_present(cpu, true);
59 }
60 
61 static void cpu_init(void)
62 {
63 	int ret;
64 
65 	nr_cpus = 0;
66 	ret = dt_for_each_cpu_node(cpu_set, NULL);
67 	assert(ret == 0);
68 	set_cpu_online(0, true);
69 }
70 
71 unsigned int mem_region_get_flags(phys_addr_t paddr)
72 {
73 	struct mem_region *r;
74 
75 	for (r = mem_regions; r->end; ++r) {
76 		if (paddr >= r->start && paddr < r->end)
77 			return r->flags;
78 	}
79 
80 	return MR_F_UNKNOWN;
81 }
82 
83 static void mem_init(phys_addr_t freemem_start)
84 {
85 	struct dt_pbus_reg regs[NR_INITIAL_MEM_REGIONS];
86 	struct mem_region primary, mem = {
87 		.start = (phys_addr_t)-1,
88 	};
89 	phys_addr_t base, top;
90 	int nr_regs, nr_io = 0, i;
91 
92 	/*
93 	 * mach-virt I/O regions:
94 	 *   - The first 1G (arm/arm64)
95 	 *   - 512M at 256G (arm64, arm uses highmem=off)
96 	 *   - 512G at 512G (arm64, arm uses highmem=off)
97 	 */
98 	mem_regions[nr_io++] = (struct mem_region){ 0, (1ul << 30), MR_F_IO };
99 #ifdef __aarch64__
100 	mem_regions[nr_io++] = (struct mem_region){ (1ul << 38), (1ul << 38) | (1ul << 29), MR_F_IO };
101 	mem_regions[nr_io++] = (struct mem_region){ (1ul << 39), (1ul << 40), MR_F_IO };
102 #endif
103 
104 	nr_regs = dt_get_memory_params(regs, NR_INITIAL_MEM_REGIONS - nr_io);
105 	assert(nr_regs > 0);
106 
107 	primary = (struct mem_region){ 0 };
108 
109 	for (i = 0; i < nr_regs; ++i) {
110 		struct mem_region *r = &mem_regions[nr_io + i];
111 
112 		r->start = regs[i].addr;
113 		r->end = regs[i].addr + regs[i].size;
114 
115 		/*
116 		 * pick the region we're in for our primary region
117 		 */
118 		if (freemem_start >= r->start && freemem_start < r->end) {
119 			r->flags |= MR_F_PRIMARY;
120 			primary = *r;
121 		}
122 
123 		/*
124 		 * set the lowest and highest addresses found,
125 		 * ignoring potential gaps
126 		 */
127 		if (r->start < mem.start)
128 			mem.start = r->start;
129 		if (r->end > mem.end)
130 			mem.end = r->end;
131 	}
132 	assert(primary.end != 0);
133 	assert(!(mem.start & ~PHYS_MASK) && !((mem.end - 1) & ~PHYS_MASK));
134 
135 	__phys_offset = primary.start;	/* PHYS_OFFSET */
136 	__phys_end = primary.end;	/* PHYS_END */
137 
138 	phys_alloc_init(freemem_start, primary.end - freemem_start);
139 	phys_alloc_set_minimum_alignment(SMP_CACHE_BYTES);
140 
141 	phys_alloc_get_unused(&base, &top);
142 	base = PAGE_ALIGN(base);
143 	top = top & PAGE_MASK;
144 	assert(sizeof(long) == 8 || !(base >> 32));
145 	if (sizeof(long) != 8 && (top >> 32) != 0)
146 		top = ((uint64_t)1 << 32);
147 	free_pages((void *)(unsigned long)base, top - base);
148 	page_alloc_ops_enable();
149 }
150 
151 void setup(const void *fdt)
152 {
153 	void *freemem = &stacktop;
154 	const char *bootargs, *tmp;
155 	u32 fdt_size;
156 	int ret;
157 
158 	/*
159 	 * Before calling mem_init we need to move the fdt and initrd
160 	 * to safe locations. We move them to construct the memory
161 	 * map illustrated below:
162 	 *
163 	 *    +----------------------+   <-- top of physical memory
164 	 *    |                      |
165 	 *    ~                      ~
166 	 *    |                      |
167 	 *    +----------------------+   <-- top of initrd
168 	 *    |                      |
169 	 *    +----------------------+   <-- top of FDT
170 	 *    |                      |
171 	 *    +----------------------+   <-- top of cpu0's stack
172 	 *    |                      |
173 	 *    +----------------------+   <-- top of text/data/bss sections,
174 	 *    |                      |       see arm/flat.lds
175 	 *    |                      |
176 	 *    +----------------------+   <-- load address
177 	 *    |                      |
178 	 *    +----------------------+
179 	 */
180 	fdt_size = fdt_totalsize(fdt);
181 	ret = fdt_move(fdt, freemem, fdt_size);
182 	assert(ret == 0);
183 	ret = dt_init(freemem);
184 	assert(ret == 0);
185 	freemem += fdt_size;
186 
187 	ret = dt_get_initrd(&tmp, &initrd_size);
188 	assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
189 	if (ret == 0) {
190 		initrd = freemem;
191 		memmove(initrd, tmp, initrd_size);
192 		freemem += initrd_size;
193 	}
194 
195 	/* call init functions */
196 	mem_init(PAGE_ALIGN((unsigned long)freemem));
197 	cpu_init();
198 
199 	/* cpu_init must be called before thread_info_init */
200 	thread_info_init(current_thread_info(), 0);
201 
202 	/* mem_init must be called before io_init */
203 	io_init();
204 
205 	/* finish setup */
206 	ret = dt_get_bootargs(&bootargs);
207 	assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
208 	setup_args_progname(bootargs);
209 
210 	if (initrd) {
211 		/* environ is currently the only file in the initrd */
212 		char *env = malloc(initrd_size);
213 		memcpy(env, initrd, initrd_size);
214 		setup_env(env, initrd_size);
215 	}
216 }
217