1/* 2 * Copyright (c) 2009-2016, ETH Zurich. All rights reserved. 3 * 4 * This file is distributed under the terms in the attached LICENSE file. If 5 * you do not find this file, copies can be found by writing to: ETH Zurich 6 * D-INFK, CAB F.78, Universitaetstrasse 6, CH-8092 Zurich, Attn: Systems Group. 7 */ 8 9/** 10 * \file 11 * \brief CPU driver boot code for ARMv7A cores. 12 */ 13#include <kernel.h> 14 15#include <barrelfish_kpi/arm_core_data.h> 16#include <boot_protocol.h> 17#include <cp15.h> 18#include <dev/cpuid_arm_dev.h> 19#include <getopt/getopt.h> 20#include <global.h> 21#include <kcb.h> 22#include <multiboot.h> 23#include <paging_kernel_arch.h> 24#include <serial.h> 25#include <stdio.h> 26 27#define MSG(format, ...) printk( LOG_NOTE, "ARMv7-A: "format, ## __VA_ARGS__ ) 28 29void boot_bsp_core(void *pointer, void *cpu_driver_entry, 30 void *cpu_driver_base); 31void boot_app_core(struct armv7_boot_record *bootrec); 32 33extern char boot_start; 34 35coreid_t my_core_id; 36 37extern union arm_l1_entry l1_low [ARM_L1_MAX_ENTRIES]; 38extern union arm_l1_entry l1_high[ARM_L1_MAX_ENTRIES]; 39 40/* This is the table of boot records on which core spin, waiting for a boot 41 * request. Presently there's only one, but if we want to scale to more than 42 * a few cores, we'll probably want to hash into this based on MPID. */ 43struct armv7_boot_record boot_records[1]; 44 45/* There is only one copy of the global locks, which is allocated alongside 46 * the BSP kernel. All kernels have their pointers set to the BSP copy. */ 47static struct global bsp_global __attribute__((section(".boot"))); 48struct global *global= &bsp_global; 49 50/* The BSP core's KCB is allocated here. Application cores will have theirs 51 * allocated at user level. */ 52struct kcb bsp_kcb __attribute__((section(".boot"))); 53 54struct arm_core_data boot_core_data __attribute__((section(".boot"))); 55 56/* Print a little information about the processor, and check that it supports 57 * the features we require. */ 58static bool check_cpuid(void) __attribute__((noinline)); 59static bool 60check_cpuid(void) { 61 uint32_t midr= cp15_read_midr(); 62 63 /* XXX - Mackerel should give nice strings to print. */ 64 MSG("This is an "); 65 66 if(cpuid_arm_midr_architecture_extract((uint8_t *)&midr) != 0xf) { 67 printf("unsupported ARMv6 or earlier core\n"); 68 return false; 69 } 70 71 switch(cpuid_arm_midr_implementer_extract((uint8_t *)&midr)) { 72 case cpuid_arm_impl_arm: 73 printf("ARM "); 74 switch(cpuid_arm_midr_part_extract((uint8_t *)&midr)) { 75 case cpuid_arm_part_a5: 76 printf("Cortex-A5 "); 77 break; 78 case cpuid_arm_part_a7: 79 printf("Cortex-A7 "); 80 break; 81 case cpuid_arm_part_a8: 82 printf("Cortex-A8 "); 83 break; 84 case cpuid_arm_part_a9: 85 printf("Cortex-A9 "); 86 break; 87 case cpuid_arm_part_a15: 88 printf("Cortex-A15 "); 89 break; 90 case cpuid_arm_part_a17: 91 printf("Cortex-A17 "); 92 break; 93 case cpuid_arm_part_a53: 94 printf("Cortex-A53 "); 95 break; 96 case cpuid_arm_part_a57: 97 printf("Cortex-A57 "); 98 break; 99 case cpuid_arm_part_a72: 100 printf("Cortex-A72 "); 101 break; 102 case cpuid_arm_part_a73: 103 printf("Cortex-A73 "); 104 break; 105 } 106 printf("r%dp%d\n", 107 cpuid_arm_midr_variant_extract((uint8_t *)&midr), 108 cpuid_arm_midr_revision_extract((uint8_t *)&midr)); 109 break; 110 111 case cpuid_arm_impl_dec: 112 printf("Unknown DEC core\n"); 113 break; 114 case cpuid_arm_impl_motorola: 115 printf("Unknown Motorola core\n"); 116 break; 117 case cpuid_arm_impl_qualcomm: 118 printf("Unknown Qualcomm core\n"); 119 break; 120 case cpuid_arm_impl_marvell: 121 printf("Unknown Marvell core\n"); 122 break; 123 case cpuid_arm_impl_intel: 124 printf("Unknown Intel core\n"); 125 break; 126 127 default: 128 printf("Unknown manufacturer's core\n"); 129 break; 130 } 131 132 uint32_t id_pfr1= cp15_read_id_pfr1(); 133 if(cpuid_arm_id_pfr1_security_extract((uint8_t *)&id_pfr1) == 134 cpuid_arm_sec_ni) { 135 MSG(" Security extensions required but not implemented\n"); 136 return false; 137 } 138 else { 139 MSG(" Security extensions implemented\n"); 140 } 141 142 MSG(" Virtualisation extensions "); 143 if(cpuid_arm_id_pfr1_virtualisation_extract((uint8_t *)&id_pfr1) == 144 cpuid_arm_ftr_i) { 145 printf("implemented.\n"); 146 } 147 else { 148 printf("not implemented.\n"); 149 } 150 151 MSG(" Generic timer "); 152 if(cpuid_arm_id_pfr1_generic_timer_extract((uint8_t *)&id_pfr1) == 153 cpuid_arm_ftr_i) { 154 printf("implemented.\n"); 155 } 156 else { 157 printf("not implemented.\n"); 158 } 159 160 return true; 161} 162 163extern char kernel_stack_top; 164 165/* On many platforms, we have no way to set the argument register values when 166 * starting the boot driver. In that case, the static loader will place those 167 * values here, which we'll detect by seeing that the multiboot pointer isn't 168 * 0xdeadbeef. */ 169struct boot_arguments { 170 void *pointer; 171 void *cpu_driver_entry; 172 void *cpu_driver_base; 173} boot_arguments= { (void *)0xdeadbeef, NULL, NULL }; 174 175/* The boot driver needs the index of the console port, but nothing else. The 176 * argument list is left untouched, for the CPU driver. */ 177static struct cmdarg bootargs[] = { 178 { "consolePort", ArgType_UInt, { .uinteger = (void *)0 } }, 179 { NULL, 0, { NULL } } 180}; 181 182static void 183init_bootargs(void) { 184 bootargs[0].var.uinteger= &serial_console_port; 185} 186 187void switch_and_jump(void *cpu_driver_entry, lvaddr_t boot_pointer, 188 lpaddr_t ttbr0, lpaddr_t ttbr1, lvaddr_t mailbox) 189 __attribute__((noreturn)); 190 191__attribute__((noreturn)) 192void boot_app_core(struct armv7_boot_record *bootrec) { 193 my_core_id = cp15_get_cpu_id(); 194 195 MSG("APP core %"PRIu32" booting.\n", bootrec->target_mpid); 196 197 /* Get the core_data structure from the boot record. */ 198 struct arm_core_data *app_core_data= 199 (struct arm_core_data *)mem_to_local_phys(bootrec->core_data); 200 201 MSG("CPU driver entry point is KV:%08"PRIx32"\n", 202 app_core_data->entry_point); 203 204 MSG("Page tables at P:%08"PRIx32" and P:%08"PRIx32".\n", 205 app_core_data->kernel_l1_low, 206 app_core_data->kernel_l1_high); 207 208 MSG("Switching page tables and jumping - see you in arch_init().\n"); 209 switch_and_jump((void *)app_core_data->entry_point, 210 bootrec->core_data, 211 app_core_data->kernel_l1_low, 212 app_core_data->kernel_l1_high, 213 local_phys_to_mem((lpaddr_t)bootrec)); 214} 215 216/** 217 * \brief Entry point called from boot.S for the BSP kernel. 218 * 219 * \param pointer address of \c multiboot_info on the BSP; 220 */ 221__attribute__((noreturn)) 222void boot_bsp_core(void *pointer, void *cpu_driver_entry, 223 void *cpu_driver_base) 224{ 225 my_core_id = cp15_get_cpu_id(); 226 227 /* Place all AP cores in the WFE loop. */ 228 plat_advance_aps(); 229 230 /* If this pointer has been modified by the loader, it means we're got a 231 * statically-allocated multiboot info structure, as we're executing from 232 * ROM, in a simulator, or otherwise unable to use a full bootloader. */ 233 if(boot_arguments.pointer != (void *)0xdeadbeef) { 234 pointer= boot_arguments.pointer; 235 cpu_driver_entry= boot_arguments.cpu_driver_entry; 236 cpu_driver_base= boot_arguments.cpu_driver_base; 237 } 238 239 /* Grab the multiboot header, so we can find our command line. Note that 240 * we're still executing with physical addresses, to we need to convert 241 * the pointer back from the kernel-virtual address that the CPU driver 242 * will use. */ 243 struct multiboot_info *mbi= 244 (struct multiboot_info *)mem_to_local_phys((lvaddr_t)pointer); 245 246 /* If there's no commandline passed, panic on port 0. */ 247 if(!(mbi->flags & MULTIBOOT_INFO_FLAG_HAS_CMDLINE)) { 248 serial_early_init(0); 249 panic("No commandline arguments.\n"); 250 } 251 252 /* Parse the commandline, to find which console port to connect to. */ 253 init_bootargs(); 254 const char *cmdline= (const char *)mbi->cmdline; 255 parse_commandline(cmdline, bootargs); 256 257 /* Initialise the serial port driver using the physical address of the 258 * port, so that we can start printing before we enable the MMU. */ 259 serial_early_init(serial_console_port); 260 261 /* The spinlocks are in the BSS, and thus already zeroed, but it's polite 262 * to explicitly initialize them here... */ 263 spinlock_init(&global->locks.print); 264 265 MSG("Boot driver invoked as: %s\n", cmdline); 266 267 /* These, likewise, use physical addresses directly. */ 268 check_cpuid(); 269 //platform_print_id(); 270 271 /* Print kernel address for debugging with gdb. */ 272 MSG("First byte of boot driver at 0x%"PRIxLVADDR"\n", 273 local_phys_to_mem((uint32_t)&boot_start)); 274 MSG("First byte of CPU driver at %p\n", cpu_driver_base); 275 276 /* Get the memory map. */ 277 if(!(mbi->flags & MULTIBOOT_INFO_FLAG_HAS_MMAP)) 278 panic("No memory map.\n"); 279 struct multiboot_mmap *mmap= (struct multiboot_mmap *)mbi->mmap_addr; 280 if(mbi->mmap_length == 0) panic("Memory map is empty.\n"); 281 282 /* Find the first RAM region. */ 283 size_t region; 284 for(region= 0; 285 region < mbi->mmap_length && 286 mmap[region].type != MULTIBOOT_MEM_TYPE_RAM; 287 region++); 288 if(region == mbi->mmap_length) panic("No RAM regions.\n"); 289 290 /* Make sure there's some RAM we can use. */ 291 if(mmap[region].base_addr > (uint64_t)UINT32_MAX) 292 panic("First RAM region is >4GB - I can't address it.\n"); 293 lpaddr_t ram_base= (uint32_t)mmap[region].base_addr; 294 295 /* Truncate the region if necessary. */ 296 size_t ram_size; 297 if(mmap[region].base_addr + (mmap[region].length - 1) > 298 (uint64_t)UINT32_MAX) { 299 ram_size= 300 (uint32_t)((uint64_t)UINT32_MAX - mmap[region].base_addr + 1); 301 printf("Truncated first RAM region to fit in 4GB.\n"); 302 } 303 else ram_size= (uint32_t)mmap[region].length; 304 305 if(ram_size > RAM_WINDOW_SIZE) { 306 panic("Reduce the first MMAP entry to <1GB, otherwise everything\n" 307 "breaks. This is really dumb, and must be fixed.\n"); 308 } 309 310 MSG("CPU driver entry point is %p\n", cpu_driver_entry); 311 312 /* Fill in the boot data structure for the CPU driver. */ 313 /* We need to pass in anything we've allocated. */ 314 boot_core_data.multiboot_header= local_phys_to_mem((lpaddr_t)mbi); 315 boot_core_data.global= local_phys_to_mem((lpaddr_t)&bsp_global); 316 boot_core_data.kcb= local_phys_to_mem((lpaddr_t)&bsp_kcb); 317 boot_core_data.target_bootrecs= local_phys_to_mem((lpaddr_t)&boot_records); 318 /* We're starting the BSP core, so its commandline etc. is that given in 319 * the multiboot header. */ 320 assert(mbi->mods_count > 0); 321 memcpy(&boot_core_data.kernel_module, 322 (void *)mbi->mods_addr, 323 sizeof(struct multiboot_modinfo)); 324 boot_core_data.cmdline= local_phys_to_mem(mbi->cmdline); 325 326 /* Relocate the boot data pointer for the CPU driver. */ 327 lvaddr_t boot_pointer= local_phys_to_mem((lpaddr_t)&boot_core_data); 328 MSG("Boot data structure at kernel VA %08x\n", boot_pointer); 329 330 /* Create the kernel page tables. */ 331 MSG("Initialising kernel page tables.\n"); 332 paging_init(ram_base, ram_size, &boot_core_data); 333 334 MSG("Switching page tables and jumping - see you in arch_init().\n"); 335 switch_and_jump(cpu_driver_entry, boot_pointer, (lpaddr_t)l1_low, 336 (lpaddr_t)l1_high, 0); 337} 338 339void 340switch_and_jump(void *cpu_driver_entry, lvaddr_t boot_pointer, lpaddr_t ttbr0, 341 lpaddr_t ttbr1, lvaddr_t bootrec) { 342 /* Switch the MMU on. */ 343 enable_mmu(ttbr0, ttbr1); 344 345 /* We're now executing with the kernel window mapped. If we're on a 346 * platform that doesn't have RAM at 0x80000000, then we're still using 347 * physical addresses through the uncached device mappings in TTBR0. 348 * Otherwise our physical addresses were mapped 1-1 by the kernel window. 349 * 350 * In either case, we can't safely use the UART, as there's no guarantee 351 * that its device registers weren't in what is now the kernel window 352 * (this is the case on the Zynq). */ 353 354 /* This is important. We need to clean and invalidate the data caches, to 355 * ensure that anything we've modified since enabling them is visible 356 * after we make the jump. */ 357 invalidate_data_caches_pouu(true); 358 359 /* Long jump to the CPU driver entry point, passing the kernel-virtual 360 * address of the boot_core_data structure. */ 361 __asm("mov r0, %[pointer]\n" 362 "mov r1, %[bootrec]\n" 363 "mov pc, %[jump_target]\n" 364 : /* No outputs */ 365 : [jump_target] "r"(cpu_driver_entry), 366 [bootrec] "r"(bootrec), 367 [pointer] "r"(boot_pointer) 368 : "r0", "r1"); 369 370 panic("Shut up GCC, I'm not returning.\n"); 371} 372