xref: /kvm-unit-tests/lib/arm/setup.c (revision 0df901e0379da84a0eaec1daf464c699995dddf5)
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>
2368ea0e0bSAndrew Jones #include <asm/smp.h>
245e61cba0SAndrew Jones 
25*0df901e0SAndrew Jones #include "io.h"
26*0df901e0SAndrew Jones 
275e61cba0SAndrew Jones extern unsigned long stacktop;
285e61cba0SAndrew Jones 
292c2545b0SAndrew Jones char *initrd;
302c2545b0SAndrew Jones u32 initrd_size;
312c2545b0SAndrew Jones 
32da905c9dSAndrew Jones u64 cpus[NR_CPUS] = { [0 ... NR_CPUS-1] = (u64)~0 };
335e61cba0SAndrew Jones int nr_cpus;
345e61cba0SAndrew Jones 
3562de081aSAndrew Jones struct mem_region mem_regions[NR_MEM_REGIONS];
365e61cba0SAndrew Jones phys_addr_t __phys_offset, __phys_end;
375e61cba0SAndrew Jones 
38d9729025SAndrew Jones int mpidr_to_cpu(uint64_t mpidr)
39d9729025SAndrew Jones {
40d9729025SAndrew Jones 	int i;
41d9729025SAndrew Jones 
42d9729025SAndrew Jones 	for (i = 0; i < nr_cpus; ++i)
43d9729025SAndrew Jones 		if (cpus[i] == (mpidr & MPIDR_HWID_BITMASK))
44d9729025SAndrew Jones 			return i;
45d9729025SAndrew Jones 	return -1;
46d9729025SAndrew Jones }
47d9729025SAndrew Jones 
487a20b74eSAndrew Jones static void cpu_set(int fdtnode __unused, u64 regval, void *info __unused)
495e61cba0SAndrew Jones {
5068ea0e0bSAndrew Jones 	int cpu = nr_cpus++;
5162de081aSAndrew Jones 
523a9d03a4SAndrew Jones 	assert_msg(cpu < NR_CPUS, "Number cpus exceeds maximum supported (%d).", NR_CPUS);
533a9d03a4SAndrew Jones 
5468ea0e0bSAndrew Jones 	cpus[cpu] = regval;
5568ea0e0bSAndrew Jones 	set_cpu_present(cpu, true);
565e61cba0SAndrew Jones }
575e61cba0SAndrew Jones 
585e61cba0SAndrew Jones static void cpu_init(void)
595e61cba0SAndrew Jones {
6018ab6cadSAndrew Jones 	int ret;
6118ab6cadSAndrew Jones 
625e61cba0SAndrew Jones 	nr_cpus = 0;
6318ab6cadSAndrew Jones 	ret = dt_for_each_cpu_node(cpu_set, NULL);
6418ab6cadSAndrew Jones 	assert(ret == 0);
6568ea0e0bSAndrew Jones 	set_cpu_online(0, true);
665e61cba0SAndrew Jones }
675e61cba0SAndrew Jones 
685e61cba0SAndrew Jones static void mem_init(phys_addr_t freemem_start)
695e61cba0SAndrew Jones {
7062de081aSAndrew Jones 	struct dt_pbus_reg regs[NR_MEM_REGIONS];
7162de081aSAndrew Jones 	struct mem_region primary, mem = {
7262de081aSAndrew Jones 		.start = (phys_addr_t)-1,
7362de081aSAndrew Jones 	};
7462bdc67fSAndrew Jones 	phys_addr_t base, top;
7562de081aSAndrew Jones 	int nr_regs, i;
765e61cba0SAndrew Jones 
7762de081aSAndrew Jones 	nr_regs = dt_get_memory_params(regs, NR_MEM_REGIONS);
7862de081aSAndrew Jones 	assert(nr_regs > 0);
795e61cba0SAndrew Jones 
80ccc5f542SAndrew Jones 	primary = (struct mem_region){ 0 };
815e61cba0SAndrew Jones 
8262de081aSAndrew Jones 	for (i = 0; i < nr_regs; ++i) {
8362de081aSAndrew Jones 		mem_regions[i].start = regs[i].addr;
8462de081aSAndrew Jones 		mem_regions[i].end = regs[i].addr + regs[i].size;
855e61cba0SAndrew Jones 
8662de081aSAndrew Jones 		/*
8762de081aSAndrew Jones 		 * pick the region we're in for our primary region
8862de081aSAndrew Jones 		 */
8962de081aSAndrew Jones 		if (freemem_start >= mem_regions[i].start
9062de081aSAndrew Jones 				&& freemem_start < mem_regions[i].end) {
9162de081aSAndrew Jones 			mem_regions[i].flags |= MR_F_PRIMARY;
9262de081aSAndrew Jones 			primary = mem_regions[i];
9362de081aSAndrew Jones 		}
945e61cba0SAndrew Jones 
9562de081aSAndrew Jones 		/*
9662de081aSAndrew Jones 		 * set the lowest and highest addresses found,
9762de081aSAndrew Jones 		 * ignoring potential gaps
9862de081aSAndrew Jones 		 */
9962de081aSAndrew Jones 		if (mem_regions[i].start < mem.start)
10062de081aSAndrew Jones 			mem.start = mem_regions[i].start;
10162de081aSAndrew Jones 		if (mem_regions[i].end > mem.end)
10262de081aSAndrew Jones 			mem.end = mem_regions[i].end;
10362de081aSAndrew Jones 	}
10462de081aSAndrew Jones 	assert(primary.end != 0);
10562de081aSAndrew Jones 	assert(!(mem.start & ~PHYS_MASK) && !((mem.end - 1) & ~PHYS_MASK));
10662de081aSAndrew Jones 
107ccc5f542SAndrew Jones 	__phys_offset = primary.start;	/* PHYS_OFFSET */
108ccc5f542SAndrew Jones 	__phys_end = primary.end;	/* PHYS_END */
10962de081aSAndrew Jones 
11062de081aSAndrew Jones 	phys_alloc_init(freemem_start, primary.end - freemem_start);
1115e61cba0SAndrew Jones 	phys_alloc_set_minimum_alignment(SMP_CACHE_BYTES);
112153d1936SAndrew Jones 
11362bdc67fSAndrew Jones 	phys_alloc_get_unused(&base, &top);
11462bdc67fSAndrew Jones 	base = PAGE_ALIGN(base);
11562bdc67fSAndrew Jones 	top = top & PAGE_MASK;
11662bdc67fSAndrew Jones 	assert(sizeof(long) == 8 || !(base >> 32));
11762bdc67fSAndrew Jones 	if (sizeof(long) != 8 && (top >> 32) != 0)
11862bdc67fSAndrew Jones 		top = ((uint64_t)1 << 32);
11962bdc67fSAndrew Jones 	free_pages((void *)(unsigned long)base, top - base);
12062bdc67fSAndrew Jones 	page_alloc_ops_enable();
1215e61cba0SAndrew Jones }
1225e61cba0SAndrew Jones 
123dd4af6b1SAndrew Jones void setup(const void *fdt)
1245e61cba0SAndrew Jones {
1253a98026dSAndrew Jones 	void *freemem = &stacktop;
1262c2545b0SAndrew Jones 	const char *bootargs, *tmp;
1275e61cba0SAndrew Jones 	u32 fdt_size;
12818ab6cadSAndrew Jones 	int ret;
1295e61cba0SAndrew Jones 
1305e61cba0SAndrew Jones 	/*
1312c2545b0SAndrew Jones 	 * Before calling mem_init we need to move the fdt and initrd
1322c2545b0SAndrew Jones 	 * to safe locations. We move them to construct the memory
1333a98026dSAndrew Jones 	 * map illustrated below:
1343a98026dSAndrew Jones 	 *
1353a98026dSAndrew Jones 	 *    +----------------------+   <-- top of physical memory
1363a98026dSAndrew Jones 	 *    |                      |
1373a98026dSAndrew Jones 	 *    ~                      ~
1383a98026dSAndrew Jones 	 *    |                      |
1392c2545b0SAndrew Jones 	 *    +----------------------+   <-- top of initrd
1402c2545b0SAndrew Jones 	 *    |                      |
1413a98026dSAndrew Jones 	 *    +----------------------+   <-- top of FDT
1423a98026dSAndrew Jones 	 *    |                      |
1433a98026dSAndrew Jones 	 *    +----------------------+   <-- top of cpu0's stack
1443a98026dSAndrew Jones 	 *    |                      |
1453a98026dSAndrew Jones 	 *    +----------------------+   <-- top of text/data/bss sections,
1463a98026dSAndrew Jones 	 *    |                      |       see arm/flat.lds
1473a98026dSAndrew Jones 	 *    |                      |
1483a98026dSAndrew Jones 	 *    +----------------------+   <-- load address
1493a98026dSAndrew Jones 	 *    |                      |
1503a98026dSAndrew Jones 	 *    +----------------------+
1515e61cba0SAndrew Jones 	 */
1525e61cba0SAndrew Jones 	fdt_size = fdt_totalsize(fdt);
1533a98026dSAndrew Jones 	ret = fdt_move(fdt, freemem, fdt_size);
15418ab6cadSAndrew Jones 	assert(ret == 0);
1553a98026dSAndrew Jones 	ret = dt_init(freemem);
15618ab6cadSAndrew Jones 	assert(ret == 0);
1573a98026dSAndrew Jones 	freemem += fdt_size;
1585e61cba0SAndrew Jones 
1592c2545b0SAndrew Jones 	ret = dt_get_initrd(&tmp, &initrd_size);
1602c2545b0SAndrew Jones 	assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
1612c2545b0SAndrew Jones 	if (ret == 0) {
1622c2545b0SAndrew Jones 		initrd = freemem;
1632c2545b0SAndrew Jones 		memmove(initrd, tmp, initrd_size);
1642c2545b0SAndrew Jones 		freemem += initrd_size;
1652c2545b0SAndrew Jones 	}
1662c2545b0SAndrew Jones 
1673a98026dSAndrew Jones 	/* call init functions */
16862bdc67fSAndrew Jones 	mem_init(PAGE_ALIGN((unsigned long)freemem));
1695e61cba0SAndrew Jones 	cpu_init();
1705e61cba0SAndrew Jones 
1714f58b399SAndrew Jones 	/* cpu_init must be called before thread_info_init */
172f6d10793SAndrew Jones 	thread_info_init(current_thread_info(), 0);
173f6d10793SAndrew Jones 
1744f58b399SAndrew Jones 	/* mem_init must be called before io_init */
1754f58b399SAndrew Jones 	io_init();
1764f58b399SAndrew Jones 
1773a98026dSAndrew Jones 	/* finish setup */
17818ab6cadSAndrew Jones 	ret = dt_get_bootargs(&bootargs);
179d81b83fdSAndrew Jones 	assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
180809ebcb3SAndrew Jones 	setup_args_progname(bootargs);
1814968651eSAndrew Jones 
1824968651eSAndrew Jones 	if (initrd) {
1834968651eSAndrew Jones 		/* environ is currently the only file in the initrd */
1844968651eSAndrew Jones 		char *env = malloc(initrd_size);
1854968651eSAndrew Jones 		memcpy(env, initrd, initrd_size);
1864968651eSAndrew Jones 		setup_env(env, initrd_size);
1874968651eSAndrew Jones 	}
1885e61cba0SAndrew Jones }
189