aboutsummaryrefslogtreecommitdiff
#![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),
            );
        }
    }
}