xref: /kvm-unit-tests/lib/arm/setup.c (revision a41100276a89128703db61361f6be878c6473005)
15e61cba0SAndrew Jones /*
25e61cba0SAndrew Jones  * Initialize machine setup information and I/O.
35e61cba0SAndrew Jones  *
45e61cba0SAndrew Jones  * After running setup() unit tests may query how many cpus they have
55e61cba0SAndrew Jones  * (nr_cpus), how much memory they have (PHYS_END - PHYS_OFFSET), may
65e61cba0SAndrew Jones  * use dynamic memory allocation (malloc, etc.), printf, and exit.
75e61cba0SAndrew Jones  * Finally, argc and argv are also ready to be passed to main().
85e61cba0SAndrew Jones  *
95e61cba0SAndrew Jones  * Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com>
105e61cba0SAndrew Jones  *
115e61cba0SAndrew Jones  * This work is licensed under the terms of the GNU LGPL, version 2.
125e61cba0SAndrew Jones  */
138cca5668SAndrew Jones #include <libcflat.h>
148cca5668SAndrew Jones #include <libfdt/libfdt.h>
158cca5668SAndrew Jones #include <devicetree.h>
168cca5668SAndrew Jones #include <alloc.h>
17dc47ac61SPaolo Bonzini #include <alloc_phys.h>
1862bdc67fSAndrew Jones #include <alloc_page.h>
1963d5cbecSThomas Huth #include <argv.h>
20f6d10793SAndrew Jones #include <asm/thread_info.h>
218cca5668SAndrew Jones #include <asm/setup.h>
228cca5668SAndrew Jones #include <asm/page.h>
23410b3bf0SAlexandru Elisei #include <asm/processor.h>
2468ea0e0bSAndrew Jones #include <asm/smp.h>
25*a4110027SAndrew Jones #include <asm/timer.h>
265e61cba0SAndrew Jones 
270df901e0SAndrew Jones #include "io.h"
280df901e0SAndrew Jones 
29ef31e31dSAndrew Jones #define NR_INITIAL_MEM_REGIONS 16
30ef31e31dSAndrew Jones 
315e61cba0SAndrew Jones extern unsigned long stacktop;
325e61cba0SAndrew Jones 
33*a4110027SAndrew Jones struct timer_state __timer_state;
34*a4110027SAndrew Jones 
352c2545b0SAndrew Jones char *initrd;
362c2545b0SAndrew Jones u32 initrd_size;
372c2545b0SAndrew Jones 
38da905c9dSAndrew Jones u64 cpus[NR_CPUS] = { [0 ... NR_CPUS-1] = (u64)~0 };
395e61cba0SAndrew Jones int nr_cpus;
405e61cba0SAndrew Jones 
41ef31e31dSAndrew Jones static struct mem_region __initial_mem_regions[NR_INITIAL_MEM_REGIONS + 1];
42ef31e31dSAndrew Jones struct mem_region *mem_regions = __initial_mem_regions;
435e61cba0SAndrew Jones phys_addr_t __phys_offset, __phys_end;
445e61cba0SAndrew Jones 
45410b3bf0SAlexandru Elisei u32 dcache_line_size;
46410b3bf0SAlexandru Elisei 
47d9729025SAndrew Jones int mpidr_to_cpu(uint64_t mpidr)
48d9729025SAndrew Jones {
49d9729025SAndrew Jones 	int i;
50d9729025SAndrew Jones 
51d9729025SAndrew Jones 	for (i = 0; i < nr_cpus; ++i)
52d9729025SAndrew Jones 		if (cpus[i] == (mpidr & MPIDR_HWID_BITMASK))
53d9729025SAndrew Jones 			return i;
54d9729025SAndrew Jones 	return -1;
55d9729025SAndrew Jones }
56d9729025SAndrew Jones 
577a20b74eSAndrew Jones static void cpu_set(int fdtnode __unused, u64 regval, void *info __unused)
585e61cba0SAndrew Jones {
5968ea0e0bSAndrew Jones 	int cpu = nr_cpus++;
6062de081aSAndrew Jones 
613a9d03a4SAndrew Jones 	assert_msg(cpu < NR_CPUS, "Number cpus exceeds maximum supported (%d).", NR_CPUS);
623a9d03a4SAndrew Jones 
6368ea0e0bSAndrew Jones 	cpus[cpu] = regval;
6468ea0e0bSAndrew Jones 	set_cpu_present(cpu, true);
655e61cba0SAndrew Jones }
665e61cba0SAndrew Jones 
675e61cba0SAndrew Jones static void cpu_init(void)
685e61cba0SAndrew Jones {
6918ab6cadSAndrew Jones 	int ret;
7018ab6cadSAndrew Jones 
715e61cba0SAndrew Jones 	nr_cpus = 0;
7218ab6cadSAndrew Jones 	ret = dt_for_each_cpu_node(cpu_set, NULL);
7318ab6cadSAndrew Jones 	assert(ret == 0);
7468ea0e0bSAndrew Jones 	set_cpu_online(0, true);
75410b3bf0SAlexandru Elisei 	/*
76410b3bf0SAlexandru Elisei 	 * DminLine is log2 of the number of words in the smallest cache line; a
77410b3bf0SAlexandru Elisei 	 * word is 4 bytes.
78410b3bf0SAlexandru Elisei 	 */
79410b3bf0SAlexandru Elisei 	dcache_line_size = 1 << (CTR_DMINLINE(get_ctr()) + 2);
805e61cba0SAndrew Jones }
815e61cba0SAndrew Jones 
82ef31e31dSAndrew Jones unsigned int mem_region_get_flags(phys_addr_t paddr)
83ef31e31dSAndrew Jones {
84ef31e31dSAndrew Jones 	struct mem_region *r;
85ef31e31dSAndrew Jones 
86ef31e31dSAndrew Jones 	for (r = mem_regions; r->end; ++r) {
87ef31e31dSAndrew Jones 		if (paddr >= r->start && paddr < r->end)
88ef31e31dSAndrew Jones 			return r->flags;
89ef31e31dSAndrew Jones 	}
90ef31e31dSAndrew Jones 
91ef31e31dSAndrew Jones 	return MR_F_UNKNOWN;
92ef31e31dSAndrew Jones }
93ef31e31dSAndrew Jones 
945e61cba0SAndrew Jones static void mem_init(phys_addr_t freemem_start)
955e61cba0SAndrew Jones {
96ef31e31dSAndrew Jones 	struct dt_pbus_reg regs[NR_INITIAL_MEM_REGIONS];
9762de081aSAndrew Jones 	struct mem_region primary, mem = {
9862de081aSAndrew Jones 		.start = (phys_addr_t)-1,
9962de081aSAndrew Jones 	};
10062bdc67fSAndrew Jones 	phys_addr_t base, top;
101ef31e31dSAndrew Jones 	int nr_regs, nr_io = 0, i;
1025e61cba0SAndrew Jones 
103ef31e31dSAndrew Jones 	/*
104ef31e31dSAndrew Jones 	 * mach-virt I/O regions:
105ef31e31dSAndrew Jones 	 *   - The first 1G (arm/arm64)
106ef31e31dSAndrew Jones 	 *   - 512M at 256G (arm64, arm uses highmem=off)
107ef31e31dSAndrew Jones 	 *   - 512G at 512G (arm64, arm uses highmem=off)
108ef31e31dSAndrew Jones 	 */
109ef31e31dSAndrew Jones 	mem_regions[nr_io++] = (struct mem_region){ 0, (1ul << 30), MR_F_IO };
110ef31e31dSAndrew Jones #ifdef __aarch64__
111ef31e31dSAndrew Jones 	mem_regions[nr_io++] = (struct mem_region){ (1ul << 38), (1ul << 38) | (1ul << 29), MR_F_IO };
112ef31e31dSAndrew Jones 	mem_regions[nr_io++] = (struct mem_region){ (1ul << 39), (1ul << 40), MR_F_IO };
113ef31e31dSAndrew Jones #endif
114ef31e31dSAndrew Jones 
115ef31e31dSAndrew Jones 	nr_regs = dt_get_memory_params(regs, NR_INITIAL_MEM_REGIONS - nr_io);
11662de081aSAndrew Jones 	assert(nr_regs > 0);
1175e61cba0SAndrew Jones 
118ccc5f542SAndrew Jones 	primary = (struct mem_region){ 0 };
1195e61cba0SAndrew Jones 
12062de081aSAndrew Jones 	for (i = 0; i < nr_regs; ++i) {
121ef31e31dSAndrew Jones 		struct mem_region *r = &mem_regions[nr_io + i];
122ef31e31dSAndrew Jones 
123ef31e31dSAndrew Jones 		r->start = regs[i].addr;
124ef31e31dSAndrew Jones 		r->end = regs[i].addr + regs[i].size;
1255e61cba0SAndrew Jones 
12662de081aSAndrew Jones 		/*
12762de081aSAndrew Jones 		 * pick the region we're in for our primary region
12862de081aSAndrew Jones 		 */
129ef31e31dSAndrew Jones 		if (freemem_start >= r->start && freemem_start < r->end) {
130ef31e31dSAndrew Jones 			r->flags |= MR_F_PRIMARY;
131ef31e31dSAndrew Jones 			primary = *r;
13262de081aSAndrew Jones 		}
1335e61cba0SAndrew Jones 
13462de081aSAndrew Jones 		/*
13562de081aSAndrew Jones 		 * set the lowest and highest addresses found,
13662de081aSAndrew Jones 		 * ignoring potential gaps
13762de081aSAndrew Jones 		 */
138ef31e31dSAndrew Jones 		if (r->start < mem.start)
139ef31e31dSAndrew Jones 			mem.start = r->start;
140ef31e31dSAndrew Jones 		if (r->end > mem.end)
141ef31e31dSAndrew Jones 			mem.end = r->end;
14262de081aSAndrew Jones 	}
14362de081aSAndrew Jones 	assert(primary.end != 0);
14462de081aSAndrew Jones 	assert(!(mem.start & ~PHYS_MASK) && !((mem.end - 1) & ~PHYS_MASK));
14562de081aSAndrew Jones 
146ccc5f542SAndrew Jones 	__phys_offset = primary.start;	/* PHYS_OFFSET */
147ccc5f542SAndrew Jones 	__phys_end = primary.end;	/* PHYS_END */
14862de081aSAndrew Jones 
14962de081aSAndrew Jones 	phys_alloc_init(freemem_start, primary.end - freemem_start);
1505e61cba0SAndrew Jones 	phys_alloc_set_minimum_alignment(SMP_CACHE_BYTES);
151153d1936SAndrew Jones 
15262bdc67fSAndrew Jones 	phys_alloc_get_unused(&base, &top);
15362bdc67fSAndrew Jones 	base = PAGE_ALIGN(base);
15462bdc67fSAndrew Jones 	top = top & PAGE_MASK;
15562bdc67fSAndrew Jones 	assert(sizeof(long) == 8 || !(base >> 32));
15662bdc67fSAndrew Jones 	if (sizeof(long) != 8 && (top >> 32) != 0)
15762bdc67fSAndrew Jones 		top = ((uint64_t)1 << 32);
15862bdc67fSAndrew Jones 	free_pages((void *)(unsigned long)base, top - base);
15962bdc67fSAndrew Jones 	page_alloc_ops_enable();
1605e61cba0SAndrew Jones }
1615e61cba0SAndrew Jones 
162*a4110027SAndrew Jones static void timer_save_state(void)
163*a4110027SAndrew Jones {
164*a4110027SAndrew Jones 	const struct fdt_property *prop;
165*a4110027SAndrew Jones 	const void *fdt = dt_fdt();
166*a4110027SAndrew Jones 	int node, len;
167*a4110027SAndrew Jones 	u32 *data;
168*a4110027SAndrew Jones 
169*a4110027SAndrew Jones 	node = fdt_node_offset_by_compatible(fdt, -1, "arm,armv8-timer");
170*a4110027SAndrew Jones 	assert(node >= 0 || node == -FDT_ERR_NOTFOUND);
171*a4110027SAndrew Jones 
172*a4110027SAndrew Jones 	if (node == -FDT_ERR_NOTFOUND) {
173*a4110027SAndrew Jones 		__timer_state.ptimer.irq = -1;
174*a4110027SAndrew Jones 		__timer_state.vtimer.irq = -1;
175*a4110027SAndrew Jones 		return;
176*a4110027SAndrew Jones 	}
177*a4110027SAndrew Jones 
178*a4110027SAndrew Jones 	/*
179*a4110027SAndrew Jones 	 * From Linux devicetree timer binding documentation
180*a4110027SAndrew Jones 	 *
181*a4110027SAndrew Jones 	 * interrupts <type irq flags>:
182*a4110027SAndrew Jones 	 *	secure timer irq
183*a4110027SAndrew Jones 	 *	non-secure timer irq		(ptimer)
184*a4110027SAndrew Jones 	 *	virtual timer irq		(vtimer)
185*a4110027SAndrew Jones 	 *	hypervisor timer irq
186*a4110027SAndrew Jones 	 */
187*a4110027SAndrew Jones 	prop = fdt_get_property(fdt, node, "interrupts", &len);
188*a4110027SAndrew Jones 	assert(prop && len == (4 * 3 * sizeof(u32)));
189*a4110027SAndrew Jones 
190*a4110027SAndrew Jones 	data = (u32 *)prop->data;
191*a4110027SAndrew Jones 	assert(fdt32_to_cpu(data[3]) == 1 /* PPI */);
192*a4110027SAndrew Jones 	__timer_state.ptimer.irq = fdt32_to_cpu(data[4]);
193*a4110027SAndrew Jones 	__timer_state.ptimer.irq_flags = fdt32_to_cpu(data[5]);
194*a4110027SAndrew Jones 	assert(fdt32_to_cpu(data[6]) == 1 /* PPI */);
195*a4110027SAndrew Jones 	__timer_state.vtimer.irq = fdt32_to_cpu(data[7]);
196*a4110027SAndrew Jones 	__timer_state.vtimer.irq_flags = fdt32_to_cpu(data[8]);
197*a4110027SAndrew Jones }
198*a4110027SAndrew Jones 
199dd4af6b1SAndrew Jones void setup(const void *fdt)
2005e61cba0SAndrew Jones {
2013a98026dSAndrew Jones 	void *freemem = &stacktop;
2022c2545b0SAndrew Jones 	const char *bootargs, *tmp;
2035e61cba0SAndrew Jones 	u32 fdt_size;
20418ab6cadSAndrew Jones 	int ret;
2055e61cba0SAndrew Jones 
2065e61cba0SAndrew Jones 	/*
2072c2545b0SAndrew Jones 	 * Before calling mem_init we need to move the fdt and initrd
2082c2545b0SAndrew Jones 	 * to safe locations. We move them to construct the memory
2093a98026dSAndrew Jones 	 * map illustrated below:
2103a98026dSAndrew Jones 	 *
2113a98026dSAndrew Jones 	 *    +----------------------+   <-- top of physical memory
2123a98026dSAndrew Jones 	 *    |                      |
2133a98026dSAndrew Jones 	 *    ~                      ~
2143a98026dSAndrew Jones 	 *    |                      |
2152c2545b0SAndrew Jones 	 *    +----------------------+   <-- top of initrd
2162c2545b0SAndrew Jones 	 *    |                      |
2173a98026dSAndrew Jones 	 *    +----------------------+   <-- top of FDT
2183a98026dSAndrew Jones 	 *    |                      |
2193a98026dSAndrew Jones 	 *    +----------------------+   <-- top of cpu0's stack
2203a98026dSAndrew Jones 	 *    |                      |
2213a98026dSAndrew Jones 	 *    +----------------------+   <-- top of text/data/bss sections,
2223a98026dSAndrew Jones 	 *    |                      |       see arm/flat.lds
2233a98026dSAndrew Jones 	 *    |                      |
2243a98026dSAndrew Jones 	 *    +----------------------+   <-- load address
2253a98026dSAndrew Jones 	 *    |                      |
2263a98026dSAndrew Jones 	 *    +----------------------+
2275e61cba0SAndrew Jones 	 */
2285e61cba0SAndrew Jones 	fdt_size = fdt_totalsize(fdt);
2293a98026dSAndrew Jones 	ret = fdt_move(fdt, freemem, fdt_size);
23018ab6cadSAndrew Jones 	assert(ret == 0);
2313a98026dSAndrew Jones 	ret = dt_init(freemem);
23218ab6cadSAndrew Jones 	assert(ret == 0);
2333a98026dSAndrew Jones 	freemem += fdt_size;
2345e61cba0SAndrew Jones 
2352c2545b0SAndrew Jones 	ret = dt_get_initrd(&tmp, &initrd_size);
2362c2545b0SAndrew Jones 	assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
2372c2545b0SAndrew Jones 	if (ret == 0) {
2382c2545b0SAndrew Jones 		initrd = freemem;
2392c2545b0SAndrew Jones 		memmove(initrd, tmp, initrd_size);
2402c2545b0SAndrew Jones 		freemem += initrd_size;
2412c2545b0SAndrew Jones 	}
2422c2545b0SAndrew Jones 
2433a98026dSAndrew Jones 	/* call init functions */
24462bdc67fSAndrew Jones 	mem_init(PAGE_ALIGN((unsigned long)freemem));
2455e61cba0SAndrew Jones 	cpu_init();
2465e61cba0SAndrew Jones 
2474f58b399SAndrew Jones 	/* cpu_init must be called before thread_info_init */
248f6d10793SAndrew Jones 	thread_info_init(current_thread_info(), 0);
249f6d10793SAndrew Jones 
2504f58b399SAndrew Jones 	/* mem_init must be called before io_init */
2514f58b399SAndrew Jones 	io_init();
2524f58b399SAndrew Jones 
2533a98026dSAndrew Jones 	/* finish setup */
254*a4110027SAndrew Jones 	timer_save_state();
255*a4110027SAndrew Jones 
25618ab6cadSAndrew Jones 	ret = dt_get_bootargs(&bootargs);
257d81b83fdSAndrew Jones 	assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
258809ebcb3SAndrew Jones 	setup_args_progname(bootargs);
2594968651eSAndrew Jones 
2604968651eSAndrew Jones 	if (initrd) {
2614968651eSAndrew Jones 		/* environ is currently the only file in the initrd */
2624968651eSAndrew Jones 		char *env = malloc(initrd_size);
2634968651eSAndrew Jones 		memcpy(env, initrd, initrd_size);
2644968651eSAndrew Jones 		setup_env(env, initrd_size);
2654968651eSAndrew Jones 	}
2665e61cba0SAndrew Jones }
267