xref: /kvm-unit-tests/lib/arm/setup.c (revision 9a008986caac35c1120b8552eb610b7793fe613a)
1 /*
2  * Initialize machine setup information and I/O.
3  *
4  * After running setup() unit tests may query how many cpus they have
5  * (nr_cpus), how much memory they have (PHYS_END - PHYS_OFFSET), may
6  * use dynamic memory allocation (malloc, etc.), printf, and exit.
7  * Finally, argc and argv are also ready to be passed to main().
8  *
9  * Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com>
10  *
11  * This work is licensed under the terms of the GNU LGPL, version 2.
12  */
13 #include <libcflat.h>
14 #include <libfdt/libfdt.h>
15 #include <devicetree.h>
16 #include <alloc.h>
17 #include <asm/thread_info.h>
18 #include <asm/setup.h>
19 #include <asm/page.h>
20 #include <asm/mmu.h>
21 #include <asm/smp.h>
22 
23 extern unsigned long stacktop;
24 extern void io_init(void);
25 extern void setup_args_progname(const char *args);
26 
27 u32 cpus[NR_CPUS] = { [0 ... NR_CPUS-1] = (~0U) };
28 int nr_cpus;
29 
30 struct mem_region mem_regions[NR_MEM_REGIONS];
31 phys_addr_t __phys_offset, __phys_end;
32 
33 static void cpu_set(int fdtnode __unused, u32 regval, void *info __unused)
34 {
35 	int cpu = nr_cpus++;
36 
37 	if (cpu >= NR_CPUS) {
38 		printf("Number cpus exceeds maximum supported (%d).\n",
39 			NR_CPUS);
40 		assert(0);
41 	}
42 	cpus[cpu] = regval;
43 	set_cpu_present(cpu, true);
44 }
45 
46 static void cpu_init(void)
47 {
48 	int ret;
49 
50 	nr_cpus = 0;
51 	ret = dt_for_each_cpu_node(cpu_set, NULL);
52 	assert(ret == 0);
53 	set_cpu_online(0, true);
54 }
55 
56 static void mem_init(phys_addr_t freemem_start)
57 {
58 	struct dt_pbus_reg regs[NR_MEM_REGIONS];
59 	struct mem_region primary, mem = {
60 		.start = (phys_addr_t)-1,
61 	};
62 	int nr_regs, i;
63 
64 	nr_regs = dt_get_memory_params(regs, NR_MEM_REGIONS);
65 	assert(nr_regs > 0);
66 
67 	primary.end = 0;
68 
69 	for (i = 0; i < nr_regs; ++i) {
70 		mem_regions[i].start = regs[i].addr;
71 		mem_regions[i].end = regs[i].addr + regs[i].size;
72 
73 		/*
74 		 * pick the region we're in for our primary region
75 		 */
76 		if (freemem_start >= mem_regions[i].start
77 				&& freemem_start < mem_regions[i].end) {
78 			mem_regions[i].flags |= MR_F_PRIMARY;
79 			primary = mem_regions[i];
80 		}
81 
82 		/*
83 		 * set the lowest and highest addresses found,
84 		 * ignoring potential gaps
85 		 */
86 		if (mem_regions[i].start < mem.start)
87 			mem.start = mem_regions[i].start;
88 		if (mem_regions[i].end > mem.end)
89 			mem.end = mem_regions[i].end;
90 	}
91 	assert(primary.end != 0);
92 	assert(!(mem.start & ~PHYS_MASK) && !((mem.end - 1) & ~PHYS_MASK));
93 
94 	__phys_offset = mem.start;	/* PHYS_OFFSET */
95 	__phys_end = mem.end;		/* PHYS_END */
96 
97 	phys_alloc_init(freemem_start, primary.end - freemem_start);
98 	phys_alloc_set_minimum_alignment(SMP_CACHE_BYTES);
99 
100 	mmu_enable_idmap();
101 }
102 
103 void setup(const void *fdt)
104 {
105 	const char *bootargs;
106 	u32 fdt_size;
107 	int ret;
108 
109 	/*
110 	 * Move the fdt to just above the stack. The free memory
111 	 * then starts just after the fdt.
112 	 */
113 	fdt_size = fdt_totalsize(fdt);
114 	ret = fdt_move(fdt, &stacktop, fdt_size);
115 	assert(ret == 0);
116 	ret = dt_init(&stacktop);
117 	assert(ret == 0);
118 
119 	mem_init(PAGE_ALIGN((unsigned long)&stacktop + fdt_size));
120 	io_init();
121 	cpu_init();
122 
123 	thread_info_init(current_thread_info(), 0);
124 
125 	ret = dt_get_bootargs(&bootargs);
126 	assert(ret == 0);
127 	setup_args_progname(bootargs);
128 }
129