xref: /kvm-unit-tests/lib/powerpc/setup.c (revision c76b0d0a3842ba312a2d8512f7a3728f4598bf94)
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
5*c76b0d0aSNicholas Piggin  * (nr_cpus_present), 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>
188e4e0512SNicholas Piggin #include <alloc_page.h>
1963d5cbecSThomas Huth #include <argv.h>
20d72b0449SAndrew Jones #include <asm/setup.h>
21*c76b0d0aSNicholas Piggin #include <asm/smp.h>
22d72b0449SAndrew Jones #include <asm/page.h>
2399bb51c2SNicholas Piggin #include <asm/ptrace.h>
24610c5a9cSNicholas Piggin #include <asm/processor.h>
256842bc34SLaurent Vivier #include <asm/hcall.h>
264ff26ec5SThomas Huth #include "io.h"
27d72b0449SAndrew Jones 
28d72b0449SAndrew Jones extern unsigned long stacktop;
29d72b0449SAndrew Jones 
3001c82c0aSAndrew Jones char *initrd;
3101c82c0aSAndrew Jones u32 initrd_size;
3201c82c0aSAndrew Jones 
33*c76b0d0aSNicholas Piggin u32 cpu_to_hwid[NR_CPUS] = { [0 ... NR_CPUS-1] = (~0U) };
34*c76b0d0aSNicholas Piggin int nr_cpus_present;
35f4d8d939SSuraj Jitindar Singh uint64_t tb_hz;
36d72b0449SAndrew Jones 
37d72b0449SAndrew Jones struct mem_region mem_regions[NR_MEM_REGIONS];
38d72b0449SAndrew Jones phys_addr_t __physical_start, __physical_end;
39d72b0449SAndrew Jones unsigned __icache_bytes, __dcache_bytes;
40d72b0449SAndrew Jones 
41d72b0449SAndrew Jones struct cpu_set_params {
42d72b0449SAndrew Jones 	unsigned icache_bytes;
43d72b0449SAndrew Jones 	unsigned dcache_bytes;
44f4d8d939SSuraj Jitindar Singh 	uint64_t tb_hz;
45d72b0449SAndrew Jones };
46d72b0449SAndrew Jones 
477a20b74eSAndrew Jones static void cpu_set(int fdtnode, u64 regval, void *info)
48d72b0449SAndrew Jones {
49*c76b0d0aSNicholas Piggin 	const struct fdt_property *prop;
50*c76b0d0aSNicholas Piggin 	u32 *threads;
51d72b0449SAndrew Jones 	static bool read_common_info = false;
52d72b0449SAndrew Jones 	struct cpu_set_params *params = info;
53*c76b0d0aSNicholas Piggin 	int nr_threads;
54*c76b0d0aSNicholas Piggin 	int len, i;
55d72b0449SAndrew Jones 
56*c76b0d0aSNicholas Piggin 	/* Get the id array of threads on this node */
57*c76b0d0aSNicholas Piggin 	prop = fdt_get_property(dt_fdt(), fdtnode,
58*c76b0d0aSNicholas Piggin 				"ibm,ppc-interrupt-server#s", &len);
59*c76b0d0aSNicholas Piggin 	assert(prop);
603a9d03a4SAndrew Jones 
61*c76b0d0aSNicholas Piggin 	nr_threads = len >> 2; /* Divide by 4 since 4 bytes per thread */
62*c76b0d0aSNicholas Piggin 	threads = (u32 *)prop->data; /* Array of valid ids */
63*c76b0d0aSNicholas Piggin 
64*c76b0d0aSNicholas Piggin 	for (i = 0; i < nr_threads; i++) {
65*c76b0d0aSNicholas Piggin 		if (nr_cpus_present >= NR_CPUS) {
66*c76b0d0aSNicholas Piggin 			static bool warned = false;
67*c76b0d0aSNicholas Piggin 			if (!warned) {
68*c76b0d0aSNicholas Piggin 				printf("Warning: Number of present CPUs exceeds maximum supported (%d).\n", NR_CPUS);
69*c76b0d0aSNicholas Piggin 				warned = true;
70*c76b0d0aSNicholas Piggin 			}
71*c76b0d0aSNicholas Piggin 			break;
72*c76b0d0aSNicholas Piggin 		}
73*c76b0d0aSNicholas Piggin 		cpu_to_hwid[nr_cpus_present++] = fdt32_to_cpu(threads[i]);
74*c76b0d0aSNicholas Piggin 	}
75d72b0449SAndrew Jones 
76d72b0449SAndrew Jones 	if (!read_common_info) {
77d72b0449SAndrew Jones 		const struct fdt_property *prop;
78d72b0449SAndrew Jones 		u32 *data;
79d72b0449SAndrew Jones 
80d72b0449SAndrew Jones 		prop = fdt_get_property(dt_fdt(), fdtnode,
81d72b0449SAndrew Jones 					"i-cache-line-size", NULL);
82d72b0449SAndrew Jones 		assert(prop != NULL);
83d72b0449SAndrew Jones 		data = (u32 *)prop->data;
84d72b0449SAndrew Jones 		params->icache_bytes = fdt32_to_cpu(*data);
85d72b0449SAndrew Jones 
86d72b0449SAndrew Jones 		prop = fdt_get_property(dt_fdt(), fdtnode,
87d72b0449SAndrew Jones 					"d-cache-line-size", NULL);
88d72b0449SAndrew Jones 		assert(prop != NULL);
89d72b0449SAndrew Jones 		data = (u32 *)prop->data;
90d72b0449SAndrew Jones 		params->dcache_bytes = fdt32_to_cpu(*data);
91d72b0449SAndrew Jones 
92f4d8d939SSuraj Jitindar Singh 		prop = fdt_get_property(dt_fdt(), fdtnode,
93f4d8d939SSuraj Jitindar Singh 					"timebase-frequency", NULL);
94f4d8d939SSuraj Jitindar Singh 		assert(prop != NULL);
95f4d8d939SSuraj Jitindar Singh 		data = (u32 *)prop->data;
96f4d8d939SSuraj Jitindar Singh 		params->tb_hz = fdt32_to_cpu(*data);
97f4d8d939SSuraj Jitindar Singh 
98d72b0449SAndrew Jones 		read_common_info = true;
99d72b0449SAndrew Jones 	}
100d72b0449SAndrew Jones }
101d72b0449SAndrew Jones 
102610c5a9cSNicholas Piggin bool cpu_has_hv;
10300af1c84SNicholas Piggin bool cpu_has_power_mce; /* POWER CPU machine checks */
10400af1c84SNicholas Piggin bool cpu_has_siar;
105cd27b4baSNicholas Piggin bool cpu_has_heai;
10600af1c84SNicholas Piggin bool cpu_has_prefix;
10700af1c84SNicholas Piggin bool cpu_has_sc_lev; /* sc interrupt has LEV field in SRR1 */
108*c76b0d0aSNicholas Piggin bool cpu_has_pause_short;
109610c5a9cSNicholas Piggin 
110*c76b0d0aSNicholas Piggin static void cpu_init_params(void)
111d72b0449SAndrew Jones {
112d72b0449SAndrew Jones 	struct cpu_set_params params;
113d72b0449SAndrew Jones 	int ret;
114d72b0449SAndrew Jones 
115*c76b0d0aSNicholas Piggin 	nr_cpus_present = 0;
116d72b0449SAndrew Jones 	ret = dt_for_each_cpu_node(cpu_set, &params);
117d72b0449SAndrew Jones 	assert(ret == 0);
118d72b0449SAndrew Jones 	__icache_bytes = params.icache_bytes;
119d72b0449SAndrew Jones 	__dcache_bytes = params.dcache_bytes;
120f4d8d939SSuraj Jitindar Singh 	tb_hz = params.tb_hz;
1216842bc34SLaurent Vivier 
122cd27b4baSNicholas Piggin 	switch (mfspr(SPR_PVR) & PVR_VERSION_MASK) {
123cd27b4baSNicholas Piggin 	case PVR_VER_POWER10:
12400af1c84SNicholas Piggin 		cpu_has_prefix = true;
12500af1c84SNicholas Piggin 		cpu_has_sc_lev = true;
126*c76b0d0aSNicholas Piggin 		cpu_has_pause_short = true;
127cd27b4baSNicholas Piggin 	case PVR_VER_POWER9:
128cd27b4baSNicholas Piggin 	case PVR_VER_POWER8E:
129cd27b4baSNicholas Piggin 	case PVR_VER_POWER8NVL:
130cd27b4baSNicholas Piggin 	case PVR_VER_POWER8:
13100af1c84SNicholas Piggin 		cpu_has_power_mce = true;
132cd27b4baSNicholas Piggin 		cpu_has_heai = true;
13300af1c84SNicholas Piggin 		cpu_has_siar = true;
134cd27b4baSNicholas Piggin 		break;
135cd27b4baSNicholas Piggin 	default:
136cd27b4baSNicholas Piggin 		break;
137cd27b4baSNicholas Piggin 	}
13800af1c84SNicholas Piggin 
13900af1c84SNicholas Piggin 	if (!cpu_has_hv) /* HEIR is HV register */
14000af1c84SNicholas Piggin 		cpu_has_heai = false;
141610c5a9cSNicholas Piggin }
142d72b0449SAndrew Jones 
143d72b0449SAndrew Jones static void mem_init(phys_addr_t freemem_start)
144d72b0449SAndrew Jones {
145d72b0449SAndrew Jones 	struct dt_pbus_reg regs[NR_MEM_REGIONS];
146d72b0449SAndrew Jones 	struct mem_region primary, mem = {
147d72b0449SAndrew Jones 		.start = (phys_addr_t)-1,
148d72b0449SAndrew Jones 	};
149d72b0449SAndrew Jones 	int nr_regs, i;
1508e4e0512SNicholas Piggin 	phys_addr_t base, top;
151d72b0449SAndrew Jones 
152d72b0449SAndrew Jones 	nr_regs = dt_get_memory_params(regs, NR_MEM_REGIONS);
153d72b0449SAndrew Jones 	assert(nr_regs > 0);
154d72b0449SAndrew Jones 
155d72b0449SAndrew Jones 	primary.end = 0;
156d72b0449SAndrew Jones 
157d72b0449SAndrew Jones 	for (i = 0; i < nr_regs; ++i) {
158d72b0449SAndrew Jones 		mem_regions[i].start = regs[i].addr;
159d72b0449SAndrew Jones 		mem_regions[i].end = regs[i].addr + regs[i].size;
160d72b0449SAndrew Jones 
161d72b0449SAndrew Jones 		/*
162d72b0449SAndrew Jones 		 * pick the region we're in for our primary region
163d72b0449SAndrew Jones 		 */
164d72b0449SAndrew Jones 		if (freemem_start >= mem_regions[i].start
165d72b0449SAndrew Jones 				&& freemem_start < mem_regions[i].end) {
166d72b0449SAndrew Jones 			mem_regions[i].flags |= MR_F_PRIMARY;
167d72b0449SAndrew Jones 			primary = mem_regions[i];
168d72b0449SAndrew Jones 		}
169d72b0449SAndrew Jones 
170d72b0449SAndrew Jones 		/*
171d72b0449SAndrew Jones 		 * set the lowest and highest addresses found,
172d72b0449SAndrew Jones 		 * ignoring potential gaps
173d72b0449SAndrew Jones 		 */
174d72b0449SAndrew Jones 		if (mem_regions[i].start < mem.start)
175d72b0449SAndrew Jones 			mem.start = mem_regions[i].start;
176d72b0449SAndrew Jones 		if (mem_regions[i].end > mem.end)
177d72b0449SAndrew Jones 			mem.end = mem_regions[i].end;
178d72b0449SAndrew Jones 	}
179d72b0449SAndrew Jones 	assert(primary.end != 0);
180d72b0449SAndrew Jones //	assert(!(mem.start & ~PHYS_MASK) && !((mem.end - 1) & ~PHYS_MASK));
181d72b0449SAndrew Jones 
182d72b0449SAndrew Jones 	__physical_start = mem.start;	/* PHYSICAL_START */
183d72b0449SAndrew Jones 	__physical_end = mem.end;	/* PHYSICAL_END */
184d72b0449SAndrew Jones 
185d72b0449SAndrew Jones 	phys_alloc_init(freemem_start, primary.end - freemem_start);
186d72b0449SAndrew Jones 	phys_alloc_set_minimum_alignment(__icache_bytes > __dcache_bytes
187d72b0449SAndrew Jones 					 ? __icache_bytes : __dcache_bytes);
1888e4e0512SNicholas Piggin 
1898e4e0512SNicholas Piggin 	phys_alloc_get_unused(&base, &top);
1908e4e0512SNicholas Piggin 	base = PAGE_ALIGN(base);
1918e4e0512SNicholas Piggin 	top &= PAGE_MASK;
1928e4e0512SNicholas Piggin 	page_alloc_init_area(0, base >> PAGE_SHIFT, top >> PAGE_SHIFT);
1938e4e0512SNicholas Piggin 	page_alloc_ops_enable();
194d72b0449SAndrew Jones }
195d72b0449SAndrew Jones 
19629815050SNicholas Piggin #define EXCEPTION_STACK_SIZE	SZ_64K
19729815050SNicholas Piggin 
19829815050SNicholas Piggin static char boot_exception_stack[EXCEPTION_STACK_SIZE];
199*c76b0d0aSNicholas Piggin struct cpu cpus[NR_CPUS];
200*c76b0d0aSNicholas Piggin 
201*c76b0d0aSNicholas Piggin void cpu_init(struct cpu *cpu, int cpu_id)
202*c76b0d0aSNicholas Piggin {
203*c76b0d0aSNicholas Piggin 	cpu->server_no = cpu_id;
204*c76b0d0aSNicholas Piggin 
205*c76b0d0aSNicholas Piggin 	cpu->stack = (unsigned long)memalign(SZ_4K, SZ_64K);
206*c76b0d0aSNicholas Piggin 	cpu->stack += SZ_64K - 64;
207*c76b0d0aSNicholas Piggin 	cpu->exception_stack = (unsigned long)memalign(SZ_4K, SZ_64K);
208*c76b0d0aSNicholas Piggin 	cpu->exception_stack += SZ_64K - 64;
209*c76b0d0aSNicholas Piggin }
21029815050SNicholas Piggin 
211d72b0449SAndrew Jones void setup(const void *fdt)
212d72b0449SAndrew Jones {
213efe42003SAndrew Jones 	void *freemem = &stacktop;
21401c82c0aSAndrew Jones 	const char *bootargs, *tmp;
215*c76b0d0aSNicholas Piggin 	struct cpu *cpu;
216d72b0449SAndrew Jones 	u32 fdt_size;
217d72b0449SAndrew Jones 	int ret;
218d72b0449SAndrew Jones 
219610c5a9cSNicholas Piggin 	cpu_has_hv = !!(mfmsr() & (1ULL << MSR_HV_BIT));
220610c5a9cSNicholas Piggin 
221*c76b0d0aSNicholas Piggin 	memset(cpus, 0xff, sizeof(cpus));
222*c76b0d0aSNicholas Piggin 
223*c76b0d0aSNicholas Piggin 	cpu = &cpus[0];
224*c76b0d0aSNicholas Piggin 	cpu->server_no = fdt_boot_cpuid_phys(fdt);
225*c76b0d0aSNicholas Piggin 	cpu->exception_stack = (unsigned long)boot_exception_stack;
226*c76b0d0aSNicholas Piggin 	cpu->exception_stack += EXCEPTION_STACK_SIZE - 64;
227*c76b0d0aSNicholas Piggin 
228*c76b0d0aSNicholas Piggin 	mtspr(SPR_SPRG0, (unsigned long)cpu);
229*c76b0d0aSNicholas Piggin 	__current_cpu = cpu;
23029815050SNicholas Piggin 
231610c5a9cSNicholas Piggin 	enable_mcheck();
232610c5a9cSNicholas Piggin 
233d72b0449SAndrew Jones 	/*
23401c82c0aSAndrew Jones 	 * Before calling mem_init we need to move the fdt and initrd
23501c82c0aSAndrew Jones 	 * to safe locations. We move them to construct the memory
236efe42003SAndrew Jones 	 * map illustrated below:
237efe42003SAndrew Jones 	 *
238efe42003SAndrew Jones 	 * +----------------------+   <-- top of physical memory
239efe42003SAndrew Jones 	 * |                      |
240efe42003SAndrew Jones 	 * ~                      ~
241efe42003SAndrew Jones 	 * |                      |
24201c82c0aSAndrew Jones 	 * +----------------------+   <-- top of initrd
24301c82c0aSAndrew Jones 	 * |                      |
244efe42003SAndrew Jones 	 * +----------------------+   <-- top of FDT
245efe42003SAndrew Jones 	 * |                      |
246efe42003SAndrew Jones 	 * +----------------------+   <-- top of cpu0's stack
247efe42003SAndrew Jones 	 * |                      |
24875a58702SAndrew Jones 	 * +----------------------+   <-- top of text/data/bss/toc sections,
24975a58702SAndrew Jones 	 * |                      |       see powerpc/flat.lds
250efe42003SAndrew Jones 	 * |                      |
251efe42003SAndrew Jones 	 * +----------------------+   <-- load address
252efe42003SAndrew Jones 	 * |                      |
253efe42003SAndrew Jones 	 * +----------------------+
254d72b0449SAndrew Jones 	 */
255d72b0449SAndrew Jones 	fdt_size = fdt_totalsize(fdt);
256efe42003SAndrew Jones 	ret = fdt_move(fdt, freemem, fdt_size);
257d72b0449SAndrew Jones 	assert(ret == 0);
258efe42003SAndrew Jones 	ret = dt_init(freemem);
259d72b0449SAndrew Jones 	assert(ret == 0);
260efe42003SAndrew Jones 	freemem += fdt_size;
261d72b0449SAndrew Jones 
26201c82c0aSAndrew Jones 	ret = dt_get_initrd(&tmp, &initrd_size);
26301c82c0aSAndrew Jones 	assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
26401c82c0aSAndrew Jones 	if (ret == 0) {
26501c82c0aSAndrew Jones 		initrd = freemem;
26601c82c0aSAndrew Jones 		memmove(initrd, tmp, initrd_size);
26701c82c0aSAndrew Jones 		freemem += initrd_size;
26801c82c0aSAndrew Jones 	}
26901c82c0aSAndrew Jones 
27099bb51c2SNicholas Piggin 	assert(STACK_INT_FRAME_SIZE % 16 == 0);
27199bb51c2SNicholas Piggin 
272*c76b0d0aSNicholas Piggin 	/* set parameters from dt */
273*c76b0d0aSNicholas Piggin 	cpu_init_params();
274*c76b0d0aSNicholas Piggin 
275*c76b0d0aSNicholas Piggin 	/* Interrupt Endianness */
276*c76b0d0aSNicholas Piggin 	if (machine_is_pseries()) {
277*c76b0d0aSNicholas Piggin #if  __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
278*c76b0d0aSNicholas Piggin 		hcall(H_SET_MODE, 1, 4, 0, 0);
279*c76b0d0aSNicholas Piggin #else
280*c76b0d0aSNicholas Piggin 		hcall(H_SET_MODE, 0, 4, 0, 0);
281*c76b0d0aSNicholas Piggin #endif
282*c76b0d0aSNicholas Piggin 	}
283*c76b0d0aSNicholas Piggin 
284*c76b0d0aSNicholas Piggin 	cpu_init_ipis();
285d35482d5SAndrew Jones 
286d35482d5SAndrew Jones 	/* cpu_init must be called before mem_init */
287efe42003SAndrew Jones 	mem_init(PAGE_ALIGN((unsigned long)freemem));
288d35482d5SAndrew Jones 
289d35482d5SAndrew Jones 	/* mem_init must be called before io_init */
290d72b0449SAndrew Jones 	io_init();
291d72b0449SAndrew Jones 
292efe42003SAndrew Jones 	/* finish setup */
293d72b0449SAndrew Jones 	ret = dt_get_bootargs(&bootargs);
294d81b83fdSAndrew Jones 	assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
295809ebcb3SAndrew Jones 	setup_args_progname(bootargs);
296f266c3e8SAndrew Jones 
297f266c3e8SAndrew Jones 	if (initrd) {
298f266c3e8SAndrew Jones 		/* environ is currently the only file in the initrd */
299f266c3e8SAndrew Jones 		char *env = malloc(initrd_size);
300f266c3e8SAndrew Jones 		memcpy(env, initrd, initrd_size);
301f266c3e8SAndrew Jones 		setup_env(env, initrd_size);
302f266c3e8SAndrew Jones 	}
303d72b0449SAndrew Jones }
304