瀏覽代碼

Add more functions

luojia65 4 年之前
父節點
當前提交
1d27778ed6
共有 14 個文件被更改,包括 553 次插入77 次删除
  1. 99 7
      src/ecall.rs
  2. 9 7
      src/ecall/base.rs
  3. 17 0
      src/ecall/ipi.rs
  4. 67 0
      src/ecall/legacy.rs
  5. 45 0
      src/ecall/timer.rs
  6. 12 0
      src/extension.rs
  7. 59 0
      src/hart_mask.rs
  8. 42 0
      src/ipi.rs
  9. 97 9
      src/legacy_stdio.rs
  10. 20 54
      src/lib.rs
  11. 11 0
      src/logo.rs
  12. 6 0
      src/logo.txt
  13. 31 0
      src/privileged.rs
  14. 38 0
      src/timer.rs

+ 99 - 7
src/ecall.rs

@@ -1,19 +1,81 @@
-//! 这是一个私有模块
-//! 它将会处理所有的SBI调用陷入
-
+//! 这个模块将会处理所有的SBI调用陷入
 // 你应该在riscv-rt或其它中断处理函数里,调用这个模块的内容
+
 mod base;
+mod ipi;
+mod legacy;
+mod timer;
 
-const EXTENSION_BASE: usize = 0x10;
+pub const EXTENSION_BASE: usize = 0x10;
+pub const EXTENSION_TIMER: usize = 0x54494D45;
+pub const EXTENSION_IPI: usize = 0x735049;
+// const EXTENSION_RFENCE: usize = 0x52464E43;
+// const EXTENSION_HSM: usize = 0x48534D;
 
+const LEGACY_SET_TIMER: usize = 0x0;
+const LEGACY_CONSOLE_PUTCHAR: usize = 0x01;
+const LEGACY_CONSOLE_GETCHAR: usize = 0x02;
+// const LEGACY_CLEAR_IPI: usize = 0x03;
+const LEGACY_SEND_IPI: usize = 0x04;
+// const LEGACY_REMOTE_FENCE_I: usize = 0x05;
+// const LEGACY_REMOTE_SFENCE_VMA: usize = 0x06;
+// const LEGACY_REMOTE_SFENCE_VMA_ASID: usize = 0x07;
+// const LEGACY_SHUTDOWN: usize = 0x08;
+
+/// Supervisor environment call handler function
+///
 /// You should call this function in your runtime's exception handler.
-/// If the incoming exception is caused by `ecall`,
+/// If the incoming exception is caused by supervisor `ecall`,
 /// call this function with parameters extracted from trap frame.
+/// After this function returns, store the return value into `a0` and `a1` parameters.
+///
+/// This function also adapts to the legacy functions.
+/// If the supervisor called any of legacy function, the `a0` and `a1` parameter
+/// is transferred to `SbiRet`'s error and value respectively.
+/// So you should store the result into `a0` and `a1` in any function calls including legacy functions.
+///
+/// # Example
+///
+/// A typical usage:
+///
+/// ```no_run
+/// #[exception]
+/// fn handle_exception(ctx: &mut TrapFrame) {
+///     if mcause::read().cause() == Trap::Exception(Exception::SupervisorEnvCall) {
+///         let params = [ctx.a0, ctx.a1, ctx.a2, ctx.a3];
+///         let ans = rustsbi::ecall(ctx.a7, ctx.a6, params);
+///         ctx.a0 = ans.error;
+///         ctx.a1 = ans.value;
+///         mepc::write(mepc::read().wrapping_add(4));
+///     }
+///     // other conditions..
+/// }
+/// ```
+///
+/// Do not forget to advance `mepc` by 4 after an ecall is handled.
+/// This skips the `ecall` instruction itself which is 4-byte long in all conditions.
 #[inline]
 pub fn handle_ecall(extension: usize, function: usize, param: [usize; 4]) -> SbiRet {
     match extension {
         EXTENSION_BASE => base::handle_ecall_base(function, param[0]),
-        _ => todo!(),
+        EXTENSION_TIMER => match () {
+            #[cfg(target_pointer_width = "64")]
+            () => timer::handle_ecall_timer_64(function, param[0]),
+            #[cfg(target_pointer_width = "32")]
+            () => timer::handle_ecall_timer_32(function, param[0], param[1]),
+        },
+        EXTENSION_IPI => ipi::handle_ecall_ipi(function, param[0], param[1]),
+        LEGACY_SET_TIMER => match () {
+            #[cfg(target_pointer_width = "64")]
+            () => legacy::set_timer_64(param[0]),
+            #[cfg(target_pointer_width = "32")]
+            () => legacy::set_timer_32(param[0], param[1]),
+        }
+        .legacy_void(param[0], param[1]),
+        LEGACY_CONSOLE_PUTCHAR => legacy::console_putchar(param[0]).legacy_void(param[0], param[1]),
+        LEGACY_CONSOLE_GETCHAR => legacy::console_getchar().legacy_return(param[1]),
+        LEGACY_SEND_IPI => legacy::send_ipi(param[0]).legacy_void(param[0], param[1]),
+        _ => SbiRet::not_supported(),
     }
 }
 
