123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246 |
- /* Copyright (c) 2017-2018 SiFive Inc. All rights reserved.
- 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. */
- _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
- /* 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
- _init:
- _fini:
- 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
- /* This shim allows main() to be passed a set of arguments that can satisfy the
- * requirements of the C API. */
- .section .rodata.libgloss.start
- argv:
- .dc.a name
- envp:
- .dc.a 0
- name:
- .asciz "libgloss"
|