xref: /kvm-unit-tests/lib/powerpc/setup.c (revision dfc1fec2fbde04ad607e1aed560cf7059350c70f)
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 (PHYSICAL_END - PHYSICAL_START),
6  * may 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) 2016, 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 <argv.h>
19 #include <asm/setup.h>
20 #include <asm/page.h>
21 #include <asm/ptrace.h>
22 #include <asm/hcall.h>
23 #include "io.h"
24 
25 extern unsigned long stacktop;
26 
27 char *initrd;
28 u32 initrd_size;
29 
30 u32 cpus[NR_CPUS] = { [0 ... NR_CPUS-1] = (~0U) };
31 int nr_cpus;
32 uint64_t tb_hz;
33 
34 struct mem_region mem_regions[NR_MEM_REGIONS];
35 phys_addr_t __physical_start, __physical_end;
36 unsigned __icache_bytes, __dcache_bytes;
37 
38 struct cpu_set_params {
39 	unsigned icache_bytes;
40 	unsigned dcache_bytes;
41 	uint64_t tb_hz;
42 };
43 
44 #define EXCEPTION_STACK_SIZE	(32*1024) /* 32kB */
45 
46 static char exception_stack[NR_CPUS][EXCEPTION_STACK_SIZE];
47 
48 static void cpu_set(int fdtnode, u64 regval, void *info)
49 {
50 	static bool read_common_info = false;
51 	struct cpu_set_params *params = info;
52 	int cpu = nr_cpus++;
53 
54 	assert_msg(cpu < NR_CPUS, "Number cpus exceeds maximum supported (%d).", NR_CPUS);
55 
56 	cpus[cpu] = regval;
57 
58 	/* set exception stack address for this CPU (in SPGR0) */
59 	asm volatile ("mtsprg0 %[addr]" ::
60 		      [addr] "r" (exception_stack[cpu + 1]));
61 
62 	if (!read_common_info) {
63 		const struct fdt_property *prop;
64 		u32 *data;
65 
66 		prop = fdt_get_property(dt_fdt(), fdtnode,
67 					"i-cache-line-size", NULL);
68 		assert(prop != NULL);
69 		data = (u32 *)prop->data;
70 		params->icache_bytes = fdt32_to_cpu(*data);
71 
72 		prop = fdt_get_property(dt_fdt(), fdtnode,
73 					"d-cache-line-size", NULL);
74 		assert(prop != NULL);
75 		data = (u32 *)prop->data;
76 		params->dcache_bytes = fdt32_to_cpu(*data);
77 
78 		prop = fdt_get_property(dt_fdt(), fdtnode,
79 					"timebase-frequency", NULL);
80 		assert(prop != NULL);
81 		data = (u32 *)prop->data;
82 		params->tb_hz = fdt32_to_cpu(*data);
83 
84 		read_common_info = true;
85 	}
86 }
87 
88 static void cpu_init(void)
89 {
90 	struct cpu_set_params params;
91 	int ret;
92 
93 	nr_cpus = 0;
94 	ret = dt_for_each_cpu_node(cpu_set, &params);
95 	assert(ret == 0);
96 	__icache_bytes = params.icache_bytes;
97 	__dcache_bytes = params.dcache_bytes;
98 	tb_hz = params.tb_hz;
99 
100 	/* Interrupt Endianness */
101 
102 #if  __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
103         hcall(H_SET_MODE, 1, 4, 0, 0);
104 #else
105         hcall(H_SET_MODE, 0, 4, 0, 0);
106 #endif
107 }
108 
109 static void mem_init(phys_addr_t freemem_start)
110 {
111 	struct dt_pbus_reg regs[NR_MEM_REGIONS];
112 	struct mem_region primary, mem = {
113 		.start = (phys_addr_t)-1,
114 	};
115 	int nr_regs, i;
116 
117 	nr_regs = dt_get_memory_params(regs, NR_MEM_REGIONS);
118 	assert(nr_regs > 0);
119 
120 	primary.end = 0;
121 
122 	for (i = 0; i < nr_regs; ++i) {
123 		mem_regions[i].start = regs[i].addr;
124 		mem_regions[i].end = regs[i].addr + regs[i].size;
125 
126 		/*
127 		 * pick the region we're in for our primary region
128 		 */
129 		if (freemem_start >= mem_regions[i].start
130 				&& freemem_start < mem_regions[i].end) {
131 			mem_regions[i].flags |= MR_F_PRIMARY;
132 			primary = mem_regions[i];
133 		}
134 
135 		/*
136 		 * set the lowest and highest addresses found,
137 		 * ignoring potential gaps
138 		 */
139 		if (mem_regions[i].start < mem.start)
140 			mem.start = mem_regions[i].start;
141 		if (mem_regions[i].end > mem.end)
142 			mem.end = mem_regions[i].end;
143 	}
144 	assert(primary.end != 0);
145 //	assert(!(mem.start & ~PHYS_MASK) && !((mem.end - 1) & ~PHYS_MASK));
146 
147 	__physical_start = mem.start;	/* PHYSICAL_START */
148 	__physical_end = mem.end;	/* PHYSICAL_END */
149 
150 	phys_alloc_init(freemem_start, primary.end - freemem_start);
151 	phys_alloc_set_minimum_alignment(__icache_bytes > __dcache_bytes
152 					 ? __icache_bytes : __dcache_bytes);
153 }
154 
155 void setup(const void *fdt)
156 {
157 	void *freemem = &stacktop;
158 	const char *bootargs, *tmp;
159 	u32 fdt_size;
160 	int ret;
161 
162 	/*
163 	 * Before calling mem_init we need to move the fdt and initrd
164 	 * to safe locations. We move them to construct the memory
165 	 * map illustrated below:
166 	 *
167 	 * +----------------------+   <-- top of physical memory
168 	 * |                      |
169 	 * ~                      ~
170 	 * |                      |
171 	 * +----------------------+   <-- top of initrd
172 	 * |                      |
173 	 * +----------------------+   <-- top of FDT
174 	 * |                      |
175 	 * +----------------------+   <-- top of cpu0's stack
176 	 * |                      |
177 	 * +----------------------+   <-- top of text/data/bss/toc sections,
178 	 * |                      |       see powerpc/flat.lds
179 	 * |                      |
180 	 * +----------------------+   <-- load address
181 	 * |                      |
182 	 * +----------------------+
183 	 */
184 	fdt_size = fdt_totalsize(fdt);
185 	ret = fdt_move(fdt, freemem, fdt_size);
186 	assert(ret == 0);
187 	ret = dt_init(freemem);
188 	assert(ret == 0);
189 	freemem += fdt_size;
190 
191 	ret = dt_get_initrd(&tmp, &initrd_size);
192 	assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
193 	if (ret == 0) {
194 		initrd = freemem;
195 		memmove(initrd, tmp, initrd_size);
196 		freemem += initrd_size;
197 	}
198 
199 	assert(STACK_INT_FRAME_SIZE % 16 == 0);
200 
201 	/* call init functions */
202 	cpu_init();
203 
204 	/* cpu_init must be called before mem_init */
205 	mem_init(PAGE_ALIGN((unsigned long)freemem));
206 
207 	/* mem_init must be called before io_init */
208 	io_init();
209 
210 	/* finish setup */
211 	ret = dt_get_bootargs(&bootargs);
212 	assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
213 	setup_args_progname(bootargs);
214 
215 	if (initrd) {
216 		/* environ is currently the only file in the initrd */
217 		char *env = malloc(initrd_size);
218 		memcpy(env, initrd, initrd_size);
219 		setup_env(env, initrd_size);
220 	}
221 }
222