瀏覽代碼

Merge pull request #48 from jakezhu9/feat/sbi_2

feat: support System Suspend extension and CPPC extension
Luo Jia / Zhouqi Jiang 1 年之前
父節點
當前提交
01fd94d3ab
共有 5 個文件被更改,包括 433 次插入24 次删除
  1. 3 0
      CHANGELOG.md
  2. 131 0
      src/cppc.rs
  3. 204 23
      src/instance.rs
  4. 7 1
      src/lib.rs
  5. 88 0
      src/susp.rs

+ 3 - 0
CHANGELOG.md

@@ -9,6 +9,9 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
 ### Added
 
+- support System Suspend extension
+- support CPPC extension 
+
 ### Modified
 
 - run on provided `MachineInfo` by default; bare metal M-mode environment should gate `machine`

+ 131 - 0
src/cppc.rs

@@ -0,0 +1,131 @@
+use sbi_spec::binary::SbiRet;
+
+/// CPPC Extension
+///
+/// ACPI defines the Collaborative Processor Performance Control (CPPC) mechanism,
+/// which is an abstract and flexible mechanism for the supervisor-mode
+/// power-management software to collaborate with an entity in the platform to
+/// manage the performance of the processors.
+///
+/// The SBI CPPC extension provides an abstraction to access the CPPC registers
+/// through SBI calls. The CPPC registers can be memory locations shared with a
+/// separate platform entity such as a BMC. Even though CPPC is defined in the ACPI
+/// specification, it may be possible to implement a CPPC driver based on
+/// Device Tree.
+///
+/// The table below defines 32-bit identifiers for all CPPC registers
+/// to be used by the SBI CPPC functions. The first half of the 32-bit register
+/// space corresponds to the registers as defined by the ACPI specification.
+/// The second half provides the information not defined in the ACPI specification,
+/// but is additionally required by the supervisor-mode power-management software.
+///
+/// | Register ID             | Register                              | Bit Width | Attribute    | Description               
+/// | ----------------------- | ------------------------------------- | --------- | ------------ | ---------------------------
+/// | 0x00000000              | HighestPerformance                    | 32        | Read-only    | ACPI Spec 6.5: 8.4.6.1.1.1  
+/// | 0x00000001              | NominalPerformance                    | 32        | Read-only    | ACPI Spec 6.5: 8.4.6.1.1.2   
+/// | 0x00000002              | LowestNonlinearPerformance            | 32        | Read-only    | ACPI Spec 6.5: 8.4.6.1.1.4
+/// | 0x00000003              | LowestPerformance                     | 32        | Read-only    | ACPI Spec 6.5: 8.4.6.1.1.5
+/// | 0x00000004              | GuaranteedPerformanceRegister         | 32        | Read-only    | ACPI Spec 6.5: 8.4.6.1.1.6
+/// | 0x00000005              | DesiredPerformanceRegister            | 32        | Read / Write | ACPI Spec 6.5: 8.4.6.1.2.3
+/// | 0x00000006              | MinimumPerformanceRegister            | 32        | Read / Write | ACPI Spec 6.5: 8.4.6.1.2.2
+/// | 0x00000007              | MaximumPerformanceRegister            | 32        | Read / Write | ACPI Spec 6.5: 8.4.6.1.2.1
+/// | 0x00000008              | PerformanceReductionToleranceRegister | 32        | Read / Write | ACPI Spec 6.5: 8.4.6.1.2.4
+/// | 0x00000009              | TimeWindowRegister                    | 32        | Read / Write | ACPI Spec 6.5: 8.4.6.1.2.5
+/// | 0x0000000A              | CounterWraparoundTime                 | 32 / 64   | Read-only    | ACPI Spec 6.5: 8.4.6.1.3.1
+/// | 0x0000000B              | ReferencePerformanceCounterRegister   | 32 / 64   | Read-only    | ACPI Spec 6.5: 8.4.6.1.3.1
+/// | 0x0000000C              | DeliveredPerformanceCounterRegister   | 32 / 64   | Read-only    | ACPI Spec 6.5: 8.4.6.1.3.1
+/// | 0x0000000D              | PerformanceLimitedRegister            | 32        | Read / Write | ACPI Spec 6.5: 8.4.6.1.3.2
+/// | 0x0000000E              | CPPCEnableRegister                    | 32        | Read / Write | ACPI Spec 6.5: 8.4.6.1.4
+/// | 0x0000000F              | AutonomousSelectionEnable             | 32        | Read / Write | ACPI Spec 6.5: 8.4.6.1.5
+/// | 0x00000010              | AutonomousActivityWindowRegister      | 32        | Read / Write | ACPI Spec 6.5: 8.4.6.1.6
+/// | 0x00000011              | EnergyPerformancePreferenceRegister   | 32        | Read / Write | ACPI Spec 6.5: 8.4.6.1.7  
+/// | 0x00000012              | ReferencePerformance                  | 32        | Read-only    | ACPI Spec 6.5: 8.4.6.1.1.3  
+/// | 0x00000013              | LowestFrequency                       | 32        | Read-only    | ACPI Spec 6.5: 8.4.6.1.1.7
+/// | 0x00000014              | NominalFrequency                      | 32        | Read-only    | ACPI Spec 6.5: 8.4.6.1.1.7
+/// | 0x00000015 - 0x7FFFFFFF |                                       |           |              | Reserved for future use.     
+/// | 0x80000000              | TransitionLatency                     | 32        | Read-only    | Provides the maximum (worst-case) performance state transition latency in nanoseconds.
+/// | 0x80000001 - 0xFFFFFFFF |                                       |           |              | Reserved for future use.   
+///
+pub trait Cppc: Send + Sync {
+    /// Probe whether the CPPC register as specified by the `reg_id` parameter
+    /// is implemented or not by the platform.
+    ///
+    /// # Return value
+    ///
+    /// If the register is implemented, `SbiRet.value` will contain the register
+    /// width. If the register is not implemented, `SbiRet.value` will be set to 0.
+    ///
+    /// The possible return error codes returned in `SbiRet.error` are shown in the table below:
+    ///
+    /// | Error code                | Description   
+    /// | ------------------------- | ---------------
+    /// | `SbiRet::success()`       | Probe completed successfully.
+    /// | `SbiRet::invalid_param()` | `reg_id` is reserved.
+    /// | `SbiRet::failed()`        | The probe request failed for unspecified or unknown other reasons.
+    fn probe(&self, reg_id: u32) -> SbiRet;
+    /// Reads the register as specified in the `reg_id` parameter.
+    ///
+    /// # Return value
+    ///
+    /// Returns the value of the register in `SbiRet.value`. When supervisor mode XLEN is 32,
+    /// the `SbiRet.value` will only contain the lower 32 bits of the CPPC register value.
+    ///
+    /// The possible return error codes returned in `SbiRet.error` are shown in the table below:
+    ///
+    /// | Error code                | Description
+    /// | ------------------------- | -------------------
+    /// | `SbiRet::success()`       | Read completed successfully.
+    /// | `SbiRet::invalid_param()` | `reg_id` is reserved.
+    /// | `SbiRet::not_supported()` | `reg_id` is not implemented by the platform.
+    /// | `SbiRet::denied()`        | `reg_id` is a write-only register.  
+    /// | `SbiRet::failed()`        | The read request failed for unspecified or unknown other reasons.
+    fn read(&self, reg_id: u32) -> SbiRet;
+    /// Reads the upper 32-bit value of the register specified in the `reg_id`
+    /// parameter.
+    ///
+    /// # Return value
+    ///
+    /// Returns the value of the register in `SbiRet.value`. This function always
+    /// returns zero in `SbiRet.value` when supervisor mode XLEN is 64 or higher.
+    ///
+    /// The possible return error codes returned in `SbiRet.error` are shown in the table below:
+    ///
+    /// | Error code                | Description
+    /// | ------------------------- | -------------------
+    /// | `SbiRet::success()`       | Read completed successfully.
+    /// | `SbiRet::invalid_param()` | `reg_id` is reserved.
+    /// | `SbiRet::not_supported()` | `reg_id` is not implemented by the platform.
+    /// | `SbiRet::denied()`        | `reg_id` is a write-only register.  
+    /// | `SbiRet::failed()`        | The read request failed for unspecified or unknown other reasons.
+    fn read_hi(&self, reg_id: u32) -> SbiRet;
+    /// Writes the value passed in the `val` parameter to the register as
+    /// specified in the `reg_id` parameter.
+    ///
+    /// # Return value
+    ///
+    /// The possible return error codes returned in `SbiRet.error` are shown in the table below:
+    ///
+    /// | Error code                | Description        
+    /// | ------------------------- | -------------------
+    /// | `SbiRet::success()`       | Write completed successfully.  
+    /// | `SbiRet::invalid_param()` | `reg_id` is reserved.       
+    /// | `SbiRet::not_supported()` | `reg_id` is not implemented by the platform.  
+    /// | `SbiRet::denied()`        | `reg_id` is a read-only register.
+    /// | `SbiRet::failed()`        | The write request failed for unspecified or unknown other reasons.
+    fn write(&self, reg_id: u32, val: u64) -> SbiRet;
+}
+
+impl<T: Cppc> Cppc for &T {
+    fn probe(&self, reg_id: u32) -> SbiRet {
+        T::probe(self, reg_id)
+    }
+    fn read(&self, reg_id: u32) -> SbiRet {
+        T::read(self, reg_id)
+    }
+    fn read_hi(&self, reg_id: u32) -> SbiRet {
+        T::read_hi(self, reg_id)
+    }
+    fn write(&self, reg_id: u32, val: u64) -> SbiRet {
+        T::write(self, reg_id, val)
+    }
+}

