xref: /kvm-unit-tests/lib/powerpc/setup.c (revision 2981505035dde9521e8465cd3da6da8c66378ff1)
1d72b0449SAndrew Jones /*
2d72b0449SAndrew Jones  * Initialize machine setup information and I/O.
3d72b0449SAndrew Jones  *
4d72b0449SAndrew Jones  * After running setup() unit tests may query how many cpus they have
5d72b0449SAndrew Jones  * (nr_cpus), how much memory they have (PHYSICAL_END - PHYSICAL_START),
6d72b0449SAndrew Jones  * may use dynamic memory allocation (malloc, etc.), printf, and exit.
7d72b0449SAndrew Jones  * Finally, argc and argv are also ready to be passed to main().
8d72b0449SAndrew Jones  *
9d72b0449SAndrew Jones  * Copyright (C) 2016, Red Hat Inc, Andrew Jones <drjones@redhat.com>
10d72b0449SAndrew Jones  *
11d72b0449SAndrew Jones  * This work is licensed under the terms of the GNU LGPL, version 2.
12d72b0449SAndrew Jones  */
13d72b0449SAndrew Jones #include <libcflat.h>
14d72b0449SAndrew Jones #include <libfdt/libfdt.h>
15d72b0449SAndrew Jones #include <devicetree.h>
16d72b0449SAndrew Jones #include <alloc.h>
17dc47ac61SPaolo Bonzini #include <alloc_phys.h>
1863d5cbecSThomas Huth #include <argv.h>
19d72b0449SAndrew Jones #include <asm/setup.h>
20d72b0449SAndrew Jones #include <asm/page.h>
2199bb51c2SNicholas Piggin #include <asm/ptrace.h>
22610c5a9cSNicholas Piggin #include <asm/processor.h>
236842bc34SLaurent Vivier #include <asm/hcall.h>
244ff26ec5SThomas Huth #include "io.h"
25d72b0449SAndrew Jones 
26d72b0449SAndrew Jones extern unsigned long stacktop;
27d72b0449SAndrew Jones 
2801c82c0aSAndrew Jones char *initrd;
2901c82c0aSAndrew Jones u32 initrd_size;
3001c82c0aSAndrew Jones 
31d72b0449SAndrew Jones u32 cpus[NR_CPUS] = { [0 ... NR_CPUS-1] = (~0U) };
32d72b0449SAndrew Jones int nr_cpus;
33f4d8d939SSuraj Jitindar Singh uint64_t tb_hz;
34d72b0449SAndrew Jones 
35d72b0449SAndrew Jones struct mem_region mem_regions[NR_MEM_REGIONS];
36d72b0449SAndrew Jones phys_addr_t __physical_start, __physical_end;
37d72b0449SAndrew Jones unsigned __icache_bytes, __dcache_bytes;
38d72b0449SAndrew Jones 
39d72b0449SAndrew Jones struct cpu_set_params {
40d72b0449SAndrew Jones 	unsigned icache_bytes;
41d72b0449SAndrew Jones 	unsigned dcache_bytes;
42f4d8d939SSuraj Jitindar Singh 	uint64_t tb_hz;
43d72b0449SAndrew Jones };
44d72b0449SAndrew Jones 
457a20b74eSAndrew Jones static void cpu_set(int fdtnode, u64 regval, void *info)
46d72b0449SAndrew Jones {
47d72b0449SAndrew Jones 	static bool read_common_info = false;
48d72b0449SAndrew Jones 	struct cpu_set_params *params = info;
49d72b0449SAndrew Jones 	int cpu = nr_cpus++;
50d72b0449SAndrew Jones 
513a9d03a4SAndrew Jones 	assert_msg(cpu < NR_CPUS, "Number cpus exceeds maximum supported (%d).", NR_CPUS);
523a9d03a4SAndrew Jones 
53d72b0449SAndrew Jones 	cpus[cpu] = regval;
54d72b0449SAndrew Jones 
55d72b0449SAndrew Jones 	if (!read_common_info) {
56d72b0449SAndrew Jones 		const struct fdt_property *prop;
57d72b0449SAndrew Jones 		u32 *data;
58d72b0449SAndrew Jones 
59d72b0449SAndrew Jones 		prop = fdt_get_property(dt_fdt(), fdtnode,
60d72b0449SAndrew Jones 					"i-cache-line-size", NULL);
61d72b0449SAndrew Jones 		assert(prop != NULL);
62d72b0449SAndrew Jones 		data = (u32 *)prop->data;
63d72b0449SAndrew Jones 		params->icache_bytes = fdt32_to_cpu(*data);
64d72b0449SAndrew Jones 
65d72b0449SAndrew Jones 		prop = fdt_get_property(dt_fdt(), fdtnode,
66d72b0449SAndrew Jones 					"d-cache-line-size", NULL);
67d72b0449SAndrew Jones 		assert(prop != NULL);
68d72b0449SAndrew Jones 		data = (u32 *)prop->data;
69d72b0449SAndrew Jones 		params->dcache_bytes = fdt32_to_cpu(*data);
70d72b0449SAndrew Jones 
71f4d8d939SSuraj Jitindar Singh 		prop = fdt_get_property(dt_fdt(), fdtnode,
72f4d8d939SSuraj Jitindar Singh 					"timebase-frequency", NULL);
73f4d8d939SSuraj Jitindar Singh 		assert(prop != NULL);
74f4d8d939SSuraj Jitindar Singh 		data = (u32 *)prop->data;
75f4d8d939SSuraj Jitindar Singh 		params->tb_hz = fdt32_to_cpu(*data);
76f4d8d939SSuraj Jitindar Singh 
77d72b0449SAndrew Jones 		read_common_info = true;
78d72b0449SAndrew Jones 	}
79d72b0449SAndrew Jones }
80d72b0449SAndrew Jones 
81610c5a9cSNicholas Piggin bool cpu_has_hv;
8200af1c84SNicholas Piggin bool cpu_has_power_mce; /* POWER CPU machine checks */
8300af1c84SNicholas Piggin bool cpu_has_siar;
84cd27b4baSNicholas Piggin bool cpu_has_heai;
8500af1c84SNicholas Piggin bool cpu_has_prefix;
8600af1c84SNicholas Piggin bool cpu_has_sc_lev; /* sc interrupt has LEV field in SRR1 */
87610c5a9cSNicholas Piggin 
88d72b0449SAndrew Jones static void cpu_init(void)
89d72b0449SAndrew Jones {
90d72b0449SAndrew Jones 	struct cpu_set_params params;
91d72b0449SAndrew Jones 	int ret;
92d72b0449SAndrew Jones 
93d72b0449SAndrew Jones 	nr_cpus = 0;
94d72b0449SAndrew Jones 	ret = dt_for_each_cpu_node(cpu_set, &params);
95d72b0449SAndrew Jones 	assert(ret == 0);
96d72b0449SAndrew Jones 	__icache_bytes = params.icache_bytes;
97d72b0449SAndrew Jones 	__dcache_bytes = params.dcache_bytes;
98f4d8d939SSuraj Jitindar Singh 	tb_hz = params.tb_hz;
996842bc34SLaurent Vivier 
1006842bc34SLaurent Vivier 	/* Interrupt Endianness */
101610c5a9cSNicholas Piggin 	if (machine_is_pseries()) {
1026842bc34SLaurent Vivier #if  __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
1036842bc34SLaurent Vivier 		hcall(H_SET_MODE, 1, 4, 0, 0);
1046842bc34SLaurent Vivier #else
1056842bc34SLaurent Vivier 		hcall(H_SET_MODE, 0, 4, 0, 0);
1066842bc34SLaurent Vivier #endif
107d72b0449SAndrew Jones 	}
108cd27b4baSNicholas Piggin 
109cd27b4baSNicholas Piggin 	switch (mfspr(SPR_PVR) & PVR_VERSION_MASK) {
110cd27b4baSNicholas Piggin 	case PVR_VER_POWER10:
11100af1c84SNicholas Piggin 		cpu_has_prefix = true;
11200af1c84SNicholas Piggin 		cpu_has_sc_lev = true;
113cd27b4baSNicholas Piggin 	case PVR_VER_POWER9:
114cd27b4baSNicholas Piggin 	case PVR_VER_POWER8E:
115cd27b4baSNicholas Piggin 	case PVR_VER_POWER8NVL:
116cd27b4baSNicholas Piggin 	case PVR_VER_POWER8:
11700af1c84SNicholas Piggin 		cpu_has_power_mce = true;
118cd27b4baSNicholas Piggin 		cpu_has_heai = true;
11900af1c84SNicholas Piggin 		cpu_has_siar = true;
120cd27b4baSNicholas Piggin 		break;
121cd27b4baSNicholas Piggin 	default:
122cd27b4baSNicholas Piggin 		break;
123cd27b4baSNicholas Piggin 	}
12400af1c84SNicholas Piggin 
12500af1c84SNicholas Piggin 	if (!cpu_has_hv) /* HEIR is HV register */
12600af1c84SNicholas Piggin 		cpu_has_heai = false;
127610c5a9cSNicholas Piggin }
128d72b0449SAndrew Jones 
129d72b0449SAndrew Jones static void mem_init(phys_addr_t freemem_start)
130d72b0449SAndrew Jones {
131d72b0449SAndrew Jones 	struct dt_pbus_reg regs[NR_MEM_REGIONS];
132d72b0449SAndrew Jones 	struct mem_region primary, mem = {
133d72b0449SAndrew Jones 		.start = (phys_addr_t)-1,
134d72b0449SAndrew Jones 	};
135d72b0449SAndrew Jones 	int nr_regs, i;
136d72b0449SAndrew Jones 
137d72b0449SAndrew Jones 	nr_regs = dt_get_memory_params(regs, NR_MEM_REGIONS);
138d72b0449SAndrew Jones 	assert(nr_regs > 0);
139d72b0449SAndrew Jones 
140d72b0449SAndrew Jones 	primary.end = 0;
141d72b0449SAndrew Jones 
142d72b0449SAndrew Jones 	for (i = 0; i < nr_regs; ++i) {
143d72b0449SAndrew Jones 		mem_regions[i].start = regs[i].addr;
144d72b0449SAndrew Jones 		mem_regions[i].end = regs[i].addr + regs[i].size;
145d72b0449SAndrew Jones 
146d72b0449SAndrew Jones 		/*
147d72b0449SAndrew Jones 		 * pick the region we're in for our primary region
148d72b0449SAndrew Jones 		 */
149d72b0449SAndrew Jones 		if (freemem_start >= mem_regions[i].start
150d72b0449SAndrew Jones 				&& freemem_start < mem_regions[i].end) {
151d72b0449SAndrew Jones 			mem_regions[i].flags |= MR_F_PRIMARY;
152d72b0449SAndrew Jones 			primary = mem_regions[i];
153d72b0449SAndrew Jones 		}
154d72b0449SAndrew Jones 
155d72b0449SAndrew Jones 		/*
156d72b0449SAndrew Jones 		 * set the lowest and highest addresses found,
157d72b0449SAndrew Jones 		 * ignoring potential gaps
158d72b0449SAndrew Jones 		 */
159d72b0449SAndrew Jones 		if (mem_regions[i].start < mem.start)
160d72b0449SAndrew Jones 			mem.start = mem_regions[i].start;
161d72b0449SAndrew Jones 		if (mem_regions[i].end > mem.end)
162d72b0449SAndrew Jones 			mem.end = mem_regions[i].end;
163d72b0449SAndrew Jones 	}
164d72b0449SAndrew Jones 	assert(primary.end != 0);
165d72b0449SAndrew Jones //	assert(!(mem.start & ~PHYS_MASK) && !((mem.end - 1) & ~PHYS_MASK));
166d72b0449SAndrew Jones 
167d72b0449SAndrew Jones 	__physical_start = mem.start;	/* PHYSICAL_START */
168d72b0449SAndrew Jones 	__physical_end = mem.end;	/* PHYSICAL_END */
169d72b0449SAndrew Jones 
170d72b0449SAndrew Jones 	phys_alloc_init(freemem_start, primary.end - freemem_start);
171d72b0449SAndrew Jones 	phys_alloc_set_minimum_alignment(__icache_bytes > __dcache_bytes
172d72b0449SAndrew Jones 					 ? __icache_bytes : __dcache_bytes);
173d72b0449SAndrew Jones }
174d72b0449SAndrew Jones 
175*29815050SNicholas Piggin #define EXCEPTION_STACK_SIZE	SZ_64K
176*29815050SNicholas Piggin 
177*29815050SNicholas Piggin static char boot_exception_stack[EXCEPTION_STACK_SIZE];
178*29815050SNicholas Piggin 
179d72b0449SAndrew Jones void setup(const void *fdt)
180d72b0449SAndrew Jones {
181efe42003SAndrew Jones 	void *freemem = &stacktop;
18201c82c0aSAndrew Jones 	const char *bootargs, *tmp;
183d72b0449SAndrew Jones 	u32 fdt_size;
184d72b0449SAndrew Jones 	int ret;
185d72b0449SAndrew Jones 
186610c5a9cSNicholas Piggin 	cpu_has_hv = !!(mfmsr() & (1ULL << MSR_HV_BIT));
187610c5a9cSNicholas Piggin 
188*29815050SNicholas Piggin 	/* set exception stack address for this CPU (in SPGR0) */
189*29815050SNicholas Piggin 	asm volatile ("mtsprg0 %[addr]" ::
190*29815050SNicholas Piggin 		      [addr] "r" (boot_exception_stack + EXCEPTION_STACK_SIZE - 64));
191*29815050SNicholas Piggin 
192610c5a9cSNicholas Piggin 	enable_mcheck();
193610c5a9cSNicholas Piggin 
194d72b0449SAndrew Jones 	/*
19501c82c0aSAndrew Jones 	 * Before calling mem_init we need to move the fdt and initrd
19601c82c0aSAndrew Jones 	 * to safe locations. We move them to construct the memory
197efe42003SAndrew Jones 	 * map illustrated below:
198efe42003SAndrew Jones 	 *
199efe42003SAndrew Jones 	 * +----------------------+   <-- top of physical memory
200efe42003SAndrew Jones 	 * |                      |
201efe42003SAndrew Jones 	 * ~                      ~
202efe42003SAndrew Jones 	 * |                      |
20301c82c0aSAndrew Jones 	 * +----------------------+   <-- top of initrd
20401c82c0aSAndrew Jones 	 * |                      |
205efe42003SAndrew Jones 	 * +----------------------+   <-- top of FDT
206efe42003SAndrew Jones 	 * |                      |
207efe42003SAndrew Jones 	 * +----------------------+   <-- top of cpu0's stack
208efe42003SAndrew Jones 	 * |                      |
20975a58702SAndrew Jones 	 * +----------------------+   <-- top of text/data/bss/toc sections,
21075a58702SAndrew Jones 	 * |                      |       see powerpc/flat.lds
211efe42003SAndrew Jones 	 * |                      |
212efe42003SAndrew Jones 	 * +----------------------+   <-- load address
213efe42003SAndrew Jones 	 * |                      |
214efe42003SAndrew Jones 	 * +----------------------+
215d72b0449SAndrew Jones 	 */
216d72b0449SAndrew Jones 	fdt_size = fdt_totalsize(fdt);
217efe42003SAndrew Jones 	ret = fdt_move(fdt, freemem, fdt_size);
218d72b0449SAndrew Jones 	assert(ret == 0);
219efe42003SAndrew Jones 	ret = dt_init(freemem);
220d72b0449SAndrew Jones 	assert(ret == 0);
221efe42003SAndrew Jones 	freemem += fdt_size;
222d72b0449SAndrew Jones 
22301c82c0aSAndrew Jones 	ret = dt_get_initrd(&tmp, &initrd_size);
22401c82c0aSAndrew Jones 	assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
22501c82c0aSAndrew Jones 	if (ret == 0) {
22601c82c0aSAndrew Jones 		initrd = freemem;
22701c82c0aSAndrew Jones 		memmove(initrd, tmp, initrd_size);
22801c82c0aSAndrew Jones 		freemem += initrd_size;
22901c82c0aSAndrew Jones 	}
23001c82c0aSAndrew Jones 
23199bb51c2SNicholas Piggin 	assert(STACK_INT_FRAME_SIZE % 16 == 0);
23299bb51c2SNicholas Piggin 
233efe42003SAndrew Jones 	/* call init functions */
234d72b0449SAndrew Jones 	cpu_init();
235d35482d5SAndrew Jones 
236d35482d5SAndrew Jones 	/* cpu_init must be called before mem_init */
237efe42003SAndrew Jones 	mem_init(PAGE_ALIGN((unsigned long)freemem));
238d35482d5SAndrew Jones 
239d35482d5SAndrew Jones 	/* mem_init must be called before io_init */
240d72b0449SAndrew Jones 	io_init();
241d72b0449SAndrew Jones 
242efe42003SAndrew Jones 	/* finish setup */
243d72b0449SAndrew Jones 	ret = dt_get_bootargs(&bootargs);
244d81b83fdSAndrew Jones 	assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
245809ebcb3SAndrew Jones 	setup_args_progname(bootargs);
246f266c3e8SAndrew Jones 
247f266c3e8SAndrew Jones 	if (initrd) {
248f266c3e8SAndrew Jones 		/* environ is currently the only file in the initrd */
249f266c3e8SAndrew Jones 		char *env = malloc(initrd_size);
250f266c3e8SAndrew Jones 		memcpy(env, initrd, initrd_size);
251f266c3e8SAndrew Jones 		setup_env(env, initrd_size);
252f266c3e8SAndrew Jones 	}
253d72b0449SAndrew Jones }
254