Browse Source

Update SBI documents, add hart suspend function

luojia65 3 years ago
parent
commit
fc926410e1
9 changed files with 152 additions and 27 deletions
  1. 4 0
      README.md
  2. 7 1
      src/ecall.rs
  3. 12 2
      src/ecall/hsm.rs
  4. 3 4
      src/hart_mask.rs
  5. 109 15
      src/hsm.rs
  6. 4 0
      src/ipi.rs
  7. 7 1
      src/reset.rs
  8. 5 3
      src/rfence.rs
  9. 1 1
      src/timer.rs

+ 4 - 0
README.md

@@ -59,3 +59,7 @@ This project is licensed under either of
 
 - MIT license ([LICENSE-MIT](LICENSE-MIT) or [http://opensource.org/licenses/MIT](http://opensource.org/licenses/MIT))
 - Mulan PSL v2 ([LICENSE-MULAN](LICENSE-MULAN) or [https://opensource.org/licenses/MulanPSL-2.0](https://opensource.org/licenses/MulanPSL-2.0))
+
+Documents from RISC-V SBI Specification are used in this project. These documents are (C) RISC-V Founcation 
+under Creative Commons Attribution 4.0 International License (CC-BY 4.0).
+The full license text is available at https://creativecommons.org/licenses/by/4.0/.

+ 7 - 1
src/ecall.rs

@@ -103,7 +103,7 @@ pub struct SbiRet {
 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_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));
@@ -122,6 +122,12 @@ impl SbiRet {
             value: 0,
         }
     }
+    pub(crate) fn invalid_param() -> SbiRet {
+        SbiRet {
+            error: SBI_ERR_INVALID_PARAM,
+            value: 0,
+        }
+    }
     pub(crate) fn legacy_ok(legacy_value: usize) -> SbiRet {
         SbiRet {
             error: legacy_value,

+ 12 - 2
src/ecall/hsm.rs

@@ -4,6 +4,7 @@ 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;
+const FUNCTION_HSM_HART_SUSPEND: usize = 0x3;
 
 #[inline]
 pub fn handle_ecall_hsm(function: usize, param0: usize, param1: usize, param2: usize) -> SbiRet {
@@ -11,13 +12,14 @@ pub fn handle_ecall_hsm(function: usize, param0: usize, param1: usize, param2: u
         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),
+        FUNCTION_HSM_HART_SUSPEND => hart_suspend(param0, param1, param2),
         _ => SbiRet::not_supported(),
     }
 }
 
 #[inline]
-fn hart_start(hartid: usize, start_addr: usize, private_value: usize) -> SbiRet {
-    crate::hsm::hart_start(hartid, start_addr, private_value)
+fn hart_start(hartid: usize, start_addr: usize, opaque: usize) -> SbiRet {
+    crate::hsm::hart_start(hartid, start_addr, opaque)
 }
 
 #[inline]
@@ -29,3 +31,11 @@ fn hart_stop(hartid: usize) -> SbiRet {
 fn hart_get_status(hartid: usize) -> SbiRet {
     crate::hsm::hart_get_status(hartid)
 }
+
+#[inline]
+fn hart_suspend(suspend_type: usize, resume_addr: usize, opaque: usize) -> SbiRet {
+    if suspend_type > u32::MAX as usize { // valid suspend type should be a `u32` typed value
+        return SbiRet::invalid_param()
+    }
+    crate::hsm::hart_suspend(suspend_type as u32, resume_addr, opaque)
+}

+ 3 - 4
src/hart_mask.rs

@@ -16,7 +16,7 @@ impl HartMask {
     /// - The `vaddr` is a scalar bit-vector containing hartids. 
     ///   Should return address from supervisor level.
     /// - The `base` is the starting hartid from which bit-vector must be computed.
-    ///   If `base` equals `usize::max_value()`, that means `vaddr` is ignored and all available harts must be considered.
+    ///   If `base` equals `usize::MAX`, that means `vaddr` is ignored and all available harts must be considered.
     /// - The `max_hart_id` should be returned by SBI implementation for maximum hart id this hart mask supports.
     ///
     /// # Unsafety
@@ -33,9 +33,8 @@ impl HartMask {
     /// 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);
-        if self.base == usize::max_value() {
-            // If `base` equals `usize::max_value()`, 
-            // that means `vaddr` is ignored and all available harts must be considered.
+        if self.base == usize::MAX {
+            // If `base` equals `usize::MAX`, that means `vaddr` is ignored and all available harts must be considered.
             return true;
         }
         if hart_id < self.base {

+ 109 - 15
src/hsm.rs

@@ -5,19 +5,38 @@ use crate::ecall::SbiRet;
 /// The Hart State Management (HSM) Extension introduces a set hart states and a set of functions 
 /// which allow the supervisor-mode software to request a hart state change.
 ///
-/// The possible hart states are as follows:
+/// The possible hart states along with a unique State ID are as follows:
+///
+/// | State ID | State Name | Description 
+/// |:---------|:-----------|:------------
+/// | 0 | STARTED | The hart is physically powered-up and executing normally. 
+/// | 1 | STOPPED | The hart is not executing in supervisor-mode or any lower privilege mode. It is probably powered-down by the SBI implementation if the underlying platform has a mechanism to physically power-down harts. 
+/// | 2 | START_PENDING | Some other hart has requested to start (or power-up) the hart from the **STOPPED** state and the SBI implementation is still working to get the hart in the **STARTED** state. 
+/// | 3 | STOP_PENDING | The hart has requested to stop (or power-down) itself from the STARTED state and the SBI implementation is still working to get the hart in the **STOPPED** state. 
+/// | 4 | SUSPENDED | This hart is in a platform specific suspend (or low power) state. 
+/// | 5 | SUSPEND_PENDING | The hart has requestd to put itself in a platform specific low power state from the **STARTED** state and the SBI implementation is still working to get the hart in the platform specific **SUSPENDED** state. 
+/// | 6 | RESUME_PENDING | An interrupt or platform specific hardware event has caused the hart to resume normal execution from the **SUSPENDED** state and the SBI implementation is still working to get the hart in the **STARTED** state. 
 ///
-/// - STOPPED: The hart is not executing in supervisor-mode or any lower privilege mode. 
-///   It is probably powered-down by the SBI implementation if the underlying platform has a mechanism 
-///   to physically power-down harts.
-/// - STARTED: The hart is physically powered-up and executing normally.
-/// - START_PENDING: Some other hart has requested to start (or power-up) the hart from the STOPPED state
-///   and the SBI implementation is still working to get the hart in the STARTED state.
-/// - STOP_PENDING: The hart has requested to stop (or power-down) itself from the STARTED state
-///   and the SBI implementation is still working to get the hart in the STOPPED state.
-/// 
 /// At any point in time, a hart should be in one of the above mentioned hart states.
 ///
+/// A platform can have multiple harts grouped into a hierarchical topology groups (namely cores, clusters, nodes, etc) 
+/// with separate platform specific low-power states for each hierarchical group.
+/// These platform specific low-power states of hierarchial topology groups can be represented as platform specific suspend states of a hart. 
+/// A SBI implementation can utilize the suspend states of higher topology groups using one of the following approaches:
+/// 
+/// . *Platform-coordinated:* In this approach, when a hart becomes idle the supervisor-mode power-managment software
+///   will request deepest suspend state for the hart and higher topology groups. 
+///   A SBI implementation should choose a suspend state at higher topology group which is:
+/// .. Not deeper than the specified suspend state
+/// .. Wake-up latency is not higher than the wake-up latency of the specified suspend state
+/// . *OS-inititated:* In this approach, the supervisor-mode power-managment software will directly request a suspend state
+///   for higher topology group after the last hart in that group becomes idle.
+///   When a hart becomes idle, the supervisor-mode power-managment software will always select suspend state for the hart itself
+///   but it will select a suspend state for a higher topology group only if the hart is the last running hart in the group.
+///   A SBI implementation should:
+/// .. Never choose a suspend state for higher topology group different from the specified suspend state
+/// .. Always prefer most recent suspend state requested for higher topology group
+/// 
 /// Ref: [Section 8, RISC-V Supervisor Binary Interface Specification](https://github.com/riscv/riscv-sbi-doc/blob/master/riscv-sbi.adoc#8-hart-state-management-extension-eid-0x48534d-hsm)
 pub trait Hsm: Send {
     /// Request the SBI implementation to start executing the given hart at specified address in supervisor-mode.
@@ -54,8 +73,8 @@ pub trait Hsm: Send {
     /// |:--------------|:--------------
     /// | `satp`        | 0
     /// | `sstatus.SIE` | 0
-    /// | a0            | `hartid`
-    /// | a1            | `opaque`
+    /// | a0            | hartid
+    /// | a1            | `opaque` parameter
     fn hart_start(&mut self, hartid: usize, start_addr: usize, opaque: usize) -> SbiRet;
     /// Request the SBI implementation to stop executing the calling hart in supervisor-mode 
     /// and return it’s ownership to the SBI implementation. 
@@ -96,8 +115,75 @@ pub trait Hsm: Send {
     ///
     /// | Error code  | Description 
     /// |:------------|:------------
-    /// | SBI_ERR_INVALID_PARAM | The given `hartid` or `start_addr` is not valid
+    /// | SBI_ERR_INVALID_PARAM | The given `hartid` is not valid
     fn hart_get_status(&self, hartid: usize) -> SbiRet;
+    /// Request the SBI implementation to put the calling hart in a platform specfic suspend (or low power) state
+    /// specified by the `suspend_type` parameter. 
+    /// 
+    /// The hart will automatically come out of suspended state and resume normal execution
+    /// when it recieves an interrupt or platform specific hardware event.
+    /// 
+    /// # Suspend behavior
+    /// 
+    /// The platform specific suspend states for a hart can be either retentive or non-rententive in nature.
+    ///
+    /// A retentive suspend state will preserve hart register and CSR values for all privilege modes,
+    /// whereas a non-retentive suspend state will not preserve hart register and CSR values.
+    ///
+    /// # Resuming
+    /// 
+    /// Resuming from a retentive suspend state is straight forward and the supervisor-mode software 
+    /// will see SBI suspend call return without any failures.
+    /// 
+    /// Resuming from a non-retentive suspend state is relatively more involved and requires software 
+    /// to restore various hart registers and CSRs for all privilege modes. 
+    /// Upon resuming from non-retentive suspend state, the hart will jump to supervisor-mode at address 
+    /// specified by `resume_addr` with specific registers values described in the table below:
+    ///
+    /// | Register Name | Register Value
+    /// |:--------------|:--------------
+    /// | `satp`        | 0
+    /// | `sstatus.SIE` | 0
+    /// | a0            | hartid
+    /// | a1            | `opaque` parameter
+    ///
+    /// # Parameters
+    ///
+    /// The `suspend_type` parameter is 32 bits wide and the possible values are shown in the table below:
+    ///
+    /// | Value                   | Description
+    /// |:------------------------|:--------------
+    /// | 0x00000000              | Default retentive suspend
+    /// | 0x00000001 - 0x0FFFFFFF | Reserved for future use
+    /// | 0x10000000 - 0x7FFFFFFF | Platform specific retentive suspend
+    /// | 0x80000000              | Default non-retentive suspend
+    /// | 0x80000001 - 0x8FFFFFFF | Reserved for future use
+    /// | 0x90000000 - 0xFFFFFFFF | Platform specific non-retentive suspend
+    /// | > 0xFFFFFFFF            | Reserved (and non-existent on RV32)
+    ///
+    /// The `resume_addr` parameter points to a runtime-specified physical address,
+    /// where the hart can resume execution in supervisor-mode after a non-retentive
+    /// suspend.
+    /// 
+    /// The `opaque` parameter is a XLEN-bit value which will be set in the `a1`
+    /// register when the hart resumes exectution at `resume_addr` after a
+    /// non-retentive suspend.
+    /// 
+    /// # Return value
+    /// 
+    /// The possible return error codes returned in `SbiRet.error` are shown in the table below:
+    /// 
+    /// | Error code              | Description
+    /// |:------------------------|:------------
+    /// | SBI_SUCCESS             | Hart has suspended and resumed back successfully from a retentive suspend state.
+    /// | SBI_ERR_INVALID_PARAM   | `suspend_type` is not valid.
+    /// | SBI_ERR_NOT_SUPPORTED   | `suspend_type` is valid but not implemented.
+    /// | SBI_ERR_INVALID_ADDRESS | `resume_addr` is not valid possibly due to following reasons: it is not a valid physical address, or the address is prohibited by PMP to run in supervisor mode.
+    /// | SBI_ERR_FAILED          | The suspend request failed for unknown reasons.
+    fn hart_suspend(&self, suspend_type: u32, resume_addr: usize, opaque: usize) -> SbiRet {
+        drop((suspend_type, resume_addr, opaque));
+        SbiRet::not_supported()
+    }
 }
 
 use alloc::boxed::Box;
@@ -118,9 +204,9 @@ pub(crate) fn probe_hsm() -> bool {
     HSM.lock().as_ref().is_some()
 }
 
-pub(crate) fn hart_start(hartid: usize, start_addr: usize, private_value: usize) -> SbiRet {
+pub(crate) fn hart_start(hartid: usize, start_addr: usize, opaque: usize) -> SbiRet {
     if let Some(obj) = &mut *HSM.lock() {
-        return obj.hart_start(hartid, start_addr, private_value);
+        return obj.hart_start(hartid, start_addr, opaque);
     }
     SbiRet::not_supported()
 }
@@ -138,3 +224,11 @@ pub(crate) fn hart_get_status(hartid: usize) -> SbiRet {
     }
     SbiRet::not_supported()
 }
+
+pub(crate) fn hart_suspend(suspend_type: u32, resume_addr: usize, opaque: usize) -> SbiRet {
+    if let Some(obj) = &mut *HSM.lock() {
+        let suspend_type = suspend_type as u32;
+        return obj.hart_suspend(suspend_type, resume_addr, opaque);
+    }
+    SbiRet::not_supported()
+}

+ 4 - 0
src/ipi.rs

@@ -8,6 +8,10 @@ pub trait Ipi: Send {
     /// 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.
+    ///
+    /// # Return value
+    /// 
+    /// Should return error code `SBI_SUCCESS` if IPI was sent to all the targeted harts successfully.
     fn send_ipi_many(&mut self, hart_mask: HartMask) -> SbiRet;
 }
 

+ 7 - 1
src/reset.rs

@@ -27,7 +27,13 @@ pub trait Reset: Send {
     ///
     /// # Return value
     /// 
-    /// Returns SBI_ERR_INVALID_PARAM, SBI_ERR_NOT_SUPPORTED or SBI_ERR_FAILED through `SbiRet.error` upon failure.
+    /// The possible return error codes returned in `SbiRet.error` are shown in the table below:
+    ///
+    /// | Error code            | Description
+    /// |:----------------------|:---------------
+    /// | SBI_ERR_INVALID_PARAM | `reset_type` or `reset_reason` is not valid.
+    /// | SBI_ERR_NOT_SUPPORTED | `reset_type` is valid but not implemented.
+    /// | SBI_ERR_FAILED        | Reset request failed for unknown reasons.
     fn system_reset(&self, reset_type: usize, reset_reason: usize) -> SbiRet;
 
     /// Legacy extension's reset function

+ 5 - 3
src/rfence.rs

@@ -8,11 +8,13 @@ use crate::ecall::SbiRet;
 /// interface access from supervisor level.
 ///
 /// The remote fence function acts as a full TLB flush if
-/// - `start_addr` and `size` are both 0
-/// - `size` is equal to `usize::max_value()`
+/// - `start_addr` and `size` are both 0, and
+/// - `size` is equal to `usize::MAX`.
 pub trait Rfence: Send {
     /// Instructs remote harts to execute `FENCE.I` instruction.
     ///
+    /// # Return value
+    ///
     /// Returns `SBI_SUCCESS` when remote fence was sent to all the targeted harts successfully.
     fn remote_fence_i(&mut self, hart_mask: HartMask) -> SbiRet;
     /// Instructs the remote harts to execute one or more `SFENCE.VMA` instructions, 
@@ -110,7 +112,7 @@ pub trait Rfence: Send {
     /// | SBI_ERR_NOT_SUPPORTED     | This function is not supported as it is not implemented or one of the target hart doesn’t support hypervisor extension.
     /// | SBI_ERR_INVALID_ADDRESS   | `start_addr` or `size` is not valid.
     fn remote_hfence_vvma(&mut self, hart_mask: HartMask, start_addr: usize, size: usize) -> SbiRet {
-        drop((hart_mask, start_addr, size, size));
+        drop((hart_mask, start_addr, size));
         SbiRet::not_supported()
     }
 }

+ 1 - 1
src/timer.rs

@@ -6,7 +6,7 @@ pub trait Timer: Send {
     ///
     /// 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.
+    /// or it can instead mask the timer interrupt by clearing `sie.STIE` CSR bit.
     fn set_timer(&mut self, stime_value: u64);
 }