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