xref: /kvm-unit-tests/lib/powerpc/setup.c (revision 809ebcb3f0739a22c7377460f457788456044e93)
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 (PHYSICAL_END - PHYSICAL_START),
6  * may 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) 2016, 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/setup.h>
18 #include <asm/page.h>
19 #include <asm/hcall.h>
20 
21 extern unsigned long stacktop;
22 extern void io_init(void);
23 extern void setup_args_progname(const char *args);
24 
25 u32 cpus[NR_CPUS] = { [0 ... NR_CPUS-1] = (~0U) };
26 int nr_cpus;
27 
28 struct mem_region mem_regions[NR_MEM_REGIONS];
29 phys_addr_t __physical_start, __physical_end;
30 unsigned __icache_bytes, __dcache_bytes;
31 
32 struct cpu_set_params {
33 	unsigned icache_bytes;
34 	unsigned dcache_bytes;
35 };
36 
37 #define EXCEPTION_STACK_SIZE	(32*1024) /* 32kB */
38 
39 static char exception_stack[NR_CPUS][EXCEPTION_STACK_SIZE];
40 
41 static void cpu_set(int fdtnode, u32 regval, void *info)
42 {
43 	static bool read_common_info = false;
44 	struct cpu_set_params *params = info;
45 	int cpu = nr_cpus++;
46 
47 	if (cpu >= NR_CPUS) {
48 		printf("Number cpus exceeds maximum supported (%d).\n",
49 			NR_CPUS);
50 		assert(0);
51 	}
52 	cpus[cpu] = regval;
53 
54 	/* set exception stack address for this CPU (in SPGR0) */
55 
56 	asm volatile ("mtsprg0 %[addr]" ::
57 		      [addr] "r" (exception_stack[cpu + 1]));
58 
59 	if (!read_common_info) {
60 		const struct fdt_property *prop;
61 		u32 *data;
62 
63 		prop = fdt_get_property(dt_fdt(), fdtnode,
64 					"i-cache-line-size", NULL);
65 		assert(prop != NULL);
66 		data = (u32 *)prop->data;
67 		params->icache_bytes = fdt32_to_cpu(*data);
68 
69 		prop = fdt_get_property(dt_fdt(), fdtnode,
70 					"d-cache-line-size", NULL);
71 		assert(prop != NULL);
72 		data = (u32 *)prop->data;
73 		params->dcache_bytes = fdt32_to_cpu(*data);
74 
75 		read_common_info = true;
76 	}
77 }
78 
79 static void cpu_init(void)
80 {
81 	struct cpu_set_params params;
82 	int ret;
83 
84 	nr_cpus = 0;
85 	ret = dt_for_each_cpu_node(cpu_set, &params);
86 	assert(ret == 0);
87 	__icache_bytes = params.icache_bytes;
88 	__dcache_bytes = params.dcache_bytes;
89 
90 	/* Interrupt Endianness */
91 
92 #if  __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
93         hcall(H_SET_MODE, 1, 4, 0, 0);
94 #else
95         hcall(H_SET_MODE, 0, 4, 0, 0);
96 #endif
97 }
98 
99 static void mem_init(phys_addr_t freemem_start)
100 {
101 	struct dt_pbus_reg regs[NR_MEM_REGIONS];
102 	struct mem_region primary, mem = {
103 		.start = (phys_addr_t)-1,
104 	};
105 	int nr_regs, i;
106 
107 	nr_regs = dt_get_memory_params(regs, NR_MEM_REGIONS);
108 	assert(nr_regs > 0);
109 
110 	primary.end = 0;
111 
112 	for (i = 0; i < nr_regs; ++i) {
113 		mem_regions[i].start = regs[i].addr;
114 		mem_regions[i].end = regs[i].addr + regs[i].size;
115 
116 		/*
117 		 * pick the region we're in for our primary region
118 		 */
119 		if (freemem_start >= mem_regions[i].start
120 				&& freemem_start < mem_regions[i].end) {
121 			mem_regions[i].flags |= MR_F_PRIMARY;
122 			primary = mem_regions[i];
123 		}
124 
125 		/*
126 		 * set the lowest and highest addresses found,
127 		 * ignoring potential gaps
128 		 */
129 		if (mem_regions[i].start < mem.start)
130 			mem.start = mem_regions[i].start;
131 		if (mem_regions[i].end > mem.end)
132 			mem.end = mem_regions[i].end;
133 	}
134 	assert(primary.end != 0);
135 //	assert(!(mem.start & ~PHYS_MASK) && !((mem.end - 1) & ~PHYS_MASK));
136 
137 	__physical_start = mem.start;	/* PHYSICAL_START */
138 	__physical_end = mem.end;	/* PHYSICAL_END */
139 
140 	phys_alloc_init(freemem_start, primary.end - freemem_start);
141 	phys_alloc_set_minimum_alignment(__icache_bytes > __dcache_bytes
142 					 ? __icache_bytes : __dcache_bytes);
143 }
144 
145 void setup(const void *fdt)
146 {
147 	const char *bootargs;
148 	u32 fdt_size;
149 	int ret;
150 
151 	/*
152 	 * Move the fdt to just above the stack. The free memory
153 	 * then starts just after the fdt.
154 	 */
155 	fdt_size = fdt_totalsize(fdt);
156 	ret = fdt_move(fdt, &stacktop, fdt_size);
157 	assert(ret == 0);
158 	ret = dt_init(&stacktop);
159 	assert(ret == 0);
160 
161 	cpu_init();
162 	mem_init(PAGE_ALIGN((unsigned long)&stacktop + fdt_size));
163 	io_init();
164 
165 	ret = dt_get_bootargs(&bootargs);
166 	assert(ret == 0);
167 	setup_args_progname(bootargs);
168 }
169