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 init code for ARMv7A cores. 12 */ 13#include <kernel.h> 14 15#include <barrelfish_kpi/arm_core_data.h> 16#include <barrelfish_kpi/flags_arch.h> 17#include <bitmacros.h> 18#include <boot_protocol.h> 19#include <coreboot.h> 20#include <cp15.h> 21#include <dev/cpuid_arm_dev.h> 22#include <elf/elf.h> 23#include <exceptions.h> 24#include <getopt/getopt.h> 25#include <global.h> 26#include <init.h> 27#include <kcb.h> 28#include <kernel_multiboot.h> 29#include <offsets.h> 30#include <paging_kernel_arch.h> 31#include <arch/arm/platform.h> 32#include <serial.h> 33#include <startup_arch.h> 34#include <stdio.h> 35#include <string.h> 36 37/* 38 * Forward declarations 39 */ 40 41#if 0 42static void bsp_init( void *pointer ); 43static void nonbsp_init( void *pointer ); 44#endif 45 46#define MSG(format, ...) printk( LOG_NOTE, "ARMv7-A: "format, ## __VA_ARGS__ ) 47 48/* This is the kernel stack, allocated in the BSS. */ 49uintptr_t kernel_stack[KERNEL_STACK_SIZE/sizeof(uintptr_t)] 50 __attribute__((aligned(8))); 51 52static bool is_bsp = false; 53 54// 55// Kernel command line variables and binding options 56// 57 58uint32_t periphclk = 0; 59uint32_t periphbase = 0; 60uint32_t timerirq = 0; 61uint32_t cntfrq = 0; 62 63const char *kernel_command_line; 64 65static struct cmdarg cmdargs[] = { 66 { "consolePort", ArgType_UInt, { .uinteger = &serial_console_port }}, 67 { "debugPort", ArgType_UInt, { .uinteger = &serial_debug_port }}, 68 { "loglevel", ArgType_Int, { .integer = &kernel_loglevel }}, 69 { "logmask", ArgType_Int, { .integer = &kernel_log_subsystem_mask }}, 70 { "timeslice", ArgType_UInt, { .uinteger = &config_timeslice }}, 71 { "periphclk", ArgType_UInt, { .uinteger = &periphclk }}, 72 { "periphbase", ArgType_UInt, { .uinteger = &periphbase }}, 73 { "timerirq" , ArgType_UInt, { .uinteger = &timerirq }}, 74 { "cntfrq" , ArgType_UInt, { .uinteger = &cntfrq }}, 75 { NULL, 0, { NULL } } 76}; 77 78/** 79 * \brief Is this the BSP? 80 * \return True iff the current core is the bootstrap processor. 81 */ 82bool cpu_is_bsp(void) 83{ 84 return is_bsp; 85} 86 87bool arch_core_is_bsp(void) 88{ 89 return cpu_is_bsp(); 90} 91 92#define EXCEPTION_MODE_STACK_BYTES 256 93 94/* 95 * Exception mode stacks 96 * 97 * These are small stacks used to figure out where to spill registers. As 98 * these are banked functions are expected to leave them as found (ie. so they 99 * do not need to be reset next time around). 100 */ 101char abt_stack[EXCEPTION_MODE_STACK_BYTES] __attribute__((aligned(8))); 102char irq_stack[EXCEPTION_MODE_STACK_BYTES] __attribute__((aligned(8))); 103char fiq_stack[EXCEPTION_MODE_STACK_BYTES] __attribute__((aligned(8))); 104char undef_stack[EXCEPTION_MODE_STACK_BYTES] __attribute__((aligned(8))); 105char svc_stack[EXCEPTION_MODE_STACK_BYTES] __attribute__((aligned(8))); 106 107void set_stack_for_mode(uint8_t mode, void *sp_mode); 108 109/** 110 * Initialise the banked exception-mode stack registers. 111 * 112 * The kernel doesn't actually need separate stacks for different modes, as 113 * it's reentrant, but it's useful for debugging in-kernel faults. 114 */ 115static void 116exceptions_load_stacks(void) { 117 set_stack_for_mode(ARM_MODE_ABT, abt_stack + EXCEPTION_MODE_STACK_BYTES); 118 set_stack_for_mode(ARM_MODE_IRQ, irq_stack + EXCEPTION_MODE_STACK_BYTES); 119 set_stack_for_mode(ARM_MODE_FIQ, fiq_stack + EXCEPTION_MODE_STACK_BYTES); 120 set_stack_for_mode(ARM_MODE_UND, undef_stack + EXCEPTION_MODE_STACK_BYTES); 121 set_stack_for_mode(ARM_MODE_SVC, svc_stack + EXCEPTION_MODE_STACK_BYTES); 122} 123 124/** 125 * \brief Continue kernel initialization in kernel address space. 126 * 127 * This function sets up exception handling, initializes devices and enables 128 * interrupts. After that it calls arm_kernel_startup(), which should not 129 * return (if it does, this function halts the kernel). 130 */ 131void 132arch_init(struct arm_core_data *boot_core_data, 133 struct armv7_boot_record *bootrec) { 134 /* Now we're definitely executing inside the kernel window, with 135 * translation and caches available, and all pointers relocated to their 136 * correct virtual address. The low mappings are still enabled, but we 137 * shouldn't be accessing them any longer, no matter where RAM is located. 138 * */ 139 140 /* There's no boot record iff we're the first core to boot. */ 141 is_bsp= (bootrec == NULL); 142 143 /* Save our core data. */ 144 core_data= boot_core_data; 145 146 /* Let the paging code know where the kernel page tables are. Note that 147 * paging_map_device() won't work until this is called. */ 148 paging_load_pointers(core_data); 149 150 /* Reinitialise the serial port, as it may have moved, and we need to map 151 * it into high memory. */ 152 /* XXX - reread the args to update serial_console_port. */ 153 serial_console_init(is_bsp); 154 155 /* Load the global lock address. */ 156 global= (struct global *)core_data->global; 157 158 /* Select high vectors */ 159 uint32_t sctlr= cp15_read_sctlr(); 160 sctlr|= BIT(13); 161 cp15_write_sctlr(sctlr); 162 163 my_core_id = cp15_get_cpu_id(); 164 165 MSG("Barrelfish CPU driver starting on ARMv7\n"); 166 MSG("Core data at %p\n", core_data); 167 MSG("Global data at %p\n", global); 168 MSG("Boot record at %p\n", bootrec); 169 errval_t errval; 170 assert(core_data != NULL); 171 assert(paging_mmu_enabled()); 172 173 if(!is_bsp) { 174 MSG("APP core.\n"); 175 platform_notify_bsp(&bootrec->done); 176 } 177 178 /* Read the build ID, and store it. */ 179 const char *build_id_name= 180 ((const char *)&build_id_nhdr) + sizeof(struct Elf32_Nhdr); 181 182 if(build_id_nhdr.n_type != NT_GNU_BUILD_ID || 183 build_id_nhdr.n_namesz != 4 || 184 strncmp(build_id_name, "GNU", 4)) { 185 panic("Build ID missing or corrupted.\n"); 186 } 187 188 if(build_id_nhdr.n_descsz > MAX_BUILD_ID) { 189 panic("Build ID is more than %zu bytes.\n", MAX_BUILD_ID); 190 } 191 192 core_data->build_id.length= build_id_nhdr.n_descsz; 193 const char *build_id_data= build_id_name + build_id_nhdr.n_namesz; 194 memcpy(core_data->build_id.data, build_id_data, build_id_nhdr.n_descsz); 195 196 MSG("Build ID: "); 197 for(size_t i= 0; i < core_data->build_id.length; i++) 198 printf("%02x", build_id_data[i]); 199 printf("\n"); 200 201 struct multiboot_info *mb= 202 (struct multiboot_info *)core_data->multiboot_header; 203 204 /* On the BSP core, initialise our core_data command line. */ 205 if(is_bsp) { 206 const char *mb_cmdline= 207 (const char *)local_phys_to_mem((lpaddr_t)mb->cmdline); 208 memcpy(core_data->cmdline_buf, mb_cmdline, 209 min(MAXCMDLINE-1, strlen(mb_cmdline))); 210 core_data->cmdline_buf[MAXCMDLINE-1]= '\0'; 211 } 212 213 MSG("Multiboot info:\n"); 214 MSG(" info header at 0x%"PRIxLVADDR"\n", (lvaddr_t)mb); 215 MSG(" mods_addr is P:0x%"PRIxLPADDR"\n", (lpaddr_t)mb->mods_addr); 216 MSG(" mods_count is 0x%"PRIu32"\n", mb->mods_count); 217 MSG(" cmdline is at P:0x%"PRIxLPADDR"\n", (lpaddr_t)mb->cmdline); 218 MSG(" cmdline reads '%s'\n", local_phys_to_mem((lpaddr_t)mb->cmdline)); 219 MSG(" mmap_length is 0x%"PRIu32"\n", mb->mmap_length); 220 MSG(" mmap_addr is P:0x%"PRIxLPADDR"\n", (lpaddr_t)mb->mmap_addr); 221 MSG(" multiboot_flags is 0x%"PRIu32"\n", mb->flags); 222 223#if 0 224 if(cpu_is_bsp()) bsp_init( NULL ); 225 else nonbsp_init(NULL); 226#endif 227 228 MSG("Initializing exceptions.\n"); 229 230 if(is_bsp) { 231 /* Map the exception vectors. */ 232 paging_map_vectors(); 233 } 234 235 /* Initialise the exception stack pointers. */ 236 exceptions_load_stacks(); 237 238 /* Relocate the KCB into our new address space. */ 239 kcb_current= (struct kcb *)(lpaddr_t)core_data->kcb; 240 MSG("KCB at %p\n", kcb_current); 241 242 MSG("Parsing command line\n"); 243 kernel_command_line = (const char *)core_data->cmdline; 244 parse_commandline((const char *)core_data->cmdline, cmdargs); 245 config_timeslice = min(max(config_timeslice, 1), 20); 246 247 errval = serial_debug_init(); 248 if (err_is_fail(errval)) { 249 MSG("Failed to initialize debug port: %d", serial_debug_port); 250 } 251 MSG("Debug port initialized.\n"); 252 253 254 platform_revision_init(); 255 if (cpu_is_bsp()) { 256 MSG("%"PRIu32" cores in system\n", platform_get_core_count()); 257 MSG("Initializing the interrupt controller\n"); 258 platform_init_ic_bsp(); 259 } else { 260 platform_init_ic_app(); 261 } 262 263 MSG("Enabling timers\n"); 264 platform_timer_init(config_timeslice); 265 266 MSG("Enabling cycle counter user access\n"); 267 /* enable user-mode access to the performance counter */ 268 __asm volatile ("mcr p15, 0, %0, C9, C14, 0\n\t" :: "r"(1)); 269 /* disable counter overflow interrupts (just in case) */ 270 __asm volatile ("mcr p15, 0, %0, C9, C14, 2\n\t" :: "r"(0x8000000f)); 271 272 MSG("Enabling VFP\n"); 273 // full access to cp10 and cp11 274 __asm volatile ("ldr r0, =(0xf << 20)\n" 275 "mcr p15, 0, r0, c1, c0, 2\n"); 276 // enable floating-point extensions 277 __asm volatile ("mov r3, #0x40000000\n" 278 "vmsr fpexc, r3\n"); 279 280 // uint32_t mvfr0, mvfr1; 281 // __asm volatile ("vmrs %0, mvfr0\n" 282 // "vmrs %1, mvfr1\n" 283 // : "=r" (mvfr0), "=r" (mvfr1)); 284 // MSG("VFP: MVFR0=%08x MVFR1=%08x\n", mvfr0, mvfr1); 285 286 MSG("Setting coreboot spawn handler\n"); 287 coreboot_set_spawn_handler(CPU_ARM7, boot_aps); 288 289 MSG("Calling arm_kernel_startup\n"); 290 arm_kernel_startup(); 291} 292 293#if 0 294/** 295 * \brief Initialization for the BSP (the first core to be booted). 296 * \param pointer address of \c multiboot_info 297 */ 298static void bsp_init( void *pointer ) 299{ 300 struct multiboot_info *mb = pointer; 301 302 MSG("We seem to be a BSP; multiboot info:\n"); 303 MSG(" mods_addr is 0x%"PRIxLVADDR"\n", (lvaddr_t)mb->mods_addr); 304 MSG(" mods_count is 0x%"PRIxLVADDR"\n", (lvaddr_t)mb->mods_count); 305 MSG(" cmdline is 0x%"PRIxLVADDR"\n", (lvaddr_t)mb->cmdline); 306 MSG(" cmdline reads '%s'\n", mb->cmdline); 307 MSG(" mmap_length is 0x%"PRIxLVADDR"\n", (lvaddr_t)mb->mmap_length); 308 MSG(" mmap_addr is 0x%"PRIxLVADDR"\n", (lvaddr_t)mb->mmap_addr); 309 MSG(" multiboot_flags is 0x%"PRIxLVADDR"\n", (lvaddr_t)mb->flags); 310} 311 312/** 313 * \brief Initialization on a non-BSP (second and successive cores). 314 * \param pointer address of the global structure set up by \c bsp_init 315 */ 316static void nonbsp_init( void *pointer ) 317{ 318 panic("Unimplemented.\n"); 319 320#if 0 321 MSG("We seem to be an AP.\n"); 322 // global = (struct global *)GLOBAL_VBASE; 323 324 // Our core data (struct arm_core_data) is placed one page before the 325 // first byte of the kernel image 326 core_data = (struct arm_core_data *) 327 ((lpaddr_t)&kernel_first_byte - BASE_PAGE_SIZE); 328 core_data->cmdline = (lpaddr_t)&core_data->kernel_cmdline; 329 kcb_current = (struct kcb*) (lpaddr_t)core_data->kcb; 330 my_core_id = core_data->dst_core_id; 331 332 // Tell the BSP that we are started up 333 platform_notify_bsp(); 334 335 // Print kernel address for debugging with gdb 336 MSG("Barrelfish non-BSP CPU driver starting at addr 0x%"PRIxLVADDR" on core %"PRIuCOREID"\n", 337 local_phys_to_mem((lpaddr_t)&kernel_first_byte), my_core_id); 338#endif 339} 340#endif 341