Browse Source

feat: support System Suspend extension

jakezhu9 1 year ago
parent
commit
28f4752e0c
3 changed files with 200 additions and 24 deletions
  1. 108 23
      src/instance.rs
  2. 4 1
      src/lib.rs
  3. 88 0
      src/susp.rs

+ 108 - 23
src/instance.rs

@@ -1,6 +1,6 @@
 use crate::{
-    spec::binary::SbiRet, Console, Fence, HartMask, Hsm, Ipi, Pmu, Reset, Timer, IMPL_ID_RUSTSBI,
-    RUSTSBI_VERSION, SBI_SPEC_MAJOR, SBI_SPEC_MINOR,
+    spec::binary::SbiRet, Console, Fence, HartMask, Hsm, Ipi, Pmu, Reset, Susp, Timer,
+    IMPL_ID_RUSTSBI, RUSTSBI_VERSION, SBI_SPEC_MAJOR, SBI_SPEC_MINOR,
 };
 use core::convert::Infallible;
 #[cfg(feature = "machine")]
@@ -12,7 +12,7 @@ use spec::binary::Physical;
 /// By now RustSBI supports to run instance based interface on systems has environment pointer width
 /// that is the same as supervisor pointer width.
 #[derive(Clone, Debug)]
-pub struct RustSBI<T, I, R, H, S, P, C> {
+pub struct RustSBI<T, I, R, H, S, P, C, SU> {
     timer: Option<T>,
     ipi: Option<I>,
     rfnc: Option<R>,
@@ -20,6 +20,7 @@ pub struct RustSBI<T, I, R, H, S, P, C> {
     srst: Option<S>,
     pmu: Option<P>,
     dbcn: Option<C>,
+    susp: Option<SU>,
     #[cfg(not(feature = "machine"))]
     info: MachineInfo,
 }
@@ -38,13 +39,22 @@ pub struct MachineInfo {
     pub mimpid: usize,
 }
 
-impl<T: Timer, I: Ipi, R: Fence, H: Hsm, S: Reset, P: Pmu, C: Console>
-    RustSBI<T, I, R, H, S, P, C>
+impl<T: Timer, I: Ipi, R: Fence, H: Hsm, S: Reset, P: Pmu, C: Console, SU: Susp>
+    RustSBI<T, I, R, H, S, P, C, SU>
 {
     /// Create RustSBI instance on current machine environment for all the SBI extensions
     #[cfg(feature = "machine")]
     #[inline]
-    pub const fn new_machine(timer: T, ipi: I, rfnc: R, hsm: H, srst: S, pmu: P, dbcn: C) -> Self {
+    pub const fn new_machine(
+        timer: T,
+        ipi: I,
+        rfnc: R,
+        hsm: H,
+        srst: S,
+        pmu: P,
+        dbcn: C,
+        susp: SU,
+    ) -> Self {
         Self {
             timer: Some(timer),
             ipi: Some(ipi),
@@ -53,6 +63,7 @@ impl<T: Timer, I: Ipi, R: Fence, H: Hsm, S: Reset, P: Pmu, C: Console>
             srst: Some(srst),
             pmu: Some(pmu),
             dbcn: Some(dbcn),
+            susp: Some(susp),
         }
     }
 
@@ -68,6 +79,7 @@ impl<T: Timer, I: Ipi, R: Fence, H: Hsm, S: Reset, P: Pmu, C: Console>
         srst: S,
         pmu: P,
         dbcn: C,
+        susp: SU,
         info: MachineInfo,
     ) -> Self {
         Self {
@@ -78,6 +90,7 @@ impl<T: Timer, I: Ipi, R: Fence, H: Hsm, S: Reset, P: Pmu, C: Console>
             srst: Some(srst),
             pmu: Some(pmu),
             dbcn: Some(dbcn),
+            susp: Some(susp),
             info,
         }
     }
@@ -292,6 +305,19 @@ impl<T: Timer, I: Ipi, R: Fence, H: Hsm, S: Reset, P: Pmu, C: Console>
                     _ => SbiRet::not_supported(),
                 }
             }
+            spec::susp::EID_SUSP => {
+                let Some(susp) = &mut self.susp else {
+                    return SbiRet::not_supported();
+                };
+                let [param0, param1, param2] = [param[0], param[1], param[2]];
+                match function {
+                    spec::susp::SUSPEND => match u32::try_from(param0) {
+                        Ok(sleep_type) => susp.system_suspend(sleep_type, param1, param2),
+                        _ => SbiRet::invalid_param(),
+                    },
+                    _ => SbiRet::not_supported(),
+                }
+            }
             _ => SbiRet::not_supported(),
         }
     }
