blob: 347400cabc4f2874a67332c95b79cfe4fb3a5ef5 [file] [log] [blame]
use core::hint;
/// Tock programs' entry point. Called by the kernel at program start. Sets up
/// the stack then calls rust_start() for the remainder of setup.
#[doc(hidden)]
#[naked]
#[no_mangle]
#[link_section = ".start"]
// The args for this function are:
// app_start: usize,
// mem_start: usize,
// memory_len: usize,
// app_heap_break: usize,
// Due to Rust issue: https://github.com/rust-lang/rust/issues/42779 we can't have
// args to the function
pub unsafe extern "C" fn _start() -> ! {
asm!(
// Compute the stack top.
//
// struct hdr* myhdr = (struct hdr*) app_start;
// uint32_t stacktop = (((uint32_t) mem_start + myhdr->stack_size + 7) & 0xfffffff8);
"lw t0, 36(a0) // t0 = myhdr->stack_size
addi t0, t0, 7 // t0 = myhdr->stack_size + 7
add t0, t0, a1 // t0 = mem_start + myhdr->stack_size + 7
li t1, 7 // t1 = 7
not t1, t1 // t1 = ~0x7
and t0, t0, t1 // t0 = (mem_start + myhdr->stack_size + 7) & ~0x7
//
// Compute the app data size and where initial app brk should go.
// This includes the GOT, data, and BSS sections. However, we can't be sure
// the linker puts them back-to-back, but we do assume that BSS is last
// (i.e. myhdr->got_start < myhdr->bss_start && myhdr->data_start <
// myhdr->bss_start). With all of that true, then the size is equivalent
// to the end of the BSS section.
//
// uint32_t appdata_size = myhdr->bss_start + myhdr->bss_size;
lw t1, 24(a0) // t1 = myhdr->bss_start
lw t2, 28(a0) // t2 = myhdr->bss_size
lw t3, 4(a0) // t3 = myhdr->got_start
add t1, t1, t2 // t1 = bss_start + bss_size
//
// Move arguments we need to keep over to callee-saved locations.
mv s0, a0 // s0 = void* app_start
mv s1, t0 // s1 = stack_top
mv s2, a3 // s2 = app_heap_break
//
// Now we may want to move the stack pointer. If the kernel set the
// `app_heap_break` larger than we need (and we are going to call `brk()`
// to reduce it) then our stack pointer will fit and we can move it now.
// Otherwise after the first syscall (the memop to set the brk), the return
// will use a stack that is outside of the process accessible memory.
//
bgt t1, a3, skip_set_sp // Compare `app_heap_break` with new brk.
// If our current `app_heap_break` is larger
// then we need to move the stack pointer
// before we call the `brk` syscall.
mv sp, t0 // Update the stack pointer
skip_set_sp: // Back to regularly scheduled programming.
// Call `brk` to set to requested memory
// memop(0, stacktop + appdata_size);
li a0, 4 // a0 = 4 // memop syscall
li a1, 0 // a1 = 0
mv a2, t1 // a2 = appdata_size
ecall // memop
//
// Debug support, tell the kernel the stack location
//
// memop(10, stacktop);
li a0, 4 // a0 = 4 // memop syscall
li a1, 10 // a1 = 10
mv a2, s1 // a2 = stacktop
ecall // memop
//
// Debug support, tell the kernel the heap location
//
// memop(11, stacktop + appdata_size);
li a0, 4 // a0 = 4 // memop syscall
li a1, 11 // a1 = 11
mv a2, t1 // a2 = appdata_size
ecall // memop
//
// Setup initial stack pointer for normal execution
// Call into the rest of startup. This should never return.
mv sp, s1 // sp = stacktop
mv a0, s0 // first arg is app_start
mv s0, sp // Set the frame pointer to sp.
mv a1, s1 // second arg is stacktop
mv a2, t1 // third arg is app_heap_break that we told the kernel
jal rust_start"
: // No output operands
:
: "memory", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17",
"x5", "x6", "x7", "x28", "x29", "x30", "x31", "x1" // Clobbers
: "volatile" // Options
);
hint::unreachable_unchecked();
}
/// Ensure an abort symbol exists.
#[cfg(target_arch = "riscv32")]
#[link_section = ".start"]
#[export_name = "abort"]
pub extern "C" fn abort() {
unsafe {
asm! ("
// Simply go back to the start as if we had just booted.
j _start
"
:
:
:
: "volatile");
}
}