pmu.rs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. use sbi_spec::binary::SbiRet;
  2. use spec::{binary::SharedPtr, pmu::shmem_size::SIZE};
  3. /// Performance Monitoring Unit extension.
  4. ///
  5. /// The RISC-V hardware performance counters such as `mcycle`, `minstret`, and `mhpmcounterX` CSRs
  6. /// are accessible as read-only from supervisor-mode using `cycle`, `instret`, and `hpmcounterX` CSRs.
  7. /// The SBI performance monitoring unit (PMU) extension is an interface for supervisor-mode to configure
  8. /// and use the RISC-V hardware performance counters with assistance from the machine-mode (or hypervisor-mode).
  9. /// These hardware performance counters can only be started, stopped, or configured from machine-mode
  10. /// using `mcountinhibit` and `mhpmeventX` CSRs.
  11. /// Due to this, a machine-mode SBI implementation may choose to disallow its SBI PMU extension
  12. /// if `mcountinhibit` CSR is not implemented by the RISC-V platform.
  13. ///
  14. /// A RISC-V platform generally supports monitoring of various hardware events using a limited number
  15. /// of hardware performance counters which are up to 64 bits wide.
  16. /// In addition, an SBI implementation can also provide firmware performance counters which can monitor firmware events
  17. /// such as number of misaligned load/store instructions, number of RFENCEs, number of IPIs, etc.
  18. /// The firmware counters are always 64 bits wide.
  19. ///
  20. /// The SBI PMU extension provides:
  21. ///
  22. /// 1. An interface for supervisor-mode software to discover and configure per-HART hardware/firmware counters
  23. /// 2. A typical perf compatible interface for hardware/firmware performance counters and events
  24. /// 3. Full access to micro-architecture’s raw event encodings
  25. ///
  26. /// To define SBI PMU extension calls, we first define important entities `counter_idx`, `event_idx`, and `event_data`.
  27. /// The `counter_idx` is a logical number assigned to each hardware/firmware counter.
  28. /// The `event_idx `represents a hardware (or firmware) event, whereas
  29. /// the `event_data` is 64 bits wide and represents additional configuration (or parameters) for
  30. /// a hardware (or firmware) event.
  31. ///
  32. /// The event_idx is a 20 bits wide number encoded as follows:
  33. ///
  34. /// ```text
  35. /// event_idx[19:16] = type;
  36. /// event_idx[15:0] = code;
  37. /// ```
  38. pub trait Pmu {
  39. /// Returns the number of counters (both hardware and firmware).
  40. ///
  41. /// The value is returned in `SbiRet.value`; this call always returns `SbiRet::success()`.
  42. fn num_counters(&self) -> usize;
  43. /// Get details about the specified counter such as underlying CSR number, width of the counter,
  44. /// type of counter (hardware/firmware, etc.).
  45. ///
  46. /// The `counter_info` returned by this SBI call is encoded as follows:
  47. ///
  48. /// ```text
  49. /// counter_info[11:0] = CSR; // (12bit CSR number)
  50. /// counter_info[17:12] = Width; // (One less than number of bits in CSR)
  51. /// counter_info[XLEN-2:18] = Reserved; // Reserved for future use
  52. /// counter_info[XLEN-1] = Type; // (0 = hardware and 1 = firmware)
  53. /// ```
  54. /// If `counter_info.type` == `1` then `counter_info.csr` and `counter_info.width` should be ignored.
  55. ///
  56. /// # Return value
  57. ///
  58. /// Returns the `counter_info` described above in `SbiRet.value`.
  59. ///
  60. /// The possible return error codes returned in `SbiRet.error` are shown in the table below:
  61. ///
  62. /// | Return code | Description
  63. /// |:----------------------------|:----------------------------------------------
  64. /// | `SbiRet::success()` | `counter_info` read successfully.
  65. /// | `SbiRet::invalid_param()` | `counter_idx` points to an invalid counter.
  66. fn counter_get_info(&self, counter_idx: usize) -> SbiRet;
  67. /// Find and configure a counter from a set of counters which is not started (or enabled)
  68. /// and can monitor the specified event.
  69. ///
  70. /// # Parameters
  71. ///
  72. /// The `counter_idx_base` and `counter_idx_mask` parameters represent the set of counters,
  73. /// whereas the `event_idx` represents the event to be monitored
  74. /// and `event_data` represents any additional event configuration.
  75. ///
  76. /// The `config_flags` parameter represents additional configuration and filter flags on counters.
  77. /// The bit definitions of the `config_flags` parameter are shown in the table below:
  78. ///
  79. /// | Flag Name | Bits | Description
  80. /// |:-------------------------------|:-------------|:------------
  81. /// | `SBI_PMU_CFG_FLAG_SKIP_MATCH` | `0:0` | Skip the counter matching
  82. /// | `SBI_PMU_CFG_FLAG_CLEAR_VALUE` | `1:1` | Clear (or zero) the counter value in counter configuration
  83. /// | `SBI_PMU_CFG_FLAG_AUTO_START` | `2:2` | Start the counter after configuring a matching counter
  84. /// | `SBI_PMU_CFG_FLAG_SET_VUINH` | `3:3` | Event counting inhibited in VU-mode
  85. /// | `SBI_PMU_CFG_FLAG_SET_VSINH` | `4:4` | Event counting inhibited in VS-mode
  86. /// | `SBI_PMU_CFG_FLAG_SET_UINH` | `5:5` | Event counting inhibited in U-mode
  87. /// | `SBI_PMU_CFG_FLAG_SET_SINH` | `6:6` | Event counting inhibited in S-mode
  88. /// | `SBI_PMU_CFG_FLAG_SET_MINH` | `7:7` | Event counting inhibited in M-mode
  89. /// | _RESERVED_ | `8:(XLEN-1)` | _All non-zero values are reserved for future use._
  90. ///
  91. /// *NOTE:* When *SBI_PMU_CFG_FLAG_SKIP_MATCH* is set in `config_flags`, the
  92. /// SBI implementation will unconditionally select the first counter from the
  93. /// set of counters specified by the `counter_idx_base` and `counter_idx_mask`.
  94. ///
  95. /// *NOTE:* The *SBI_PMU_CFG_FLAG_AUTO_START* flag in `config_flags` has no
  96. /// impact on the value of counter.
  97. ///
  98. /// *NOTE:* The `config_flags[3:7]` bits are event filtering hints so these
  99. /// can be ignored or overridden by the SBI implementation for security concerns
  100. /// or due to lack of event filtering support in the underlying RISC-V platform.
  101. ///
  102. /// # Return value
  103. ///
  104. /// Returns the `counter_idx` in `sbiret.value` upon success.
  105. ///
  106. /// In case of failure, the possible error codes returned in `sbiret.error` are shown in the table below:
  107. ///
  108. /// | Return code | Description
  109. /// |:--------------------------|:----------------------------------------------
  110. /// | `SbiRet::success()` | counter found and configured successfully.
  111. /// | `SbiRet::invalid_param()` | the set of counters has at least one invalid counter.
  112. /// | `SbiRet::not_supported()` | none of the counters can monitor the specified event.
  113. // TODO: (breaking change) change parameter `counter_idx_base` and `counter_idx_mask` into `CounterMask` for RustSBI 0.5.0
  114. fn counter_config_matching(
  115. &self,
  116. counter_idx_base: usize,
  117. counter_idx_mask: usize,
  118. config_flags: usize,
  119. event_idx: usize,
  120. event_data: u64,
  121. ) -> SbiRet;
  122. /// Start or enable a set of counters on the calling HART with the specified initial value.
  123. ///
  124. /// # Parameters
  125. ///
  126. /// The `counter_idx_base` and `counter_idx_mask` parameters represent the set of counters.
  127. /// whereas the `initial_value` parameter specifies the initial value of the counter.
  128. ///
  129. /// The bit definitions of the `start_flags` parameter are shown in the table below:
  130. ///
  131. /// | Flag Name | Bits | Description
  132. /// |:-------------------------------|:-------------|:------------
  133. /// | `SBI_PMU_START_SET_INIT_VALUE` | `0:0` | Set the value of counters based on the `initial_value` parameter.
  134. /// | _RESERVED_ | `1:(XLEN-1)` | _All non-zero values are reserved for future use._
  135. ///
  136. /// *NOTE*: When `SBI_PMU_START_SET_INIT_VALUE` is not set in `start_flags`, the value of counter will
  137. /// not be modified, and event counting will start from the current value of counter.
  138. ///
  139. /// # Return value
  140. ///
  141. /// The possible return error codes returned in `SbiRet.error` are shown in the table below:
  142. ///
  143. /// | Return code | Description
  144. /// |:----------------------------|:----------------------------------------------
  145. /// | `SbiRet::success()` | counter started successfully.
  146. /// | `SbiRet::invalid_param()` | the set of counters has at least one invalid counter.
  147. /// | `SbiRet::already_started()` | the set of counters includes at least one counter which is already started.
  148. // TODO: (breaking change) change parameter `counter_idx_base` and `counter_idx_mask` into `CounterMask` for RustSBI 0.5.0
  149. fn counter_start(
  150. &self,
  151. counter_idx_base: usize,
  152. counter_idx_mask: usize,
  153. start_flags: usize,
  154. initial_value: u64,
  155. ) -> SbiRet;
  156. /// Stop or disable a set of counters on the calling HART.
  157. ///
  158. /// # Parameters
  159. ///
  160. /// The `counter_idx_base` and `counter_idx_mask` parameters represent the set of counters.
  161. /// The bit definitions of the `stop_flags` parameter are shown in the table below:
  162. ///
  163. /// | Flag Name | Bits | Description
  164. /// |:--------------------------|:-------------|:------------
  165. /// | `SBI_PMU_STOP_FLAG_RESET` | `0:0` | Reset the counter to event mapping.
  166. /// | _RESERVED_ | `1:(XLEN-1)` | *All non-zero values are reserved for future use.*
  167. ///
  168. /// # Return value
  169. ///
  170. /// The possible return error codes returned in `SbiRet.error` are shown in the table below:
  171. ///
  172. /// | Return code | Description
  173. /// |:----------------------------|:----------------------------------------------
  174. /// | `SbiRet::success()` | counter stopped successfully.
  175. /// | `SbiRet::invalid_param()` | the set of counters has at least one invalid counter.
  176. /// | `SbiRet::already_stopped()` | the set of counters includes at least one counter which is already stopped.
  177. // TODO: (breaking change) change parameter `counter_idx_base` and `counter_idx_mask` into `CounterMask` for RustSBI 0.5.0
  178. fn counter_stop(
  179. &self,
  180. counter_idx_base: usize,
  181. counter_idx_mask: usize,
  182. stop_flags: usize,
  183. ) -> SbiRet;
  184. /// Provide the current value of a firmware counter in `SbiRet.value`.
  185. ///
  186. /// On RV32 systems, the `SbiRet.value` will only contain the lower 32 bits from the value of
  187. /// the firmware counter.
  188. ///
  189. /// # Parameters
  190. ///
  191. /// This function should be only used to read a firmware counter. It will return an error
  192. /// when the user provides a hardware counter in `counter_idx` parameter.
  193. ///
  194. /// # Return value
  195. ///
  196. /// The possible return error codes returned in `SbiRet.error` are shown in the table below:
  197. ///
  198. /// | Return code | Description
  199. /// |:--------------------------|:----------------------------------------------
  200. /// | `SbiRet::success()` | firmware counter read successfully.
  201. /// | `SbiRet::invalid_param()` | `counter_idx` points to a hardware counter or an invalid counter.
  202. fn counter_fw_read(&self, counter_idx: usize) -> SbiRet;
  203. /// Provide the upper 32 bits from the current value of firmware counter in `SbiRet.value`.
  204. ///
  205. /// This function always returns zero in `SbiRet.value` for RV64 (or higher) systems.
  206. ///
  207. /// # Return value
  208. ///
  209. /// The possible return error codes returned in `SbiRet.error` are shown in the table below:
  210. ///
  211. /// | Return code | Description
  212. /// |:--------------------------|:----------------------------------------------
  213. /// | `SbiRet::success()` | firmware counter read successfully.
  214. /// | `SbiRet::invalid_param()` | `counter_idx` points to a hardware counter or an invalid counter.
  215. #[inline]
  216. fn counter_fw_read_hi(&self, counter_idx: usize) -> SbiRet {
  217. match () {
  218. #[cfg(not(target_pointer_width = "32"))]
  219. () => {
  220. let _ = counter_idx;
  221. SbiRet::success(0)
  222. }
  223. #[cfg(target_pointer_width = "32")]
  224. () => {
  225. let _ = counter_idx;
  226. SbiRet::not_supported()
  227. }
  228. }
  229. }
  230. /// Set and enable the PMU snapshot shared memory on the calling hart.
  231. ///
  232. /// This is an optional function and the SBI implementation may choose not to implement it.
  233. ///
  234. /// # Parameters
  235. ///
  236. /// If `shmem` physical address is not all-ones bitwise, then `shmem` specifies
  237. /// the snapshot shared memory physical base address.
  238. /// The `shmem` MUST be 4096 bytes (i.e. RV64 page) aligned and the size of the snapshot
  239. /// shared memory must be 4096 bytes.
  240. ///
  241. /// If `shmem` physical address is all-ones bitwise, then the PMU snapshot shared memory
  242. /// is cleared and disabled.
  243. ///
  244. /// The `flags` parameter is reserved for future use and must be zero.
  245. ///
  246. /// The layout of the snapshot shared memory is described in the table below.
  247. ///
  248. /// | Name | Offset | Size | Description |
  249. /// |:------|:-------|:-----|:-----------|
  250. /// | `counter_overflow_bitmap` | `0x0000` | `8` | A bitmap of all logical overflown counters relative to the `counter_idx_base`. This is valid only if the `Sscofpmf` ISA extension is available. Otherwise, it must be zero. |
  251. /// | `counter_values` | `0x0008` | `512` | An array of 64-bit logical counters where each index represents the value of each logical counter associated with hardware/firmware relative to the `counter_idx_base`. |
  252. /// | _Reserved_ | `0x0208` | `3576` | Reserved for future use. |
  253. ///
  254. /// Any future revisions to this structure should be made in a backward compatible manner and will be associated with an SBI version.
  255. ///
  256. /// The logical counter indicies in the `counter_overflow_bitmap` and `counter_values` array are
  257. /// relative w.r.t to `counter_idx_base` argument present in the `pmu_counter_stop` and
  258. /// `pmu_counter_start` functions.
  259. /// This allows the users to use snapshot feature for more than `XLEN` counters if required.
  260. ///
  261. /// This function should be invoked only once per hart at boot time. Once configured, the SBI
  262. /// implementation has read/write access to the shared memory when `pmu_counter_stop` is
  263. /// invoked with the `TAKE_SNAPSHOT` flag set. The SBI implementation has read only access when
  264. /// `pmu_counter_start` is invoked with the `INIT_SNAPSHOT` flag set.
  265. /// The SBI implementation must not access this memory any other time.
  266. ///
  267. /// # Return value
  268. ///
  269. /// The possible return error codes returned in `SbiRet.error` are shown in the table below:
  270. ///
  271. /// | Return code | Description
  272. /// |:--------------------------|:----------------------------------------------
  273. /// | `SbiRet::success()` | Shared memory was set or cleared successfully.
  274. /// | `SbiRet::not_supported()` | The SBI PMU snapshot functionality is not available in the SBI implementation.
  275. /// | `SbiRet::invalid_param()` | The `flags` parameter is not zero or the `shmem` is not 4096-byte aligned.
  276. /// | `SbiRet::invalid_address()` | The shared memory pointed to by the `shmem` and is not writable or does not satisfy other requirements of shared memory
  277. /// | `SbiRet::failed()` | The request failed for unspecified or unknown other reasons.
  278. #[inline]
  279. fn snapshot_set_shmem(&self, shmem: SharedPtr<[u8; SIZE]>, flags: usize) -> SbiRet {
  280. // Optional function, `not_supported` is returned if not implemented.
  281. let _ = (shmem, flags);
  282. SbiRet::not_supported()
  283. }
  284. /// Function internal to macros. Do not use.
  285. #[doc(hidden)]
  286. #[inline]
  287. fn _rustsbi_probe(&self) -> usize {
  288. sbi_spec::base::UNAVAILABLE_EXTENSION.wrapping_add(1)
  289. }
  290. }
  291. impl<T: Pmu> Pmu for &T {
  292. #[inline]
  293. fn num_counters(&self) -> usize {
  294. T::num_counters(self)
  295. }
  296. #[inline]
  297. fn counter_get_info(&self, counter_idx: usize) -> SbiRet {
  298. T::counter_get_info(self, counter_idx)
  299. }
  300. #[inline]
  301. fn counter_config_matching(
  302. &self,
  303. counter_idx_base: usize,
  304. counter_idx_mask: usize,
  305. config_flags: usize,
  306. event_idx: usize,
  307. event_data: u64,
  308. ) -> SbiRet {
  309. T::counter_config_matching(
  310. self,
  311. counter_idx_base,
  312. counter_idx_mask,
  313. config_flags,
  314. event_idx,
  315. event_data,
  316. )
  317. }
  318. #[inline]
  319. fn counter_start(
  320. &self,
  321. counter_idx_base: usize,
  322. counter_idx_mask: usize,
  323. start_flags: usize,
  324. initial_value: u64,
  325. ) -> SbiRet {
  326. T::counter_start(
  327. self,
  328. counter_idx_base,
  329. counter_idx_mask,
  330. start_flags,
  331. initial_value,
  332. )
  333. }
  334. #[inline]
  335. fn counter_stop(
  336. &self,
  337. counter_idx_base: usize,
  338. counter_idx_mask: usize,
  339. stop_flags: usize,
  340. ) -> SbiRet {
  341. T::counter_stop(self, counter_idx_base, counter_idx_mask, stop_flags)
  342. }
  343. #[inline]
  344. fn counter_fw_read(&self, counter_idx: usize) -> SbiRet {
  345. T::counter_fw_read(self, counter_idx)
  346. }
  347. #[inline]
  348. fn counter_fw_read_hi(&self, counter_idx: usize) -> SbiRet {
  349. T::counter_fw_read_hi(self, counter_idx)
  350. }
  351. #[inline]
  352. fn snapshot_set_shmem(&self, shmem: SharedPtr<[u8; SIZE]>, flags: usize) -> SbiRet {
  353. T::snapshot_set_shmem(self, shmem, flags)
  354. }
  355. }
  356. impl<T: Pmu> Pmu for Option<T> {
  357. #[inline]
  358. fn num_counters(&self) -> usize {
  359. self.as_ref()
  360. .map(|inner| T::num_counters(inner))
  361. .unwrap_or(0)
  362. }
  363. #[inline]
  364. fn counter_get_info(&self, counter_idx: usize) -> SbiRet {
  365. self.as_ref()
  366. .map(|inner| T::counter_get_info(inner, counter_idx))
  367. .unwrap_or(SbiRet::not_supported())
  368. }
  369. #[inline]
  370. fn counter_config_matching(
  371. &self,
  372. counter_idx_base: usize,
  373. counter_idx_mask: usize,
  374. config_flags: usize,
  375. event_idx: usize,
  376. event_data: u64,
  377. ) -> SbiRet {
  378. self.as_ref().map_or(SbiRet::not_supported(), |inner| {
  379. T::counter_config_matching(
  380. inner,
  381. counter_idx_base,
  382. counter_idx_mask,
  383. config_flags,
  384. event_idx,
  385. event_data,
  386. )
  387. })
  388. }
  389. #[inline]
  390. fn counter_start(
  391. &self,
  392. counter_idx_base: usize,
  393. counter_idx_mask: usize,
  394. start_flags: usize,
  395. initial_value: u64,
  396. ) -> SbiRet {
  397. self.as_ref()
  398. .map(|inner| {
  399. T::counter_start(
  400. inner,
  401. counter_idx_base,
  402. counter_idx_mask,
  403. start_flags,
  404. initial_value,
  405. )
  406. })
  407. .unwrap_or(SbiRet::not_supported())
  408. }
  409. #[inline]
  410. fn counter_stop(
  411. &self,
  412. counter_idx_base: usize,
  413. counter_idx_mask: usize,
  414. stop_flags: usize,
  415. ) -> SbiRet {
  416. self.as_ref()
  417. .map(|inner| T::counter_stop(inner, counter_idx_base, counter_idx_mask, stop_flags))
  418. .unwrap_or(SbiRet::not_supported())
  419. }
  420. #[inline]
  421. fn counter_fw_read(&self, counter_idx: usize) -> SbiRet {
  422. self.as_ref()
  423. .map(|inner| T::counter_fw_read(inner, counter_idx))
  424. .unwrap_or(SbiRet::not_supported())
  425. }
  426. #[inline]
  427. fn counter_fw_read_hi(&self, counter_idx: usize) -> SbiRet {
  428. self.as_ref()
  429. .map(|inner| T::counter_fw_read_hi(inner, counter_idx))
  430. .unwrap_or(SbiRet::not_supported())
  431. }
  432. #[inline]
  433. fn snapshot_set_shmem(&self, shmem: SharedPtr<[u8; SIZE]>, flags: usize) -> SbiRet {
  434. self.as_ref()
  435. .map(|inner| T::snapshot_set_shmem(inner, shmem, flags))
  436. .unwrap_or(SbiRet::not_supported())
  437. }
  438. #[inline]
  439. fn _rustsbi_probe(&self) -> usize {
  440. match self {
  441. Some(_) => sbi_spec::base::UNAVAILABLE_EXTENSION.wrapping_add(1),
  442. None => sbi_spec::base::UNAVAILABLE_EXTENSION,
  443. }
  444. }
  445. }