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 <memregions.h> 17 #include <alloc.h> 18 #include <alloc_phys.h> 19 #include <alloc_page.h> 20 #include <vmalloc.h> 21 #include <auxinfo.h> 22 #include <argv.h> 23 #include <asm/thread_info.h> 24 #include <asm/setup.h> 25 #include <asm/page.h> 26 #include <asm/processor.h> 27 #include <asm/smp.h> 28 #include <asm/timer.h> 29 #include <asm/psci.h> 30 31 #include "io.h" 32 33 #define MAX_DT_MEM_REGIONS 16 34 #define NR_EXTRA_MEM_REGIONS 64 35 #define NR_MEM_REGIONS (MAX_DT_MEM_REGIONS + NR_EXTRA_MEM_REGIONS) 36 37 extern unsigned long _text, _etext, _data, _edata; 38 39 char *initrd; 40 u32 initrd_size; 41 42 u64 cpus[NR_CPUS] = { [0 ... NR_CPUS-1] = (u64)~0 }; 43 int nr_cpus; 44 45 static struct mem_region arm_mem_regions[NR_MEM_REGIONS + 1]; 46 phys_addr_t __phys_offset = (phys_addr_t)-1, __phys_end = 0; 47 48 extern void exceptions_init(void); 49 extern void asm_mmu_disable(void); 50 51 int mpidr_to_cpu(uint64_t mpidr) 52 { 53 int i; 54 55 for (i = 0; i < nr_cpus; ++i) 56 if (cpus[i] == (mpidr & MPIDR_HWID_BITMASK)) 57 return i; 58 return -1; 59 } 60 61 static void cpu_set_fdt(int fdtnode __unused, u64 regval, void *info __unused) 62 { 63 int cpu = nr_cpus++; 64 65 assert_msg(cpu < NR_CPUS, "Number cpus exceeds maximum supported (%d).", NR_CPUS); 66 67 cpus[cpu] = regval; 68 set_cpu_present(cpu, true); 69 } 70 71 #ifdef CONFIG_EFI 72 73 #include <acpi.h> 74 75 static int cpu_set_acpi(struct acpi_subtable_header *header) 76 { 77 int cpu = nr_cpus++; 78 struct acpi_madt_generic_interrupt *gicc = (void *)header; 79 80 assert_msg(cpu < NR_CPUS, "Number cpus exceeds maximum supported (%d).", NR_CPUS); 81 82 cpus[cpu] = gicc->arm_mpidr; 83 set_cpu_present(cpu, true); 84 85 return 0; 86 } 87 88 static void cpu_init_acpi(void) 89 { 90 acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_INTERRUPT, cpu_set_acpi); 91 } 92 93 #else 94 95 static void cpu_init_acpi(void) 96 { 97 assert_msg(false, "ACPI not available"); 98 } 99 100 #endif 101 102 static void cpu_init(void) 103 { 104 int ret; 105 106 nr_cpus = 0; 107 if (dt_available()) { 108 ret = dt_for_each_cpu_node(cpu_set_fdt, NULL); 109 assert(ret == 0); 110 } else { 111 cpu_init_acpi(); 112 } 113 114 set_cpu_online(0, true); 115 } 116 117 static void arm_memregions_add_assumed(void) 118 { 119 struct mem_region *code, *data; 120 121 /* Split the region with the code into two regions; code and data */ 122 memregions_split((unsigned long)&_etext, &code, &data); 123 assert(code); 124 code->flags |= MR_F_CODE; 125 126 /* 127 * mach-virt I/O regions: 128 * - The first 1G (arm/arm64) 129 * - 512M at 256G (arm64, arm uses highmem=off) 130 * - 512G at 512G (arm64, arm uses highmem=off) 131 */ 132 memregions_add(&(struct mem_region){ 0, (1ul << 30), MR_F_IO }); 133 #ifdef __aarch64__ 134 memregions_add(&(struct mem_region){ (1ul << 38), (1ul << 38) | (1ul << 29), MR_F_IO }); 135 memregions_add(&(struct mem_region){ (1ul << 39), (1ul << 40), MR_F_IO }); 136 #endif 137 } 138 139 static void mem_allocator_init(phys_addr_t freemem_start, phys_addr_t freemem_end) 140 { 141 phys_addr_t base, top; 142 143 freemem_start = PAGE_ALIGN(freemem_start); 144 freemem_end &= PAGE_MASK; 145 146 phys_alloc_init(freemem_start, freemem_end - freemem_start); 147 phys_alloc_set_minimum_alignment(SMP_CACHE_BYTES); 148 149 phys_alloc_get_unused(&base, &top); 150 base = PAGE_ALIGN(base); 151 top &= PAGE_MASK; 152 assert(sizeof(long) == 8 || !(base >> 32)); 153 if (sizeof(long) != 8 && (top >> 32) != 0) 154 top = ((uint64_t)1 << 32); 155 page_alloc_init_area(0, base >> PAGE_SHIFT, top >> PAGE_SHIFT); 156 page_alloc_ops_enable(); 157 } 158 159 static void mem_init(phys_addr_t freemem_start) 160 { 161 struct mem_region *freemem, *r, mem = { 162 .start = (phys_addr_t)-1, 163 }; 164 165 freemem = memregions_find(freemem_start); 166 assert(freemem && !(freemem->flags & (MR_F_IO | MR_F_CODE))); 167 168 for (r = mem_regions; r->end; ++r) { 169 if (!(r->flags & MR_F_IO)) { 170 if (r->start < mem.start) 171 mem.start = r->start; 172 if (r->end > mem.end) 173 mem.end = r->end; 174 } 175 } 176 assert(mem.end && !(mem.start & ~PHYS_MASK)); 177 mem.end &= PHYS_MASK; 178 179 /* Check for holes */ 180 r = memregions_find(mem.start); 181 while (r && r->end != mem.end) 182 r = memregions_find(r->end); 183 assert(r); 184 185 /* Ensure our selected freemem range is somewhere in our full range */ 186 assert(freemem_start >= mem.start && freemem->end <= mem.end); 187 188 __phys_offset = mem.start; /* PHYS_OFFSET */ 189 __phys_end = mem.end; /* PHYS_END */ 190 191 mem_allocator_init(freemem_start, freemem->end); 192 } 193 194 static void freemem_push_fdt(void **freemem, const void *fdt) 195 { 196 u32 fdt_size; 197 int ret; 198 199 fdt_size = fdt_totalsize(fdt); 200 ret = fdt_move(fdt, *freemem, fdt_size); 201 assert(ret == 0); 202 ret = dt_init(*freemem); 203 assert(ret == 0); 204 *freemem += fdt_size; 205 } 206 207 static void freemem_push_dt_initrd(void **freemem) 208 { 209 const char *tmp; 210 int ret; 211 212 ret = dt_get_initrd(&tmp, &initrd_size); 213 assert(ret == 0 || ret == -FDT_ERR_NOTFOUND); 214 if (ret == 0) { 215 initrd = *freemem; 216 memmove(initrd, tmp, initrd_size); 217 *freemem += initrd_size; 218 } 219 } 220 221 static void initrd_setup(void) 222 { 223 char *env; 224 225 if (!initrd) 226 return; 227 228 /* environ is currently the only file in the initrd */ 229 env = malloc(initrd_size); 230 memcpy(env, initrd, initrd_size); 231 setup_env(env, initrd_size); 232 } 233 234 void setup(const void *fdt, phys_addr_t freemem_start) 235 { 236 void *freemem; 237 const char *bootargs; 238 int ret; 239 240 assert(sizeof(long) == 8 || freemem_start < (3ul << 30)); 241 freemem = (void *)(unsigned long)freemem_start; 242 243 freemem_push_fdt(&freemem, fdt); 244 freemem_push_dt_initrd(&freemem); 245 246 memregions_init(arm_mem_regions, NR_MEM_REGIONS); 247 memregions_add_dt_regions(MAX_DT_MEM_REGIONS); 248 arm_memregions_add_assumed(); 249 mem_init(PAGE_ALIGN((unsigned long)freemem)); 250 251 psci_set_conduit(); 252 cpu_init(); 253 254 /* cpu_init must be called before thread_info_init */ 255 thread_info_init(current_thread_info(), 0); 256 257 /* mem_init must be called before io_init */ 258 io_init(); 259 260 timer_save_state(); 261 262 ret = dt_get_bootargs(&bootargs); 263 assert(ret == 0 || ret == -FDT_ERR_NOTFOUND); 264 setup_args_progname(bootargs); 265 266 initrd_setup(); 267 268 if (!(auxinfo.flags & AUXINFO_MMU_OFF)) 269 setup_vm(); 270 } 271 272 #ifdef CONFIG_EFI 273 274 #include <efi.h> 275 276 static efi_status_t setup_rsdp(efi_bootinfo_t *efi_bootinfo) 277 { 278 efi_status_t status; 279 struct acpi_table_rsdp *rsdp; 280 281 /* 282 * RSDP resides in an EFI_ACPI_RECLAIM_MEMORY region, which is not used 283 * by kvm-unit-tests arm64 memory allocator. So it is not necessary to 284 * copy the data structure to another memory region to prevent 285 * unintentional overwrite. 286 */ 287 status = efi_get_system_config_table(ACPI_20_TABLE_GUID, (void **)&rsdp); 288 if (status != EFI_SUCCESS) 289 return status; 290 291 set_efi_rsdp(rsdp); 292 293 return EFI_SUCCESS; 294 } 295 296 static efi_status_t efi_mem_init(efi_bootinfo_t *efi_bootinfo) 297 { 298 struct mem_region *freemem_mr = NULL, *code, *data; 299 phys_addr_t freemem_start; 300 void *freemem; 301 302 memregions_efi_init(&efi_bootinfo->mem_map, &freemem_mr); 303 if (!freemem_mr) 304 return EFI_OUT_OF_RESOURCES; 305 306 memregions_split((unsigned long)&_etext, &code, &data); 307 assert(code && (code->flags & MR_F_CODE)); 308 if (data) 309 data->flags &= ~MR_F_CODE; 310 311 for (struct mem_region *m = mem_regions; m->end; ++m) { 312 if (m != code) 313 assert(!(m->flags & MR_F_CODE)); 314 315 if (!(m->flags & MR_F_IO)) { 316 if (m->start < __phys_offset) 317 __phys_offset = m->start; 318 if (m->end > __phys_end) 319 __phys_end = m->end; 320 } 321 } 322 __phys_end &= PHYS_MASK; 323 324 freemem = (void *)PAGE_ALIGN(freemem_mr->start); 325 326 if (efi_bootinfo->fdt) 327 freemem_push_fdt(&freemem, efi_bootinfo->fdt); 328 329 freemem_start = PAGE_ALIGN((unsigned long)freemem); 330 assert(sizeof(long) == 8 || freemem_start < (3ul << 30)); 331 332 asm_mmu_disable(); 333 334 mem_allocator_init(freemem_start, freemem_mr->end); 335 336 return EFI_SUCCESS; 337 } 338 339 efi_status_t setup_efi(efi_bootinfo_t *efi_bootinfo) 340 { 341 efi_status_t status; 342 343 exceptions_init(); 344 345 memregions_init(arm_mem_regions, NR_MEM_REGIONS); 346 347 status = efi_mem_init(efi_bootinfo); 348 if (status != EFI_SUCCESS) { 349 printf("Failed to initialize memory: "); 350 switch (status) { 351 case EFI_OUT_OF_RESOURCES: 352 printf("No free memory region\n"); 353 break; 354 default: 355 printf("Unknown error\n"); 356 break; 357 } 358 return status; 359 } 360 361 if (!dt_available()) { 362 status = setup_rsdp(efi_bootinfo); 363 if (status != EFI_SUCCESS) { 364 printf("Cannot find RSDP in EFI system table\n"); 365 return status; 366 } 367 } 368 369 psci_set_conduit(); 370 cpu_init(); 371 /* cpu_init must be called before thread_info_init */ 372 thread_info_init(current_thread_info(), 0); 373 /* mem_init must be called before io_init */ 374 io_init(); 375 376 timer_save_state(); 377 378 initrd_setup(); 379 380 if (!(auxinfo.flags & AUXINFO_MMU_OFF)) 381 setup_vm(); 382 383 return EFI_SUCCESS; 384 } 385 386 #endif 387