riscv32: Initial support of RISC-V Add support for riscv32, this is based on the libtock-c support https://github.com/tock/libtock-c/commit/33df8f5c87068d153dc8560692258478d3ea749f Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
diff --git a/.cargo/config b/.cargo/config index 10d0ff2..973dfb5 100644 --- a/.cargo/config +++ b/.cargo/config
@@ -5,3 +5,10 @@ "-C", "relocation-model=static", "-D", "warnings", ] + +[target.riscv32imac-unknown-none-elf] +rustflags = [ + "-C", "link-arg=-Triscv32_layout.ld", + "-C", "relocation-model=static", + "-D", "warnings", +]
diff --git a/riscv32_layout.ld b/riscv32_layout.ld new file mode 100644 index 0000000..6a37365 --- /dev/null +++ b/riscv32_layout.ld
@@ -0,0 +1,16 @@ +/* Layout for the RISC-V 32 boards, used by the examples in this repository. */ + +MEMORY { + FLASH (rx) : ORIGIN = 0x20430000, LENGTH = 32M + SRAM (rwx) : ORIGIN = 0x80000000, LENGTH = 512K +} + +/* + * Any change to STACK_SIZE should be accompanied by a corresponding change to + * `elf2tab`'s `--stack` option + */ +STACK_SIZE = 2048; + +MPU_MIN_ALIGN = 1K; + +INCLUDE layout.ld
diff --git a/src/debug.rs b/src/debug.rs index 24b2188..c35a3b7 100644 --- a/src/debug.rs +++ b/src/debug.rs
@@ -13,6 +13,7 @@ Console::new().write(buffer); } +#[cfg(target_arch = "arm")] pub fn print_stack_pointer() { let stack_pointer; unsafe { asm!("mov $0, sp" : "=r"(stack_pointer) : : : "volatile") }; @@ -23,6 +24,9 @@ Console::new().write(buffer); } +#[cfg(target_arch = "riscv32")] +pub fn print_stack_pointer() {} + #[inline(always)] // Initial stack size is too small (128 bytes currently) pub fn dump_address(address: *const usize) { let mut buffer = [b' '; 28];
diff --git a/src/entry_point.rs b/src/entry_point.rs index 6a96eb1..9942b19 100644 --- a/src/entry_point.rs +++ b/src/entry_point.rs
@@ -58,6 +58,7 @@ /// 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] @@ -164,6 +165,123 @@ 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 + // + // 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 a1, s1 // second arg is stackto + 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)]
diff --git a/src/lib.rs b/src/lib.rs index 9b0888d..8047028 100644 --- a/src/lib.rs +++ b/src/lib.rs
@@ -22,19 +22,19 @@ pub mod timer; pub mod unwind_symbols; -#[cfg(target_arch = "arm")] +#[cfg(any(target_arch = "arm", target_arch = "riscv32"))] pub mod entry_point; -#[cfg(target_arch = "arm")] +#[cfg(any(target_arch = "arm", target_arch = "riscv32"))] mod lang_items; -#[cfg(target_arch = "arm")] +#[cfg(any(target_arch = "arm", target_arch = "riscv32"))] pub mod syscalls; -#[cfg(not(target_arch = "arm"))] +#[cfg(not(any(target_arch = "arm", target_arch = "riscv32")))] #[path = "syscalls_mock.rs"] mod syscalls; -#[cfg(target_arch = "arm")] +#[cfg(any(target_arch = "arm", target_arch = "riscv32"))] #[global_allocator] static ALLOCATOR: linked_list_allocator::LockedHeap = linked_list_allocator::LockedHeap::empty();
diff --git a/src/syscalls.rs b/src/syscalls.rs index fa0362a..049d102 100644 --- a/src/syscalls.rs +++ b/src/syscalls.rs
@@ -2,6 +2,7 @@ use crate::callback::SubscribableCallback; use crate::shared_memory::SharedMemory; +#[cfg(target_arch = "arm")] pub fn yieldk() { // Note: A process stops yielding when there is a callback ready to run, // which the kernel executes by modifying the stack frame pushed by the @@ -35,6 +36,21 @@ } } +#[cfg(target_arch = "riscv32")] +pub fn yieldk() { + /* TODO: Stop yielding */ + unsafe { + asm! ( + "li a0, 0 + ecall" + : + : + : "memory", "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", + "t0", "t1", "t2", "t3", "t4", "t5", "t6", "ra" + : "volatile"); + } +} + pub fn yieldk_for<F: Fn() -> bool>(cond: F) { while !cond() { yieldk(); @@ -72,6 +88,7 @@ callback.call_rust(arg0, arg1, arg2); } +#[cfg(target_arch = "arm")] pub unsafe fn subscribe_ptr( major: usize, minor: usize, @@ -86,6 +103,24 @@ res } +#[cfg(target_arch = "riscv32")] +pub unsafe fn subscribe_ptr( + major: usize, + minor: usize, + cb: *const unsafe extern "C" fn(usize, usize, usize, usize), + ud: usize, +) -> isize { + let res; + asm!("li a0, 1 + ecall" + : "=r" (res) + : "r" (major), "r" (minor), "r" (cb), "r" (ud) + : "memory" + : "volatile" ); + res +} + +#[cfg(target_arch = "arm")] pub unsafe fn command(major: usize, minor: usize, arg1: usize, arg2: usize) -> isize { let res; asm!("svc 2" : "={r0}"(res) @@ -95,6 +130,18 @@ res } +#[cfg(target_arch = "riscv32")] +pub unsafe fn command(major: usize, minor: usize, arg1: usize, arg2: usize) -> isize { + let res; + asm!("li a0, 2 + ecall" + : "=r" (res) + : "r" (major), "r" (minor), "r" (arg1), "r" (arg2) + : "memory" + : "volatile"); + res +} + pub fn allow( driver_number: usize, allow_number: usize, @@ -120,6 +167,7 @@ } } +#[cfg(target_arch = "arm")] pub unsafe fn allow_ptr(major: usize, minor: usize, slice: *mut u8, len: usize) -> isize { let res; asm!("svc 3" : "={r0}"(res) @@ -129,6 +177,19 @@ res } +#[cfg(target_arch = "riscv32")] +pub unsafe fn allow_ptr(major: usize, minor: usize, slice: *mut u8, len: usize) -> isize { + let res; + asm!("li a0, 3 + ecall" + : "=r" (res) + : "r" (major), "r" (minor), "r" (slice), "r" (len) + : "memory" + : "volatile"); + res +} + +#[cfg(target_arch = "arm")] pub unsafe fn memop(major: u32, arg1: usize) -> isize { let res; asm!("svc 4" : "={r0}"(res) @@ -137,3 +198,15 @@ : "volatile"); res } + +#[cfg(target_arch = "riscv32")] +pub unsafe fn memop(major: u32, arg1: usize) -> isize { + let res; + asm!("li a0, 4 + ecall" + : "=r" (res) + : "r" (major), "r" (arg1) + : "memory" + : "volatile"); + res +}