Merge pull request #88 from alistair23/alistair/riscv32
riscv32: Initial support of RISC-V
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
+}