@@ -27,8 +89,38 @@ pub struct SbiRet {
     pub value: usize,
 }
 
+const SBI_SUCCESS: usize = 0;
+// const SBI_ERR_FAILED: usize = usize::from_ne_bytes(isize::to_ne_bytes(-1));
+const SBI_ERR_NOT_SUPPORTED: usize = usize::from_ne_bytes(isize::to_ne_bytes(-2));
+// const SBI_ERR_INVALID_PARAM: usize = usize::from_ne_bytes(isize::to_ne_bytes(-3));
+// const SBI_ERR_DENIED: usize = usize::from_ne_bytes(isize::to_ne_bytes(-4));
+// const SBI_ERR_INVALID_ADDRESS: usize = usize::from_ne_bytes(isize::to_ne_bytes(-5));
+// const SBI_ERR_ALREADY_AVAILABLE: usize = usize::from_ne_bytes(isize::to_ne_bytes(-6));
+
 impl SbiRet {
     pub(crate) fn ok(value: usize) -> SbiRet {
-        SbiRet { error: 0, value }
+        SbiRet {
+            error: SBI_SUCCESS,
+            value,
+        }
+    }
+    pub(crate) fn not_supported() -> SbiRet {
+        SbiRet {
+            error: SBI_ERR_NOT_SUPPORTED,
+            value: 0,
+        }
+    }
+    // only used for legacy where a0, a1 return value is not modified
+    pub(crate) fn legacy_void(self, a0: usize, a1: usize) -> SbiRet {
+        SbiRet {
+            error: a0,
+            value: a1,
+        }
+    }
+    pub(crate) fn legacy_return(self, a1: usize) -> SbiRet {
+        SbiRet {
+            error: self.error,
+            value: a1,
+        }
     }
 }

+ 9 - 7
src/ecall/base.rs

@@ -11,6 +11,7 @@ const FUNCTION_BASE_GET_MVENDORID: usize = 0x4;
 const FUNCTION_BASE_GET_MARCHID: usize = 0x5;
 const FUNCTION_BASE_GET_MIMPID: usize = 0x6;
 