+ 204 - 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, Cppc, 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, CP> {
     timer: Option<T>,
     ipi: Option<I>,
     rfnc: Option<R>,
@@ -20,6 +20,8 @@ pub struct RustSBI<T, I, R, H, S, P, C> {
     srst: Option<S>,
     pmu: Option<P>,
     dbcn: Option<C>,
+    susp: Option<SU>,
+    cppc: Option<CP>,
     #[cfg(not(feature = "machine"))]
     info: MachineInfo,
 }
@@ -38,13 +40,23 @@ 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, CP: Cppc>
+    RustSBI<T, I, R, H, S, P, C, SU, CP>
 {
     /// 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,
+        cppc: CP,
+    ) -> Self {
         Self {
             timer: Some(timer),
             ipi: Some(ipi),
@@ -53,6 +65,8 @@ 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),
+            cppc: Some(cppc),
         }
     }
 
@@ -68,6 +82,8 @@ impl<T: Timer, I: Ipi, R: Fence, H: Hsm, S: Reset, P: Pmu, C: Console>
         srst: S,
         pmu: P,
         dbcn: C,
+        susp: SU,
+        cppc: CP,
         info: MachineInfo,
     ) -> Self {
         Self {
@@ -78,6 +94,8 @@ 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),
+            cppc: Some(cppc),
             info,
         }
     }
