blob: c9390f617183140cb2b80dece4c95e391398ba5c [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)]
#[no_mangle]
#[naked]
#[link_section = ".start"]
pub unsafe extern "C" fn _start(
app_start: usize,
mem_start: usize,
_memory_len: usize,
app_heap_break: usize,
) -> ! {
asm!("
// Because ROPI-RWPI support in LLVM/rustc is incomplete, Rust
// applications must be statically linked. An offset between the
// location the program is linked at and its actual location in flash
// would cause references in .data and .rodata to point to the wrong
// data. To mitigate this, this section checks that .text (and .start)
// are loaded at the correct location. If the application was linked and
// loaded correctly, the location of the first instruction (read using
// the Program Counter) will match the intended location of .start. We
// don't have an easy way to signal an error, so for now we just yield
// if the location is wrong.
sub r4, pc, #4 // r4 = pc
ldr r5, =.start // r5 = address of .start
cmp r4, r5
beq .Lstack_init // Jump to stack initialization if pc was correct
movw r0, #8 // LowLevelDebug driver number
movw r1, #1 // LowLevelDebug 'print status code' command
movw r2, #2 // LowLevelDebug relocation failed status code
svc 2 // command() syscall
.Lyield_loop:
svc 0 // yield() syscall (in infinite loop)
b .Lyield_loop
.Lstack_init:
// Compute the stacktop (stack_start). The stacktop is computed as
// stack_size + mem_start plus padding to align the stack to a multiple
// of 8 bytes. The 8 byte alignment is to follow ARM AAPCS:
// http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka4127.html
ldr r4, [r0, #36] // r4 = app_start->stack_size
add r4, r4, r1 // r4 = app_start->stack_size + mem_start
add r4, #7 // r4 = app_start->stack_size + mem_start + 7
bic r4, r4, #7 // r4 = (app_start->stack_size + mem_start + 7) & ~0x7
mov sp, r4 // sp = r4
// We need to pass app_start, stacktop and app_heap_break to rust_start.
// Temporarily store them in r6, r7 and r8
mov r6, r0
mov r7, sp
// Debug support, tell the kernel the stack location
//
// memop(10, stacktop)
// r7 contains stacktop
mov r0, #10
mov r1, r7
svc 4
// Debug support, tell the kernel the heap_start location
mov r0, r6
ldr r4, [r0, #24] // r4 = app_start->bss_start
ldr r5, [r0, #28] // r5 = app_start->bss_size
add r4, r4, r5 // r4 = bss_start + bss_size
//
// memop(11, r4)
mov r0, #11
mov r1, r4
svc 4
// Store heap_start (and soon to be app_heap_break) in r8
mov r8, r4
// There is a possibility that stack + .data + .bss is greater than
// 3072. Therefore setup the initial app_heap_break to heap_start (that
// is zero initial heap) and let rust_start determine where the actual
// app_heap_break should go.
//
// Also, because app_heap_break is where the unprivileged MPU region
// ends, in case mem_start + stack + .data + .bss is greater than
// initial app_heap_break (mem_start + 3072), we will get a memory fault
// in rust_start when initializing .data and .bss. Setting
// app_heap_break to heap_start avoids that.
// memop(0, r8)
mov r0, #0
mov r1, r8
svc 4
// NOTE: If there is a hard-fault before this point, then
// process_detail_fmt in kernel/src/process.rs panics which
// will result in us losing the PC of the instruction
// generating the hard-fault. Therefore any code before
// this point is critical code
// Setup parameters needed by rust_start
// r6 (app_start), r7 (stacktop), r8 (app_heap_break)
mov r0, r6
mov r1, r7
mov r2, r8
// Call rust_start
bl rust_start"
: // No output operands
: "{r0}"(app_start), "{r1}"(mem_start), "{r3}"(app_heap_break) // Input operands
: "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r8", "r12",
"cc", "memory" // Clobbers
: "volatile" // Options
);
hint::unreachable_unchecked()
}