xref: /kvm-unit-tests/lib/powerpc/setup.c (revision 3a9d03a4a3347e5f70d6651bdf4c28bba024dad1)
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>
17d72b0449SAndrew Jones #include <asm/setup.h>
18d72b0449SAndrew Jones #include <asm/page.h>
196842bc34SLaurent Vivier #include <asm/hcall.h>
20d72b0449SAndrew Jones 
21d72b0449SAndrew Jones extern unsigned long stacktop;
22d72b0449SAndrew Jones extern void io_init(void);
23809ebcb3SAndrew Jones extern void setup_args_progname(const char *args);
24f266c3e8SAndrew Jones extern void setup_env(char *env, int size);
25d72b0449SAndrew Jones 
2601c82c0aSAndrew Jones char *initrd;
2701c82c0aSAndrew Jones u32 initrd_size;
2801c82c0aSAndrew Jones 
29d72b0449SAndrew Jones u32 cpus[NR_CPUS] = { [0 ... NR_CPUS-1] = (~0U) };
30d72b0449SAndrew Jones int nr_cpus;
31f4d8d939SSuraj Jitindar Singh uint64_t tb_hz;
32d72b0449SAndrew Jones 
33d72b0449SAndrew Jones struct mem_region mem_regions[NR_MEM_REGIONS];
34d72b0449SAndrew Jones phys_addr_t __physical_start, __physical_end;
35d72b0449SAndrew Jones unsigned __icache_bytes, __dcache_bytes;
36d72b0449SAndrew Jones 
37d72b0449SAndrew Jones struct cpu_set_params {
38d72b0449SAndrew Jones 	unsigned icache_bytes;
39d72b0449SAndrew Jones 	unsigned dcache_bytes;
40f4d8d939SSuraj Jitindar Singh 	uint64_t tb_hz;
41d72b0449SAndrew Jones };
42d72b0449SAndrew Jones 
436842bc34SLaurent Vivier #define EXCEPTION_STACK_SIZE	(32*1024) /* 32kB */
446842bc34SLaurent Vivier 
456842bc34SLaurent Vivier static char exception_stack[NR_CPUS][EXCEPTION_STACK_SIZE];
466842bc34SLaurent Vivier 
477a20b74eSAndrew Jones static void cpu_set(int fdtnode, u64 regval, void *info)
48d72b0449SAndrew Jones {
49d72b0449SAndrew Jones 	static bool read_common_info = false;
50d72b0449SAndrew Jones 	struct cpu_set_params *params = info;
51d72b0449SAndrew Jones 	int cpu = nr_cpus++;
52d72b0449SAndrew Jones 
53*3a9d03a4SAndrew Jones 	assert_msg(cpu < NR_CPUS, "Number cpus exceeds maximum supported (%d).", NR_CPUS);
54*3a9d03a4SAndrew Jones 
55d72b0449SAndrew Jones 	cpus[cpu] = regval;
56d72b0449SAndrew Jones 
576842bc34SLaurent Vivier 	/* set exception stack address for this CPU (in SPGR0) */
586842bc34SLaurent Vivier 	asm volatile ("mtsprg0 %[addr]" ::
596842bc34SLaurent Vivier 		      [addr] "r" (exception_stack[cpu + 1]));
606842bc34SLaurent Vivier 
61d72b0449SAndrew Jones 	if (!read_common_info) {
62d72b0449SAndrew Jones 		const struct fdt_property *prop;
63d72b0449SAndrew Jones 		u32 *data;
64d72b0449SAndrew Jones 
65d72b0449SAndrew Jones 		prop = fdt_get_property(dt_fdt(), fdtnode,
66d72b0449SAndrew Jones 					"i-cache-line-size", NULL);
67d72b0449SAndrew Jones 		assert(prop != NULL);
68d72b0449SAndrew Jones 		data = (u32 *)prop->data;
69d72b0449SAndrew Jones 		params->icache_bytes = fdt32_to_cpu(*data);
70d72b0449SAndrew Jones 
71d72b0449SAndrew Jones 		prop = fdt_get_property(dt_fdt(), fdtnode,
72d72b0449SAndrew Jones 					"d-cache-line-size", NULL);
73d72b0449SAndrew Jones 		assert(prop != NULL);
74d72b0449SAndrew Jones 		data = (u32 *)prop->data;
75d72b0449SAndrew Jones 		params->dcache_bytes = fdt32_to_cpu(*data);
76d72b0449SAndrew Jones 
77f4d8d939SSuraj Jitindar Singh 		prop = fdt_get_property(dt_fdt(), fdtnode,
78f4d8d939SSuraj Jitindar Singh 					"timebase-frequency", NULL);
79f4d8d939SSuraj Jitindar Singh 		assert(prop != NULL);
80f4d8d939SSuraj Jitindar Singh 		data = (u32 *)prop->data;
81f4d8d939SSuraj Jitindar Singh 		params->tb_hz = fdt32_to_cpu(*data);
82f4d8d939SSuraj Jitindar Singh 
83d72b0449SAndrew Jones 		read_common_info = true;
84d72b0449SAndrew Jones 	}
85d72b0449SAndrew Jones }
86d72b0449SAndrew Jones 
87d72b0449SAndrew Jones static void cpu_init(void)
88d72b0449SAndrew Jones {
89d72b0449SAndrew Jones 	struct cpu_set_params params;
90d72b0449SAndrew Jones 	int ret;
91d72b0449SAndrew Jones 
92d72b0449SAndrew Jones 	nr_cpus = 0;
93d72b0449SAndrew Jones 	ret = dt_for_each_cpu_node(cpu_set, &params);
94d72b0449SAndrew Jones 	assert(ret == 0);
95d72b0449SAndrew Jones 	__icache_bytes = params.icache_bytes;
96d72b0449SAndrew Jones 	__dcache_bytes = params.dcache_bytes;
97f4d8d939SSuraj Jitindar Singh 	tb_hz = params.tb_hz;
986842bc34SLaurent Vivier 
996842bc34SLaurent Vivier 	/* Interrupt Endianness */
1006842bc34SLaurent Vivier 
1016842bc34SLaurent Vivier #if  __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
1026842bc34SLaurent Vivier         hcall(H_SET_MODE, 1, 4, 0, 0);
1036842bc34SLaurent Vivier #else
1046842bc34SLaurent Vivier         hcall(H_SET_MODE, 0, 4, 0, 0);
1056842bc34SLaurent Vivier #endif
106d72b0449SAndrew Jones }
107d72b0449SAndrew Jones 
108d72b0449SAndrew Jones static void mem_init(phys_addr_t freemem_start)
109d72b0449SAndrew Jones {
110d72b0449SAndrew Jones 	struct dt_pbus_reg regs[NR_MEM_REGIONS];
111d72b0449SAndrew Jones 	struct mem_region primary, mem = {
112d72b0449SAndrew Jones 		.start = (phys_addr_t)-1,
113d72b0449SAndrew Jones 	};
114d72b0449SAndrew Jones 	int nr_regs, i;
115d72b0449SAndrew Jones 
116d72b0449SAndrew Jones 	nr_regs = dt_get_memory_params(regs, NR_MEM_REGIONS);
117d72b0449SAndrew Jones 	assert(nr_regs > 0);
118d72b0449SAndrew Jones 
119d72b0449SAndrew Jones 	primary.end = 0;
120d72b0449SAndrew Jones 
121d72b0449SAndrew Jones 	for (i = 0; i < nr_regs; ++i) {
122d72b0449SAndrew Jones 		mem_regions[i].start = regs[i].addr;
123d72b0449SAndrew Jones 		mem_regions[i].end = regs[i].addr + regs[i].size;
124d72b0449SAndrew Jones 
125d72b0449SAndrew Jones 		/*
126d72b0449SAndrew Jones 		 * pick the region we're in for our primary region
127d72b0449SAndrew Jones 		 */
128d72b0449SAndrew Jones 		if (freemem_start >= mem_regions[i].start
129d72b0449SAndrew Jones 				&& freemem_start < mem_regions[i].end) {
130d72b0449SAndrew Jones 			mem_regions[i].flags |= MR_F_PRIMARY;
131d72b0449SAndrew Jones 			primary = mem_regions[i];
132d72b0449SAndrew Jones 		}
133d72b0449SAndrew Jones 
134d72b0449SAndrew Jones 		/*
135d72b0449SAndrew Jones 		 * set the lowest and highest addresses found,
136d72b0449SAndrew Jones 		 * ignoring potential gaps
137d72b0449SAndrew Jones 		 */
138d72b0449SAndrew Jones 		if (mem_regions[i].start < mem.start)
139d72b0449SAndrew Jones 			mem.start = mem_regions[i].start;
140d72b0449SAndrew Jones 		if (mem_regions[i].end > mem.end)
141d72b0449SAndrew Jones 			mem.end = mem_regions[i].end;
142d72b0449SAndrew Jones 	}
143d72b0449SAndrew Jones 	assert(primary.end != 0);
144d72b0449SAndrew Jones //	assert(!(mem.start & ~PHYS_MASK) && !((mem.end - 1) & ~PHYS_MASK));
145d72b0449SAndrew Jones 
146d72b0449SAndrew Jones 	__physical_start = mem.start;	/* PHYSICAL_START */
147d72b0449SAndrew Jones 	__physical_end = mem.end;	/* PHYSICAL_END */
148d72b0449SAndrew Jones 
149d72b0449SAndrew Jones 	phys_alloc_init(freemem_start, primary.end - freemem_start);
150d72b0449SAndrew Jones 	phys_alloc_set_minimum_alignment(__icache_bytes > __dcache_bytes
151d72b0449SAndrew Jones 					 ? __icache_bytes : __dcache_bytes);
152d72b0449SAndrew Jones }
153d72b0449SAndrew Jones 
154d72b0449SAndrew Jones void setup(const void *fdt)
155d72b0449SAndrew Jones {
156efe42003SAndrew Jones 	void *freemem = &stacktop;
15701c82c0aSAndrew Jones 	const char *bootargs, *tmp;
158d72b0449SAndrew Jones 	u32 fdt_size;
159d72b0449SAndrew Jones 	int ret;
160d72b0449SAndrew Jones 
161d72b0449SAndrew Jones 	/*
16201c82c0aSAndrew Jones 	 * Before calling mem_init we need to move the fdt and initrd
16301c82c0aSAndrew Jones 	 * to safe locations. We move them to construct the memory
164efe42003SAndrew Jones 	 * map illustrated below:
165efe42003SAndrew Jones 	 *
166efe42003SAndrew Jones 	 * +----------------------+   <-- top of physical memory
167efe42003SAndrew Jones 	 * |                      |
168efe42003SAndrew Jones 	 * ~                      ~
169efe42003SAndrew Jones 	 * |                      |
17001c82c0aSAndrew Jones 	 * +----------------------+   <-- top of initrd
17101c82c0aSAndrew Jones 	 * |                      |
172efe42003SAndrew Jones 	 * +----------------------+   <-- top of FDT
173efe42003SAndrew Jones 	 * |                      |
174efe42003SAndrew Jones 	 * +----------------------+   <-- top of cpu0's stack
175efe42003SAndrew Jones 	 * |                      |
17675a58702SAndrew Jones 	 * +----------------------+   <-- top of text/data/bss/toc sections,
17775a58702SAndrew Jones 	 * |                      |       see powerpc/flat.lds
178efe42003SAndrew Jones 	 * |                      |
179efe42003SAndrew Jones 	 * +----------------------+   <-- load address
180efe42003SAndrew Jones 	 * |                      |
181efe42003SAndrew Jones 	 * +----------------------+
182d72b0449SAndrew Jones 	 */
183d72b0449SAndrew Jones 	fdt_size = fdt_totalsize(fdt);
184efe42003SAndrew Jones 	ret = fdt_move(fdt, freemem, fdt_size);
185d72b0449SAndrew Jones 	assert(ret == 0);
186efe42003SAndrew Jones 	ret = dt_init(freemem);
187d72b0449SAndrew Jones 	assert(ret == 0);
188efe42003SAndrew Jones 	freemem += fdt_size;
189d72b0449SAndrew Jones 
19001c82c0aSAndrew Jones 	ret = dt_get_initrd(&tmp, &initrd_size);
19101c82c0aSAndrew Jones 	assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
19201c82c0aSAndrew Jones 	if (ret == 0) {
19301c82c0aSAndrew Jones 		initrd = freemem;
19401c82c0aSAndrew Jones 		memmove(initrd, tmp, initrd_size);
19501c82c0aSAndrew Jones 		freemem += initrd_size;
19601c82c0aSAndrew Jones 	}
19701c82c0aSAndrew Jones 
198efe42003SAndrew Jones 	/* call init functions */
199d72b0449SAndrew Jones 	cpu_init();
200d35482d5SAndrew Jones 
201d35482d5SAndrew Jones 	/* cpu_init must be called before mem_init */
202efe42003SAndrew Jones 	mem_init(PAGE_ALIGN((unsigned long)freemem));
203d35482d5SAndrew Jones 
204d35482d5SAndrew Jones 	/* mem_init must be called before io_init */
205d72b0449SAndrew Jones 	io_init();
206d72b0449SAndrew Jones 
207efe42003SAndrew Jones 	/* finish setup */
208d72b0449SAndrew Jones 	ret = dt_get_bootargs(&bootargs);
209d81b83fdSAndrew Jones 	assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
210809ebcb3SAndrew Jones 	setup_args_progname(bootargs);
211f266c3e8SAndrew Jones 
212f266c3e8SAndrew Jones 	if (initrd) {
213f266c3e8SAndrew Jones 		/* environ is currently the only file in the initrd */
214f266c3e8SAndrew Jones 		char *env = malloc(initrd_size);
215f266c3e8SAndrew Jones 		memcpy(env, initrd, initrd_size);
216f266c3e8SAndrew Jones 		setup_env(env, initrd_size);
217f266c3e8SAndrew Jones 	}
218d72b0449SAndrew Jones }
219