xref: /kvm-unit-tests/lib/powerpc/setup.c (revision 610c5a9c11fc1e8fe936925b8a4975015ffe4b5e)
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>
22*610c5a9cSNicholas 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 
456842bc34SLaurent Vivier #define EXCEPTION_STACK_SIZE	(32*1024) /* 32kB */
466842bc34SLaurent Vivier 
476842bc34SLaurent Vivier static char exception_stack[NR_CPUS][EXCEPTION_STACK_SIZE];
486842bc34SLaurent Vivier 
497a20b74eSAndrew Jones static void cpu_set(int fdtnode, u64 regval, void *info)
50d72b0449SAndrew Jones {
51d72b0449SAndrew Jones 	static bool read_common_info = false;
52d72b0449SAndrew Jones 	struct cpu_set_params *params = info;
53d72b0449SAndrew Jones 	int cpu = nr_cpus++;
54d72b0449SAndrew Jones 
553a9d03a4SAndrew Jones 	assert_msg(cpu < NR_CPUS, "Number cpus exceeds maximum supported (%d).", NR_CPUS);
563a9d03a4SAndrew Jones 
57d72b0449SAndrew Jones 	cpus[cpu] = regval;
58d72b0449SAndrew Jones 
596842bc34SLaurent Vivier 	/* set exception stack address for this CPU (in SPGR0) */
606842bc34SLaurent Vivier 	asm volatile ("mtsprg0 %[addr]" ::
616842bc34SLaurent Vivier 		      [addr] "r" (exception_stack[cpu + 1]));
626842bc34SLaurent Vivier 
63d72b0449SAndrew Jones 	if (!read_common_info) {
64d72b0449SAndrew Jones 		const struct fdt_property *prop;
65d72b0449SAndrew Jones 		u32 *data;
66d72b0449SAndrew Jones 
67d72b0449SAndrew Jones 		prop = fdt_get_property(dt_fdt(), fdtnode,
68d72b0449SAndrew Jones 					"i-cache-line-size", NULL);
69d72b0449SAndrew Jones 		assert(prop != NULL);
70d72b0449SAndrew Jones 		data = (u32 *)prop->data;
71d72b0449SAndrew Jones 		params->icache_bytes = fdt32_to_cpu(*data);
72d72b0449SAndrew Jones 
73d72b0449SAndrew Jones 		prop = fdt_get_property(dt_fdt(), fdtnode,
74d72b0449SAndrew Jones 					"d-cache-line-size", NULL);
75d72b0449SAndrew Jones 		assert(prop != NULL);
76d72b0449SAndrew Jones 		data = (u32 *)prop->data;
77d72b0449SAndrew Jones 		params->dcache_bytes = fdt32_to_cpu(*data);
78d72b0449SAndrew Jones 
79f4d8d939SSuraj Jitindar Singh 		prop = fdt_get_property(dt_fdt(), fdtnode,
80f4d8d939SSuraj Jitindar Singh 					"timebase-frequency", NULL);
81f4d8d939SSuraj Jitindar Singh 		assert(prop != NULL);
82f4d8d939SSuraj Jitindar Singh 		data = (u32 *)prop->data;
83f4d8d939SSuraj Jitindar Singh 		params->tb_hz = fdt32_to_cpu(*data);
84f4d8d939SSuraj Jitindar Singh 
85d72b0449SAndrew Jones 		read_common_info = true;
86d72b0449SAndrew Jones 	}
87d72b0449SAndrew Jones }
88d72b0449SAndrew Jones 
89*610c5a9cSNicholas Piggin bool cpu_has_hv;
90*610c5a9cSNicholas Piggin 
91d72b0449SAndrew Jones static void cpu_init(void)
92d72b0449SAndrew Jones {
93d72b0449SAndrew Jones 	struct cpu_set_params params;
94d72b0449SAndrew Jones 	int ret;
95d72b0449SAndrew Jones 
96d72b0449SAndrew Jones 	nr_cpus = 0;
97d72b0449SAndrew Jones 	ret = dt_for_each_cpu_node(cpu_set, &params);
98d72b0449SAndrew Jones 	assert(ret == 0);
99d72b0449SAndrew Jones 	__icache_bytes = params.icache_bytes;
100d72b0449SAndrew Jones 	__dcache_bytes = params.dcache_bytes;
101f4d8d939SSuraj Jitindar Singh 	tb_hz = params.tb_hz;
1026842bc34SLaurent Vivier 
1036842bc34SLaurent Vivier 	/* Interrupt Endianness */
104*610c5a9cSNicholas Piggin 	if (machine_is_pseries()) {
1056842bc34SLaurent Vivier #if  __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
1066842bc34SLaurent Vivier 		hcall(H_SET_MODE, 1, 4, 0, 0);
1076842bc34SLaurent Vivier #else
1086842bc34SLaurent Vivier 		hcall(H_SET_MODE, 0, 4, 0, 0);
1096842bc34SLaurent Vivier #endif
110d72b0449SAndrew Jones 	}
111*610c5a9cSNicholas Piggin }
112d72b0449SAndrew Jones 
113d72b0449SAndrew Jones static void mem_init(phys_addr_t freemem_start)
114d72b0449SAndrew Jones {
115d72b0449SAndrew Jones 	struct dt_pbus_reg regs[NR_MEM_REGIONS];
116d72b0449SAndrew Jones 	struct mem_region primary, mem = {
117d72b0449SAndrew Jones 		.start = (phys_addr_t)-1,
118d72b0449SAndrew Jones 	};
119d72b0449SAndrew Jones 	int nr_regs, i;
120d72b0449SAndrew Jones 
121d72b0449SAndrew Jones 	nr_regs = dt_get_memory_params(regs, NR_MEM_REGIONS);
122d72b0449SAndrew Jones 	assert(nr_regs > 0);
123d72b0449SAndrew Jones 
124d72b0449SAndrew Jones 	primary.end = 0;
125d72b0449SAndrew Jones 
126d72b0449SAndrew Jones 	for (i = 0; i < nr_regs; ++i) {
127d72b0449SAndrew Jones 		mem_regions[i].start = regs[i].addr;
128d72b0449SAndrew Jones 		mem_regions[i].end = regs[i].addr + regs[i].size;
129d72b0449SAndrew Jones 
130d72b0449SAndrew Jones 		/*
131d72b0449SAndrew Jones 		 * pick the region we're in for our primary region
132d72b0449SAndrew Jones 		 */
133d72b0449SAndrew Jones 		if (freemem_start >= mem_regions[i].start
134d72b0449SAndrew Jones 				&& freemem_start < mem_regions[i].end) {
135d72b0449SAndrew Jones 			mem_regions[i].flags |= MR_F_PRIMARY;
136d72b0449SAndrew Jones 			primary = mem_regions[i];
137d72b0449SAndrew Jones 		}
138d72b0449SAndrew Jones 
139d72b0449SAndrew Jones 		/*
140d72b0449SAndrew Jones 		 * set the lowest and highest addresses found,
141d72b0449SAndrew Jones 		 * ignoring potential gaps
142d72b0449SAndrew Jones 		 */
143d72b0449SAndrew Jones 		if (mem_regions[i].start < mem.start)
144d72b0449SAndrew Jones 			mem.start = mem_regions[i].start;
145d72b0449SAndrew Jones 		if (mem_regions[i].end > mem.end)
146d72b0449SAndrew Jones 			mem.end = mem_regions[i].end;
147d72b0449SAndrew Jones 	}
148d72b0449SAndrew Jones 	assert(primary.end != 0);
149d72b0449SAndrew Jones //	assert(!(mem.start & ~PHYS_MASK) && !((mem.end - 1) & ~PHYS_MASK));
150d72b0449SAndrew Jones 
151d72b0449SAndrew Jones 	__physical_start = mem.start;	/* PHYSICAL_START */
152d72b0449SAndrew Jones 	__physical_end = mem.end;	/* PHYSICAL_END */
153d72b0449SAndrew Jones 
154d72b0449SAndrew Jones 	phys_alloc_init(freemem_start, primary.end - freemem_start);
155d72b0449SAndrew Jones 	phys_alloc_set_minimum_alignment(__icache_bytes > __dcache_bytes
156d72b0449SAndrew Jones 					 ? __icache_bytes : __dcache_bytes);
157d72b0449SAndrew Jones }
158d72b0449SAndrew Jones 
159d72b0449SAndrew Jones void setup(const void *fdt)
160d72b0449SAndrew Jones {
161efe42003SAndrew Jones 	void *freemem = &stacktop;
16201c82c0aSAndrew Jones 	const char *bootargs, *tmp;
163d72b0449SAndrew Jones 	u32 fdt_size;
164d72b0449SAndrew Jones 	int ret;
165d72b0449SAndrew Jones 
166*610c5a9cSNicholas Piggin 	cpu_has_hv = !!(mfmsr() & (1ULL << MSR_HV_BIT));
167*610c5a9cSNicholas Piggin 
168*610c5a9cSNicholas Piggin 	enable_mcheck();
169*610c5a9cSNicholas Piggin 
170d72b0449SAndrew Jones 	/*
17101c82c0aSAndrew Jones 	 * Before calling mem_init we need to move the fdt and initrd
17201c82c0aSAndrew Jones 	 * to safe locations. We move them to construct the memory
173efe42003SAndrew Jones 	 * map illustrated below:
174efe42003SAndrew Jones 	 *
175efe42003SAndrew Jones 	 * +----------------------+   <-- top of physical memory
176efe42003SAndrew Jones 	 * |                      |
177efe42003SAndrew Jones 	 * ~                      ~
178efe42003SAndrew Jones 	 * |                      |
17901c82c0aSAndrew Jones 	 * +----------------------+   <-- top of initrd
18001c82c0aSAndrew Jones 	 * |                      |
181efe42003SAndrew Jones 	 * +----------------------+   <-- top of FDT
182efe42003SAndrew Jones 	 * |                      |
183efe42003SAndrew Jones 	 * +----------------------+   <-- top of cpu0's stack
184efe42003SAndrew Jones 	 * |                      |
18575a58702SAndrew Jones 	 * +----------------------+   <-- top of text/data/bss/toc sections,
18675a58702SAndrew Jones 	 * |                      |       see powerpc/flat.lds
187efe42003SAndrew Jones 	 * |                      |
188efe42003SAndrew Jones 	 * +----------------------+   <-- load address
189efe42003SAndrew Jones 	 * |                      |
190efe42003SAndrew Jones 	 * +----------------------+
191d72b0449SAndrew Jones 	 */
192d72b0449SAndrew Jones 	fdt_size = fdt_totalsize(fdt);
193efe42003SAndrew Jones 	ret = fdt_move(fdt, freemem, fdt_size);
194d72b0449SAndrew Jones 	assert(ret == 0);
195efe42003SAndrew Jones 	ret = dt_init(freemem);
196d72b0449SAndrew Jones 	assert(ret == 0);
197efe42003SAndrew Jones 	freemem += fdt_size;
198d72b0449SAndrew Jones 
19901c82c0aSAndrew Jones 	ret = dt_get_initrd(&tmp, &initrd_size);
20001c82c0aSAndrew Jones 	assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
20101c82c0aSAndrew Jones 	if (ret == 0) {
20201c82c0aSAndrew Jones 		initrd = freemem;
20301c82c0aSAndrew Jones 		memmove(initrd, tmp, initrd_size);
20401c82c0aSAndrew Jones 		freemem += initrd_size;
20501c82c0aSAndrew Jones 	}
20601c82c0aSAndrew Jones 
20799bb51c2SNicholas Piggin 	assert(STACK_INT_FRAME_SIZE % 16 == 0);
20899bb51c2SNicholas Piggin 
209efe42003SAndrew Jones 	/* call init functions */
210d72b0449SAndrew Jones 	cpu_init();
211d35482d5SAndrew Jones 
212d35482d5SAndrew Jones 	/* cpu_init must be called before mem_init */
213efe42003SAndrew Jones 	mem_init(PAGE_ALIGN((unsigned long)freemem));
214d35482d5SAndrew Jones 
215d35482d5SAndrew Jones 	/* mem_init must be called before io_init */
216d72b0449SAndrew Jones 	io_init();
217d72b0449SAndrew Jones 
218efe42003SAndrew Jones 	/* finish setup */
219d72b0449SAndrew Jones 	ret = dt_get_bootargs(&bootargs);
220d81b83fdSAndrew Jones 	assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
221809ebcb3SAndrew Jones 	setup_args_progname(bootargs);
222f266c3e8SAndrew Jones 
223f266c3e8SAndrew Jones 	if (initrd) {
224f266c3e8SAndrew Jones 		/* environ is currently the only file in the initrd */
225f266c3e8SAndrew Jones 		char *env = malloc(initrd_size);
226f266c3e8SAndrew Jones 		memcpy(env, initrd, initrd_size);
227f266c3e8SAndrew Jones 		setup_env(env, initrd_size);
228f266c3e8SAndrew Jones 	}
229d72b0449SAndrew Jones }
230