浏览代码

SRST (reset) & HSM (hart state) extension support

luojia65 4 年之前
父节点
当前提交
4f72e595ed

+ 2 - 2
platform/k210/src/main.rs

@@ -181,8 +181,8 @@ fn main() -> ! {
 
         struct Reset;
         impl rustsbi::Reset for Reset {
-            fn reset(&self) -> ! {
-                println!("[rustsbi] reset triggered! todo: shutdown all harts on k210; program halt");
+            fn system_reset(&self, reset_type: usize, reset_reason: usize) -> rustsbi::SbiRet {
+                println!("[rustsbi] reset triggered! todo: shutdown all harts on k210; program halt. Type: {}, reason: {}", reset_type, reset_reason);
                 loop {}
             }
         }

+ 1 - 1
platform/qemu/Cargo.toml

@@ -12,7 +12,7 @@ rustsbi = { path = "../../rustsbi" }
 
 linked_list_allocator = "0.8"
 lazy_static = { version = "1", features = ["spin_no_std"] }
-spin = "0.6"
+spin = "0.7"
 riscv = { git = "https://github.com/rust-embedded/riscv", features = ["inline-asm"] }
 
 # 这几个其实不用,应该使用对应的hal库实现

+ 15 - 2
platform/qemu/src/hal.rs

@@ -10,16 +10,29 @@ pub use clint::Clint;
 
 pub struct Reset;
 
+const TEST_FAIL: u64 = 0x3333;
+const TEST_PASS: u64 = 0x5555;
+const TEST_RESET: u64 = 0x7777;
+
 impl rustsbi::Reset for Reset {
-    fn reset(&self) -> ! {
+    fn system_reset(&self, reset_type: usize, reset_reason: usize) -> rustsbi::SbiRet {
         // todo: only exit after all harts finished
         // loop {}
         const VIRT_TEST: *mut u64 = 0x10_0000 as *mut u64;
         // Fail = 0x3333,
         // Pass = 0x5555,
         // Reset = 0x7777,
+        let mut value = match reset_type {
+            rustsbi::reset::RESET_TYPE_SHUTDOWN => TEST_PASS,
+            rustsbi::reset::RESET_TYPE_COLD_REBOOT => TEST_RESET,
+            rustsbi::reset::RESET_TYPE_WARM_REBOOT => TEST_RESET,
+            _ => TEST_FAIL,
+        };
+        if reset_reason == rustsbi::reset::RESET_REASON_SYSTEM_FAILURE {
+            value = TEST_FAIL;
+        };
         unsafe {
-            core::ptr::write_volatile(VIRT_TEST, 0x5555);
+            core::ptr::write_volatile(VIRT_TEST, value);
         }
         unreachable!()
     }

+ 1 - 1
rustsbi/Cargo.toml

@@ -17,5 +17,5 @@ edition = "2018"
 embedded-hal = "1.0.0-alpha.1"
 nb = "1.0"
 riscv = "0.6"
-spin = "0.6"
+spin = "0.7"
 lazy_static = { version = "1", features = ["spin_no_std"] }

+ 8 - 2
rustsbi/src/ecall.rs

@@ -2,15 +2,18 @@
 // 你应该在riscv-rt或其它中断处理函数里,调用这个模块的内容
 
 mod base;
+mod hsm;
 mod ipi;
 mod legacy;
+mod srst;
 mod timer;
 
 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;
+pub const EXTENSION_HSM: usize = 0x48534D;
+pub const EXTENSION_SRST: usize = 0x53525354;
 
 const LEGACY_SET_TIMER: usize = 0x0;
 const LEGACY_CONSOLE_PUTCHAR: usize = 0x01;
@@ -65,6 +68,8 @@ pub fn handle_ecall(extension: usize, function: usize, param: [usize; 4]) -> Sbi
             () => timer::handle_ecall_timer_32(function, param[0], param[1]),
         },
         EXTENSION_IPI => ipi::handle_ecall_ipi(function, param[0], param[1]),
+        EXTENSION_HSM => hsm::handle_ecall_hsm(function, param[0], param[1], param[2]),
+        EXTENSION_SRST => srst::handle_ecall_srst(function, param[0], param[1]),
         LEGACY_SET_TIMER => match () {
             #[cfg(target_pointer_width = "64")]
             () => legacy::set_timer_64(param[0]),
@@ -80,7 +85,8 @@ pub fn handle_ecall(extension: usize, function: usize, param: [usize; 4]) -> Sbi
     }
 }
 
-/// Returned by handle_ecall function
+/// Call result returned by SBI
+///
 /// After `handle_ecall` finished, you should save returned `error` in `a0`, and `value` in `a1`.
 #[repr(C)]
 pub struct SbiRet {

+ 31 - 0
rustsbi/src/ecall/hsm.rs

@@ -0,0 +1,31 @@
+//! hsm extension
+use super::SbiRet;
+
+const FUNCTION_HSM_HART_START: usize = 0x0;
+const FUNCTION_HSM_HART_STOP: usize = 0x1;
+const FUNCTION_HSM_HART_GET_STATUS: usize = 0x2;
+
+#[inline]
+pub fn handle_ecall_hsm(function: usize, param0: usize, param1: usize, param2: usize) -> SbiRet {
+    match function {
+        FUNCTION_HSM_HART_START => hart_start(param0, param1, param2),
+        FUNCTION_HSM_HART_STOP => hart_stop(param0),
+        FUNCTION_HSM_HART_GET_STATUS => hart_get_status(param0),
+        _ => SbiRet::not_supported(),
+    }
+}
+
+#[inline]
+fn hart_start(hartid: usize, start_addr: usize, priv_: usize) -> SbiRet {
+    crate::hsm::hart_start(hartid, start_addr, priv_)
+}
+
+#[inline]
+fn hart_stop(hartid: usize) -> SbiRet {
+    crate::hsm::hart_stop(hartid)
+}
+
+#[inline]
+fn hart_get_status(hartid: usize) -> SbiRet {
+    crate::hsm::hart_get_status(hartid)
+}

+ 1 - 2
rustsbi/src/ecall/legacy.rs

@@ -68,6 +68,5 @@ pub fn set_timer_32(arg0: usize, arg1: usize) -> SbiRet {
 
 #[inline]
 pub fn shutdown() -> SbiRet {
-    // todo: ref: opensbi; much more complex process
-    crate::reset::reset()
+    crate::reset::legacy_reset()
 }

+ 16 - 0
rustsbi/src/ecall/srst.rs

@@ -0,0 +1,16 @@
+use super::SbiRet;
+
+const FUNCTION_SYSTEM_RESET: usize = 0x0;
+
+#[inline]
+pub fn handle_ecall_srst(function: usize, param0: usize, param1: usize) -> SbiRet {
+    match function {
+        FUNCTION_SYSTEM_RESET => system_reset(param0, param1),
+        _ => SbiRet::not_supported(),
+    }
+}
+
+#[inline]
+fn system_reset(reset_type: usize, reset_reason: usize) -> SbiRet {
+    crate::reset::system_reset(reset_type, reset_reason)
+}

+ 3 - 0
rustsbi/src/extension.rs

@@ -6,6 +6,9 @@ pub fn probe_extension(extension: usize) -> bool {
         EXTENSION_BASE => true,
         EXTENSION_TIMER => crate::timer::probe_timer(),
         EXTENSION_IPI => crate::ipi::probe_ipi(),
+        // EXTENSION_RFENCE
+        EXTENSION_SRST => crate::reset::probe_reset(),
+        EXTENSION_HSM => crate::hsm::probe_hsm(),
         // new extensions should be added here to be probed
         _ => false,
     }

+ 49 - 0
rustsbi/src/hsm.rs

@@ -0,0 +1,49 @@
+use crate::ecall::SbiRet;
+
+/// Hart State Management Extension
+pub trait Hsm: Send {
+    fn hart_start(&mut self, hartid: usize, start_addr: usize, priv_: usize) -> SbiRet;
+
+    fn hart_stop(&mut self, hartid: usize) -> SbiRet;
+
+    fn hart_get_status(&self, hartid: usize) -> SbiRet;
+}
+
+use alloc::boxed::Box;
+use spin::Mutex;
+
+lazy_static::lazy_static! {
+    static ref HSM: Mutex<Option<Box<dyn Hsm>>> =
+        Mutex::new(None);
+}
+
+#[doc(hidden)] // use through a macro or a call from implementation
+pub fn init_hsm<T: Hsm + Send + 'static>(hsm: T) {
+    *HSM.lock() = Some(Box::new(hsm));
+}
+
+#[inline]
+pub(crate) fn probe_hsm() -> bool {
+    HSM.lock().as_ref().is_some()
+}
+
+pub(crate) fn hart_start(hartid: usize, start_addr: usize, priv_: usize) -> SbiRet {
+    if let Some(obj) = &mut *HSM.lock() {
+        return obj.hart_start(hartid, start_addr, priv_);
+    }
+    SbiRet::not_supported()
+}
+
+pub(crate) fn hart_stop(hartid: usize) -> SbiRet {
+    if let Some(obj) = &mut *HSM.lock() {
+        return obj.hart_stop(hartid);
+    }
+    SbiRet::not_supported()
+}
+
+pub(crate) fn hart_get_status(hartid: usize) -> SbiRet {
+    if let Some(obj) = &mut *HSM.lock() {
+        return obj.hart_get_status(hartid);
+    }
+    SbiRet::not_supported()
+}

+ 5 - 1
rustsbi/src/lib.rs

@@ -24,10 +24,12 @@ pub mod legacy_stdio;
 mod ecall;
 mod extension;
 mod hart_mask;
+mod hsm;
 mod ipi;
 mod logo;
 mod privileged;
-mod reset;
+#[doc(hidden)]
+pub mod reset;
 mod timer;
 
 const SBI_SPEC_MAJOR: usize = 0;
@@ -40,7 +42,9 @@ const IMPL_ID_RUSTSBI: usize = 4;
 const RUSTSBI_VERSION: usize = 1; 
 
 pub use ecall::handle_ecall as ecall;
+pub use ecall::SbiRet;
 pub use hart_mask::HartMask;
+pub use hsm::Hsm;
 pub use ipi::{init_ipi, Ipi};
 pub use logo::LOGO;
 pub use privileged::enter_privileged;

+ 65 - 7
rustsbi/src/reset.rs

@@ -1,27 +1,85 @@
-//! Legacy reset method
-
-/// Legacy reset method
+/// System Reset Extension
+///
+/// Provides a function that allow the supervisor software to request system-level reboot or shutdown.
+/// 
+/// The term "system" refers to the world-view of supervisor software and the underlying SBI implementation 
+/// could be machine mode firmware or hypervisor.
 pub trait Reset: Send {
+    /// Reset the system based on provided `reset_type` and `reset_reason`. 
+    ///
+    /// This is a synchronous call and does not return if it succeeds.
+    ///
+    /// # Warm reboot and cold reboot
+    /// 
+    /// When supervisor software is running natively, the SBI implementation is machine mode firmware. 
+    /// In this case, shutdown is equivalent to physical power down of the entire system and 
+    /// cold reboot is equivalent to physical power cycle of the entire system. Further, warm reboot 
+    /// is equivalent to a power cycle of main processor and parts of the system but not the entire system. 
+    /// 
+    /// For example, on a server class system with a BMC (board management controller), 
+    /// a warm reboot will not power cycle the BMC whereas a cold reboot will definitely power cycle the BMC.
+    ///
+    /// When supervisor software is running inside a virtual machine, the SBI implementation is a hypervisor. 
+    /// The shutdown, cold reboot and warm reboot will behave functionally the same as the native case but might 
+    /// not result in any physical power changes.
+    ///
+    /// # Return value
+    /// 
+    /// Returns SBI_ERR_INVALID_PARAM, SBI_ERR_NOT_SUPPORTED or SBI_ERR_FAILED through `SbiRet.error` upon failure.
+    fn system_reset(&self, reset_type: usize, reset_reason: usize) -> SbiRet;
+
+    /// Legacy extension's reset function
+    ///
     /// Puts all the harts to shut down state from supervisor point of view. This SBI call doesn’t return.
-    fn reset(&self) -> !;
+    #[doc(hidden)]
+    fn legacy_reset(&self) -> ! {
+        self.system_reset(RESET_TYPE_SHUTDOWN, RESET_REASON_NO_REASON);
+        unreachable!()
+    }
 }
 
 use alloc::boxed::Box;
 use spin::Mutex;
 
+use crate::ecall::SbiRet;
+
 lazy_static::lazy_static! {
     static ref RESET: Mutex<Option<Box<dyn Reset>>> =
         Mutex::new(None);
 }
 
+#[doc(hidden)] #[allow(unused)]
+pub const RESET_TYPE_SHUTDOWN: usize = 0x0000_0000;
+#[doc(hidden)] #[allow(unused)]
+pub const RESET_TYPE_COLD_REBOOT: usize = 0x0000_0001;
+#[doc(hidden)] #[allow(unused)]
+pub const RESET_TYPE_WARM_REBOOT: usize = 0x0000_0002;
+
+#[doc(hidden)] #[allow(unused)]
+pub const RESET_REASON_NO_REASON: usize = 0x0000_0000;
+#[doc(hidden)] #[allow(unused)]
+pub const RESET_REASON_SYSTEM_FAILURE: usize = 0x0000_0001;
+
 #[doc(hidden)] // use through a macro
 pub fn init_reset<T: Reset + Send + 'static>(reset: T) {
     *RESET.lock() = Some(Box::new(reset));
 }
 
-pub(crate) fn reset() -> ! {
+#[inline]
+pub(crate) fn probe_reset() -> bool {
+    RESET.lock().as_ref().is_some()
+}
+
+pub(crate) fn system_reset(reset_type: usize, reset_reason: usize) -> SbiRet {
+    if let Some(obj) = &*RESET.lock() {
+        return obj.system_reset(reset_type, reset_reason);
+    }
+    SbiRet::not_supported()
+}
+
+pub(crate) fn legacy_reset() -> ! {
     if let Some(obj) = &*RESET.lock() {
-        obj.reset();
+        obj.legacy_reset()
     }
-    panic!("no reset handler available; this is okay if your platform didn't declare a reset handler")
+    unreachable!("no reset handler available; this is okay if your platform didn't declare a legacy reset handler")
 }