diff options
| -rw-r--r-- | fw/fe310/bsp/gloss/crt0.S | 295 | ||||
| -rw-r--r-- | fw/fe310/bsp/init/Makefile | 13 | ||||
| -rw-r--r-- | fw/fe310/bsp/init/entry.S | 63 | 
3 files changed, 139 insertions, 232 deletions
diff --git a/fw/fe310/bsp/gloss/crt0.S b/fw/fe310/bsp/gloss/crt0.S index 920ee4b..a8c5a74 100644 --- a/fw/fe310/bsp/gloss/crt0.S +++ b/fw/fe310/bsp/gloss/crt0.S @@ -1,246 +1,77 @@ -/* Copyright (c) 2017-2018  SiFive Inc. All rights reserved. +#========================================================================= +# crt0.S : Entry point for RISC-V user programs +#========================================================================= -   This copyrighted material is made available to anyone wishing to use, -   modify, copy, or redistribute it subject to the terms and conditions -   of the FreeBSD License.   This program is distributed in the hope that -   it will be useful, but WITHOUT ANY WARRANTY expressed or implied, -   including the implied warranties of MERCHANTABILITY or FITNESS FOR -   A PARTICULAR PURPOSE.  A copy of this license is available at -   http://www.opensource.org/licenses. -*/ - -/* crt0.S: Entry point for RISC-V METAL programs. */ - -.section .text.libgloss.start -.global _start -.type   _start, @function - -  /* _start is defined by the METAL to have been called with the following -   * arguments: -   *   a0: the hart ID of the currently executing hart.  Harts can start at -   *       any arbitrary point, it's the C library's job to ensure the code is -   *       safe. -   *   a1: a pointer to a description of the machine on which this code is -   *       currently executing.  This is probably 0 on an embedded system -   *       because they tend to not be dynamically portable.  As such, newlib -   *       ignores this argument. -   *   a2: a pointer to a function that must be run after the envirnoment has -   *       been initialized, but before user code can be expected to be run. -   *       If this is 0 then there is no function to be run. */ +  .section .text.libgloss.start +  .global _start +  .type   _start, @function  _start: -.cfi_startproc -.cfi_undefined ra - -  /* This is a bit funky: it's not usually sane for _start to return, but in -   * this case we actually want to in order to signal an error to the METAL. */ -  mv s0, ra - -  /* Before doing anything we must initialize the global pointer, as we cannot -   * safely perform any access that may be relaxed without GP being set.  This -   * is done with relaxation disabled to avoid relaxing the address calculation -   * to just "addi gp, gp, 0". */ -.option push -.option norelax -  la gp, __global_pointer$ -.option pop - -  /* The METAL is designed for a bare-metal environment and therefor is expected -   * to define its own stack pointer.  We also align the stack pointer here -   * because the only RISC-V ABI that's currently defined mandates 16-byte -   * stack alignment. */ -  la sp, _sp - -  /* Increment by hartid number of stack sizes */ -  li t0, 0 -  la t1, __stack_size -1: -  beq t0, a0, 1f -  add sp, sp, t1 -  addi t0, t0, 1 -  j 1b -1: -  andi sp, sp, -16 - -  /* If we're not hart 0, skip the initialization work */ -  la t0, __metal_boot_hart -  bne a0, t0, _skip_init +  # Initialize global pointer +  .option push +  .option norelax +1:auipc gp, %pcrel_hi(__global_pointer$) +  addi  gp, gp, %pcrel_lo(1b) +  .option pop + +  # Clear the bss segment +  la      a0, metal_segment_bss_start +  la      a2, metal_segment_bss_end +  sub     a2, a2, a0 +  li      a1, 0 +  call    memset + +  # Copy data segment +  la      a0, metal_segment_data_target_start +  la      a1, metal_segment_data_source_start +  la      a2, metal_segment_data_target_end +  sub     a2, a2, a0 +  call    memcpy -  /* Embedded systems frequently require relocating the data segment before C -   * code can be run -- for example, the data segment may exist in flash upon -   * boot and then need to get relocated into a non-persistant writable memory -   * before C code can execute.  If this is the case we do so here.  This step -   * is optional: if the METAL provides an environment in which this relocation -   * is not necessary then it must simply set metal_segment_data_source_start to -   * be equal to metal_segment_data_target_start. */ -  la t0, metal_segment_data_source_start -  la t1, metal_segment_data_target_start -  la t2, metal_segment_data_target_end - -  beq t0, t1, 2f -  bge t1, t2, 2f - -1: -#if __riscv_xlen == 32 -  lw   a0, 0(t0) -  addi t0, t0, 4 -  sw   a0, 0(t1) -  addi t1, t1, 4 -  blt  t1, t2, 1b -#else -  ld   a0, 0(t0) -  addi t0, t0, 8 -  sd   a0, 0(t1) -  addi t1, t1, 8 -  blt  t1, t2, 1b -#endif -2: - -  /* Copy the ITIM section */ -  la t0, metal_segment_itim_source_start -  la t1, metal_segment_itim_target_start -  la t2, metal_segment_itim_target_end - -  beq t0, t1, 2f -  bge t1, t2, 2f - -1: -#if __riscv_xlen == 32 -  lw   a0, 0(t0) -  addi t0, t0, 4 -  sw   a0, 0(t1) -  addi t1, t1, 4 -  blt  t1, t2, 1b -#else -  ld   a0, 0(t0) -  addi t0, t0, 8 -  sd   a0, 0(t1) -  addi t1, t1, 8 -  blt  t1, t2, 1b -#endif -2: - -  /* Fence all subsequent instruction fetches until after the ITIM writes -     complete */    fence.i -  /* Zero the BSS segment. */ -  la t1, metal_segment_bss_target_start -  la t2, metal_segment_bss_target_end - -  bge t1, t2, 2f - -1: -#if __riscv_xlen == 32 -  sw   x0, 0(t1) -  addi t1, t1, 4 -  blt  t1, t2, 1b -#else -  sd   x0, 0(t1) -  addi t1, t1, 8 -  blt  t1, t2, 1b -#endif -2: - -  /* At this point we're in an environment that can execute C code.  The first -   * thing to do is to make the callback to the parent environment if it's been -   * requested to do so. */ -  beqz a2, 1f -  jalr a2 -1: - -  /* The RISC-V port only uses new-style constructors and destructors. */ -  la a0, __libc_fini_array -  call atexit -  call __libc_init_array - -_skip_init: - -  /* Synchronize harts so that secondary harts wait until hart 0 finishes -     initializing */ -  call __metal_synchronize_harts - -  /* Check RISC-V isa and enable FS bits if Floating Point architecture. */ -  csrr a5, misa -  li   a4, 0x10028 -  and  a5, a5, a4 -  beqz a5, 1f -  csrr a5, mstatus -  lui  a4, 0x2 -  or   a5, a5, a4 -  csrw mstatus, a5 -  csrwi fcsr, 0 -1: - -  /* This is a C runtime, so main() is defined to have some arguments.  Since -   * there's nothing sane the METAL can pass we don't bother with that but -   * instead just setup as close to a NOP as we can. */ -  li a0, 1     /* argc=1 */ -  la a1, argv  /* argv = {"libgloss", NULL} */ -  la a2, envp  /* envp = {NULL} */ -  call secondary_main - -  /* Call exit to handle libc's cleanup routines.  Under normal contains this -   * shouldn't even get called, but I'm still not using a tail call here -   * because returning to the METAL is the right thing to do in pathological -   * situations. */ -  call exit - -  /* And here's where we return.  Again, it's a bit odd but the METAL defines -   * this as a bad idea (ie, as opposed to leaving it undefined) and at this -   * point it's really the only thing left to do. */ -  mv ra, s0 -  ret - -.cfi_endproc - -/* RISC-V systems always use __libc_{init,fini}_array, but for compatibility we - * define _{init,fini} to do nothing. */ -.global _init -.type   _init, @function -.global _fini -.type   _fini, @function +  # Copy itim segment +  la      a0, metal_segment_itim_target_start +  la      a1, metal_segment_itim_source_start +  la      a2, metal_segment_itim_target_end +  sub     a2, a2, a0 +  call    memcpy + +  # Copy lim segment +  la      a0, metal_segment_lim_target_start +  la      a1, metal_segment_lim_source_start +  la      a2, metal_segment_lim_target_end +  sub     a2, a2, a0 +  call    memcpy + +  la      a0, __libc_fini_array   # Register global termination functions +  call    atexit                  #  to be called upon exit +  call    __libc_init_array       # Run global initialization functions + +  li      a0, 1     # a0 = argc = 1 +  la      a1, argv  # a1 = argv = {"libgloss", NULL} +  la      a2, envp  # a2 = envp = NULL +  call    main +  tail    exit +  .size  _start, .-_start + +  .global _init +  .type   _init, @function +  .global _fini +  .type   _fini, @function  _init:  _fini: +  # These don't have to do anything since we use init_array/fini_array.    ret -.size _init, .-_init -.size _fini, .-_fini - -/* By default, secondary_main will cause secondary harts to spin forever. - * Users can redefine secondary_main themselves to run code on secondary harts */ -.weak   secondary_main -.global secondary_main -.type   secondary_main, @function - -secondary_main: -  addi sp, sp, -16 -#if __riscv_xlen == 32 -  sw ra, 4(sp) -#else -  sd ra, 8(sp) -#endif -  csrr t0, mhartid -  la t1, __metal_boot_hart -  beq t0, t1, 2f -1: -  wfi -  j 1b -2: -  call main -#if __riscv_xlen == 32 -  lw ra, 4(sp) -#else -  ld ra, 8(sp) -#endif -  addi sp, sp, 16 -  ret +  .size  _init, .-_init +  .size  _fini, .-_fini  /* This shim allows main() to be passed a set of arguments that can satisfy the   * requirements of the C API. */ -.section .rodata.libgloss.start +  .section .rodata.libgloss.start +  .balign 8  argv: -.dc.a name +  .dc.a name  envp: -.dc.a 0 +  .dc.a 0  name: -.asciz "libgloss" - +  .asciz "libgloss" diff --git a/fw/fe310/bsp/init/Makefile b/fw/fe310/bsp/init/Makefile new file mode 100644 index 0000000..2df25e7 --- /dev/null +++ b/fw/fe310/bsp/init/Makefile @@ -0,0 +1,13 @@ +include ../../common.mk +CFLAGS += -I$(bsp_dir)/include -I$(bsp_dir)/drivers + +%.o: %.c +	$(CC) $(CFLAGS) -c $< + +%.o: %.S +	$(CC) $(CFLAGS) -c $< + +all: entry.o + +clean: +	rm -f *.o diff --git a/fw/fe310/bsp/init/entry.S b/fw/fe310/bsp/init/entry.S new file mode 100644 index 0000000..6b80217 --- /dev/null +++ b/fw/fe310/bsp/init/entry.S @@ -0,0 +1,63 @@ +/* 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 +1:auipc gp, %pcrel_hi(__global_pointer$) +  addi  gp, gp, %pcrel_lo(1b) +.option pop + + /* Set up a trap vector */ +  la t0, eos_trap_vector +  csrw mtvec, t0 + + /* This is a bare-metal environment and therefor is expected +  * to define its own stack pointer.  We also align the stack pointer here +  * because the only RISC-V ABI that's currently defined mandates 16-byte +  * stack alignment. */ +  la sp, _sp +  andi sp, sp, -16 + + /* At this point we can enter the C runtime's startup file. */ +  call _start + + /* If we've made it back here then there's probably something wrong. +  * 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 + +/* 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  | 
