Restructure entry point - put architecture dependent code in separate files
diff --git a/src/entry_point.rs b/src/entry_point.rs deleted file mode 100644 index 25aaad4..0000000 --- a/src/entry_point.rs +++ /dev/null
@@ -1,390 +0,0 @@ -use crate::memop; -use crate::syscalls; -use core::intrinsics; -use core::ptr; - -// _start and rust_start are the first two procedures executed when a Tock -// application starts. _start is invoked directly by the Tock kernel; it -// performs stack setup then calls rust_start. rust_start performs data -// relocation and sets up the heap before calling the rustc-generated main. -// rust_start and _start are tightly coupled. -// -// The memory layout is controlled by the linker script. -// -// When the kernel gives control to us, we get r0-r3 values that is as follows. -// -// +--------------+ <- (r2) mem.len() -// | Grant | -// +--------------+ -// | Unused | -// S +--------------+ <- (r3) app_heap_break -// R | Heap | (hardcoded to mem_start + 3072 in -// A +--------------| Processs::create which could be lesser than -// M | .bss | mem_start + stack + .data + .bss) -// +--------------| -// | .data | -// +--------------+ -// | Stack | -// +--------------+ <- (r1) mem_start -// -// +--------------+ -// | .text | -// F +--------------+ -// L | .crt0_header | -// A +--------------+ <- (r0) app_start -// S | Protected | -// H | Region | -// +--------------+ -// -// We want to organize the memory as follows. -// -// +--------------+ <- app_heap_break -// | Heap | -// +--------------| <- heap_start -// | .bss | -// +--------------| -// | .data | -// +--------------+ <- stack_start (stacktop) -// | Stack | -// | (grows down) | -// +--------------+ <- mem_start -// -// app_heap_break and mem_start are given to us by the kernel. The stack size is -// determined using pointer app_start, and is used with mem_start to compute -// stack_start (stacktop). The placement of .data and .bss are given to us by -// the linker script; the heap is located between the end of .bss and -// app_heap_break. This requires that .bss is the last (highest-address) section -// placed by the linker script. - -/// Tock programs' entry point. Called by the kernel at program start. Sets up -/// the stack then calls rust_start() for the remainder of setup. -#[cfg(target_arch = "arm")] -#[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", "r7", "r8", "r12", - "cc", "memory" // Clobbers - : "volatile" // Options - ); - intrinsics::unreachable(); -} - -/// Tock programs' entry point. Called by the kernel at program start. Sets up -/// the stack then calls rust_start() for the remainder of setup. -#[cfg(target_arch = "riscv32")] -#[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. - // - add t2, t0, t1 // t2 = stacktop + appdata_size - bgt t2, 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, t2 // a2 = stacktop + 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 = 10 - mv a2, t2 // a2 = stacktop + 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, s2 // third arg is app_heap_break - jal rust_start" - : // No output operands - : - : "memory", "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", - "t0", "t1", "t2", "t3", "t4", "t5", "t6", "ra" // Clobbers - : "volatile" // Options - ); - intrinsics::unreachable(); -} - -/// 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"); - } -} - -/// The header encoded at the beginning of .text by the linker script. It is -/// accessed by rust_start() using its app_start parameter. -#[repr(C)] -struct LayoutHeader { - got_sym_start: usize, - got_start: usize, - got_size: usize, - data_sym_start: usize, - data_start: usize, - data_size: usize, - bss_start: usize, - bss_size: usize, - reldata_start: usize, - stack_size: usize, -} - -/// Rust setup, called by _start. Uses the extern "C" calling convention so that -/// the assembly in _start knows how to call it (the Rust ABI is not defined). -/// Sets up the data segment (including relocations) and the heap, then calls -/// into the rustc-generated main(). This cannot use mutable global variables or -/// global references to globals until it is done setting up the data segment. -#[no_mangle] -pub unsafe extern "C" fn rust_start(app_start: usize, stacktop: usize, app_heap_break: usize) -> ! { - extern "C" { - // This function is created internally by `rustc`. See - // `src/lang_items.rs` for more details. - fn main(argc: isize, argv: *const *const u8) -> isize; - } - - // Copy .data into its final location in RAM (determined by the linker - // script -- should be immediately above the stack). - let layout_header: &LayoutHeader = core::mem::transmute(app_start); - - let data_flash_start_addr = app_start + layout_header.data_sym_start; - - intrinsics::copy_nonoverlapping( - data_flash_start_addr as *const u8, - stacktop as *mut u8, - layout_header.data_size, - ); - - // Zero .bss (specified by the linker script). - let bss_end = layout_header.bss_start + layout_header.bss_size; // 1 past the end of .bss - for i in layout_header.bss_start..bss_end { - core::ptr::write(i as *mut u8, 0); - } - - // TODO: Wait for rustc to have working ROPI-RWPI relocation support, then - // implement dynamic relocations here. At the moment, rustc does not have - // working ROPI-RWPI support, and it is not clear what that support would - // look like at the LLVM level. Once we know what the relocation strategy - // looks like we can write the dynamic linker. - - // Initialize the heap. Unlike libtock-c's newlib allocator, which can use - // `sbrk` system call to dynamically request heap memory from the kernel, we - // need to tell `linked_list_allocator` where the heap starts and ends. - // - // Heap size is set using `elf2tab` with `--app-heap` option, which is - // currently at 1024. If you change the `elf2tab` heap size, make sure to - // make the corresponding change here. - const HEAP_SIZE: usize = 1024; - - // we could have also bss_end for app_heap_start - let app_heap_start = app_heap_break; - let app_heap_end = app_heap_break + HEAP_SIZE; - - // Tell the kernel the new app heap break. - memop::set_brk(app_heap_end as *const u8); - - HEAP.init(app_heap_start, HEAP_SIZE); - - main(0, ptr::null()); - - loop { - syscalls::raw::yieldk(); - } -} - -use core::alloc::GlobalAlloc; -use core::alloc::Layout; -use core::ptr::NonNull; -use linked_list_allocator::Heap; - -#[global_allocator] -static ALLOCATOR: TockAllocator = TockAllocator; - -static mut HEAP: Heap = Heap::empty(); - -struct TockAllocator; - -unsafe impl GlobalAlloc for TockAllocator { - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - HEAP.allocate_first_fit(layout) - .ok() - .map_or(ptr::null_mut(), NonNull::as_ptr) - } - - unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { - HEAP.deallocate(NonNull::new_unchecked(ptr), layout) - } -}
diff --git a/src/entry_point/mod.rs b/src/entry_point/mod.rs new file mode 100644 index 0000000..6c1f964 --- /dev/null +++ b/src/entry_point/mod.rs
@@ -0,0 +1,167 @@ +use crate::memop; +use crate::syscalls; +use core::intrinsics; +use core::ptr; + +// _start and rust_start are the first two procedures executed when a Tock +// application starts. _start is invoked directly by the Tock kernel; it +// performs stack setup then calls rust_start. rust_start performs data +// relocation and sets up the heap before calling the rustc-generated main. +// rust_start and _start are tightly coupled. +// +// The memory layout is controlled by the linker script. +// +// When the kernel gives control to us, we get r0-r3 values that is as follows. +// +// +--------------+ <- (r2) mem.len() +// | Grant | +// +--------------+ +// | Unused | +// S +--------------+ <- (r3) app_heap_break +// R | Heap | (hardcoded to mem_start + 3072 in +// A +--------------| Processs::create which could be lesser than +// M | .bss | mem_start + stack + .data + .bss) +// +--------------| +// | .data | +// +--------------+ +// | Stack | +// +--------------+ <- (r1) mem_start +// +// +--------------+ +// | .text | +// F +--------------+ +// L | .crt0_header | +// A +--------------+ <- (r0) app_start +// S | Protected | +// H | Region | +// +--------------+ +// +// We want to organize the memory as follows. +// +// +--------------+ <- app_heap_break +// | Heap | +// +--------------| <- heap_start +// | .bss | +// +--------------| +// | .data | +// +--------------+ <- stack_start (stacktop) +// | Stack | +// | (grows down) | +// +--------------+ <- mem_start +// +// app_heap_break and mem_start are given to us by the kernel. The stack size is +// determined using pointer app_start, and is used with mem_start to compute +// stack_start (stacktop). The placement of .data and .bss are given to us by +// the linker script; the heap is located between the end of .bss and +// app_heap_break. This requires that .bss is the last (highest-address) section +// placed by the linker script. + +#[cfg_attr(target_arch = "riscv32", path = "start_item_riscv32.rs")] +#[cfg_attr(target_arch = "arm", path = "start_item_arm.rs")] +#[cfg_attr( + not(any(target_arch = "arm", target_arch = "riscv32")), + path = "start_item_mock.rs" +)] +mod start_item; + +/// The header encoded at the beginning of .text by the linker script. It is +/// accessed by rust_start() using its app_start parameter. +#[repr(C)] +struct LayoutHeader { + got_sym_start: usize, + got_start: usize, + got_size: usize, + data_sym_start: usize, + data_start: usize, + data_size: usize, + bss_start: usize, + bss_size: usize, + reldata_start: usize, + stack_size: usize, +} + +/// Rust setup, called by _start. Uses the extern "C" calling convention so that +/// the assembly in _start knows how to call it (the Rust ABI is not defined). +/// Sets up the data segment (including relocations) and the heap, then calls +/// into the rustc-generated main(). This cannot use mutable global variables or +/// global references to globals until it is done setting up the data segment. +#[no_mangle] +pub unsafe extern "C" fn rust_start(app_start: usize, stacktop: usize, app_heap_break: usize) -> ! { + extern "C" { + // This function is created internally by `rustc`. See + // `src/lang_items.rs` for more details. + fn main(argc: isize, argv: *const *const u8) -> isize; + } + + // Copy .data into its final location in RAM (determined by the linker + // script -- should be immediately above the stack). + let layout_header: &LayoutHeader = core::mem::transmute(app_start); + + let data_flash_start_addr = app_start + layout_header.data_sym_start; + + intrinsics::copy_nonoverlapping( + data_flash_start_addr as *const u8, + stacktop as *mut u8, + layout_header.data_size, + ); + + // Zero .bss (specified by the linker script). + let bss_end = layout_header.bss_start + layout_header.bss_size; // 1 past the end of .bss + for i in layout_header.bss_start..bss_end { + core::ptr::write(i as *mut u8, 0); + } + + // TODO: Wait for rustc to have working ROPI-RWPI relocation support, then + // implement dynamic relocations here. At the moment, rustc does not have + // working ROPI-RWPI support, and it is not clear what that support would + // look like at the LLVM level. Once we know what the relocation strategy + // looks like we can write the dynamic linker. + + // Initialize the heap. Unlike libtock-c's newlib allocator, which can use + // `sbrk` system call to dynamically request heap memory from the kernel, we + // need to tell `linked_list_allocator` where the heap starts and ends. + // + // Heap size is set using `elf2tab` with `--app-heap` option, which is + // currently at 1024. If you change the `elf2tab` heap size, make sure to + // make the corresponding change here. + const HEAP_SIZE: usize = 1024; + + // we could have also bss_end for app_heap_start + let app_heap_start = app_heap_break; + let app_heap_end = app_heap_break + HEAP_SIZE; + + // Tell the kernel the new app heap break. + memop::set_brk(app_heap_end as *const u8); + + HEAP.init(app_heap_start, HEAP_SIZE); + + main(0, ptr::null()); + + loop { + syscalls::raw::yieldk(); + } +} + +use core::alloc::GlobalAlloc; +use core::alloc::Layout; +use core::ptr::NonNull; +use linked_list_allocator::Heap; + +#[global_allocator] +static ALLOCATOR: TockAllocator = TockAllocator; + +static mut HEAP: Heap = Heap::empty(); + +struct TockAllocator; + +unsafe impl GlobalAlloc for TockAllocator { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + HEAP.allocate_first_fit(layout) + .ok() + .map_or(ptr::null_mut(), NonNull::as_ptr) + } + + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + HEAP.deallocate(NonNull::new_unchecked(ptr), layout) + } +}
diff --git a/src/entry_point/start_item_arm.rs b/src/entry_point/start_item_arm.rs new file mode 100644 index 0000000..b415aac --- /dev/null +++ b/src/entry_point/start_item_arm.rs
@@ -0,0 +1,113 @@ +use core::intrinsics; + +/// 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", "r7", "r8", "r12", + "cc", "memory" // Clobbers + : "volatile" // Options + ); + intrinsics::unreachable(); +}
diff --git a/src/entry_point/start_item_mock.rs b/src/entry_point/start_item_mock.rs new file mode 100644 index 0000000..ec3db09 --- /dev/null +++ b/src/entry_point/start_item_mock.rs
@@ -0,0 +1 @@ +// Empty file needed to provide a correct start_item module
diff --git a/src/entry_point/start_item_riscv32.rs b/src/entry_point/start_item_riscv32.rs new file mode 100644 index 0000000..1511b2d --- /dev/null +++ b/src/entry_point/start_item_riscv32.rs
@@ -0,0 +1,118 @@ +use core::intrinsics; + +/// 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. + // + add t2, t0, t1 // t2 = stacktop + appdata_size + bgt t2, 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, t2 // a2 = stacktop + 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 = 10 + mv a2, t2 // a2 = stacktop + 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, s2 // third arg is app_heap_break + jal rust_start" + : // No output operands + : + : "memory", "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", + "t0", "t1", "t2", "t3", "t4", "t5", "t6", "ra" // Clobbers + : "volatile" // Options + ); + intrinsics::unreachable(); +} + +/// 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"); + } +}