hsm.rs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. use sbi_spec::binary::SbiRet;
  2. /// Hart State Management Extension
  3. ///
  4. /// The Hart State Management (HSM) Extension introduces a set hart states and a set of functions
  5. /// which allow the supervisor-mode software to request a hart state change.
  6. ///
  7. /// # Hart states
  8. ///
  9. /// The possible hart states along with a unique State ID are as follows:
  10. ///
  11. /// | State ID | State Name | Description
  12. /// |:---------|:-----------|:------------
  13. /// | 0 | STARTED | The hart is physically powered-up and executing normally.
  14. /// | 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.
  15. /// | 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.
  16. /// | 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.
  17. /// | 4 | SUSPENDED | This hart is in a platform specific suspend (or low power) state.
  18. /// | 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.
  19. /// | 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.
  20. ///
  21. /// At any point in time, a hart should be in one of the above mentioned hart states.
  22. ///
  23. /// # Topology hart groups
  24. ///
  25. /// A platform can have multiple harts grouped into a hierarchical topology groups (namely cores, clusters, nodes, etc)
  26. /// with separate platform specific low-power states for each hierarchical group.
  27. /// These platform specific low-power states of hierarchial topology groups can be represented as platform specific suspend states of a hart.
  28. /// A SBI implementation can utilize the suspend states of higher topology groups using one of the following approaches:
  29. ///
  30. /// 1. *Platform-coordinated:* In this approach, when a hart becomes idle the supervisor-mode power-managment software
  31. /// will request deepest suspend state for the hart and higher topology groups.
  32. /// A SBI implementation should choose a suspend state at higher topology group which is:
  33. /// - Not deeper than the specified suspend state
  34. /// - Wake-up latency is not higher than the wake-up latency of the specified suspend state
  35. /// 2. *OS-inititated:* In this approach, the supervisor-mode power-managment software will directly request a suspend state
  36. /// for higher topology group after the last hart in that group becomes idle.
  37. /// When a hart becomes idle, the supervisor-mode power-managment software will always select suspend state for the hart itself
  38. /// but it will select a suspend state for a higher topology group only if the hart is the last running hart in the group.
  39. /// A SBI implementation should:
  40. /// - Never choose a suspend state for higher topology group different from the specified suspend state
  41. /// - Always prefer most recent suspend state requested for higher topology group
  42. ///
  43. /// Ref: [Section 8, RISC-V Supervisor Binary Interface Specification](https://github.com/riscv-non-isa/riscv-sbi-doc/blob/master/riscv-sbi.adoc#8-hart-state-management-extension-eid-0x48534d-hsm)
  44. pub trait Hsm: Send + Sync {
  45. /// Request the SBI implementation to start executing the given hart at specified address in supervisor-mode.
  46. ///
  47. /// This call is asynchronous - more specifically, the `sbi_hart_start()` may return before target hart
  48. /// starts executing as long as the SBI implemenation is capable of ensuring the return code is accurate.
  49. ///
  50. /// It is recommended that if the SBI implementation is a platform runtime firmware executing in machine-mode (M-mode)
  51. /// then it MUST configure PMP and other the M-mode state before executing in supervisor-mode.
  52. ///
  53. /// # Parameters
  54. ///
  55. /// - The `hartid` parameter specifies the target hart which is to be started.
  56. /// - The `start_addr` parameter points to a runtime-specified physical address, where the hart can start executing in supervisor-mode.
  57. /// - The `opaque` parameter is a `usize` value which will be set in the `a1` register when the hart starts executing at `start_addr`.
  58. ///
  59. /// # Behavior
  60. ///
  61. /// The target hart jumps to supervisor mode at address specified by `start_addr` with following values in specific registers.
  62. ///
  63. /// | Register Name | Register Value
  64. /// |:--------------|:--------------
  65. /// | `satp` | 0
  66. /// | `sstatus.SIE` | 0
  67. /// | a0 | hartid
  68. /// | a1 | `opaque` parameter
  69. ///
  70. /// # Return value
  71. ///
  72. /// The possible return error codes returned in `SbiRet.error` are shown in the table below:
  73. ///
  74. /// | Return code | Description
  75. /// |:------------------------------|:----------------------------------------------
  76. /// | `SbiRet::success()` | Hart was previously in stopped state. It will start executing from `start_addr`.
  77. /// | `SbiRet::invalid_address()` | `start_addr` is not valid possibly due to following reasons: 1. It is not a valid physical address. 2. The address is prohibited by PMP to run in supervisor mode.
  78. /// | `SbiRet::invalid_param()` | `hartid` is not a valid hartid as corresponding hart cannot started in supervisor mode.
  79. /// | `SbiRet::already_available()` | The given hartid is already started.
  80. /// | `SbiRet::failed()` | The start request failed for unknown reasons.
  81. fn hart_start(&self, hartid: usize, start_addr: usize, opaque: usize) -> SbiRet;
  82. /// Request the SBI implementation to stop executing the calling hart in supervisor-mode
  83. /// and return it’s ownership to the SBI implementation.
  84. ///
  85. /// This call is not expected to return under normal conditions.
  86. /// The `sbi_hart_stop()` must be called with the supervisor-mode interrupts disabled.
  87. ///
  88. /// # Return value
  89. ///
  90. /// The possible return error codes returned in `SbiRet.error` are shown in the table below:
  91. ///
  92. /// | Error code | Description
  93. /// |:-------------------|:------------
  94. /// | `SbiRet::failed()` | Failed to stop execution of the current hart
  95. fn hart_stop(&self) -> SbiRet;
  96. /// Get the current status (or HSM state id) of the given hart.
  97. ///
  98. /// The harts may transition HSM states at any time due to any concurrent `sbi_hart_start()`
  99. /// or `sbi_hart_stop()` calls, the return value from this function may not represent the actual state
  100. /// of the hart at the time of return value verification.
  101. ///
  102. /// # Parameters
  103. ///
  104. /// The `hartid` parameter specifies the target hart which status is required.
  105. ///
  106. /// # Return value
  107. ///
  108. /// The possible status values returned in `SbiRet.value` are shown in the table below:
  109. ///
  110. /// | Name | Value | Description
  111. /// |:--------------|:------|:-------------------------
  112. /// | STARTED | 0 | Hart Started
  113. /// | STOPPED | 1 | Hart Stopped
  114. /// | START_PENDING | 2 | Hart start request pending
  115. /// | STOP_PENDING | 3 | Hart stop request pending
  116. ///
  117. /// The possible return error codes returned in `SbiRet.error` are shown in the table below:
  118. ///
  119. /// | Error code | Description
  120. /// |:--------------------------|:------------
  121. /// | `SbiRet::invalid_param()` | The given `hartid` is not valid
  122. fn hart_get_status(&self, hartid: usize) -> SbiRet;
  123. /// Request the SBI implementation to put the calling hart in a platform specfic suspend (or low power) state
  124. /// specified by the `suspend_type` parameter.
  125. ///
  126. /// The hart will automatically come out of suspended state and resume normal execution
  127. /// when it recieves an interrupt or platform specific hardware event.
  128. ///
  129. /// # Suspend behavior
  130. ///
  131. /// The platform specific suspend states for a hart can be either retentive or non-rententive in nature.
  132. ///
  133. /// A retentive suspend state will preserve hart register and CSR values for all privilege modes,
  134. /// whereas a non-retentive suspend state will not preserve hart register and CSR values.
  135. ///
  136. /// # Resuming
  137. ///
  138. /// Resuming from a retentive suspend state is straight forward and the supervisor-mode software
  139. /// will see SBI suspend call return without any failures.
  140. ///
  141. /// Resuming from a non-retentive suspend state is relatively more involved and requires software
  142. /// to restore various hart registers and CSRs for all privilege modes.
  143. /// Upon resuming from non-retentive suspend state, the hart will jump to supervisor-mode at address
  144. /// specified by `resume_addr` with specific registers values described in the table below:
  145. ///
  146. /// | Register Name | Register Value
  147. /// |:--------------|:--------------
  148. /// | `satp` | 0
  149. /// | `sstatus.SIE` | 0
  150. /// | a0 | hartid
  151. /// | a1 | `opaque` parameter
  152. ///
  153. /// # Parameters
  154. ///
  155. /// The `suspend_type` parameter is 32 bits wide and the possible values are shown in the table below:
  156. ///
  157. /// | Value | Description
  158. /// |:------------------------|:--------------
  159. /// | 0x00000000 | Default retentive suspend
  160. /// | 0x00000001 - 0x0FFFFFFF | _Reserved for future use_
  161. /// | 0x10000000 - 0x7FFFFFFF | Platform specific retentive suspend
  162. /// | 0x80000000 | Default non-retentive suspend
  163. /// | 0x80000001 - 0x8FFFFFFF | _Reserved for future use_
  164. /// | 0x90000000 - 0xFFFFFFFF | Platform specific non-retentive suspend
  165. /// | > 0xFFFFFFFF | _Reserved_
  166. ///
  167. /// The `resume_addr` parameter points to a runtime-specified physical address,
  168. /// where the hart can resume execution in supervisor-mode after a non-retentive
  169. /// suspend.
  170. ///
  171. /// The `opaque` parameter is a XLEN-bit value which will be set in the `a1`
  172. /// register when the hart resumes exectution at `resume_addr` after a
  173. /// non-retentive suspend.
  174. ///
  175. /// # Return value
  176. ///
  177. /// The possible return error codes returned in `SbiRet.error` are shown in the table below:
  178. ///
  179. /// | Error code | Description
  180. /// |:----------------------------|:------------
  181. /// | `SbiRet::success()` | Hart has suspended and resumed back successfully from a retentive suspend state.
  182. /// | `SbiRet::invalid_param()` | `suspend_type` is not valid.
  183. /// | `SbiRet::not_supported()` | `suspend_type` is valid but not implemented.
  184. /// | `SbiRet::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.
  185. /// | `SbiRet::failed()` | The suspend request failed for unknown reasons.
  186. fn hart_suspend(&self, suspend_type: u32, resume_addr: usize, opaque: usize) -> SbiRet {
  187. let _ = (suspend_type, resume_addr, opaque);
  188. SbiRet::not_supported()
  189. }
  190. }
  191. use crate::util::AmoOnceRef;
  192. static HSM: AmoOnceRef<dyn Hsm> = AmoOnceRef::new();
  193. /// Init HSM module
  194. pub fn init_hsm(hsm: &'static dyn Hsm) {
  195. if !HSM.try_call_once(hsm) {
  196. panic!("load sbi module when already loaded")
  197. }
  198. }
  199. #[inline]
  200. pub(crate) fn probe_hsm() -> bool {
  201. HSM.get().is_some()
  202. }
  203. #[inline]
  204. pub(crate) fn hart_start(hartid: usize, start_addr: usize, opaque: usize) -> SbiRet {
  205. if let Some(obj) = HSM.get() {
  206. return obj.hart_start(hartid, start_addr, opaque);
  207. }
  208. SbiRet::not_supported()
  209. }
  210. #[inline]
  211. pub(crate) fn hart_stop() -> SbiRet {
  212. if let Some(obj) = HSM.get() {
  213. return obj.hart_stop();
  214. }
  215. SbiRet::not_supported()
  216. }
  217. #[inline]
  218. pub(crate) fn hart_get_status(hartid: usize) -> SbiRet {
  219. if let Some(obj) = HSM.get() {
  220. return obj.hart_get_status(hartid);
  221. }
  222. SbiRet::not_supported()
  223. }
  224. #[inline]
  225. pub(crate) fn hart_suspend(suspend_type: u32, resume_addr: usize, opaque: usize) -> SbiRet {
  226. if let Some(obj) = HSM.get() {
  227. return obj.hart_suspend(suspend_type, resume_addr, opaque);
  228. }
  229. SbiRet::not_supported()
  230. }