xref: /kvm-unit-tests/lib/arm/setup.c (revision 3c7d322e87043aaad022e1999844c82d7b373aa9)
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 <asm/thread_info.h>
18 #include <asm/setup.h>
19 #include <asm/page.h>
20 #include <asm/mmu.h>
21 #include <asm/smp.h>
22 
23 extern unsigned long stacktop;
24 extern void io_init(void);
25 extern void setup_args_progname(const char *args);
26 extern void setup_env(char *env, int size);
27 
28 char *initrd;
29 u32 initrd_size;
30 
31 u64 cpus[NR_CPUS] = { [0 ... NR_CPUS-1] = (u64)~0 };
32 int nr_cpus;
33 
34 struct mem_region mem_regions[NR_MEM_REGIONS];
35 phys_addr_t __phys_offset, __phys_end;
36 
37 int mpidr_to_cpu(uint64_t mpidr)
38 {
39 	int i;
40 
41 	for (i = 0; i < nr_cpus; ++i)
42 		if (cpus[i] == (mpidr & MPIDR_HWID_BITMASK))
43 			return i;
44 	return -1;
45 }
46 
47 static void cpu_set(int fdtnode __unused, u64 regval, void *info __unused)
48 {
49 	int cpu = nr_cpus++;
50 
51 	if (cpu >= NR_CPUS) {
52 		printf("Number cpus exceeds maximum supported (%d).\n",
53 			NR_CPUS);
54 		assert(0);
55 	}
56 	cpus[cpu] = regval;
57 	set_cpu_present(cpu, true);
58 }
59 
60 static void cpu_init(void)
61 {
62 	int ret;
63 
64 	nr_cpus = 0;
65 	ret = dt_for_each_cpu_node(cpu_set, NULL);
66 	assert(ret == 0);
67 	set_cpu_online(0, true);
68 }
69 
70 static void mem_init(phys_addr_t freemem_start)
71 {
72 	struct dt_pbus_reg regs[NR_MEM_REGIONS];
73 	struct mem_region primary, mem = {
74 		.start = (phys_addr_t)-1,
75 	};
76 	int nr_regs, i;
77 
78 	nr_regs = dt_get_memory_params(regs, NR_MEM_REGIONS);
79 	assert(nr_regs > 0);
80 
81 	primary.end = 0;
82 
83 	for (i = 0; i < nr_regs; ++i) {
84 		mem_regions[i].start = regs[i].addr;
85 		mem_regions[i].end = regs[i].addr + regs[i].size;
86 
87 		/*
88 		 * pick the region we're in for our primary region
89 		 */
90 		if (freemem_start >= mem_regions[i].start
91 				&& freemem_start < mem_regions[i].end) {
92 			mem_regions[i].flags |= MR_F_PRIMARY;
93 			primary = mem_regions[i];
94 		}
95 
96 		/*
97 		 * set the lowest and highest addresses found,
98 		 * ignoring potential gaps
99 		 */
100 		if (mem_regions[i].start < mem.start)
101 			mem.start = mem_regions[i].start;
102 		if (mem_regions[i].end > mem.end)
103 			mem.end = mem_regions[i].end;
104 	}
105 	assert(primary.end != 0);
106 	assert(!(mem.start & ~PHYS_MASK) && !((mem.end - 1) & ~PHYS_MASK));
107 
108 	__phys_offset = mem.start;	/* PHYS_OFFSET */
109 	__phys_end = mem.end;		/* PHYS_END */
110 
111 	phys_alloc_init(freemem_start, primary.end - freemem_start);
112 	phys_alloc_set_minimum_alignment(SMP_CACHE_BYTES);
113 
114 	mmu_enable_idmap();
115 }
116 
117 void setup(const void *fdt)
118 {
119 	void *freemem = &stacktop;
120 	const char *bootargs, *tmp;
121 	u32 fdt_size;
122 	int ret;
123 
124 	/*
125 	 * Before calling mem_init we need to move the fdt and initrd
126 	 * to safe locations. We move them to construct the memory
127 	 * map illustrated below:
128 	 *
129 	 *    +----------------------+   <-- top of physical memory
130 	 *    |                      |
131 	 *    ~                      ~
132 	 *    |                      |
133 	 *    +----------------------+   <-- top of initrd
134 	 *    |                      |
135 	 *    +----------------------+   <-- top of FDT
136 	 *    |                      |
137 	 *    +----------------------+   <-- top of cpu0's stack
138 	 *    |                      |
139 	 *    +----------------------+   <-- top of text/data/bss sections,
140 	 *    |                      |       see arm/flat.lds
141 	 *    |                      |
142 	 *    +----------------------+   <-- load address
143 	 *    |                      |
144 	 *    +----------------------+
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 	ret = dt_get_initrd(&tmp, &initrd_size);
154 	assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
155 	if (ret == 0) {
156 		initrd = freemem;
157 		memmove(initrd, tmp, initrd_size);
158 		freemem += initrd_size;
159 	}
160 
161 	/* call init functions */
162 	cpu_init();
163 
164 	/* cpu_init must be called before thread_info_init */
165 	thread_info_init(current_thread_info(), 0);
166 
167 	/* thread_info_init must be called before mem_init */
168 	mem_init(PAGE_ALIGN((unsigned long)freemem));
169 
170 	/* mem_init must be called before io_init */
171 	io_init();
172 
173 	/* finish setup */
174 	ret = dt_get_bootargs(&bootargs);
175 	assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
176 	setup_args_progname(bootargs);
177 
178 	if (initrd) {
179 		/* environ is currently the only file in the initrd */
180 		char *env = malloc(initrd_size);
181 		memcpy(env, initrd, initrd_size);
182 		setup_env(env, initrd_size);
183 	}
184 }
185