@@ -324,17 +350,35 @@ const fn concat_u32(h: usize, l: usize) -> u64 {
 }
 
 /// Structure to build a RustSBI instance
-pub struct Builder<T, I, R, H, S, P, C> {
-    inner: RustSBI<T, I, R, H, S, P, C>,
+pub struct Builder<T, I, R, H, S, P, C, SU> {
+    inner: RustSBI<T, I, R, H, S, P, C, SU>,
 }
 
-impl Builder<Infallible, Infallible, Infallible, Infallible, Infallible, Infallible, Infallible> {
+impl
+    Builder<
+        Infallible,
+        Infallible,
+        Infallible,
+        Infallible,
+        Infallible,
+        Infallible,
+        Infallible,
+        Infallible,
+    >
+{
     /// Create a new `Builder` from current machine environment
     #[inline]
     #[cfg(feature = "machine")]
-    pub const fn new_machine(
-    ) -> Builder<Infallible, Infallible, Infallible, Infallible, Infallible, Infallible, Infallible>
-    {
+    pub const fn new_machine() -> Builder<
+        Infallible,
+        Infallible,
+        Infallible,
+        Infallible,
+        Infallible,
+        Infallible,
+        Infallible,
+        Infallible,
+    > {
         Builder {
             inner: RustSBI {
                 timer: None,
@@ -344,6 +388,7 @@ impl Builder<Infallible, Infallible, Infallible, Infallible, Infallible, Infalli
                 srst: None,
                 pmu: None,
                 dbcn: None,
+                susp: None,
             },
         }
     }
@@ -353,8 +398,16 @@ impl Builder<Infallible, Infallible, Infallible, Infallible, Infallible, Infalli
     #[cfg(not(feature = "machine"))]
     pub const fn with_machine_info(
         info: MachineInfo,
-    ) -> Builder<Infallible, Infallible, Infallible, Infallible, Infallible, Infallible, Infallible>
-    {
+    ) -> Builder<
+        Infallible,
+        Infallible,
+        Infallible,
+        Infallible,
+        Infallible,
+        Infallible,
+        Infallible,
+        Infallible,
+    > {
         Builder {
             inner: RustSBI {
                 timer: None,
@@ -364,6 +417,7 @@ impl Builder<Infallible, Infallible, Infallible, Infallible, Infallible, Infalli
                 srst: None,
                 pmu: None,
                 dbcn: None,
+                susp: None,
                 info,
             },
         }
@@ -376,10 +430,10 @@ impl Builder<Infallible, Infallible, Infallible, Infallible, Infallible, Infalli
 
 // fixme: struct `Infallible` should be replaced to never type once it's stablized
 
-impl<T, I, R, H, S, P, C> Builder<T, I, R, H, S, P, C> {
+impl<T, I, R, H, S, P, C, SU> Builder<T, I, R, H, S, P, C, SU> {
     /// Add Timer programmer extension to RustSBI
     #[inline]
-    pub fn with_timer<T2: Timer>(self, timer: T2) -> Builder<T2, I, R, H, S, P, C> {
+    pub fn with_timer<T2: Timer>(self, timer: T2) -> Builder<T2, I, R, H, S, P, C, SU> {
         Builder {
             inner: RustSBI {
                 timer: Some(timer),
@@ -389,6 +443,7 @@ impl<T, I, R, H, S, P, C> Builder<T, I, R, H, S, P, C> {
                 srst: self.inner.srst,
                 pmu: self.inner.pmu,
                 dbcn: self.inner.dbcn,
+                susp: self.inner.susp,
                 #[cfg(not(feature = "machine"))]
                 info: self.inner.info,
             },
@@ -397,7 +452,7 @@ impl<T, I, R, H, S, P, C> Builder<T, I, R, H, S, P, C> {
 
     /// Add Inter-processor Interrupt extension to RustSBI
     #[inline]
-    pub fn with_ipi<I2: Ipi>(self, ipi: I2) -> Builder<T, I2, R, H, S, P, C> {
+    pub fn with_ipi<I2: Ipi>(self, ipi: I2) -> Builder<T, I2, R, H, S, P, C, SU> {
         Builder {
             inner: RustSBI {
                 timer: self.inner.timer,
@@ -407,6 +462,7 @@ impl<T, I, R, H, S, P, C> Builder<T, I, R, H, S, P, C> {
                 srst: self.inner.srst,
                 pmu: self.inner.pmu,
                 dbcn: self.inner.dbcn,
+                susp: self.inner.susp,
                 #[cfg(not(feature = "machine"))]
                 info: self.inner.info,
             },
@@ -415,7 +471,7 @@ impl<T, I, R, H, S, P, C> Builder<T, I, R, H, S, P, C> {
 
     /// Add Remote Fence extension to RustSBI
     #[inline]
-    pub fn with_fence<R2: Fence>(self, fence: R2) -> Builder<T, I, R2, H, S, P, C> {
+    pub fn with_fence<R2: Fence>(self, fence: R2) -> Builder<T, I, R2, H, S, P, C, SU> {
         Builder {
             inner: RustSBI {
                 timer: self.inner.timer,
@@ -425,6 +481,7 @@ impl<T, I, R, H, S, P, C> Builder<T, I, R, H, S, P, C> {
                 srst: self.inner.srst,
                 pmu: self.inner.pmu,
                 dbcn: self.inner.dbcn,
+                susp: self.inner.susp,
                 #[cfg(not(feature = "machine"))]
                 info: self.inner.info,
             },
@@ -433,7 +490,7 @@ impl<T, I, R, H, S, P, C> Builder<T, I, R, H, S, P, C> {
 
     /// Add Hart State Monitor extension to RustSBI
     #[inline]
-    pub fn with_hsm<H2: Hsm>(self, hsm: H2) -> Builder<T, I, R, H2, S, P, C> {
+    pub fn with_hsm<H2: Hsm>(self, hsm: H2) -> Builder<T, I, R, H2, S, P, C, SU> {
         Builder {
             inner: RustSBI {
                 timer: self.inner.timer,
@@ -443,6 +500,7 @@ impl<T, I, R, H, S, P, C> Builder<T, I, R, H, S, P, C> {
                 srst: self.inner.srst,
                 pmu: self.inner.pmu,
                 dbcn: self.inner.dbcn,
+                susp: self.inner.susp,
                 #[cfg(not(feature = "machine"))]
                 info: self.inner.info,
             },
@@ -451,7 +509,7 @@ impl<T, I, R, H, S, P, C> Builder<T, I, R, H, S, P, C> {
 
     /// Add System Reset extension to RustSBI
     #[inline]
-    pub fn with_reset<S2: Reset>(self, reset: S2) -> Builder<T, I, R, H, S2, P, C> {
+    pub fn with_reset<S2: Reset>(self, reset: S2) -> Builder<T, I, R, H, S2, P, C, SU> {
         Builder {
             inner: RustSBI {
                 timer: self.inner.timer,
@@ -461,6 +519,7 @@ impl<T, I, R, H, S, P, C> Builder<T, I, R, H, S, P, C> {
                 srst: Some(reset),
                 pmu: self.inner.pmu,
                 dbcn: self.inner.dbcn,
+                susp: self.inner.susp,
                 #[cfg(not(feature = "machine"))]
                 info: self.inner.info,
             },
@@ -469,7 +528,7 @@ impl<T, I, R, H, S, P, C> Builder<T, I, R, H, S, P, C> {
 
     /// Add Performance Monitor Unit extension to RustSBI
     #[inline]
-    pub fn with_pmu<P2: Pmu>(self, pmu: P2) -> Builder<T, I, R, H, S, P2, C> {
+    pub fn with_pmu<P2: Pmu>(self, pmu: P2) -> Builder<T, I, R, H, S, P2, C, SU> {
         Builder {
             inner: RustSBI {
                 timer: self.inner.timer,
@@ -479,6 +538,7 @@ impl<T, I, R, H, S, P, C> Builder<T, I, R, H, S, P, C> {
                 srst: self.inner.srst,
                 pmu: Some(pmu),
                 dbcn: self.inner.dbcn,
+                susp: self.inner.susp,
                 #[cfg(not(feature = "machine"))]
                 info: self.inner.info,
             },
@@ -486,7 +546,7 @@ impl<T, I, R, H, S, P, C> Builder<T, I, R, H, S, P, C> {
     }
     /// Add Debug Console extension to RustSBI
     #[inline]
-    pub fn with_console<C2: Console>(self, console: C2) -> Builder<T, I, R, H, S, P, C2> {
+    pub fn with_console<C2: Console>(self, console: C2) -> Builder<T, I, R, H, S, P, C2, SU> {
         Builder {
             inner: RustSBI {
                 timer: self.inner.timer,
@@ -496,6 +556,25 @@ impl<T, I, R, H, S, P, C> Builder<T, I, R, H, S, P, C> {
                 srst: self.inner.srst,
                 pmu: self.inner.pmu,
                 dbcn: Some(console),
+                susp: self.inner.susp,
+                #[cfg(not(feature = "machine"))]
+                info: self.inner.info,
+            },
+        }
+    }
+    /// Add System Suspend extension to RustSBI
+    #[inline]
+    pub fn with_susp<SU2: Susp>(self, susp: SU2) -> Builder<T, I, R, H, S, P, C, SU2> {
+        Builder {
+            inner: RustSBI {
+                timer: self.inner.timer,
+                ipi: self.inner.ipi,
+                rfnc: self.inner.rfnc,
+                hsm: self.inner.hsm,
+                srst: self.inner.srst,
+                pmu: self.inner.pmu,
+                dbcn: self.inner.dbcn,
+                susp: Some(susp),
                 #[cfg(not(feature = "machine"))]
                 info: self.inner.info,
             },
@@ -504,7 +583,7 @@ impl<T, I, R, H, S, P, C> Builder<T, I, R, H, S, P, C> {
 
     /// Build the target RustSBI instance
     #[inline]
-    pub fn build(self) -> RustSBI<T, I, R, H, S, P, C> {
+    pub fn build(self) -> RustSBI<T, I, R, H, S, P, C, SU> {
         self.inner
     }
 }
@@ -625,3 +704,9 @@ impl Console for Infallible {
         unreachable!()
     }
 }
+
+impl Susp for Infallible {
+    fn system_suspend(&self, _: u32, _: usize, _: usize) -> SbiRet {
+        unreachable!()
+    }
+}

+ 4 - 1
src/lib.rs

@@ -167,6 +167,7 @@
 //! # struct MyBoardPower;
 //! # struct MyPlatPmu;
 //! # struct MyPlatDbcn;
+//! # struct MyPlatSusp;
 //! use rustsbi::RustSBI;
 //!
 //! # struct SupervisorContext;
@@ -174,7 +175,7 @@
 //! struct Executor {
 //!     ctx: SupervisorContext,
 //!     /* other environment variables ... */
-//!     sbi: RustSBI<Clint, Clint, MyPlatRfnc, MyPlatHsm, MyBoardPower, MyPlatPmu, MyPlatDbcn>,
+//!     sbi: RustSBI<Clint, Clint, MyPlatRfnc, MyPlatHsm, MyBoardPower, MyPlatPmu, MyPlatDbcn, MyPlatSusp>,
 //!     /* custom_1: CustomSBI<...> */
 //! }
 //!
@@ -511,6 +512,7 @@ mod pmu;
 mod reset;
 mod rfence;
 mod timer;
+mod susp;
 
 /// The RustSBI logo without blank lines on the beginning
 pub const LOGO: &str = r".______       __    __      _______.___________.  _______..______   __
@@ -548,6 +550,7 @@ pub use pmu::Pmu;
 pub use reset::Reset;
 pub use rfence::Rfence as Fence;
 pub use timer::Timer;
+pub use susp::Susp;
 
 #[cfg(not(feature = "machine"))]
 pub use instance::MachineInfo;

+ 88 - 0
src/susp.rs

@@ -0,0 +1,88 @@
+use sbi_spec::binary::SbiRet;
+
+/// System Suspend Extension
+///
+/// The system suspend extension defines a set of system-level sleep states and a
+/// function which allows the supervisor-mode software to request that the system
+/// transitions to a sleep state. Sleep states are identified with 32-bit wide
+/// identifiers (`sleep_type`). The possible values for the identifiers are shown
+/// in the table below:
+///
+/// | Type                    | Name           | Description    
+/// |-------------------------|----------------|-------------------------------
+/// | 0                       | SUSPEND_TO_RAM | This is a "suspend to RAM" sleep type, similar to ACPI’s S2 or S3. Entry requires all but the calling hart be in the HSM `STOPPED` state and all hart registers and CSRs saved to RAM.
+/// | 0x00000001 - 0x7fffffff |                | Reserved for future use
+/// | 0x80000000 - 0xffffffff |                | Platform-specific system sleep types
+/// | > 0xffffffff            |                | Reserved                
+///
+/// The term "system" refers to the world-view of supervisor software. The
+/// underlying SBI implementation may be provided by machine mode firmware or a
+/// hypervisor.
+///
+/// The system suspend extension does not provide any way for supported sleep types
+/// to be probed. Platforms are expected to specify their supported system sleep
+/// types and per-type wake up devices in their hardware descriptions. The
+/// `SUSPEND_TO_RAM` sleep type is the one exception, and its presence is implied
+/// by that of the extension.
+pub trait Susp: Send + Sync {
+    /// Request the SBI implementation to put the system transitions to a sleep state.
+    ///
+    /// A return from a `system_suspend()` call implies an error and an error code
+    /// will be in `sbiret.error`. A successful suspend and wake up, results in the
+    /// hart which initiated the suspend, resuming from the `STOPPED` state. To resume,
+    /// the hart will jump to supervisor-mode, at the address specified by `resume_addr`,
+    /// with the specific register values described in the table below.
+    ///
+    /// | Register Name                                     | Register Value     
+    /// | ------------------------------------------------- | ------------------
+    /// | satp                                              | 0                  
+    /// | sstatus.SIE                                       | 0                  
+    /// | a0                                                | hartid             
+    /// | a1                                                | `opaque` parameter
+    /// All other registers remain in an undefined state.
+    ///
+    /// # Parameters
+    ///
+    /// The `resume_addr` parameter points to a runtime-specified physical address,
+    /// where the hart can resume execution in supervisor-mode after a system suspend.
+    ///
+    /// *NOTE:* A single `usize` parameter is sufficient as `resume_addr`,
+    /// because the hart will resume execution in supervisor-mode with the MMU off,
+    /// hence `resume_addr` must be less than XLEN bits wide.
+    ///
+    /// The `opaque` parameter is an XLEN-bit value which will be set in the `a1`
+    /// register when the hart resumes exectution at `resume_addr` after a
+    /// system suspend.
+    ///
+    /// Besides ensuring all entry criteria for the selected sleep type are met, such
+    /// as ensuring other harts are in the `STOPPED` state, the caller must ensure all
+    /// power units and domains are in a state compatible with the selected sleep type.
+    /// The preparation of the power units, power domains, and wake-up devices used for
+    /// resumption from the system sleep state is platform specific and beyond the
+    /// scope of this specification.
+    ///
+    /// When supervisor software is running inside a virtual machine, the SBI
+    /// implementation is provided by a hypervisor. The system suspend will behave
+    /// functionally the same as the native case, but might not result in any physical
+    /// power changes.
+    ///
+    /// # Return value
+    ///
+    /// The possible return error codes returned in `SbiRet.error` are shown in the table below:
+    ///
+    /// | Error code              | Description
+    /// | ----------------------- | -----------------------
+    /// | SBI_SUCCESS             | System has suspended and resumed successfully.        
+    /// | SBI_ERR_INVALID_PARAM   | `sleep_type` is reserved or is platform-specific and unimplemented.
+    /// | SBI_ERR_NOT_SUPPORTED   | `sleep_type` is not reserved and is implemented, but the platform does not support it due to one or more missing dependencies.
+    /// | SBI_ERR_INVALID_ADDRESS | `resume_addr` is not valid, possibly due to the following reasons: * It is not a valid physical address. * Executable access to the address is prohibited by a physical memory protection mechanism or H-extension G-stage for supervisor mode.
+    /// | SBI_ERR_FAILED          | The suspend request failed for unspecified or unknown other reasons.
+    fn system_suspend(&self, sleep_type: u32, resume_addr: usize, opaque: usize) -> SbiRet;
+}
+
+impl<T: Susp> Susp for &T {
+    #[inline]
+    fn system_suspend(&self, sleep_type: u32, resume_addr: usize, opaque: usize) -> SbiRet {
+        T::system_suspend(self, sleep_type, resume_addr, opaque)
+    }
+}