/* Copyright 2018 SiFive, Inc */ /* SPDX-License-Identifier: Apache-2.0 */ /* This code executes before _start, which is contained inside the C library. * In embedded systems we want to ensure that _enter, which contains the first * code to be executed, can be loaded at a specific address. To enable this * feature we provide the '.text.metal.init.enter' section, which is * defined to have the first address being where execution should start. */ .section .text.metal.init.enter .global _enter _enter: .cfi_startproc /* Inform the debugger that there is nowhere to backtrace past _enter. */ .cfi_undefined ra /* The absolute first thing that must happen is configuring the global * pointer register, which must be done with relaxation disabled because * it's not valid to obtain the address of any symbol without GP * configured. The C environment might go ahead and do this again, but * that's safe as it's a fixed register. */ .option push .option norelax la gp, __global_pointer$ .option pop /* Set up a simple trap vector to catch anything that goes wrong early in * the boot process. */ la t0, early_trap_vector csrw mtvec, t0 /* enable chicken bit if core is bullet series*/ la t0, __metal_chicken_bit beqz t0, 1f csrwi 0x7C1, 0 1: /* There may be pre-initialization routines inside the MBI code that run in * C, so here we set up a C environment. First we set up a stack pointer, * which is left as a weak reference in order to allow initialization * routines that do not need a stack to be set up to transparently be * called. */ .weak __metal_stack_pointer la sp, __metal_stack_pointer /* Check for an initialization routine and call it if one exists, otherwise * just skip over the call entirely. Note that __metal_initialize isn't * actually a full C function, as it doesn't end up with the .bss or .data * segments having been initialized. This is done to avoid putting a * burden on systems that can be initialized without having a C environment * set up. */ .weak __metal_before_start la ra, __metal_before_start beqz ra, 1f jalr ra 1: /* At this point we can enter the C runtime's startup file. The arguments * to this function are designed to match those provided to the SEE, just * so we don't have to write another ABI. */ csrr a0, mhartid li a1, 0 li a2, 0 call _start /* If we've made it back here then there's probably something wrong. We * allow the METAL to register a handler here. */ .weak __metal_after_main la ra, __metal_after_main beqz ra, 1f jalr ra 1: /* If that handler returns then there's not a whole lot we can do. Just * try to make some noise. */ la t0, 1f csrw mtvec, t0 1: lw t1, 0(x0) j 1b .cfi_endproc /* For sanity's sake we set up an early trap vector that just does nothing. If * you end up here then there's a bug in the early boot code somewhere. */ .section .text.metal.init.trapvec .align 2 early_trap_vector: .cfi_startproc csrr t0, mcause csrr t1, mepc csrr t2, mtval j early_trap_vector .cfi_endproc /* The GCC port might not emit a __register_frame_info symbol, which eventually * results in a weak undefined reference that eventually causes crash when it * is dereference early in boot. We really shouldn't need to put this here, * but to deal with what I think is probably a bug in the linker script I'm * going to leave this in for now. At least it's fairly cheap :) */ .weak __register_frame_info .global __register_frame_info .section .text.metal.init.__register_frame_info __register_frame_info: .cfi_startproc ret .cfi_endproc