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