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 5*c76b0d0aSNicholas 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> 21*c76b0d0aSNicholas 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 33*c76b0d0aSNicholas Piggin u32 cpu_to_hwid[NR_CPUS] = { [0 ... NR_CPUS-1] = (~0U) }; 34*c76b0d0aSNicholas 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 477a20b74eSAndrew Jones static void cpu_set(int fdtnode, u64 regval, void *info) 48d72b0449SAndrew Jones { 49*c76b0d0aSNicholas Piggin const struct fdt_property *prop; 50*c76b0d0aSNicholas Piggin u32 *threads; 51d72b0449SAndrew Jones static bool read_common_info = false; 52d72b0449SAndrew Jones struct cpu_set_params *params = info; 53*c76b0d0aSNicholas Piggin int nr_threads; 54*c76b0d0aSNicholas Piggin int len, i; 55d72b0449SAndrew Jones 56*c76b0d0aSNicholas Piggin /* Get the id array of threads on this node */ 57*c76b0d0aSNicholas Piggin prop = fdt_get_property(dt_fdt(), fdtnode, 58*c76b0d0aSNicholas Piggin "ibm,ppc-interrupt-server#s", &len); 59*c76b0d0aSNicholas Piggin assert(prop); 603a9d03a4SAndrew Jones 61*c76b0d0aSNicholas Piggin nr_threads = len >> 2; /* Divide by 4 since 4 bytes per thread */ 62*c76b0d0aSNicholas Piggin threads = (u32 *)prop->data; /* Array of valid ids */ 63*c76b0d0aSNicholas Piggin 64*c76b0d0aSNicholas Piggin for (i = 0; i < nr_threads; i++) { 65*c76b0d0aSNicholas Piggin if (nr_cpus_present >= NR_CPUS) { 66*c76b0d0aSNicholas Piggin static bool warned = false; 67*c76b0d0aSNicholas Piggin if (!warned) { 68*c76b0d0aSNicholas Piggin printf("Warning: Number of present CPUs exceeds maximum supported (%d).\n", NR_CPUS); 69*c76b0d0aSNicholas Piggin warned = true; 70*c76b0d0aSNicholas Piggin } 71*c76b0d0aSNicholas Piggin break; 72*c76b0d0aSNicholas Piggin } 73*c76b0d0aSNicholas Piggin cpu_to_hwid[nr_cpus_present++] = fdt32_to_cpu(threads[i]); 74*c76b0d0aSNicholas 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; 10600af1c84SNicholas Piggin bool cpu_has_prefix; 10700af1c84SNicholas Piggin bool cpu_has_sc_lev; /* sc interrupt has LEV field in SRR1 */ 108*c76b0d0aSNicholas Piggin bool cpu_has_pause_short; 109610c5a9cSNicholas Piggin 110*c76b0d0aSNicholas Piggin static void cpu_init_params(void) 111d72b0449SAndrew Jones { 112d72b0449SAndrew Jones struct cpu_set_params params; 113d72b0449SAndrew Jones int ret; 114d72b0449SAndrew Jones 115*c76b0d0aSNicholas Piggin nr_cpus_present = 0; 116d72b0449SAndrew Jones ret = dt_for_each_cpu_node(cpu_set, ¶ms); 117d72b0449SAndrew Jones assert(ret == 0); 118d72b0449SAndrew Jones __icache_bytes = params.icache_bytes; 119d72b0449SAndrew Jones __dcache_bytes = params.dcache_bytes; 120f4d8d939SSuraj Jitindar Singh tb_hz = params.tb_hz; 1216842bc34SLaurent Vivier 122cd27b4baSNicholas Piggin switch (mfspr(SPR_PVR) & PVR_VERSION_MASK) { 123cd27b4baSNicholas Piggin case PVR_VER_POWER10: 12400af1c84SNicholas Piggin cpu_has_prefix = true; 12500af1c84SNicholas Piggin cpu_has_sc_lev = true; 126*c76b0d0aSNicholas Piggin cpu_has_pause_short = true; 127cd27b4baSNicholas Piggin case PVR_VER_POWER9: 128cd27b4baSNicholas Piggin case PVR_VER_POWER8E: 129cd27b4baSNicholas Piggin case PVR_VER_POWER8NVL: 130cd27b4baSNicholas Piggin case PVR_VER_POWER8: 13100af1c84SNicholas Piggin cpu_has_power_mce = true; 132cd27b4baSNicholas Piggin cpu_has_heai = true; 13300af1c84SNicholas Piggin cpu_has_siar = true; 134cd27b4baSNicholas Piggin break; 135cd27b4baSNicholas Piggin default: 136cd27b4baSNicholas Piggin break; 137cd27b4baSNicholas Piggin } 13800af1c84SNicholas Piggin 13900af1c84SNicholas Piggin if (!cpu_has_hv) /* HEIR is HV register */ 14000af1c84SNicholas Piggin cpu_has_heai = false; 141610c5a9cSNicholas Piggin } 142d72b0449SAndrew Jones 143d72b0449SAndrew Jones static void mem_init(phys_addr_t freemem_start) 144d72b0449SAndrew Jones { 145d72b0449SAndrew Jones struct dt_pbus_reg regs[NR_MEM_REGIONS]; 146d72b0449SAndrew Jones struct mem_region primary, mem = { 147d72b0449SAndrew Jones .start = (phys_addr_t)-1, 148d72b0449SAndrew Jones }; 149d72b0449SAndrew Jones int nr_regs, i; 1508e4e0512SNicholas Piggin phys_addr_t base, top; 151d72b0449SAndrew Jones 152d72b0449SAndrew Jones nr_regs = dt_get_memory_params(regs, NR_MEM_REGIONS); 153d72b0449SAndrew Jones assert(nr_regs > 0); 154d72b0449SAndrew Jones 155d72b0449SAndrew Jones primary.end = 0; 156d72b0449SAndrew Jones 157d72b0449SAndrew Jones for (i = 0; i < nr_regs; ++i) { 158d72b0449SAndrew Jones mem_regions[i].start = regs[i].addr; 159d72b0449SAndrew Jones mem_regions[i].end = regs[i].addr + regs[i].size; 160d72b0449SAndrew Jones 161d72b0449SAndrew Jones /* 162d72b0449SAndrew Jones * pick the region we're in for our primary region 163d72b0449SAndrew Jones */ 164d72b0449SAndrew Jones if (freemem_start >= mem_regions[i].start 165d72b0449SAndrew Jones && freemem_start < mem_regions[i].end) { 166d72b0449SAndrew Jones mem_regions[i].flags |= MR_F_PRIMARY; 167d72b0449SAndrew Jones primary = mem_regions[i]; 168d72b0449SAndrew Jones } 169d72b0449SAndrew Jones 170d72b0449SAndrew Jones /* 171d72b0449SAndrew Jones * set the lowest and highest addresses found, 172d72b0449SAndrew Jones * ignoring potential gaps 173d72b0449SAndrew Jones */ 174d72b0449SAndrew Jones if (mem_regions[i].start < mem.start) 175d72b0449SAndrew Jones mem.start = mem_regions[i].start; 176d72b0449SAndrew Jones if (mem_regions[i].end > mem.end) 177d72b0449SAndrew Jones mem.end = mem_regions[i].end; 178d72b0449SAndrew Jones } 179d72b0449SAndrew Jones assert(primary.end != 0); 180d72b0449SAndrew Jones // assert(!(mem.start & ~PHYS_MASK) && !((mem.end - 1) & ~PHYS_MASK)); 181d72b0449SAndrew Jones 182d72b0449SAndrew Jones __physical_start = mem.start; /* PHYSICAL_START */ 183d72b0449SAndrew Jones __physical_end = mem.end; /* PHYSICAL_END */ 184d72b0449SAndrew Jones 185d72b0449SAndrew Jones phys_alloc_init(freemem_start, primary.end - freemem_start); 186d72b0449SAndrew Jones phys_alloc_set_minimum_alignment(__icache_bytes > __dcache_bytes 187d72b0449SAndrew Jones ? __icache_bytes : __dcache_bytes); 1888e4e0512SNicholas Piggin 1898e4e0512SNicholas Piggin phys_alloc_get_unused(&base, &top); 1908e4e0512SNicholas Piggin base = PAGE_ALIGN(base); 1918e4e0512SNicholas Piggin top &= PAGE_MASK; 1928e4e0512SNicholas Piggin page_alloc_init_area(0, base >> PAGE_SHIFT, top >> PAGE_SHIFT); 1938e4e0512SNicholas Piggin page_alloc_ops_enable(); 194d72b0449SAndrew Jones } 195d72b0449SAndrew Jones 19629815050SNicholas Piggin #define EXCEPTION_STACK_SIZE SZ_64K 19729815050SNicholas Piggin 19829815050SNicholas Piggin static char boot_exception_stack[EXCEPTION_STACK_SIZE]; 199*c76b0d0aSNicholas Piggin struct cpu cpus[NR_CPUS]; 200*c76b0d0aSNicholas Piggin 201*c76b0d0aSNicholas Piggin void cpu_init(struct cpu *cpu, int cpu_id) 202*c76b0d0aSNicholas Piggin { 203*c76b0d0aSNicholas Piggin cpu->server_no = cpu_id; 204*c76b0d0aSNicholas Piggin 205*c76b0d0aSNicholas Piggin cpu->stack = (unsigned long)memalign(SZ_4K, SZ_64K); 206*c76b0d0aSNicholas Piggin cpu->stack += SZ_64K - 64; 207*c76b0d0aSNicholas Piggin cpu->exception_stack = (unsigned long)memalign(SZ_4K, SZ_64K); 208*c76b0d0aSNicholas Piggin cpu->exception_stack += SZ_64K - 64; 209*c76b0d0aSNicholas Piggin } 21029815050SNicholas Piggin 211d72b0449SAndrew Jones void setup(const void *fdt) 212d72b0449SAndrew Jones { 213efe42003SAndrew Jones void *freemem = &stacktop; 21401c82c0aSAndrew Jones const char *bootargs, *tmp; 215*c76b0d0aSNicholas Piggin struct cpu *cpu; 216d72b0449SAndrew Jones u32 fdt_size; 217d72b0449SAndrew Jones int ret; 218d72b0449SAndrew Jones 219610c5a9cSNicholas Piggin cpu_has_hv = !!(mfmsr() & (1ULL << MSR_HV_BIT)); 220610c5a9cSNicholas Piggin 221*c76b0d0aSNicholas Piggin memset(cpus, 0xff, sizeof(cpus)); 222*c76b0d0aSNicholas Piggin 223*c76b0d0aSNicholas Piggin cpu = &cpus[0]; 224*c76b0d0aSNicholas Piggin cpu->server_no = fdt_boot_cpuid_phys(fdt); 225*c76b0d0aSNicholas Piggin cpu->exception_stack = (unsigned long)boot_exception_stack; 226*c76b0d0aSNicholas Piggin cpu->exception_stack += EXCEPTION_STACK_SIZE - 64; 227*c76b0d0aSNicholas Piggin 228*c76b0d0aSNicholas Piggin mtspr(SPR_SPRG0, (unsigned long)cpu); 229*c76b0d0aSNicholas Piggin __current_cpu = cpu; 23029815050SNicholas Piggin 231610c5a9cSNicholas Piggin enable_mcheck(); 232610c5a9cSNicholas Piggin 233d72b0449SAndrew Jones /* 23401c82c0aSAndrew Jones * Before calling mem_init we need to move the fdt and initrd 23501c82c0aSAndrew Jones * to safe locations. We move them to construct the memory 236efe42003SAndrew Jones * map illustrated below: 237efe42003SAndrew Jones * 238efe42003SAndrew Jones * +----------------------+ <-- top of physical memory 239efe42003SAndrew Jones * | | 240efe42003SAndrew Jones * ~ ~ 241efe42003SAndrew Jones * | | 24201c82c0aSAndrew Jones * +----------------------+ <-- top of initrd 24301c82c0aSAndrew Jones * | | 244efe42003SAndrew Jones * +----------------------+ <-- top of FDT 245efe42003SAndrew Jones * | | 246efe42003SAndrew Jones * +----------------------+ <-- top of cpu0's stack 247efe42003SAndrew Jones * | | 24875a58702SAndrew Jones * +----------------------+ <-- top of text/data/bss/toc sections, 24975a58702SAndrew Jones * | | see powerpc/flat.lds 250efe42003SAndrew Jones * | | 251efe42003SAndrew Jones * +----------------------+ <-- load address 252efe42003SAndrew Jones * | | 253efe42003SAndrew Jones * +----------------------+ 254d72b0449SAndrew Jones */ 255d72b0449SAndrew Jones fdt_size = fdt_totalsize(fdt); 256efe42003SAndrew Jones ret = fdt_move(fdt, freemem, fdt_size); 257d72b0449SAndrew Jones assert(ret == 0); 258efe42003SAndrew Jones ret = dt_init(freemem); 259d72b0449SAndrew Jones assert(ret == 0); 260efe42003SAndrew Jones freemem += fdt_size; 261d72b0449SAndrew Jones 26201c82c0aSAndrew Jones ret = dt_get_initrd(&tmp, &initrd_size); 26301c82c0aSAndrew Jones assert(ret == 0 || ret == -FDT_ERR_NOTFOUND); 26401c82c0aSAndrew Jones if (ret == 0) { 26501c82c0aSAndrew Jones initrd = freemem; 26601c82c0aSAndrew Jones memmove(initrd, tmp, initrd_size); 26701c82c0aSAndrew Jones freemem += initrd_size; 26801c82c0aSAndrew Jones } 26901c82c0aSAndrew Jones 27099bb51c2SNicholas Piggin assert(STACK_INT_FRAME_SIZE % 16 == 0); 27199bb51c2SNicholas Piggin 272*c76b0d0aSNicholas Piggin /* set parameters from dt */ 273*c76b0d0aSNicholas Piggin cpu_init_params(); 274*c76b0d0aSNicholas Piggin 275*c76b0d0aSNicholas Piggin /* Interrupt Endianness */ 276*c76b0d0aSNicholas Piggin if (machine_is_pseries()) { 277*c76b0d0aSNicholas Piggin #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 278*c76b0d0aSNicholas Piggin hcall(H_SET_MODE, 1, 4, 0, 0); 279*c76b0d0aSNicholas Piggin #else 280*c76b0d0aSNicholas Piggin hcall(H_SET_MODE, 0, 4, 0, 0); 281*c76b0d0aSNicholas Piggin #endif 282*c76b0d0aSNicholas Piggin } 283*c76b0d0aSNicholas Piggin 284*c76b0d0aSNicholas Piggin cpu_init_ipis(); 285d35482d5SAndrew Jones 286d35482d5SAndrew Jones /* cpu_init must be called before mem_init */ 287efe42003SAndrew Jones mem_init(PAGE_ALIGN((unsigned long)freemem)); 288d35482d5SAndrew Jones 289d35482d5SAndrew Jones /* mem_init must be called before io_init */ 290d72b0449SAndrew Jones io_init(); 291d72b0449SAndrew Jones 292efe42003SAndrew Jones /* finish setup */ 293d72b0449SAndrew Jones ret = dt_get_bootargs(&bootargs); 294d81b83fdSAndrew Jones assert(ret == 0 || ret == -FDT_ERR_NOTFOUND); 295809ebcb3SAndrew Jones setup_args_progname(bootargs); 296f266c3e8SAndrew Jones 297f266c3e8SAndrew Jones if (initrd) { 298f266c3e8SAndrew Jones /* environ is currently the only file in the initrd */ 299f266c3e8SAndrew Jones char *env = malloc(initrd_size); 300f266c3e8SAndrew Jones memcpy(env, initrd, initrd_size); 301f266c3e8SAndrew Jones setup_env(env, initrd_size); 302f266c3e8SAndrew Jones } 303d72b0449SAndrew Jones } 304