1d72b0449SAndrew Jones /*
2d72b0449SAndrew Jones * Initialize machine setup information and I/O.
3d72b0449SAndrew Jones *
4d72b0449SAndrew Jones * After running setup() unit tests may query how many cpus they have
5c76b0d0aSNicholas Piggin * (nr_cpus_present), how much memory they have (PHYSICAL_END - PHYSICAL_START),
6d72b0449SAndrew Jones * may use dynamic memory allocation (malloc, etc.), printf, and exit.
7d72b0449SAndrew Jones * Finally, argc and argv are also ready to be passed to main().
8d72b0449SAndrew Jones *
9d72b0449SAndrew Jones * Copyright (C) 2016, Red Hat Inc, Andrew Jones <drjones@redhat.com>
10d72b0449SAndrew Jones *
11d72b0449SAndrew Jones * This work is licensed under the terms of the GNU LGPL, version 2.
12d72b0449SAndrew Jones */
13d72b0449SAndrew Jones #include <libcflat.h>
14d72b0449SAndrew Jones #include <libfdt/libfdt.h>
15d72b0449SAndrew Jones #include <devicetree.h>
16d72b0449SAndrew Jones #include <alloc.h>
17dc47ac61SPaolo Bonzini #include <alloc_phys.h>
188e4e0512SNicholas Piggin #include <alloc_page.h>
1963d5cbecSThomas Huth #include <argv.h>
20d72b0449SAndrew Jones #include <asm/setup.h>
21c76b0d0aSNicholas Piggin #include <asm/smp.h>
22d72b0449SAndrew Jones #include <asm/page.h>
2399bb51c2SNicholas Piggin #include <asm/ptrace.h>
24610c5a9cSNicholas Piggin #include <asm/processor.h>
256842bc34SLaurent Vivier #include <asm/hcall.h>
264ff26ec5SThomas Huth #include "io.h"
27d72b0449SAndrew Jones
28d72b0449SAndrew Jones extern unsigned long stacktop;
29d72b0449SAndrew Jones
3001c82c0aSAndrew Jones char *initrd;
3101c82c0aSAndrew Jones u32 initrd_size;
3201c82c0aSAndrew Jones
33c76b0d0aSNicholas Piggin u32 cpu_to_hwid[NR_CPUS] = { [0 ... NR_CPUS-1] = (~0U) };
34c76b0d0aSNicholas Piggin int nr_cpus_present;
35f4d8d939SSuraj Jitindar Singh uint64_t tb_hz;
36d72b0449SAndrew Jones
37d72b0449SAndrew Jones struct mem_region mem_regions[NR_MEM_REGIONS];
38d72b0449SAndrew Jones phys_addr_t __physical_start, __physical_end;
39d72b0449SAndrew Jones unsigned __icache_bytes, __dcache_bytes;
40d72b0449SAndrew Jones
41d72b0449SAndrew Jones struct cpu_set_params {
42d72b0449SAndrew Jones unsigned icache_bytes;
43d72b0449SAndrew Jones unsigned dcache_bytes;
44f4d8d939SSuraj Jitindar Singh uint64_t tb_hz;
45d72b0449SAndrew Jones };
46d72b0449SAndrew Jones
cpu_set(int fdtnode,u64 regval,void * info)477a20b74eSAndrew Jones static void cpu_set(int fdtnode, u64 regval, void *info)
48d72b0449SAndrew Jones {
49c76b0d0aSNicholas Piggin const struct fdt_property *prop;
50c76b0d0aSNicholas Piggin u32 *threads;
51d72b0449SAndrew Jones static bool read_common_info = false;
52d72b0449SAndrew Jones struct cpu_set_params *params = info;
53c76b0d0aSNicholas Piggin int nr_threads;
54c76b0d0aSNicholas Piggin int len, i;
55d72b0449SAndrew Jones
56c76b0d0aSNicholas Piggin /* Get the id array of threads on this node */
57c76b0d0aSNicholas Piggin prop = fdt_get_property(dt_fdt(), fdtnode,
58c76b0d0aSNicholas Piggin "ibm,ppc-interrupt-server#s", &len);
59c76b0d0aSNicholas Piggin assert(prop);
603a9d03a4SAndrew Jones
61c76b0d0aSNicholas Piggin nr_threads = len >> 2; /* Divide by 4 since 4 bytes per thread */
62c76b0d0aSNicholas Piggin threads = (u32 *)prop->data; /* Array of valid ids */
63c76b0d0aSNicholas Piggin
64c76b0d0aSNicholas Piggin for (i = 0; i < nr_threads; i++) {
65c76b0d0aSNicholas Piggin if (nr_cpus_present >= NR_CPUS) {
66c76b0d0aSNicholas Piggin static bool warned = false;
67c76b0d0aSNicholas Piggin if (!warned) {
68c76b0d0aSNicholas Piggin printf("Warning: Number of present CPUs exceeds maximum supported (%d).\n", NR_CPUS);
69c76b0d0aSNicholas Piggin warned = true;
70c76b0d0aSNicholas Piggin }
71c76b0d0aSNicholas Piggin break;
72c76b0d0aSNicholas Piggin }
73c76b0d0aSNicholas Piggin cpu_to_hwid[nr_cpus_present++] = fdt32_to_cpu(threads[i]);
74c76b0d0aSNicholas Piggin }
75d72b0449SAndrew Jones
76d72b0449SAndrew Jones if (!read_common_info) {
77d72b0449SAndrew Jones const struct fdt_property *prop;
78d72b0449SAndrew Jones u32 *data;
79d72b0449SAndrew Jones
80d72b0449SAndrew Jones prop = fdt_get_property(dt_fdt(), fdtnode,
81d72b0449SAndrew Jones "i-cache-line-size", NULL);
82d72b0449SAndrew Jones assert(prop != NULL);
83d72b0449SAndrew Jones data = (u32 *)prop->data;
84d72b0449SAndrew Jones params->icache_bytes = fdt32_to_cpu(*data);
85d72b0449SAndrew Jones
86d72b0449SAndrew Jones prop = fdt_get_property(dt_fdt(), fdtnode,
87d72b0449SAndrew Jones "d-cache-line-size", NULL);
88d72b0449SAndrew Jones assert(prop != NULL);
89d72b0449SAndrew Jones data = (u32 *)prop->data;
90d72b0449SAndrew Jones params->dcache_bytes = fdt32_to_cpu(*data);
91d72b0449SAndrew Jones
92f4d8d939SSuraj Jitindar Singh prop = fdt_get_property(dt_fdt(), fdtnode,
93f4d8d939SSuraj Jitindar Singh "timebase-frequency", NULL);
94f4d8d939SSuraj Jitindar Singh assert(prop != NULL);
95f4d8d939SSuraj Jitindar Singh data = (u32 *)prop->data;
96f4d8d939SSuraj Jitindar Singh params->tb_hz = fdt32_to_cpu(*data);
97f4d8d939SSuraj Jitindar Singh
98d72b0449SAndrew Jones read_common_info = true;
99d72b0449SAndrew Jones }
100d72b0449SAndrew Jones }
101d72b0449SAndrew Jones
102610c5a9cSNicholas Piggin bool cpu_has_hv;
10300af1c84SNicholas Piggin bool cpu_has_power_mce; /* POWER CPU machine checks */
10400af1c84SNicholas Piggin bool cpu_has_siar;
105cd27b4baSNicholas Piggin bool cpu_has_heai;
106d4c8e725SNicholas Piggin bool cpu_has_radix;
10700af1c84SNicholas Piggin bool cpu_has_prefix;
10800af1c84SNicholas Piggin bool cpu_has_sc_lev; /* sc interrupt has LEV field in SRR1 */
109c76b0d0aSNicholas Piggin bool cpu_has_pause_short;
110610c5a9cSNicholas Piggin
cpu_init_params(void)111c76b0d0aSNicholas Piggin static void cpu_init_params(void)
112d72b0449SAndrew Jones {
113d72b0449SAndrew Jones struct cpu_set_params params;
114d72b0449SAndrew Jones int ret;
115d72b0449SAndrew Jones
116c76b0d0aSNicholas Piggin nr_cpus_present = 0;
117d72b0449SAndrew Jones ret = dt_for_each_cpu_node(cpu_set, ¶ms);
118d72b0449SAndrew Jones assert(ret == 0);
119d72b0449SAndrew Jones __icache_bytes = params.icache_bytes;
120d72b0449SAndrew Jones __dcache_bytes = params.dcache_bytes;
121f4d8d939SSuraj Jitindar Singh tb_hz = params.tb_hz;
1226842bc34SLaurent Vivier
123cd27b4baSNicholas Piggin switch (mfspr(SPR_PVR) & PVR_VERSION_MASK) {
124cd27b4baSNicholas Piggin case PVR_VER_POWER10:
12500af1c84SNicholas Piggin cpu_has_prefix = true;
12600af1c84SNicholas Piggin cpu_has_sc_lev = true;
127c76b0d0aSNicholas Piggin cpu_has_pause_short = true;
128cd27b4baSNicholas Piggin case PVR_VER_POWER9:
129d4c8e725SNicholas Piggin cpu_has_radix = true;
130cd27b4baSNicholas Piggin case PVR_VER_POWER8E:
131cd27b4baSNicholas Piggin case PVR_VER_POWER8NVL:
132cd27b4baSNicholas Piggin case PVR_VER_POWER8:
13300af1c84SNicholas Piggin cpu_has_power_mce = true;
134cd27b4baSNicholas Piggin cpu_has_heai = true;
13500af1c84SNicholas Piggin cpu_has_siar = true;
136cd27b4baSNicholas Piggin break;
137cd27b4baSNicholas Piggin default:
138cd27b4baSNicholas Piggin break;
139cd27b4baSNicholas Piggin }
14000af1c84SNicholas Piggin
14100af1c84SNicholas Piggin if (!cpu_has_hv) /* HEIR is HV register */
14200af1c84SNicholas Piggin cpu_has_heai = false;
143610c5a9cSNicholas Piggin }
144d72b0449SAndrew Jones
mem_init(phys_addr_t freemem_start)145d72b0449SAndrew Jones static void mem_init(phys_addr_t freemem_start)
146d72b0449SAndrew Jones {
147d72b0449SAndrew Jones struct dt_pbus_reg regs[NR_MEM_REGIONS];
148d72b0449SAndrew Jones struct mem_region primary, mem = {
149d72b0449SAndrew Jones .start = (phys_addr_t)-1,
150d72b0449SAndrew Jones };
151d72b0449SAndrew Jones int nr_regs, i;
1528e4e0512SNicholas Piggin phys_addr_t base, top;
153d72b0449SAndrew Jones
154d72b0449SAndrew Jones nr_regs = dt_get_memory_params(regs, NR_MEM_REGIONS);
155d72b0449SAndrew Jones assert(nr_regs > 0);
156d72b0449SAndrew Jones
157d72b0449SAndrew Jones primary.end = 0;
158d72b0449SAndrew Jones
159d72b0449SAndrew Jones for (i = 0; i < nr_regs; ++i) {
160d72b0449SAndrew Jones mem_regions[i].start = regs[i].addr;
161d72b0449SAndrew Jones mem_regions[i].end = regs[i].addr + regs[i].size;
162d72b0449SAndrew Jones
163d72b0449SAndrew Jones /*
164d72b0449SAndrew Jones * pick the region we're in for our primary region
165d72b0449SAndrew Jones */
166d72b0449SAndrew Jones if (freemem_start >= mem_regions[i].start
167d72b0449SAndrew Jones && freemem_start < mem_regions[i].end) {
168d72b0449SAndrew Jones mem_regions[i].flags |= MR_F_PRIMARY;
169d72b0449SAndrew Jones primary = mem_regions[i];
170d72b0449SAndrew Jones }
171d72b0449SAndrew Jones
172d72b0449SAndrew Jones /*
173d72b0449SAndrew Jones * set the lowest and highest addresses found,
174d72b0449SAndrew Jones * ignoring potential gaps
175d72b0449SAndrew Jones */
176d72b0449SAndrew Jones if (mem_regions[i].start < mem.start)
177d72b0449SAndrew Jones mem.start = mem_regions[i].start;
178d72b0449SAndrew Jones if (mem_regions[i].end > mem.end)
179d72b0449SAndrew Jones mem.end = mem_regions[i].end;
180d72b0449SAndrew Jones }
181d72b0449SAndrew Jones assert(primary.end != 0);
182d72b0449SAndrew Jones // assert(!(mem.start & ~PHYS_MASK) && !((mem.end - 1) & ~PHYS_MASK));
183d72b0449SAndrew Jones
184d72b0449SAndrew Jones __physical_start = mem.start; /* PHYSICAL_START */
185d72b0449SAndrew Jones __physical_end = mem.end; /* PHYSICAL_END */
186d72b0449SAndrew Jones
187d72b0449SAndrew Jones phys_alloc_init(freemem_start, primary.end - freemem_start);
188d72b0449SAndrew Jones phys_alloc_set_minimum_alignment(__icache_bytes > __dcache_bytes
189d72b0449SAndrew Jones ? __icache_bytes : __dcache_bytes);
1908e4e0512SNicholas Piggin
1918e4e0512SNicholas Piggin phys_alloc_get_unused(&base, &top);
1928e4e0512SNicholas Piggin base = PAGE_ALIGN(base);
1938e4e0512SNicholas Piggin top &= PAGE_MASK;
1948e4e0512SNicholas Piggin page_alloc_init_area(0, base >> PAGE_SHIFT, top >> PAGE_SHIFT);
1958e4e0512SNicholas Piggin page_alloc_ops_enable();
196d72b0449SAndrew Jones }
197d72b0449SAndrew Jones
19829815050SNicholas Piggin #define EXCEPTION_STACK_SIZE SZ_64K
19929815050SNicholas Piggin
20029815050SNicholas Piggin static char boot_exception_stack[EXCEPTION_STACK_SIZE];
201c76b0d0aSNicholas Piggin struct cpu cpus[NR_CPUS];
202c76b0d0aSNicholas Piggin
cpu_init(struct cpu * cpu,int cpu_id)203c76b0d0aSNicholas Piggin void cpu_init(struct cpu *cpu, int cpu_id)
204c76b0d0aSNicholas Piggin {
205c76b0d0aSNicholas Piggin cpu->server_no = cpu_id;
206c76b0d0aSNicholas Piggin
207d4c8e725SNicholas Piggin cpu->stack = (unsigned long)memalign_pages(SZ_4K, SZ_64K);
208c76b0d0aSNicholas Piggin cpu->stack += SZ_64K - 64;
209d4c8e725SNicholas Piggin cpu->exception_stack = (unsigned long)memalign_pages(SZ_4K, SZ_64K);
210c76b0d0aSNicholas Piggin cpu->exception_stack += SZ_64K - 64;
211d4c8e725SNicholas Piggin cpu->pgtable = NULL;
212*93c847c1SNicholas Piggin cpu->in_user = false;
213c76b0d0aSNicholas Piggin }
21429815050SNicholas Piggin
215851ef516SNicholas Piggin bool host_is_tcg;
216851ef516SNicholas Piggin bool host_is_kvm;
217*93c847c1SNicholas Piggin bool is_hvmode;
218851ef516SNicholas Piggin
setup(const void * fdt)219d72b0449SAndrew Jones void setup(const void *fdt)
220d72b0449SAndrew Jones {
221efe42003SAndrew Jones void *freemem = &stacktop;
22201c82c0aSAndrew Jones const char *bootargs, *tmp;
223c76b0d0aSNicholas Piggin struct cpu *cpu;
224d72b0449SAndrew Jones u32 fdt_size;
225d72b0449SAndrew Jones int ret;
226d72b0449SAndrew Jones
227c76b0d0aSNicholas Piggin memset(cpus, 0xff, sizeof(cpus));
228c76b0d0aSNicholas Piggin
229c76b0d0aSNicholas Piggin cpu = &cpus[0];
230c76b0d0aSNicholas Piggin cpu->server_no = fdt_boot_cpuid_phys(fdt);
231c76b0d0aSNicholas Piggin cpu->exception_stack = (unsigned long)boot_exception_stack;
232c76b0d0aSNicholas Piggin cpu->exception_stack += EXCEPTION_STACK_SIZE - 64;
233d4c8e725SNicholas Piggin cpu->pgtable = NULL;
234*93c847c1SNicholas Piggin cpu->in_user = false;
235c76b0d0aSNicholas Piggin
236c76b0d0aSNicholas Piggin mtspr(SPR_SPRG0, (unsigned long)cpu);
237c76b0d0aSNicholas Piggin __current_cpu = cpu;
23829815050SNicholas Piggin
239*93c847c1SNicholas Piggin cpu_has_hv = !!(mfmsr() & (1ULL << MSR_HV_BIT));
240*93c847c1SNicholas Piggin
241610c5a9cSNicholas Piggin enable_mcheck();
242610c5a9cSNicholas Piggin
243d72b0449SAndrew Jones /*
24401c82c0aSAndrew Jones * Before calling mem_init we need to move the fdt and initrd
24501c82c0aSAndrew Jones * to safe locations. We move them to construct the memory
246efe42003SAndrew Jones * map illustrated below:
247efe42003SAndrew Jones *
248efe42003SAndrew Jones * +----------------------+ <-- top of physical memory
249efe42003SAndrew Jones * | |
250efe42003SAndrew Jones * ~ ~
251efe42003SAndrew Jones * | |
25201c82c0aSAndrew Jones * +----------------------+ <-- top of initrd
25301c82c0aSAndrew Jones * | |
254efe42003SAndrew Jones * +----------------------+ <-- top of FDT
255efe42003SAndrew Jones * | |
256efe42003SAndrew Jones * +----------------------+ <-- top of cpu0's stack
257efe42003SAndrew Jones * | |
25875a58702SAndrew Jones * +----------------------+ <-- top of text/data/bss/toc sections,
25975a58702SAndrew Jones * | | see powerpc/flat.lds
260efe42003SAndrew Jones * | |
261efe42003SAndrew Jones * +----------------------+ <-- load address
262efe42003SAndrew Jones * | |
263efe42003SAndrew Jones * +----------------------+
264d72b0449SAndrew Jones */
265d72b0449SAndrew Jones fdt_size = fdt_totalsize(fdt);
266efe42003SAndrew Jones ret = fdt_move(fdt, freemem, fdt_size);
267d72b0449SAndrew Jones assert(ret == 0);
268efe42003SAndrew Jones ret = dt_init(freemem);
269d72b0449SAndrew Jones assert(ret == 0);
270efe42003SAndrew Jones freemem += fdt_size;
271d72b0449SAndrew Jones
272851ef516SNicholas Piggin if (!fdt_node_check_compatible(fdt, 0, "qemu,pseries")) {
273851ef516SNicholas Piggin assert(!cpu_has_hv);
274851ef516SNicholas Piggin
275851ef516SNicholas Piggin /*
276851ef516SNicholas Piggin * host_is_tcg incorrectly does not get set when running
277851ef516SNicholas Piggin * KVM on a TCG host (using powernv HV emulation or spapr
278851ef516SNicholas Piggin * nested HV).
279851ef516SNicholas Piggin */
280851ef516SNicholas Piggin ret = fdt_subnode_offset(fdt, 0, "hypervisor");
281851ef516SNicholas Piggin if (ret < 0) {
282851ef516SNicholas Piggin host_is_tcg = true;
283851ef516SNicholas Piggin host_is_kvm = false;
284851ef516SNicholas Piggin } else {
285851ef516SNicholas Piggin /* KVM is the only supported hypervisor */
286851ef516SNicholas Piggin assert(!fdt_node_check_compatible(fdt, ret, "linux,kvm"));
287851ef516SNicholas Piggin host_is_tcg = false;
288851ef516SNicholas Piggin host_is_kvm = true;
289851ef516SNicholas Piggin }
290851ef516SNicholas Piggin } else {
291851ef516SNicholas Piggin assert(cpu_has_hv);
292851ef516SNicholas Piggin host_is_tcg = true;
293851ef516SNicholas Piggin host_is_kvm = false;
294851ef516SNicholas Piggin }
29501c82c0aSAndrew Jones ret = dt_get_initrd(&tmp, &initrd_size);
29601c82c0aSAndrew Jones assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
29701c82c0aSAndrew Jones if (ret == 0) {
29801c82c0aSAndrew Jones initrd = freemem;
29901c82c0aSAndrew Jones memmove(initrd, tmp, initrd_size);
30001c82c0aSAndrew Jones freemem += initrd_size;
30101c82c0aSAndrew Jones }
30201c82c0aSAndrew Jones
30399bb51c2SNicholas Piggin assert(STACK_INT_FRAME_SIZE % 16 == 0);
30499bb51c2SNicholas Piggin
305c76b0d0aSNicholas Piggin /* set parameters from dt */
306c76b0d0aSNicholas Piggin cpu_init_params();
307c76b0d0aSNicholas Piggin
308c76b0d0aSNicholas Piggin /* Interrupt Endianness */
309c76b0d0aSNicholas Piggin if (machine_is_pseries()) {
310c76b0d0aSNicholas Piggin #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
311c76b0d0aSNicholas Piggin hcall(H_SET_MODE, 1, 4, 0, 0);
312c76b0d0aSNicholas Piggin #else
313c76b0d0aSNicholas Piggin hcall(H_SET_MODE, 0, 4, 0, 0);
314c76b0d0aSNicholas Piggin #endif
315c76b0d0aSNicholas Piggin }
316c76b0d0aSNicholas Piggin
317c76b0d0aSNicholas Piggin cpu_init_ipis();
318d35482d5SAndrew Jones
319d35482d5SAndrew Jones /* cpu_init must be called before mem_init */
320efe42003SAndrew Jones mem_init(PAGE_ALIGN((unsigned long)freemem));
321d35482d5SAndrew Jones
322d35482d5SAndrew Jones /* mem_init must be called before io_init */
323d72b0449SAndrew Jones io_init();
324d72b0449SAndrew Jones
325efe42003SAndrew Jones /* finish setup */
326d72b0449SAndrew Jones ret = dt_get_bootargs(&bootargs);
327d81b83fdSAndrew Jones assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
328809ebcb3SAndrew Jones setup_args_progname(bootargs);
329f266c3e8SAndrew Jones
330f266c3e8SAndrew Jones if (initrd) {
331f266c3e8SAndrew Jones /* environ is currently the only file in the initrd */
332f266c3e8SAndrew Jones char *env = malloc(initrd_size);
333f266c3e8SAndrew Jones memcpy(env, initrd, initrd_size);
334f266c3e8SAndrew Jones setup_env(env, initrd_size);
335f266c3e8SAndrew Jones }
336d72b0449SAndrew Jones }
337