@@ -292,6 +310,61 @@ 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(),
+                }
+            }
+            spec::cppc::EID_CPPC => match () {
+                #[cfg(target_pointer_width = "64")]
+                () => {
+                    let Some(cppc) = &mut self.cppc else {
+                        return SbiRet::not_supported();
+                    };
+                    let [param0, param1] = [param[0], param[1]];
+                    match function {
+                        spec::cppc::PROBE => match u32::try_from(param0) {
+                            Ok(reg_id) => cppc.probe(reg_id),
+                            _ => SbiRet::invalid_param(),
+                        },
+                        spec::cppc::READ => match u32::try_from(param0) {
+                            Ok(reg_id) => cppc.read(reg_id),
+                            _ => SbiRet::invalid_param(),
+                        },
+                        spec::cppc::READ_HI => match u32::try_from(param0) {
+                            Ok(reg_id) => cppc.read_hi(reg_id),
+                            _ => SbiRet::invalid_param(),
+                        },
+                        spec::cppc::WRITE => match u32::try_from(param0) {
+                            Ok(reg_id) => cppc.write(reg_id, param1 as _),
+                            _ => SbiRet::invalid_param(),
+                        },
+                        _ => SbiRet::not_supported(),
+                    }
+                }
+                #[cfg(target_pointer_width = "32")]
+                () => {
+                    let Some(cppc) = &mut self.cppc else {
+                        return SbiRet::not_supported();
+                    };
+                    let [param0, param1, param2] = [param[0], param[1], param[2]];
+                    match function {
+                        spec::cppc::PROBE => cppc.probe(param0 as _),
+                        spec::cppc::READ => cppc.read(param0 as _),
+                        spec::cppc::READ_HI => cppc.read_hi(param0 as _),
+                        spec::cppc::WRITE => cppc.write(param0 as _, concat_u32(param2, param1)),
+                        _ => SbiRet::not_supported(),
+                    }
+                }
+            },
             _ => SbiRet::not_supported(),
         }
     }