+#[inline]
 pub fn handle_ecall_base(function: usize, param0: usize) -> SbiRet {
     match function {
         FUNCTION_BASE_GET_SPEC_VERSION => get_spec_version(),
@@ -20,7 +21,7 @@ pub fn handle_ecall_base(function: usize, param0: usize) -> SbiRet {
         FUNCTION_BASE_GET_MVENDORID => get_mvendorid(),
         FUNCTION_BASE_GET_MARCHID => get_marchid(),
         FUNCTION_BASE_GET_MIMPID => get_mimpid(),
-        _ => unimplemented!(),
+        _ => SbiRet::not_supported(),
     }
 }
 
@@ -32,21 +33,22 @@ fn get_spec_version() -> SbiRet {
 
 #[inline]
 fn get_sbi_impl_id() -> SbiRet {
-    let sbi_impl_id = 0; // todo: 可配置
+    let sbi_impl_id = crate::IMPL_ID_RUSTSBI;
     SbiRet::ok(sbi_impl_id)
 }
 
 #[inline]
 fn get_sbi_impl_version() -> SbiRet {
-    let sbi_impl_version = 0; // todo: 可配置
+    let sbi_impl_version = crate::RUSTSBI_VERSION;
     SbiRet::ok(sbi_impl_version)
 }
 
 #[inline]
-fn probe_extension(_extension_id: usize) -> SbiRet {
-    // drop(extension_id); // todo use
-    let extension_return = 0; // todo: 可配置
-    SbiRet::ok(extension_return)
+fn probe_extension(extension_id: usize) -> SbiRet {
+    const NO_EXTENSION: usize = 0;
+    const HAS_EXTENSION: usize = 1;
+    let ans = crate::extension::probe_extension(extension_id);
+    SbiRet::ok(if ans { HAS_EXTENSION } else { NO_EXTENSION })
 }
 
 #[inline]

+ 17 - 0
src/ecall/ipi.rs

@@ -0,0 +1,17 @@
+use super::SbiRet;
+
+const FUNCTION_IPI_SEND_IPI: usize = 0x0;
+
+#[inline]
+pub fn handle_ecall_ipi(function: usize, param0: usize, param1: usize) -> SbiRet {
+    match function {
+        FUNCTION_IPI_SEND_IPI => send_ipi(param0, param1),
+        _ => SbiRet::not_supported(),
+    }
+}
+
+#[inline]
+fn send_ipi(_hart_mask: usize, _hart_mask_base: usize) -> SbiRet {
+    // todo: send software interrupt to another hart
+    SbiRet::ok(0)
+}

+ 67 - 0
src/ecall/legacy.rs

@@ -0,0 +1,67 @@
+use super::SbiRet;
+use crate::hart_mask::HartMask;
+use crate::ipi::{max_hart_id, send_ipi_many};
+use crate::legacy_stdio::{legacy_stdio_getchar, legacy_stdio_putchar};
+use riscv::register::{mie, mip};
+
+#[inline]
+pub fn console_putchar(param0: usize) -> SbiRet {
+    let ch = (param0 & 0xff) as u8;
+    legacy_stdio_putchar(ch);
+    SbiRet::ok(0) // the return value 0 is ignored in legacy
+}
+
+#[inline]
+pub fn console_getchar() -> SbiRet {
+    let ch = legacy_stdio_getchar();
+    SbiRet::ok(ch as usize)
+}
+
+#[inline]
+pub fn send_ipi(hart_mask_addr: usize) -> SbiRet {
+    // note(unsafe): if any load fault, should be handled by user or supervisor
+    let hart_mask = unsafe { HartMask::from_addr(hart_mask_addr, max_hart_id()) };
+    send_ipi_many(hart_mask);
+    SbiRet::ok(0) // the return value 0 is ignored in legacy
+}
+
+#[inline]
+#[cfg(target_pointer_width = "64")]
+pub fn set_timer_64(time_value: usize) -> SbiRet {
+    crate::timer::set_timer(time_value as u64);
+
+    let mtip = mip::read().mtimer();
+    if mtip {
+        unsafe {
+            mie::clear_mtimer();
+            mip::set_stimer();
+        }
+    } else {
+        unsafe {
+            mie::set_mtimer();
+            mip::clear_stimer();
+        }
+    }
+    SbiRet::ok(0)
+}
+
+#[inline]
+#[cfg(target_pointer_width = "32")]
+pub fn set_timer_32(arg0: usize, arg1: usize) -> SbiRet {
+    let time_value = (arg0 as u64) + ((arg1 as u64) << 32);
+    crate::timer::set_timer(time_value as u64);
+
+    let mtip = mip::read().mtimer();
+    if mtip {
+        unsafe {
+            mie::clear_mtimer();
+            mip::set_stimer();
+        }
+    } else {
+        unsafe {
+            mie::set_mtimer();
+            mip::clear_stimer();
+        }
+    }
+    SbiRet::ok(0)
+}

+ 45 - 0
src/ecall/timer.rs

@@ -0,0 +1,45 @@
+use super::SbiRet;
+
+const FUNCTION_TIMER_SET_TIMER: usize = 0x0;
+
+#[inline]
+#[cfg(target_pointer_width = "64")]
+pub fn handle_ecall_timer_64(function: usize, param0: usize) -> SbiRet {
+    match function {
+        FUNCTION_TIMER_SET_TIMER => set_timer(param0),
+        _ => SbiRet::not_supported(),
+    }
+}
+
+#[inline]
+#[cfg(target_pointer_width = "32")]
+pub fn handle_ecall_timer_32(function: usize, param0: usize, param1: usize) -> SbiRet {
+    match function {
+        FUNCTION_TIMER_SET_TIMER => set_timer(param0, param1),
+        _ => SbiRet::not_supported(),
+    }
+}
+
+#[cfg(target_pointer_width = "32")]
+#[inline]
+fn set_timer(arg0: usize, arg1: usize) -> SbiRet {
+    let time_value = (arg0 as u64) + ((arg1 as u64) << 32);
+    if crate::timer::set_timer(time_value) {
+        SbiRet::ok(0)
+    } else {
+        // should be probed with probe_extension
+        SbiRet::not_supported()
+    }
+}
+
+#[cfg(target_pointer_width = "64")]
+#[inline]
+fn set_timer(arg0: usize) -> SbiRet {
+    let time_value = arg0 as u64;
+    if crate::timer::set_timer(time_value) {
+        SbiRet::ok(0)
+    } else {
+        // should be probed with probe_extension
+        SbiRet::not_supported()
+    }
+}

+ 12 - 0
src/extension.rs

@@ -0,0 +1,12 @@
+use crate::ecall::*;
+
+#[inline]
+pub fn probe_extension(extension: usize) -> bool {
+    match extension {
+        EXTENSION_BASE => true,
+        EXTENSION_TIMER => crate::timer::probe_timer(),
+        EXTENSION_IPI => crate::ipi::probe_ipi(),
+        // new extensions should be added here to be probed
+        _ => false,
+    }
+}

+ 59 - 0
src/hart_mask.rs

@@ -0,0 +1,59 @@
+use core::mem::size_of;
+
+/// Hart mask structure reference
+#[derive(Debug, Clone)]
+pub struct HartMask {
+    bit_vector: *const usize,
+    max_hart_id: usize,
+}
+
+impl HartMask {
+    /// Construct a reference to a hart mask structure.
+    ///
+    /// Caller should provide from its address from supervisor level,
+    /// and a maximum hart number for maximum hart limit.
+    ///
+    /// # Unsafety
+    ///
+    /// Caller must ensure all usize values in the bit vector is accessible.
+    pub unsafe fn from_addr(vaddr: usize, max_hart_id: usize) -> HartMask {
+        HartMask {
+            bit_vector: vaddr as *const usize,
+            max_hart_id,
+        }
+    }
+
+    /// Check if the `hart_id` is included in this hart mask structure.
+    pub fn has_bit(&self, hart_id: usize) -> bool {
+        assert!(hart_id <= self.max_hart_id);
+        let (i, j) = split_index_usize(hart_id);
+        let cur_vector = unsafe { get_vaddr_usize(self.bit_vector.add(i)) };
+        cur_vector & (1 << j) != 0
+    }
+}
+
+#[inline]
+fn split_index_usize(index: usize) -> (usize, usize) {
+    let bits_in_usize = size_of::<usize>() * 8;
+    (index / bits_in_usize, index % bits_in_usize)
+}
+
+#[inline]
+unsafe fn get_vaddr_usize(vaddr_ptr: *const usize) -> usize {
+    let mut ans: usize;
+    #[cfg(target_pointer_width = "64")]
+    asm!("
+        li      {tmp}, (1 << 17)
+        csrrs   {tmp}, mstatus, {tmp}
+        ld      {ans}, 0({vmem})
+        csrw    mstatus, {tmp}
+    ", ans = lateout(reg) ans, vmem = in(reg) vaddr_ptr, tmp = out(reg) _);
+    #[cfg(target_pointer_width = "32")]
+    asm!("
+        li      {tmp}, (1 << 17)
+        csrrs   {tmp}, mstatus, {tmp}
+        lw      {ans}, 0({vmem})
+        csrw    mstatus, {tmp}
+    ", ans = lateout(reg) ans, vmem = in(reg) vaddr_ptr, tmp = out(reg) _);
+    ans
+}

+ 42 - 0
src/ipi.rs

@@ -0,0 +1,42 @@
+use crate::hart_mask::HartMask;
+
+/// Inter-processor interrupt support
+pub trait Ipi: Send {
+    /// Get the maximum hart id available by this IPI support module
+    fn max_hart_id(&self) -> usize;
+    /// Send an inter-processor interrupt to all the harts defined in `hart_mask`.
+    ///
+    /// Interprocessor interrupts manifest at the receiving harts as the supervisor software interrupts.
+    fn send_ipi_many(&mut self, hart_mask: HartMask);
+}
+
+use alloc::boxed::Box;
+use spin::Mutex;
+
+lazy_static::lazy_static! {
+    static ref IPI: Mutex<Option<Box<dyn Ipi>>> = Mutex::new(None);
+}
+
+#[doc(hidden)] // use through a macro
+pub fn init_ipi<T: Ipi + Send + 'static>(ipi: T) {
+    *IPI.lock() = Some(Box::new(ipi));
+}
+
+#[inline]
+pub(crate) fn probe_ipi() -> bool {
+    IPI.lock().as_ref().is_none()
+}
+
+pub(crate) fn send_ipi_many(hart_mask: HartMask) {
+    if let Some(ipi) = IPI.lock().as_mut() {
+        ipi.send_ipi_many(hart_mask)
+    }
+}
+
+pub(crate) fn max_hart_id() -> usize {
+    loop {
+        if let Some(ipi) = IPI.lock().as_ref() {
+            return ipi.max_hart_id();
+        }
+    }
+}

+ 97 - 9
src/legacy_stdio.rs

@@ -4,7 +4,7 @@ use embedded_hal::serial::{Read, Write};
 use nb::block;
 
 /// Legacy standard input/output
-pub trait LegacyStdio {
+pub trait LegacyStdio: Send {
     /// Get a character from legacy stdin
     fn getchar(&mut self) -> u8;
     /// Put a character into legacy stdout
@@ -12,23 +12,18 @@ pub trait LegacyStdio {
 }
 
 /// Use serial in `embedded-hal` as legacy standard input/output
-pub struct EmbeddedHalSerial<T> {
+struct EmbeddedHalSerial<T> {
     inner: T,
 }
 
 impl<T> EmbeddedHalSerial<T> {
     /// Create a wrapper with a value
-    pub fn new(inner: T) -> Self {
+    fn new(inner: T) -> Self {
         Self { inner }
     }
-
-    /// Unwrap the struct to get underlying value
-    pub fn into_inner(self) -> T {
-        self.inner
-    }
 }
 
-impl<T> LegacyStdio for EmbeddedHalSerial<T>
+impl<T: Send> LegacyStdio for EmbeddedHalSerial<T>
 where
     T: Read<u8> + Write<u8>,
 {
@@ -45,3 +40,96 @@ where
         block!(self.inner.try_flush()).ok();
     }
 }
+
+struct Fused<T, R>(T, R);
+
+// 和上面的原理差不多,就是分开了
+impl<T, R> LegacyStdio for Fused<T, R>
+where
+    T: Write<u8> + Send + 'static,
+    R: Read<u8> + Send + 'static,
+{
+    fn getchar(&mut self) -> u8 {
+        block!(self.1.try_read()).ok().unwrap()
+    }
+
+    fn putchar(&mut self, ch: u8) {
+        block!(self.0.try_write(ch)).ok();
+        block!(self.0.try_flush()).ok();
+    }
+}
+
+use alloc::boxed::Box;
+use spin::Mutex;
+
+lazy_static::lazy_static! {
+    static ref LEGACY_STDIO: Mutex<Option<Box<dyn LegacyStdio>>> =
+        Mutex::new(None);
+}
+
+#[doc(hidden)] // use through a macro
+pub fn init_legacy_stdio_embedded_hal<T: Read<u8> + Write<u8> + Send + 'static>(serial: T) {
+    let serial = EmbeddedHalSerial::new(serial);
+    *LEGACY_STDIO.lock() = Some(Box::new(serial));
+}
+
+#[doc(hidden)] // use through a macro
+pub fn init_legacy_stdio_embedded_hal_fuse<T, R>(tx: T, rx: R)
+where
+    T: Write<u8> + Send + 'static,
+    R: Read<u8> + Send + 'static,
+{
+    let serial = Fused(tx, rx);
+    *LEGACY_STDIO.lock() = Some(Box::new(serial));
+}
+
+pub(crate) fn legacy_stdio_putchar(ch: u8) {
+    if let Some(stdio) = LEGACY_STDIO.lock().as_mut() {
+        stdio.putchar(ch)
+    }
+}
+
+pub(crate) fn legacy_stdio_getchar() -> u8 {
+    if let Some(stdio) = LEGACY_STDIO.lock().as_mut() {
+        stdio.getchar()
+    } else {
+        0 // default: always return 0
+    }
+}
+
+use core::fmt;
+
+struct Stdout;
+
+impl fmt::Write for Stdout {
+    fn write_str(&mut self, s: &str) -> fmt::Result {
+        if let Some(stdio) = LEGACY_STDIO.lock().as_mut() {
+            for byte in s.as_bytes() {
+                stdio.putchar(*byte)
+            }
+        }
+        Ok(())
+    }
+}
+
+#[doc(hidden)]
+pub fn _print(args: fmt::Arguments) {
+    use fmt::Write;
+    Stdout.write_fmt(args).unwrap();
+}
+
+/// Prints to the debug console.
+#[macro_export(local_inner_macros)]
+macro_rules! print {
+    ($($arg:tt)*) => ({
+        $crate::legacy_stdio::_print(core::format_args!($($arg)*));
+    });
+}
+
+/// Prints to the debug console, with a newline.
+#[macro_export(local_inner_macros)]
+macro_rules! println {
+    ($fmt: literal $(, $($arg: tt)+)?) => {
+        $crate::legacy_stdio::_print(core::format_args!(core::concat!($fmt, "\n") $(, $($arg)+)?));
+    }
+}

+ 20 - 54
src/lib.rs

@@ -1,62 +1,28 @@
-/*
-
-这个库的功能:
-1. 在M特权运行,帮助用户搭建运行时,暴露为SBI接口使用的接口
-2. 提供简单的pmp配置
-3. 帮助用户搭建设备树
-
-设计应该像积木一样,允许用户自己选择模块,而不是提供一个运行时
-建议用户配合riscv-sbi-rt使用
-
-todo:考虑这个库是不是单例的。
-
-*/
-
 #![no_std]
-#![feature(naked_functions)] // 未来稳定后去掉
+#![feature(asm)]
+
+extern crate alloc;
 
-pub mod ecall;
+#[doc(hidden)]
+#[macro_use]
 pub mod legacy_stdio;
+mod ecall;
+mod extension;
+mod hart_mask;
+mod ipi;
+mod logo;
+mod privileged;
+mod timer;
 
 const SBI_SPEC_MAJOR: usize = 0;
 const SBI_SPEC_MINOR: usize = 2;
 
-use legacy_stdio::LegacyStdio;
-
-/// RustSBI instance builder; only one hart should build the instance
-pub struct Builder<'b> {
-    legacy_stdio: Option<&'b dyn LegacyStdio>,
-}
-
-impl<'b> Builder<'b> {
-    /// Create a new instance builder
-    pub fn new() -> Self {
-        Builder { legacy_stdio: None }
-    }
-
-    /// Wrap a stdio handler for legacy `getchar` and `putchar` functions
-    pub fn legacy_stdio(mut self, stdio: &'b dyn LegacyStdio) -> Self {
-        self.legacy_stdio = Some(stdio);
-        self
-    }
-
-    /// Build the RustSBI instance
-    pub fn build(self) -> Instance<'b> {
-        todo!()
-    }
-}
-
-// todo: 修改API
-/// RustSBI instance
-pub struct Instance<'a> {
-    legacy_stdio: Option<&'a dyn LegacyStdio>,
-}
+const IMPL_ID_RUSTSBI: usize = 4;
+const RUSTSBI_VERSION: usize = 1; // todo: read from env!("CARGO_PKG_VERSION")
 
-impl<'a> Instance<'a> {
-    /// Start the instance; call and start the the supervisor
-    pub fn start(&mut self) {
-        // 这里如果设定了stdout,可以往里面打印一些字
-        // 可以用库的crate feature把这个功能关掉
-        todo!()
-    }
-}
+pub use ecall::handle_ecall as ecall;
+pub use hart_mask::HartMask;
+pub use ipi::{init_ipi, Ipi};
+pub use logo::LOGO;
+pub use privileged::enter_privileged;
+pub use timer::{init_timer, Timer};

+ 11 - 0
src/logo.rs

@@ -0,0 +1,11 @@
+/// The rustsbi logo.
+///
+/// ```text
+/// .______       __    __      _______.___________.  _______..______   __
+/// |   _  \     |  |  |  |    /       |           | /       ||   _  \ |  |
+/// |  |_)  |    |  |  |  |   |   (----`---|  |----`|   (----`|  |_)  ||  |
+/// |      /     |  |  |  |    \   \       |  |      \   \    |   _  < |  |
+/// |  |\  \----.|  `--'  |.----)   |      |  |  .----)   |   |  |_)  ||  |
+/// | _| `._____| \______/ |_______/       |__|  |_______/    |______/ |__|
+/// ```
+pub const LOGO: &'static str = include_str!("logo.txt");

+ 6 - 0
src/logo.txt

@@ -0,0 +1,6 @@
+.______       __    __      _______.___________.  _______..______   __
+|   _  \     |  |  |  |    /       |           | /       ||   _  \ |  |
+|  |_)  |    |  |  |  |   |   (----`---|  |----`|   (----`|  |_)  ||  |
+|      /     |  |  |  |    \   \       |  |      \   \    |   _  < |  |
+|  |\  \----.|  `--'  |.----)   |      |  |  .----)   |   |  |_)  ||  |
+| _| `._____| \______/ |_______/       |__|  |_______/    |______/ |__|

+ 31 - 0
src/privileged.rs

@@ -0,0 +1,31 @@
+/// Enter lower privilege from M code with given SBI parameters.
+///
+/// Before calling this function, you must write target start address into `mepc` register,
+/// and write target privilege into `mstatus` register.
+/// Call on all harts after the initialization process is finished.
+///
+/// After this function is called, the stack pointer register `sp` is swapped with `mscratch`,
+/// and a `mret` is called to return to `mepc` address with target privilege.
+///
+/// # Unsafety
+///
+/// This function implictly returns to the program address with the address from `mepc` register.
+/// Caller must ensure that the value in `mepc` is a valid program address.
+/// Caller should also ensure that `mstatus.MPP` register bits contain valid target privilege level.
+///
+/// # Example
+///
+/// ```rust
+/// unsafe {
+///     mepc::write(_s_mode_start as usize);
+///     mstatus::set_mpp(MPP::Supervisor);
+///     enter_privileged(mhartid::read(), dtb_pa);
+/// }
+/// ```
+#[inline]
+pub unsafe fn enter_privileged(mhartid: usize, dtb_pa: usize) -> ! {
+    asm!("
+        csrrw   sp, mscratch, sp
+        mret
+    ", in("a0") mhartid, in("a1") dtb_pa, options(nomem, noreturn))
+}

+ 38 - 0
src/timer.rs

@@ -0,0 +1,38 @@
+/// Timer programmer support
+pub trait Timer: Send {
+    /// Programs the clock for next event after `stime_value` time.
+    ///
+    /// `stime_value` is in absolute time. This function must clear the pending timer interrupt bit as well.
+    ///
+    /// If the supervisor wishes to clear the timer interrupt without scheduling the next timer event,
+    /// it can either request a timer interrupt infinitely far into the future (i.e., (uint64_t)-1),
+    /// or it can instead mask the timer interrupt by clearing sie.STIE.
+    fn set_timer(&mut self, stime_value: u64);
+}
+
+use alloc::boxed::Box;
+use spin::Mutex;
+
+lazy_static::lazy_static! {
+    static ref TIMER: Mutex<Option<Box<dyn Timer>>> = Mutex::new(None);
+}
+
+#[doc(hidden)] // use through a macro
+pub fn init_timer<T: Timer + Send + 'static>(ipi: T) {
+    *TIMER.lock() = Some(Box::new(ipi));
+}
+
+#[inline]
+pub(crate) fn probe_timer() -> bool {
+    TIMER.lock().as_ref().is_none()
+}
+
+#[inline]
+pub(crate) fn set_timer(time_value: u64) -> bool {
+    if let Some(timer) = TIMER.lock().as_mut() {
+        timer.set_timer(time_value);
+        true
+    } else {
+        false
+    }
+}