From 4754b66f72a8fbfd48e30ae0bd12a44d13a4ce10 Mon Sep 17 00:00:00 2001 From: Johannes Stoelp Date: Sun, 1 May 2022 17:50:00 +0200 Subject: initial commit Build small no_std example for rv64i target. This code executes some linux syscalls as demo and therefore expects to be executed in rv64 linux. --- .cargo/config | 9 +++++ .gitignore | 1 + Cargo.lock | 7 ++++ Cargo.toml | 8 +++++ build.rs | 61 ++++++++++++++++++++++++++++++++++ src/lib.rs | 105 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 20 +++++++++++ 7 files changed, 211 insertions(+) create mode 100644 .cargo/config create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 build.rs create mode 100644 src/lib.rs create mode 100644 src/main.rs diff --git a/.cargo/config b/.cargo/config new file mode 100644 index 0000000..6588394 --- /dev/null +++ b/.cargo/config @@ -0,0 +1,9 @@ +[build] +target = "riscv64imac-unknown-none-elf" + +[target.riscv64imac-unknown-none-elf] +# Effectively build for rv64i. +rustflags = ["-C", "target-feature=-m,-a,-c"] +runner = "qemu-riscv64" +#runner = "qemu-riscv64 -d in_asm" +#runner = "qemu-riscv64 -strace" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..51c8e32 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "user-sw" +version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..f7e203e --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "user-sw" +version = "0.1.0" +authors = ["Johannes Stoelp"] +edition = "2021" +license = "" + +[dependencies] diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..827c302 --- /dev/null +++ b/build.rs @@ -0,0 +1,61 @@ +/// Generate rust constants for linux riscv syscall numbers. +/// +/// Syscall numbers are parsed from the `linux/unistd.h` header. +/// This script uses the `riscv64-linux-gnu-*` tools to find to header, make sure those tools are +/// in the PATH. +/// +/// For simplicity, this script unwraps any Option/Result because we want to fail in case of any +/// error. + +fn main() { + // Get sysroot to find header location. + let out = std::process::Command::new("riscv64-linux-gnu-gcc") + .arg("-print-sysroot") + .output() + .unwrap(); + let sysroot = std::str::from_utf8(&out.stdout).unwrap(); + let header = format!("{}/include/linux/unistd.h", sysroot.trim_end()); + + // The header should "basically" never change. + println!("cargo:rerun-if-changed={}", header); + + // Run header through preprocessor and dump macro definitions. + let out = std::process::Command::new("riscv64-linux-gnu-cpp") + .arg("-dM") + .arg(header) + .output() + .unwrap(); + let defines = std::str::from_utf8(&out.stdout).unwrap(); + + let mut output = String::with_capacity(256); + + // Parse out lines of the format + // #define __NR_ + // and generate output strings. + for line in defines.lines() { + let line = match line.strip_prefix("#define __NR_") { + Some(line) => line, + None => continue, + }; + + let (sys, nr) = match line.split_once(' ') { + Some(split) => split, + None => continue, + }; + + let nr = match nr.parse::() { + Ok(nr) => nr, + Err(_) => continue, + }; + + output.push_str(&format!( + "#[allow(unused)] const SYS_{}: usize = {};\n", + sys.to_uppercase(), + nr + )); + } + + // Write out rust file with syscall numbers. + let outfile = format!("{}/syscalls.rs", std::env::var("OUT_DIR").unwrap()); + std::fs::write(&outfile, output).unwrap(); +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..3191f8d --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,105 @@ +#![no_std] + +/// Raw file descriptor. +/// +/// Instances of `Fd` can be constructed with [`stdout()`] and [`stderr()`]. +pub struct Fd(i32); + +impl core::fmt::Write for Fd { + fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> { + if sys::write(self.0, s.as_bytes()) != -1 { + Ok(()) + } else { + Err(core::fmt::Error) + } + } +} + +/// Construct raw file descriptor for `stdout`. +pub fn stdout() -> Fd { + Fd(1) +} + +/// Construct raw file descriptor for `stderr`. +pub fn stderr() -> Fd { + Fd(2) +} + +/// Write format string to `stdout`, swallowing any errors. +#[macro_export] +macro_rules! println { + () => ({ + $crate::println!("") + }); + ($($arg:tt)*) => ({ + use core::fmt::Write; + let _ = writeln!(&mut $crate::stdout(), $($arg)*); + }); +} + +/// Write format string to `stderr`, swallowing any errors. +#[macro_export] +macro_rules! eprintln { + () => ({ + $crate::eprintln!("") + }); + ($($arg:tt)*) => ({ + use core::fmt::Write; + let _ = writeln!(&mut $crate::stderr(), $($arg)*); + }); +} + +/// Wrapper to invoke `riscv` linux syscalls. +/// +/// Syscalls are generated according to the linux [syscall(2)][man-syscall] abi. +/// +/// # Invocation +/// ``` +/// Arch/ABI Instruction System Ret Ret +/// call # val val2 +/// ---------------------------------------- +/// riscv ecall a7 a0 a1 +/// ``` +/// +/// # Arguments +/// ``` +/// Arch/ABI arg1 arg2 arg3 arg4 arg5 arg6 +/// -------------------------------------------- +/// riscv a0 a1 a2 a3 a4 a5 +/// ``` +/// +/// [man-syscall]: https://man7.org/linux/man-pages/man2/syscall.2.html +#[cfg(target_arch = "riscv64")] +pub mod sys { + use core::arch::asm; + + // Include generated syscall numbers (build.rs). + include!(concat!(env!("OUT_DIR"), "/syscalls.rs")); + + #[inline] + pub fn write(fd: i32, buf: &[u8]) -> i32 { + let mut ret; + unsafe { + asm!( + "ecall", + in("a7") SYS_WRITE, + inlateout("a0") fd => ret, + in("a1") buf.as_ptr(), + in("a2") buf.len(), + ); + } + ret + } + + #[inline] + pub fn exit(status: i32) -> ! { + unsafe { + asm!( + "ecall", + in("a7") SYS_EXIT, + in("a0") status, + options(noreturn), + ); + } + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..f1ea8b0 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,20 @@ +#![no_std] +#![no_main] + +use user_sw::{eprintln, println, sys}; + +fn main() { + println!("Hello {} from rust main().", 1337); +} + +#[no_mangle] +pub extern "C" fn _start() -> ! { + main(); + sys::exit(0); +} + +#[panic_handler] +fn panic_handler(info: &core::panic::PanicInfo) -> ! { + eprintln!("{}", info); + sys::exit(42); +} -- cgit v1.2.3