@@ -307,6 +380,8 @@ impl<T: Timer, I: Ipi, R: Fence, H: Hsm, S: Reset, P: Pmu, C: Console>
             spec::hsm::EID_HSM => self.hsm.is_some(),
             spec::pmu::EID_PMU => self.pmu.is_some(),
             spec::dbcn::EID_DBCN => self.dbcn.is_some(),
+            spec::susp::EID_SUSP => self.susp.is_some(),
+            spec::cppc::EID_CPPC => self.cppc.is_some(),
             _ => false,
         };
         if ans {
@@ -324,17 +399,37 @@ 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, CP> {
+    inner: RustSBI<T, I, R, H, S, P, C, SU, CP>,
 }
 
-impl Builder<Infallible, Infallible, Infallible, Infallible, Infallible, Infallible, Infallible> {
+impl
+    Builder<
+        Infallible,
+        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,
+        Infallible,
+    > {
         Builder {
             inner: RustSBI {
                 timer: None,
@@ -344,6 +439,8 @@ impl Builder<Infallible, Infallible, Infallible, Infallible, Infallible, Infalli
                 srst: None,
                 pmu: None,
                 dbcn: None,
+                susp: None,
+                cppc: None,
             },
         }
     }
@@ -353,8 +450,17 @@ 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,
+        Infallible,
+    > {
         Builder {
             inner: RustSBI {
                 timer: None,
@@ -364,6 +470,8 @@ impl Builder<Infallible, Infallible, Infallible, Infallible, Infallible, Infalli
                 srst: None,
                 pmu: None,
                 dbcn: None,
+                susp: None,
+                cppc: None,
                 info,
             },
         }
@@ -376,10 +484,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, CP> Builder<T, I, R, H, S, P, C, SU, CP> {
     /// 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, CP> {
         Builder {
             inner: RustSBI {
                 timer: Some(timer),
@@ -389,6 +497,8 @@ 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,
+                cppc: self.inner.cppc,
                 #[cfg(not(feature = "machine"))]
                 info: self.inner.info,
             },
@@ -397,7 +507,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, CP> {
         Builder {
             inner: RustSBI {
                 timer: self.inner.timer,
@@ -407,6 +517,8 @@ 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,
+                cppc: self.inner.cppc,
                 #[cfg(not(feature = "machine"))]
                 info: self.inner.info,
             },
@@ -415,7 +527,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, CP> {
         Builder {
             inner: RustSBI {
                 timer: self.inner.timer,
@@ -425,6 +537,8 @@ 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,
+                cppc: self.inner.cppc,
                 #[cfg(not(feature = "machine"))]
                 info: self.inner.info,
             },
@@ -433,7 +547,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, CP> {
         Builder {
             inner: RustSBI {
                 timer: self.inner.timer,
@@ -443,6 +557,8 @@ 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,
+                cppc: self.inner.cppc,
                 #[cfg(not(feature = "machine"))]
                 info: self.inner.info,
             },
@@ -451,7 +567,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, CP> {
         Builder {
             inner: RustSBI {
                 timer: self.inner.timer,
@@ -461,6 +577,8 @@ 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,
+                cppc: self.inner.cppc,
                 #[cfg(not(feature = "machine"))]
                 info: self.inner.info,
             },
@@ -469,7 +587,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, CP> {
         Builder {
             inner: RustSBI {
                 timer: self.inner.timer,
@@ -479,6 +597,8 @@ 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,
+                cppc: self.inner.cppc,
                 #[cfg(not(feature = "machine"))]
                 info: self.inner.info,
             },
@@ -486,7 +606,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, CP> {
         Builder {
             inner: RustSBI {
                 timer: self.inner.timer,
@@ -496,6 +616,46 @@ 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,
+                cppc: self.inner.cppc,
+                #[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, CP> {
+        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),
+                cppc: self.inner.cppc,
+                #[cfg(not(feature = "machine"))]
+                info: self.inner.info,
+            },
+        }
+    }
+    /// Add CPPC extension to RustSBI
+    #[inline]
+    pub fn with_cppc<CP2: Cppc>(self, cppc: CP2) -> Builder<T, I, R, H, S, P, C, SU, CP2> {
+        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: self.inner.susp,
+                cppc: Some(cppc),
                 #[cfg(not(feature = "machine"))]
                 info: self.inner.info,
             },
@@ -504,7 +664,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, CP> {
         self.inner
     }
 }
@@ -625,3 +785,24 @@ impl Console for Infallible {
         unreachable!()
     }
 }
+
+impl Susp for Infallible {
+    fn system_suspend(&self, _: u32, _: usize, _: usize) -> SbiRet {
+        unreachable!()
+    }
+}
+
+impl Cppc for Infallible {
+    fn probe(&self, _: u32) -> SbiRet {
+        unreachable!()
+    }
+    fn read(&self, _: u32) -> SbiRet {
+        unreachable!()
+    }
+    fn read_hi(&self, _: u32) -> SbiRet {
+        unreachable!()
+    }
+    fn write(&self, _: u32, _: u64) -> SbiRet {
+        unreachable!()
+    }
+}

+ 7 - 1
src/lib.rs

@@ -167,6 +167,8 @@
 //! # struct MyBoardPower;
 //! # struct MyPlatPmu;
 //! # struct MyPlatDbcn;
+//! # struct MyPlatSusp;
+//! # struct MyPlatCppc;
 //! use rustsbi::RustSBI;
 //!
 //! # struct SupervisorContext;
@@ -174,7 +176,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, MyPlatCppc>,
 //!     /* custom_1: CustomSBI<...> */
 //! }
 //!
@@ -503,6 +505,7 @@
 #![no_std]
 
 mod console;
+mod cppc;
 mod hart_mask;
 mod hsm;
 mod instance;
@@ -510,6 +513,7 @@ mod ipi;
 mod pmu;
 mod reset;
 mod rfence;
+mod susp;
 mod timer;
 
 /// The RustSBI logo without blank lines on the beginning
@@ -540,6 +544,7 @@ pub const VERSION: &str = env!("CARGO_PKG_VERSION");
 
 pub extern crate sbi_spec as spec;
 pub use console::Console;
+pub use cppc::Cppc;
 pub use hart_mask::HartMask;
 pub use hsm::Hsm;
 pub use instance::{Builder, RustSBI};
@@ -547,6 +552,7 @@ pub use ipi::Ipi;
 pub use pmu::Pmu;
 pub use reset::Reset;
 pub use rfence::Rfence as Fence;
+pub use susp::Susp;
 pub use timer::Timer;
 
 #[cfg(not(feature = "machine"))]

+ 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  
+    /// | --------------------------- | -------------------
+    /// | `SbiRet::success()`         | System has suspended and resumed successfully.
+    /// | `SbiRet::invalid_param()`   | `sleep_type` is reserved or is platform-specific and unimplemented.
+    /// | `SbiRet::not_supported()`   | `sleep_type` is not reserved and is implemented, but the platform does not support it due to one or more missing dependencies.
+    /// | `SbiRet::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.
+    /// | `SbiRet::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)
+    }
+}