xref: /kvm-unit-tests/lib/powerpc/setup.c (revision 2981505035dde9521e8465cd3da6da8c66378ff1)
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 static void cpu_set(int fdtnode, u64 regval, void *info)
46 {
47 	static bool read_common_info = false;
48 	struct cpu_set_params *params = info;
49 	int cpu = nr_cpus++;
50 
51 	assert_msg(cpu < NR_CPUS, "Number cpus exceeds maximum supported (%d).", NR_CPUS);
52 
53 	cpus[cpu] = regval;
54 
55 	if (!read_common_info) {
56 		const struct fdt_property *prop;
57 		u32 *data;
58 
59 		prop = fdt_get_property(dt_fdt(), fdtnode,
60 					"i-cache-line-size", NULL);
61 		assert(prop != NULL);
62 		data = (u32 *)prop->data;
63 		params->icache_bytes = fdt32_to_cpu(*data);
64 
65 		prop = fdt_get_property(dt_fdt(), fdtnode,
66 					"d-cache-line-size", NULL);
67 		assert(prop != NULL);
68 		data = (u32 *)prop->data;
69 		params->dcache_bytes = fdt32_to_cpu(*data);
70 
71 		prop = fdt_get_property(dt_fdt(), fdtnode,
72 					"timebase-frequency", NULL);
73 		assert(prop != NULL);
74 		data = (u32 *)prop->data;
75 		params->tb_hz = fdt32_to_cpu(*data);
76 
77 		read_common_info = true;
78 	}
79 }
80 
81 bool cpu_has_hv;
82 bool cpu_has_power_mce; /* POWER CPU machine checks */
83 bool cpu_has_siar;
84 bool cpu_has_heai;
85 bool cpu_has_prefix;
86 bool cpu_has_sc_lev; /* sc interrupt has LEV field in SRR1 */
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 	if (machine_is_pseries()) {
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 	switch (mfspr(SPR_PVR) & PVR_VERSION_MASK) {
110 	case PVR_VER_POWER10:
111 		cpu_has_prefix = true;
112 		cpu_has_sc_lev = true;
113 	case PVR_VER_POWER9:
114 	case PVR_VER_POWER8E:
115 	case PVR_VER_POWER8NVL:
116 	case PVR_VER_POWER8:
117 		cpu_has_power_mce = true;
118 		cpu_has_heai = true;
119 		cpu_has_siar = true;
120 		break;
121 	default:
122 		break;
123 	}
124 
125 	if (!cpu_has_hv) /* HEIR is HV register */
126 		cpu_has_heai = false;
127 }
128 
129 static void mem_init(phys_addr_t freemem_start)
130 {
131 	struct dt_pbus_reg regs[NR_MEM_REGIONS];
132 	struct mem_region primary, mem = {
133 		.start = (phys_addr_t)-1,
134 	};
135 	int nr_regs, i;
136 
137 	nr_regs = dt_get_memory_params(regs, NR_MEM_REGIONS);
138 	assert(nr_regs > 0);
139 
140 	primary.end = 0;
141 
142 	for (i = 0; i < nr_regs; ++i) {
143 		mem_regions[i].start = regs[i].addr;
144 		mem_regions[i].end = regs[i].addr + regs[i].size;
145 
146 		/*
147 		 * pick the region we're in for our primary region
148 		 */
149 		if (freemem_start >= mem_regions[i].start
150 				&& freemem_start < mem_regions[i].end) {
151 			mem_regions[i].flags |= MR_F_PRIMARY;
152 			primary = mem_regions[i];
153 		}
154 
155 		/*
156 		 * set the lowest and highest addresses found,
157 		 * ignoring potential gaps
158 		 */
159 		if (mem_regions[i].start < mem.start)
160 			mem.start = mem_regions[i].start;
161 		if (mem_regions[i].end > mem.end)
162 			mem.end = mem_regions[i].end;
163 	}
164 	assert(primary.end != 0);
165 //	assert(!(mem.start & ~PHYS_MASK) && !((mem.end - 1) & ~PHYS_MASK));
166 
167 	__physical_start = mem.start;	/* PHYSICAL_START */
168 	__physical_end = mem.end;	/* PHYSICAL_END */
169 
170 	phys_alloc_init(freemem_start, primary.end - freemem_start);
171 	phys_alloc_set_minimum_alignment(__icache_bytes > __dcache_bytes
172 					 ? __icache_bytes : __dcache_bytes);
173 }
174 
175 #define EXCEPTION_STACK_SIZE	SZ_64K
176 
177 static char boot_exception_stack[EXCEPTION_STACK_SIZE];
178 
179 void setup(const void *fdt)
180 {
181 	void *freemem = &stacktop;
182 	const char *bootargs, *tmp;
183 	u32 fdt_size;
184 	int ret;
185 
186 	cpu_has_hv = !!(mfmsr() & (1ULL << MSR_HV_BIT));
187 
188 	/* set exception stack address for this CPU (in SPGR0) */
189 	asm volatile ("mtsprg0 %[addr]" ::
190 		      [addr] "r" (boot_exception_stack + EXCEPTION_STACK_SIZE - 64));
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