blob: 622b99e5d3180b91e959daa491c9b45727f752be [file] [log] [blame]
Andrew Jonesd72b0442016-02-13 09:59:42 +01001/*
2 * Initialize machine setup information and I/O.
3 *
4 * After running setup() unit tests may query how many cpus they have
Nicholas Pigginc76b0d02024-05-04 22:28:22 +10005 * (nr_cpus_present), how much memory they have (PHYSICAL_END - PHYSICAL_START),
Andrew Jonesd72b0442016-02-13 09:59:42 +01006 * 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>
Paolo Bonzinidc47ac62017-10-22 14:53:57 +020017#include <alloc_phys.h>
Nicholas Piggin8e4e0512024-05-04 22:28:21 +100018#include <alloc_page.h>
Thomas Huth63d5cbe2017-06-27 06:44:07 +020019#include <argv.h>
Andrew Jonesd72b0442016-02-13 09:59:42 +010020#include <asm/setup.h>
Nicholas Pigginc76b0d02024-05-04 22:28:22 +100021#include <asm/smp.h>
Andrew Jonesd72b0442016-02-13 09:59:42 +010022#include <asm/page.h>
Nicholas Piggin99bb51c2023-12-16 23:42:36 +100023#include <asm/ptrace.h>
Nicholas Piggin610c5a92024-05-04 22:28:15 +100024#include <asm/processor.h>
Laurent Vivier6842bc32016-03-21 12:33:30 +010025#include <asm/hcall.h>
Thomas Huth4ff26ec2018-06-06 10:35:59 +020026#include "io.h"
Andrew Jonesd72b0442016-02-13 09:59:42 +010027
28extern unsigned long stacktop;
Andrew Jonesd72b0442016-02-13 09:59:42 +010029
Andrew Jones01c82c02017-01-13 19:15:24 +010030char *initrd;
31u32 initrd_size;
32
Nicholas Pigginc76b0d02024-05-04 22:28:22 +100033u32 cpu_to_hwid[NR_CPUS] = { [0 ... NR_CPUS-1] = (~0U) };
34int nr_cpus_present;
Suraj Jitindar Singhf4d8d932016-08-19 11:10:13 +100035uint64_t tb_hz;
Andrew Jonesd72b0442016-02-13 09:59:42 +010036
37struct mem_region mem_regions[NR_MEM_REGIONS];
38phys_addr_t __physical_start, __physical_end;
39unsigned __icache_bytes, __dcache_bytes;
40
41struct cpu_set_params {
42 unsigned icache_bytes;
43 unsigned dcache_bytes;
Suraj Jitindar Singhf4d8d932016-08-19 11:10:13 +100044 uint64_t tb_hz;
Andrew Jonesd72b0442016-02-13 09:59:42 +010045};
46
Andrew Jones7a20b742016-12-09 14:05:43 +010047static void cpu_set(int fdtnode, u64 regval, void *info)
Andrew Jonesd72b0442016-02-13 09:59:42 +010048{
Nicholas Pigginc76b0d02024-05-04 22:28:22 +100049 const struct fdt_property *prop;
50 u32 *threads;
Andrew Jonesd72b0442016-02-13 09:59:42 +010051 static bool read_common_info = false;
52 struct cpu_set_params *params = info;
Nicholas Pigginc76b0d02024-05-04 22:28:22 +100053 int nr_threads;
54 int len, i;
Andrew Jonesd72b0442016-02-13 09:59:42 +010055
Nicholas Pigginc76b0d02024-05-04 22:28:22 +100056 /* Get the id array of threads on this node */
57 prop = fdt_get_property(dt_fdt(), fdtnode,
58 "ibm,ppc-interrupt-server#s", &len);
59 assert(prop);
Andrew Jones3a9d03a2017-05-22 20:18:20 +020060
Nicholas Pigginc76b0d02024-05-04 22:28:22 +100061 nr_threads = len >> 2; /* Divide by 4 since 4 bytes per thread */
62 threads = (u32 *)prop->data; /* Array of valid ids */
63
64 for (i = 0; i < nr_threads; i++) {
65 if (nr_cpus_present >= NR_CPUS) {
66 static bool warned = false;
67 if (!warned) {
68 printf("Warning: Number of present CPUs exceeds maximum supported (%d).\n", NR_CPUS);
69 warned = true;
70 }
71 break;
72 }
73 cpu_to_hwid[nr_cpus_present++] = fdt32_to_cpu(threads[i]);
74 }
Andrew Jonesd72b0442016-02-13 09:59:42 +010075
76 if (!read_common_info) {
77 const struct fdt_property *prop;
78 u32 *data;
79
80 prop = fdt_get_property(dt_fdt(), fdtnode,
81 "i-cache-line-size", NULL);
82 assert(prop != NULL);
83 data = (u32 *)prop->data;
84 params->icache_bytes = fdt32_to_cpu(*data);
85
86 prop = fdt_get_property(dt_fdt(), fdtnode,
87 "d-cache-line-size", NULL);
88 assert(prop != NULL);
89 data = (u32 *)prop->data;
90 params->dcache_bytes = fdt32_to_cpu(*data);
91
Suraj Jitindar Singhf4d8d932016-08-19 11:10:13 +100092 prop = fdt_get_property(dt_fdt(), fdtnode,
93 "timebase-frequency", NULL);
94 assert(prop != NULL);
95 data = (u32 *)prop->data;
96 params->tb_hz = fdt32_to_cpu(*data);
97
Andrew Jonesd72b0442016-02-13 09:59:42 +010098 read_common_info = true;
99 }
100}
101
Nicholas Piggin610c5a92024-05-04 22:28:15 +1000102bool cpu_has_hv;
Nicholas Piggin00af1c82024-05-04 22:28:18 +1000103bool cpu_has_power_mce; /* POWER CPU machine checks */
104bool cpu_has_siar;
Nicholas Piggincd27b4b2024-05-04 22:28:16 +1000105bool cpu_has_heai;
Nicholas Piggin00af1c82024-05-04 22:28:18 +1000106bool cpu_has_prefix;
107bool cpu_has_sc_lev; /* sc interrupt has LEV field in SRR1 */
Nicholas Pigginc76b0d02024-05-04 22:28:22 +1000108bool cpu_has_pause_short;
Nicholas Piggin610c5a92024-05-04 22:28:15 +1000109
Nicholas Pigginc76b0d02024-05-04 22:28:22 +1000110static void cpu_init_params(void)
Andrew Jonesd72b0442016-02-13 09:59:42 +0100111{
112 struct cpu_set_params params;
113 int ret;
114
Nicholas Pigginc76b0d02024-05-04 22:28:22 +1000115 nr_cpus_present = 0;
Andrew Jonesd72b0442016-02-13 09:59:42 +0100116 ret = dt_for_each_cpu_node(cpu_set, &params);
117 assert(ret == 0);
118 __icache_bytes = params.icache_bytes;
119 __dcache_bytes = params.dcache_bytes;
Suraj Jitindar Singhf4d8d932016-08-19 11:10:13 +1000120 tb_hz = params.tb_hz;
Laurent Vivier6842bc32016-03-21 12:33:30 +0100121
Nicholas Piggincd27b4b2024-05-04 22:28:16 +1000122 switch (mfspr(SPR_PVR) & PVR_VERSION_MASK) {
123 case PVR_VER_POWER10:
Nicholas Piggin00af1c82024-05-04 22:28:18 +1000124 cpu_has_prefix = true;
125 cpu_has_sc_lev = true;
Nicholas Pigginc76b0d02024-05-04 22:28:22 +1000126 cpu_has_pause_short = true;
Nicholas Piggincd27b4b2024-05-04 22:28:16 +1000127 case PVR_VER_POWER9:
128 case PVR_VER_POWER8E:
129 case PVR_VER_POWER8NVL:
130 case PVR_VER_POWER8:
Nicholas Piggin00af1c82024-05-04 22:28:18 +1000131 cpu_has_power_mce = true;
Nicholas Piggincd27b4b2024-05-04 22:28:16 +1000132 cpu_has_heai = true;
Nicholas Piggin00af1c82024-05-04 22:28:18 +1000133 cpu_has_siar = true;
Nicholas Piggincd27b4b2024-05-04 22:28:16 +1000134 break;
135 default:
136 break;
137 }
Nicholas Piggin00af1c82024-05-04 22:28:18 +1000138
139 if (!cpu_has_hv) /* HEIR is HV register */
140 cpu_has_heai = false;
Andrew Jonesd72b0442016-02-13 09:59:42 +0100141}
142
143static void mem_init(phys_addr_t freemem_start)
144{
145 struct dt_pbus_reg regs[NR_MEM_REGIONS];
146 struct mem_region primary, mem = {
147 .start = (phys_addr_t)-1,
148 };
149 int nr_regs, i;
Nicholas Piggin8e4e0512024-05-04 22:28:21 +1000150 phys_addr_t base, top;
Andrew Jonesd72b0442016-02-13 09:59:42 +0100151
152 nr_regs = dt_get_memory_params(regs, NR_MEM_REGIONS);
153 assert(nr_regs > 0);
154
155 primary.end = 0;
156
157 for (i = 0; i < nr_regs; ++i) {
158 mem_regions[i].start = regs[i].addr;
159 mem_regions[i].end = regs[i].addr + regs[i].size;
160
161 /*
162 * pick the region we're in for our primary region
163 */
164 if (freemem_start >= mem_regions[i].start
165 && freemem_start < mem_regions[i].end) {
166 mem_regions[i].flags |= MR_F_PRIMARY;
167 primary = mem_regions[i];
168 }
169
170 /*
171 * set the lowest and highest addresses found,
172 * ignoring potential gaps
173 */
174 if (mem_regions[i].start < mem.start)
175 mem.start = mem_regions[i].start;
176 if (mem_regions[i].end > mem.end)
177 mem.end = mem_regions[i].end;
178 }
179 assert(primary.end != 0);
180// assert(!(mem.start & ~PHYS_MASK) && !((mem.end - 1) & ~PHYS_MASK));
181
182 __physical_start = mem.start; /* PHYSICAL_START */
183 __physical_end = mem.end; /* PHYSICAL_END */
184
185 phys_alloc_init(freemem_start, primary.end - freemem_start);
186 phys_alloc_set_minimum_alignment(__icache_bytes > __dcache_bytes
187 ? __icache_bytes : __dcache_bytes);
Nicholas Piggin8e4e0512024-05-04 22:28:21 +1000188
189 phys_alloc_get_unused(&base, &top);
190 base = PAGE_ALIGN(base);
191 top &= PAGE_MASK;
192 page_alloc_init_area(0, base >> PAGE_SHIFT, top >> PAGE_SHIFT);
193 page_alloc_ops_enable();
Andrew Jonesd72b0442016-02-13 09:59:42 +0100194}
195
Nicholas Piggin29815052024-05-04 22:28:20 +1000196#define EXCEPTION_STACK_SIZE SZ_64K
197
198static char boot_exception_stack[EXCEPTION_STACK_SIZE];
Nicholas Pigginc76b0d02024-05-04 22:28:22 +1000199struct cpu cpus[NR_CPUS];
200
201void cpu_init(struct cpu *cpu, int cpu_id)
202{
203 cpu->server_no = cpu_id;
204
205 cpu->stack = (unsigned long)memalign(SZ_4K, SZ_64K);
206 cpu->stack += SZ_64K - 64;
207 cpu->exception_stack = (unsigned long)memalign(SZ_4K, SZ_64K);
208 cpu->exception_stack += SZ_64K - 64;
209}
Nicholas Piggin29815052024-05-04 22:28:20 +1000210
Andrew Jonesd72b0442016-02-13 09:59:42 +0100211void setup(const void *fdt)
212{
Andrew Jonesefe42002017-01-13 19:15:22 +0100213 void *freemem = &stacktop;
Andrew Jones01c82c02017-01-13 19:15:24 +0100214 const char *bootargs, *tmp;
Nicholas Pigginc76b0d02024-05-04 22:28:22 +1000215 struct cpu *cpu;
Andrew Jonesd72b0442016-02-13 09:59:42 +0100216 u32 fdt_size;
217 int ret;
218
Nicholas Piggin610c5a92024-05-04 22:28:15 +1000219 cpu_has_hv = !!(mfmsr() & (1ULL << MSR_HV_BIT));
220
Nicholas Pigginc76b0d02024-05-04 22:28:22 +1000221 memset(cpus, 0xff, sizeof(cpus));
222
223 cpu = &cpus[0];
224 cpu->server_no = fdt_boot_cpuid_phys(fdt);
225 cpu->exception_stack = (unsigned long)boot_exception_stack;
226 cpu->exception_stack += EXCEPTION_STACK_SIZE - 64;
227
228 mtspr(SPR_SPRG0, (unsigned long)cpu);
229 __current_cpu = cpu;
Nicholas Piggin29815052024-05-04 22:28:20 +1000230
Nicholas Piggin610c5a92024-05-04 22:28:15 +1000231 enable_mcheck();
232
Andrew Jonesd72b0442016-02-13 09:59:42 +0100233 /*
Andrew Jones01c82c02017-01-13 19:15:24 +0100234 * Before calling mem_init we need to move the fdt and initrd
235 * to safe locations. We move them to construct the memory
Andrew Jonesefe42002017-01-13 19:15:22 +0100236 * map illustrated below:
237 *
238 * +----------------------+ <-- top of physical memory
239 * | |
240 * ~ ~
241 * | |
Andrew Jones01c82c02017-01-13 19:15:24 +0100242 * +----------------------+ <-- top of initrd
243 * | |
Andrew Jonesefe42002017-01-13 19:15:22 +0100244 * +----------------------+ <-- top of FDT
245 * | |
246 * +----------------------+ <-- top of cpu0's stack
247 * | |
Andrew Jones75a58702017-05-25 12:39:36 +0200248 * +----------------------+ <-- top of text/data/bss/toc sections,
249 * | | see powerpc/flat.lds
Andrew Jonesefe42002017-01-13 19:15:22 +0100250 * | |
251 * +----------------------+ <-- load address
252 * | |
253 * +----------------------+
Andrew Jonesd72b0442016-02-13 09:59:42 +0100254 */
255 fdt_size = fdt_totalsize(fdt);
Andrew Jonesefe42002017-01-13 19:15:22 +0100256 ret = fdt_move(fdt, freemem, fdt_size);
Andrew Jonesd72b0442016-02-13 09:59:42 +0100257 assert(ret == 0);
Andrew Jonesefe42002017-01-13 19:15:22 +0100258 ret = dt_init(freemem);
Andrew Jonesd72b0442016-02-13 09:59:42 +0100259 assert(ret == 0);
Andrew Jonesefe42002017-01-13 19:15:22 +0100260 freemem += fdt_size;
Andrew Jonesd72b0442016-02-13 09:59:42 +0100261
Andrew Jones01c82c02017-01-13 19:15:24 +0100262 ret = dt_get_initrd(&tmp, &initrd_size);
263 assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
264 if (ret == 0) {
265 initrd = freemem;
266 memmove(initrd, tmp, initrd_size);
267 freemem += initrd_size;
268 }
269
Nicholas Piggin99bb51c2023-12-16 23:42:36 +1000270 assert(STACK_INT_FRAME_SIZE % 16 == 0);
271
Nicholas Pigginc76b0d02024-05-04 22:28:22 +1000272 /* set parameters from dt */
273 cpu_init_params();
274
275 /* Interrupt Endianness */
276 if (machine_is_pseries()) {
277#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
278 hcall(H_SET_MODE, 1, 4, 0, 0);
279#else
280 hcall(H_SET_MODE, 0, 4, 0, 0);
281#endif
282 }
283
284 cpu_init_ipis();
Andrew Jonesd35482d2016-12-09 12:54:34 +0100285
286 /* cpu_init must be called before mem_init */
Andrew Jonesefe42002017-01-13 19:15:22 +0100287 mem_init(PAGE_ALIGN((unsigned long)freemem));
Andrew Jonesd35482d2016-12-09 12:54:34 +0100288
289 /* mem_init must be called before io_init */
Andrew Jonesd72b0442016-02-13 09:59:42 +0100290 io_init();
291
Andrew Jonesefe42002017-01-13 19:15:22 +0100292 /* finish setup */
Andrew Jonesd72b0442016-02-13 09:59:42 +0100293 ret = dt_get_bootargs(&bootargs);
Andrew Jonesd81b83f2017-01-13 19:15:19 +0100294 assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
Andrew Jones809ebcb2016-07-12 10:18:41 +0200295 setup_args_progname(bootargs);
Andrew Jonesf266c3e2017-01-13 19:15:30 +0100296
297 if (initrd) {
298 /* environ is currently the only file in the initrd */
299 char *env = malloc(initrd_size);
300 memcpy(env, initrd, initrd_size);
301 setup_env(env, initrd_size);
302 }
Andrew Jonesd72b0442016-02-13 09:59:42 +0100303}