Переглянути джерело

Merge branch 'main' into doc/manual

Zhouqi Jiang 3 тижнів тому
батько
коміт
bb515adf9f
93 змінених файлів з 6339 додано та 2543 видалено
  1. 38 32
      .github/workflows/Changelog.yml
  2. 1 1
      .github/workflows/Library.yml
  3. 1 1
      .github/workflows/prototyper.yml
  4. 6 0
      Cargo.toml
  5. 12 5
      README.md
  6. 7 1
      library/rustsbi/CHANGELOG.md
  7. 1 1
      library/rustsbi/examples/derive/commons.rs
  8. 1 1
      library/rustsbi/examples/derive/main.rs
  9. 7 1
      library/rustsbi/src/ipi.rs
  10. 1 1
      library/rustsbi/src/pmu.rs
  11. 19 1
      library/rustsbi/src/rfence.rs
  12. 10 0
      library/sbi-rt/CHANGELOG.md
  13. 15 0
      library/sbi-rt/src/base.rs
  14. 4 0
      library/sbi-rt/src/cppc.rs
  15. 3 0
      library/sbi-rt/src/dbcn.rs
  16. 249 0
      library/sbi-rt/src/dbtr.rs
  17. 59 0
      library/sbi-rt/src/fwft.rs
  18. 5 1
      library/sbi-rt/src/hsm.rs
  19. 12 0
      library/sbi-rt/src/lib.rs
  20. 352 0
      library/sbi-rt/src/mpxy.rs
  21. 5 0
      library/sbi-rt/src/nacl.rs
  22. 8 0
      library/sbi-rt/src/pmu.rs
  23. 26 1
      library/sbi-rt/src/rfnc.rs
  24. 8 1
      library/sbi-rt/src/spi.rs
  25. 1 0
      library/sbi-rt/src/srst.rs
  26. 283 0
      library/sbi-rt/src/sse.rs
  27. 1 0
      library/sbi-rt/src/sta.rs
  28. 1 0
      library/sbi-rt/src/susp.rs
  29. 1 0
      library/sbi-rt/src/time.rs
  30. 10 2
      library/sbi-spec/CHANGELOG.md
  31. 0 8
      library/sbi-spec/LICENSE-MIT
  32. 0 101
      library/sbi-spec/LICENSE-MULAN
  33. 71 0
      library/sbi-spec/examples/custom-sbi-error.rs
  34. 9 1
      library/sbi-spec/src/base.rs
  35. 20 1893
      library/sbi-spec/src/binary.rs
  36. 95 0
      library/sbi-spec/src/binary/counter_mask.rs
  37. 525 0
      library/sbi-spec/src/binary/hart_mask.rs
  38. 45 0
      library/sbi-spec/src/binary/mask_commons.rs
  39. 55 0
      library/sbi-spec/src/binary/physical_slice.rs
  40. 1146 0
      library/sbi-spec/src/binary/sbi_ret.rs
  41. 91 0
      library/sbi-spec/src/binary/shared_physical_ptr.rs
  42. 29 0
      library/sbi-spec/src/binary/trigger_mask.rs
  43. 5 0
      library/sbi-spec/src/cppc.rs
  44. 4 0
      library/sbi-spec/src/dbcn.rs
  45. 9 0
      library/sbi-spec/src/dbtr.rs
  46. 3 0
      library/sbi-spec/src/fwft.rs
  47. 14 0
      library/sbi-spec/src/hsm.rs
  48. 13 7
      library/sbi-spec/src/lib.rs
  49. 27 14
      library/sbi-spec/src/mpxy.rs
  50. 10 0
      library/sbi-spec/src/pmu.rs
  51. 8 0
      library/sbi-spec/src/rfnc.rs
  52. 2 0
      library/sbi-spec/src/spi.rs
  53. 7 0
      library/sbi-spec/src/srst.rs
  54. 11 0
      library/sbi-spec/src/sse.rs
  55. 2 0
      library/sbi-spec/src/susp.rs
  56. 2 0
      library/sbi-spec/src/time.rs
  57. 1 0
      library/sbi-testing/CHANGELOG.md
  58. 1 1
      library/sbi-testing/README_EN.md
  59. 1 1
      library/sbi-testing/src/base.rs
  60. 1 1
      library/sbi-testing/src/dbcn.rs
  61. 1 1
      library/sbi-testing/src/hsm.rs
  62. 1 1
      library/sbi-testing/src/spi.rs
  63. 2 2
      library/sbi-testing/src/thread.rs
  64. 1 1
      library/sbi-testing/src/time.rs
  65. 149 15
      prototyper/README.md
  66. 1 1
      prototyper/bench-kernel/Cargo.toml
  67. 150 0
      prototyper/docs/booting-in-visionfive2-using-uboot-and-rustsbi.md
  68. 15 7
      prototyper/prototyper/Cargo.toml
  69. 12 5
      prototyper/prototyper/build.rs
  70. 22 0
      prototyper/prototyper/src/devicetree.rs
  71. 21 9
      prototyper/prototyper/src/firmware/mod.rs
  72. 2 2
      prototyper/prototyper/src/macros.rs
  73. 58 33
      prototyper/prototyper/src/main.rs
  74. 143 67
      prototyper/prototyper/src/platform/mod.rs
  75. 59 8
      prototyper/prototyper/src/riscv/csr.rs
  76. 101 1
      prototyper/prototyper/src/sbi/early_trap.rs
  77. 63 41
      prototyper/prototyper/src/sbi/features.rs
  78. 6 1
      prototyper/prototyper/src/sbi/hart_context.rs
  79. 4 14
      prototyper/prototyper/src/sbi/hsm.rs
  80. 8 16
      prototyper/prototyper/src/sbi/ipi.rs
  81. 6 1
      prototyper/prototyper/src/sbi/mod.rs
  82. 1112 0
      prototyper/prototyper/src/sbi/pmu.rs
  83. 9 0
      prototyper/prototyper/src/sbi/rfence.rs
  84. 151 26
      prototyper/prototyper/src/sbi/trap/handler.rs
  85. 161 0
      prototyper/prototyper/src/sbi/trap/helper.rs
  86. 16 6
      prototyper/prototyper/src/sbi/trap/mod.rs
  87. 16 2
      prototyper/prototyper/src/sbi/trap_stack.rs
  88. 3 0
      prototyper/test-kernel/Cargo.toml
  89. 283 2
      prototyper/test-kernel/src/main.rs
  90. 126 53
      xtask/src/bench.rs
  91. 34 13
      xtask/src/main.rs
  92. 126 85
      xtask/src/prototyper.rs
  93. 123 53
      xtask/src/test.rs

+ 38 - 32
.github/workflows/Changelog.yml

@@ -12,39 +12,45 @@ env:
   CARGO_TERM_COLOR: always
 
 jobs:
-  check-changelog:
-    name: Check if CHANGELOG.md is updated
+  check-changelogs:
+    name: Check changelogs
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@v4
-      - name: Get latest updated files
+      - uses: actions/checkout@v3
+        with:
+          fetch-depth: 0  # 需要完整提交历史来比较差异
+
+      - name: Get changed files
         run: |
-          updated_files=$(git show --name-only --pretty=format: HEAD)
-      - name: Check if changlog is updated
+          # 获取基准和当前提交的差异文件列表
+          git diff --name-only ${{ github.event.pull_request.base.sha }} ${{ github.sha }} > changed_files.txt
+
+      - name: Validate changes
         run: |
-          if git show --name-only --pretty=format: HEAD | grep -q "CHANGELOG.md"; then
-            echo "Main CHANGELOG.md changed in the latest commit."
-          else
-            echo "Main CHANGELOG.md is not changed in the latest commit."
-            exit 1
-          fi
-          for file in $updated_files; do
-            first_path=$(dirname "$file")
-            if [[ "$first_path" == *"sbi-rt"* ]]; then
-              file_path = "./sbi-rt"
-            elif [[ "$first_path" == *"sbi-spec"* ]]; then
-              file_path = "./sbi-spec"
-            elif [[ "$first_path" == *"sbi-testing"* ]]; then
-              file_path = "./sbi-testing"
-            else
-              file_path = "./"
-            fi
-            changelog_path="$file_path/CHANGELOG.md"
-            # Check if changelog is updated
-            if git diff --name-only "$file_path" | grep -q "CHANGELOG.md"; then
-              echo "File $changelog_path changed in the latest commit."
-            else
-              echo "File $changelog_path is not changed in the latest commit."
-              exit 1
-            fi
-          done
+          # 需要检查的目录数组
+          directories=(
+            "library/sbi-rt"
+            "library/sbi-spec" 
+            "library/sbi-testing" 
+            "library/rustsbi"
+            "library/macros"
+          )
+
+          exit_code=0
+          while IFS= read -r file; do
+            for dir in "${directories[@]}"; do
+              # 检查文件是否属于当前目录
+              if [[ "$file" == "$dir/"* ]]; then
+                # 检查对应的CHANGELOG是否被修改
+                if ! grep -q "^$dir/CHANGELOG.md" changed_files.txt; then
+                  echo "::error file=$dir/CHANGELOG.md::Detected changes in $dir but the corresponding CHANGELOG.md was not updated"
+                  exit_code=1
+                else
+                  echo "CHANGELOG.md updated for $dir file changes."
+                fi
+                break  # 已匹配目录,跳出内层循环
+              fi
+            done
+          done < changed_files.txt
+
+          exit $exit_code

+ 1 - 1
.github/workflows/Rust.yml → .github/workflows/Library.yml

@@ -1,4 +1,4 @@
-name: Rust
+name: Library
 
 on:
   push:

+ 1 - 1
.github/workflows/prototyper.yml

@@ -7,7 +7,7 @@
 # More details at https://github.com/rust-lang/rust-clippy
 # and https://rust-lang.github.io/rust-clippy/
 
-name: CI
+name: Prototyper
 
 on:
   pull_request:

+ 6 - 0
Cargo.toml

@@ -11,6 +11,12 @@ members = [
     "prototyper/test-kernel",
     "xtask",
 ]
+default-members = [
+    "library/macros",
+    "library/sbi-rt",
+    "library/sbi-spec",
+    "library/rustsbi",
+]
 
 [workspace.package]
 edition = "2024"

+ 12 - 5
README.md

@@ -8,10 +8,10 @@ RISC-V Supervisor Binary Interface ([SBI](https://github.com/riscv-non-isa/riscv
 
 ## Binary downloads
 
-Most users would get RustSBI binary download from the RustSBI Prototyping System. Check out the link
-[here](https://github.com/rustsbi/standalone) to download binary package for your platform.
+Most users would get RustSBI binary download from the RustSBI Prototyper. Check out the documents [here](https://github.com/rustsbi/rustsbi/tree/main/prototyper)
+to build or download images for supported platforms.
 
-Boards, SoC vendors and research groups would provide dedicated RustSBI package for supported platforms.
+Boards, SoC vendors and research groups may sometimes provide dedicated RustSBI package for supported platforms.
 There are packages exists on [awesome-rustsbi](https://github.com/rustsbi/awesome-rustsbi): it is a curated list of
 awesome things related to RustSBI, which includes some implementation projects maintained by individuals or the community.
 
@@ -25,6 +25,12 @@ We are going to target stable Rustc once 2024 edition is landed (on 2025-02-20).
 
 ## Build this project
 
+### For firmware user
+
+If you need to build RustSBI firmware for M-mode, please refer to the [RustSBI Prototyper](prototyper/README.md) documentation.
+
+### For library user
+
 RustSBI is usually used as a library or dependency. If you wish to, you may build RustSBI library itself using the
 following command:
 
@@ -55,6 +61,7 @@ Under normal circumstances these targets in Rust would start with `riscv??-` and
 - Written in Rust, builds under stable Rust
 - Capable to develop with other firmware ecosystem projects
 - Adapted for operating system kernel models on your choice
+- Included a LLM based Agent module called RustSBI Agent (https://github.com/rustsbi/Agent), which is designed to assist system software developers in their development process
 
 ## Frequently asked questions
 
@@ -68,7 +75,7 @@ Check it out at [RustSBI document main page](https://docs.rs/rustsbi).
 2. Can I use RustSBI on C based kernels?
 
 Yes, you can! RustSBI strictly follows RISC-V SBI standard. All features are prepares for all programming languages,
-as long as they support RISC-V SBI defined calling convention. 
+as long as they support RISC-V SBI defined calling convention.
 
 If your kernel language supports other SBI implementations, usually it will support RustSBI in the same way.
 
@@ -89,7 +96,7 @@ slides and blog articles of these talks are available at [RustSBI/slides](https:
 2. Contributions are welcomed! We welcome to implement and test RustSBI for both FPGA cores and real cores.
    Implementations for emulators are also welcomed. If you are ready, start your own binary project and use
    RustSBI in it!
-3. If there is a bug in RustSBI project itself, fire an issue or pull request to let us know! 
+3. If there is a bug in RustSBI project itself, fire an issue or pull request to let us know!
 
 ## License & Copyright
 

+ 7 - 1
library/rustsbi/CHANGELOG.md

@@ -9,7 +9,7 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
 ### Added
 
-- ci: add `check-changlog` and `check-commit-signatures` in `workflows`.
+- ci: add `check-changelog` and `check-commit-signatures` in `workflows`.
 - pmu: add missing `snapshot_set_shmem` function and testcases in `Pmu` trait.
 - pmu: add missing `snapshot_set_shmem` function in `Pmu` trait, impl for `&T` and `Option<T>` and `Forward` structure
 - forward: derive `Copy`, `Default`, `PartialEq`, `Eq` and `Hash` for struct Forward
@@ -22,9 +22,15 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 - susp: amend documentation on `system_suspend` function.
 - lib: replace map+unwrap_or with Option::map_or in impls
 - doc: lib: alter link to Prototyper firmware in documentation.
+- doc: lib: clarify error codes in documents of SBI IPI and RFENCE extensions
 
 ### Removed
 
+
+### Fixed
+
+- Fix typos.
+
 ## [0.4.0]
 
 ### Added

+ 1 - 1
library/rustsbi/examples/derive/commons.rs

@@ -1,4 +1,4 @@
-// Mock implementaion module. Actual SBI implementaion should implement
+// Mock implementation module. Actual SBI implementation should implement
 // those SBI extensions with machine environment specific hardware features.
 
 use rustsbi::EnvInfo;

+ 1 - 1
library/rustsbi/examples/derive/main.rs

@@ -51,7 +51,7 @@ fn main() {
     // In hypervisor: fill guest supervisor `a0` and `a1` with `SbiRet` value.
     let _ = ret; // It should be filled into context on real programs.
 
-    // Congratulations! You have learned how to use RustSBI to create your SBI implementaion.
+    // Congratulations! You have learned how to use RustSBI to create your SBI implementation.
     // You may consider using the RustSBI Prototyping System, build a standalone
     // binary package with runtime environment from scratch, or begin with your hypervisor
     // development.

+ 7 - 1
library/rustsbi/src/ipi.rs

@@ -8,7 +8,13 @@ pub trait Ipi {
     ///
     /// # Return value
     ///
-    /// Should return `SbiRet::success()` if IPI was sent to all the targeted harts successfully.
+    /// The possible return error codes returned in `SbiRet.error` are shown in the table below:
+    ///
+    /// | Return code               | Description
+    /// |:--------------------------|:----------------------------------------------
+    /// | `SbiRet::success()`       | IPI was sent to all the targeted harts successfully.
+    /// | `SbiRet::invalid_param()` | At least one hartid constructed from `hart_mask`, is not valid, i.e. either the hartid is not enabled by the platform or is not available to the supervisor.
+    /// | `SbiRet::failed()`        | The request failed for unspecified or unknown other reasons.
     fn send_ipi(&self, hart_mask: HartMask) -> SbiRet;
     /// Function internal to macros. Do not use.
     #[doc(hidden)]

+ 1 - 1
library/rustsbi/src/pmu.rs

@@ -254,7 +254,7 @@ pub trait Pmu {
     ///
     /// Any future revisions to this structure should be made in a backward compatible manner and will be associated with an SBI version.
     ///
-    /// The logical counter indicies in the `counter_overflow_bitmap` and `counter_values` array are
+    /// The logical counter indices in the `counter_overflow_bitmap` and `counter_values` array are
     /// relative w.r.t to `counter_idx_base` argument present in the `pmu_counter_stop` and
     /// `pmu_counter_start` functions.
     /// This allows the users to use snapshot feature for more than `XLEN` counters if required.

+ 19 - 1
library/rustsbi/src/rfence.rs

@@ -10,7 +10,13 @@ pub trait Rfence {
     ///
     /// # Return value
     ///
-    /// Returns `SbiRet::success()` when a remote fence was sent to all the targeted harts successfully.
+    /// The possible return error codes returned in `SbiRet.error` are shown in the table below:
+    ///
+    /// | Return code               | Description
+    /// |:--------------------------|:----------------------------------------------
+    /// | `SbiRet::success()`       | A remote fence was sent to all the targeted harts successfully.
+    /// | `SbiRet::invalid_param()` | At least one hartid constructed from `hart_mask`, is not valid, i.e. either the hartid is not enabled by the platform or is not available to the supervisor.
+    /// | `SbiRet::failed()`        | The request failed for unspecified or unknown other reasons.
     fn remote_fence_i(&self, hart_mask: HartMask) -> SbiRet;
     /// Instructs the remote harts to execute one or more `SFENCE.VMA` instructions,
     /// covering the range of virtual addresses between `start_addr` and `size`.
@@ -23,6 +29,8 @@ pub trait Rfence {
     /// |:----------------------------|:----------------------------------------------
     /// | `SbiRet::success()`         | A remote fence was sent to all the targeted harts successfully.
     /// | `SbiRet::invalid_address()` | `start_addr` or `size` is not valid.
+    /// | `SbiRet::invalid_param()`   | At least one hartid constructed from `hart_mask`, is not valid, i.e. either the hartid is not enabled by the platform or is not available to the supervisor.
+    /// | `SbiRet::failed()`          | The request failed for unspecified or unknown other reasons.
     fn remote_sfence_vma(&self, hart_mask: HartMask, start_addr: usize, size: usize) -> SbiRet;
     /// Instruct the remote harts to execute one or more `SFENCE.VMA` instructions,
     /// covering the range of virtual addresses between `start_addr` and `size`.
@@ -36,6 +44,8 @@ pub trait Rfence {
     /// |:----------------------------|:----------------------------------------------
     /// | `SbiRet::success()`         | A remote fence was sent to all the targeted harts successfully.
     /// | `SbiRet::invalid_address()` | `start_addr` or `size` is not valid.
+    /// | `SbiRet::invalid_param()`   | Either `asid`, or at least one hartid constructed from `hart_mask`, is not valid, i.e. either the hartid is not enabled by the platform or is not available to the supervisor.
+    /// | `SbiRet::failed()`          | The request failed for unspecified or unknown other reasons.
     fn remote_sfence_vma_asid(
         &self,
         hart_mask: HartMask,
@@ -58,6 +68,8 @@ pub trait Rfence {
     /// | `SbiRet::success()`         | A remote fence was sent to all the targeted harts successfully.
     /// | `SbiRet::not_supported()`   | This function is not supported as it is not implemented or one of the target harts do not support the RISC-V hypervisor extension.
     /// | `SbiRet::invalid_address()` | `start_addr` or `size` is not valid.
+    /// | `SbiRet::invalid_param()`   | Either `vmid`, or at least one hartid constructed from `hart_mask`, is not valid, i.e. either the hartid is not enabled by the platform or is not available to the supervisor.
+    /// | `SbiRet::failed()`          | The request failed for unspecified or unknown other reasons.
     #[inline]
     fn remote_hfence_gvma_vmid(
         &self,
@@ -84,6 +96,8 @@ pub trait Rfence {
     /// | `SbiRet::success()`         | A remote fence was sent to all the targeted harts successfully.
     /// | `SbiRet::not_supported()`   | This function is not supported as it is not implemented or one of the target harts do not support the RISC-V hypervisor extension.
     /// | `SbiRet::invalid_address()` | `start_addr` or `size` is not valid.
+    /// | `SbiRet::invalid_param()`   | At least one hartid constructed from `hart_mask`, is not valid, i.e. either the hartid is not enabled by the platform or is not available to the supervisor.
+    /// | `SbiRet::failed()`          | The request failed for unspecified or unknown other reasons.
     #[inline]
     fn remote_hfence_gvma(&self, hart_mask: HartMask, start_addr: usize, size: usize) -> SbiRet {
         let _ = (hart_mask, start_addr, size);
@@ -105,6 +119,8 @@ pub trait Rfence {
     /// | `SbiRet::success()`         | A remote fence was sent to all the targeted harts successfully.
     /// | `SbiRet::not_supported()`   | This function is not supported as it is not implemented or one of the target harts do not support the RISC-V hypervisor extension.
     /// | `SbiRet::invalid_address()` | `start_addr` or `size` is not valid.
+    /// | `SbiRet::invalid_param()`   | Either `asid`, or at least one hartid constructed from `hart_mask`, is not valid, i.e. either the hartid is not enabled by the platform or is not available to the supervisor.
+    /// | `SbiRet::failed()`          | The request failed for unspecified or unknown other reasons.
     #[inline]
     fn remote_hfence_vvma_asid(
         &self,
@@ -131,6 +147,8 @@ pub trait Rfence {
     /// | `SbiRet::success()`         | A remote fence was sent to all the targeted harts successfully.
     /// | `SbiRet::not_supported()`   | This function is not supported as it is not implemented or one of the target harts do not support the RISC-V hypervisor extension.
     /// | `SbiRet::invalid_address()` | `start_addr` or `size` is not valid.
+    /// | `SbiRet::invalid_param()`   | At least one hartid constructed from `hart_mask`, is not valid, i.e. either the hartid is not enabled by the platform or is not available to the supervisor.
+    /// | `SbiRet::failed()`          | The request failed for unspecified or unknown other reasons.
     #[inline]
     fn remote_hfence_vvma(&self, hart_mask: HartMask, start_addr: usize, size: usize) -> SbiRet {
         let _ = (hart_mask, start_addr, size);

+ 10 - 0
library/sbi-rt/CHANGELOG.md

@@ -12,6 +12,13 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 - pmu: add missing `pmu_snapshot_set_shmem` function.
 - pmu: `pmu_snapshot_set_shmem` function signature, documents and implementation
 - lib: re-export `sbi_spec::base::CounterMask` on crate root.
+- rt: add FWFT extension support to SBI implementation.
+- Add C language naming alias tags to all functions of the sbi-rt library
+- rt: add DBTR extension support to SBI implementation.
+- dbtr: use `TriggerMask` structure in sbi-rt DBTR functions
+- rt: add structure for SSE, FWFT, DBTR, and MPXY extensions
+- rt: add SSE extension support to SBI implementation.
+- feat(rt): add MPXY extension support to SBI runtime library.
 
 ### Modified
 
@@ -23,6 +30,8 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
 - base: fix wrong `extension_id` of `Suspend` structure
 - binary: allow 8 arguments on internal function `sbi_call_6`.
+- lib: clarify error codes in documents of SBI IPI and RFENCE extensions.
+- Fix typos.
 
 ## [0.0.3] - 2024-02-08
 
@@ -69,3 +78,4 @@ If user chooses to use `integer-impls` feature, it would fall back to older styl
 [0.0.3]: https://github.com/rustsbi/sbi-rt/compare/v0.0.2...v0.0.3
 [0.0.2]: https://github.com/rustsbi/sbi-rt/compare/v0.0.1...v0.0.2
 [0.0.1]: https://github.com/rustsbi/sbi-rt/releases/tag/v0.0.1
+

+ 15 - 0
library/sbi-rt/src/base.rs

@@ -17,6 +17,7 @@ use sbi_spec::base::{
 /// According to the introduction of chapter 4, all base extension functions
 /// must success and return no error code.
 #[inline]
+#[doc(alias = "sbi_get_spec_version")]
 pub fn get_spec_version() -> Version {
     Version::from_raw(sbi_call_0(EID_BASE, GET_SBI_SPEC_VERSION).value)
 }
@@ -31,6 +32,7 @@ pub fn get_spec_version() -> Version {
 /// According to the introduction of chapter 4, all base extension functions
 /// must success and return no error code.
 #[inline]
+#[doc(alias = "sbi_get_impl_id")]
 pub fn get_sbi_impl_id() -> usize {
     sbi_call_0(EID_BASE, GET_SBI_IMPL_ID).value
 }
@@ -43,6 +45,7 @@ pub fn get_sbi_impl_id() -> usize {
 /// According to the introduction of chapter 4, all base extension functions
 /// must success and return no error code.
 #[inline]
+#[doc(alias = "sbi_get_impl_version")]
 pub fn get_sbi_impl_version() -> usize {
     sbi_call_0(EID_BASE, GET_SBI_IMPL_VERSION).value
 }
@@ -57,6 +60,7 @@ pub fn get_sbi_impl_version() -> usize {
 /// According to the introduction of chapter 4, all base extension functions
 /// must success and return no error code.
 #[inline]
+#[doc(alias = "sbi_probe_extension")]
 pub fn probe_extension<E>(extension: E) -> ExtensionInfo
 where
     E: Extension,
@@ -74,6 +78,7 @@ where
 /// According to the introduction of chapter 4, all base extension functions
 /// must success and return no error code.
 #[inline]
+#[doc(alias = "sbi_get_mvendorid")]
 pub fn get_mvendorid() -> usize {
     sbi_call_0(EID_BASE, GET_MVENDORID).value
 }
@@ -87,6 +92,7 @@ pub fn get_mvendorid() -> usize {
 /// According to the introduction of chapter 4, all base extension functions
 /// must success and return no error code.
 #[inline]
+#[doc(alias = "sbi_get_marchid")]
 pub fn get_marchid() -> usize {
     sbi_call_0(EID_BASE, GET_MARCHID).value
 }
@@ -100,6 +106,7 @@ pub fn get_marchid() -> usize {
 /// According to the introduction of chapter 4, all base extension functions
 /// must success and return no error code.
 #[inline]
+#[doc(alias = "sbi_get_mimpid")]
 pub fn get_mimpid() -> usize {
     sbi_call_0(EID_BASE, GET_MIMPID).value
 }
@@ -139,6 +146,10 @@ define_extension! {
     Cppc(sbi_spec::cppc::EID_CPPC) /// SBI CPPC extension.
     Nacl(sbi_spec::nacl::EID_NACL) /// Nested Acceleration extension.
     Sta(sbi_spec::sta::EID_STA) /// Steal-time Accounting extension.
+    Sse(sbi_spec::sse::EID_SSE) /// Supervisor Software Events extension.
+    Fwft(sbi_spec::fwft::EID_FWFT) /// Firmware Features extension.
+    Dbtr(sbi_spec::dbtr::EID_DBTR) /// Debug Triggers extension.
+    Mpxy(sbi_spec::mpxy::EID_MPXY) /// Message Proxy extension.
 }
 
 #[cfg(feature = "integer-impls")]
@@ -194,5 +205,9 @@ mod tests {
         assert_eq!(crate::Cppc.extension_id(), 0x43505043);
         assert_eq!(crate::Nacl.extension_id(), 0x4E41434C);
         assert_eq!(crate::Sta.extension_id(), 0x535441);
+        assert_eq!(crate::Sse.extension_id(), 0x535345);
+        assert_eq!(crate::Fwft.extension_id(), 0x46574654);
+        assert_eq!(crate::Dbtr.extension_id(), 0x44425452);
+        assert_eq!(crate::Mpxy.extension_id(), 0x4D505859);
     }
 }

+ 4 - 0
library/sbi-rt/src/cppc.rs

@@ -31,6 +31,7 @@ use sbi_spec::{
 ///
 /// This function is defined in RISC-V SBI Specification chapter 14.1.
 #[inline]
+#[doc(alias = "sbi_cppc_probe")]
 pub fn cppc_probe(cppc_reg_id: u32) -> SbiRet {
     sbi_call_1(EID_CPPC, PROBE, cppc_reg_id as _)
 }
@@ -58,6 +59,7 @@ pub fn cppc_probe(cppc_reg_id: u32) -> SbiRet {
 ///
 /// This function is defined in RISC-V SBI Specification chapter 14.2.
 #[inline]
+#[doc(alias = "sbi_cppc_read")]
 pub fn cppc_read(cppc_reg_id: u32) -> SbiRet {
     sbi_call_1(EID_CPPC, READ, cppc_reg_id as _)
 }
@@ -85,6 +87,7 @@ pub fn cppc_read(cppc_reg_id: u32) -> SbiRet {
 ///
 /// This function is defined in RISC-V SBI Specification chapter 14.3.
 #[inline]
+#[doc(alias = "sbi_cppc_read_hi")]
 pub fn cppc_read_hi(cppc_reg_id: u32) -> SbiRet {
     sbi_call_1(EID_CPPC, READ_HI, cppc_reg_id as _)
 }
@@ -111,6 +114,7 @@ pub fn cppc_read_hi(cppc_reg_id: u32) -> SbiRet {
 ///
 /// This function is defined in RISC-V SBI Specification chapter 14.4.
 #[inline]
+#[doc(alias = "sbi_cppc_write")]
 pub fn cppc_write(cppc_reg_id: u32, value: u64) -> SbiRet {
     match () {
         #[cfg(target_pointer_width = "32")]

+ 3 - 0
library/sbi-rt/src/dbcn.rs

@@ -31,6 +31,7 @@ use sbi_spec::{
 ///
 /// This function is defined in RISC-V SBI Specification chapter 12.1.
 #[inline]
+#[doc(alias = "sbi_debug_console_write")]
 pub fn console_write(bytes: Physical<&[u8]>) -> SbiRet {
     sbi_call_3(
         EID_DBCN,
@@ -68,6 +69,7 @@ pub fn console_write(bytes: Physical<&[u8]>) -> SbiRet {
 /// | `SbiRet::failed()`        | Failed to read due to I/O errors.
 ///
 /// This function is defined in RISC-V SBI Specification chapter 12.2.
+#[doc(alias = "sbi_debug_console_read")]
 pub fn console_read(bytes: Physical<&mut [u8]>) -> SbiRet {
     sbi_call_3(
         EID_DBCN,
@@ -97,6 +99,7 @@ pub fn console_read(bytes: Physical<&mut [u8]>) -> SbiRet {
 ///
 /// This function is defined in RISC-V SBI Specification chapter 12.3.
 #[inline]
+#[doc(alias = "sbi_debug_console_write_byte")]
 pub fn console_write_byte(byte: u8) -> SbiRet {
     sbi_call_1(EID_DBCN, CONSOLE_WRITE_BYTE, byte as usize)
 }

+ 249 - 0
library/sbi-rt/src/dbtr.rs

@@ -0,0 +1,249 @@
+//! Debug Triggers Extension (EID #0x44425452 "DBTR")
+//!
+//! The RISC-V Sdtrig extension allows machine-mode software to directly
+//! configure debug triggers which in-turn allows native (or hosted) debugging in machine-mode
+//! without any external debugger. Unfortunately, the debug triggers are only accessible to
+//! machine-mode.
+//!
+//! The SBI debug trigger extension defines a SBI based abstraction to provide native debugging
+//! for supervisor-mode software such that it is:
+//! 1. Suitable for the rich operating systems and hypervisors running in supervisor-mode.
+//! 2. Allows Guest (VS-mode) and Hypervisor (HS-mode) to share debug triggers on a hart.
+//!
+//! Each hart on a RISC-V platform has a fixed number of debug triggers which is referred
+//! to as `trig_max` in this SBI extension. Each debug trigger is assigned a logical index
+//! called `trig_idx` by the SBI implementation where `-1 < trig_idx < trig_max`.
+
+use crate::binary::{sbi_call_1, sbi_call_2, sbi_call_3};
+use sbi_spec::binary::{SbiRet, SharedPtr, TriggerMask};
+use sbi_spec::dbtr::*;
+
+/// Get the number of debug triggers on the calling hart which can support the trigger
+/// configuration specified by `trig_tdata1` parameter.
+///
+/// This function always returns `SbiRet::success()` in `SbiRet.error`. It will return `trig_max`
+/// in `SbiRet.value` when `trig_tdata1 == 0` otherwise it will return the number of matching
+/// debug triggers in `SbiRet.value`.
+#[doc(alias = "sbi_debug_num_triggers")]
+#[inline]
+pub fn debug_num_triggers(trig_tdata1: usize) -> usize {
+    sbi_call_1(EID_DBTR, NUM_TRIGGERS, trig_tdata1).value
+}
+
+/// Set and enable the shared memory for debug trigger configuration on the calling hart.
+///
+/// If `shmem` is not all-ones bitwise then `shmem` specifies the bits of the shared memory physical base address.
+/// The `shmem` MUST be `(XLEN / 8)` bytes aligned and the size of shared
+/// memory is assumed to be `trig_max * (XLEN / 2)` bytes.
+///
+/// If `shmem` is all-ones bitwise then shared memory for debug trigger configuration is disabled
+///
+/// The `flags` parameter is reserved for future use and MUST be zero.
+///
+/// # Return value
+///
+/// | Error code                    | Description
+/// |:------------------------------|:---------------------------------
+/// | `SbiRet::success()`           | Shared memory was set or cleared successfully.
+/// | `SbiRet::invalid_param()`     | The `flags` parameter is not zero or the `shmem` parameter is not `(XLEN / 8)` bytes aligned.
+/// | `SbiRet::invalid_address()`   | The shared memory pointed to by the `shmem` parameter does not satisfy the requirements.
+/// | `SbiRet::failed()`            | The request failed for unspecified or unknown other reasons.
+#[doc(alias = "sbi_debug_set_shmem")]
+#[inline]
+pub fn debug_set_shmem(shmem: SharedPtr<u8>, flags: usize) -> SbiRet {
+    sbi_call_3(
+        EID_DBTR,
+        SET_SHMEM,
+        shmem.phys_addr_lo(),
+        shmem.phys_addr_hi(),
+        flags,
+    )
+}
+
+/// Read the debug trigger state and configuration into shared memory for a range of
+/// debug triggers specified by the `trig_idx_base` and `trig_count` parameters on the calling hart.
+///
+/// For each debug trigger with index `trig_idx_base + i` where `-1 < i < trig_count`, the
+/// debug trigger state and configuration consisting of four XLEN-bit words are written in
+/// little-endian format at `offset = i * (XLEN / 2)` of the shared memory as follows:
+///
+/// ```text
+/// word[0] = `trig_state` written by the SBI implementation
+/// word[1] = `trig_tdata1` written by the SBI implementation
+/// word[2] = `trig_tdata2` written by the SBI implementation
+/// word[3] = `trig_tdata3` written by the SBI implementation
+/// ```
+/// # Return value
+///
+/// | Error code              | Description
+/// |:------------------------|:---------------------------------
+/// | `SbiRet::success()`     | State and configuration of triggers read successfully.
+/// | `SbiRet::no_shmem()`    | Shared memory for debug triggers is disabled.
+/// | `SbiRet::bad_range()`   | Either `trig_idx_base >= trig_max` or `trig_idx_base + trig_count >= trig_max`.
+#[doc(alias = "sbi_debug_read_triggers")]
+#[inline]
+pub fn debug_read_triggers(trig_idx_base: usize, trig_count: usize) -> SbiRet {
+    sbi_call_2(EID_DBTR, READ_TRIGGERS, trig_idx_base, trig_count)
+}
+
+/// Install debug triggers based on an array of trigger configurations in the shared memory
+/// of the calling hart. The `trig_idx` assigned to each installed trigger configuration is
+/// written back in the shared memory.
+///
+/// The `trig_count` parameter represents the number of trigger configuration entries in
+/// the shared memory at offset `0x0`.
+///
+/// The i'th trigger configuration at `offset = i * (XLEN / 2)` in the shared memory
+/// consists of four consecutive XLEN-bit words in little-endian format which are
+/// organized as follows:
+///
+/// ```text
+/// word[0] = `trig_idx` written back by the SBI implementation
+/// word[1] = `trig_tdata1` read by the SBI implementation
+/// word[2] = `trig_tdata2` read by the SBI implementation
+/// word[3] = `trig_tdata3` read by the SBI implementation
+/// ```
+///
+/// Upon success, `SbiRet.value` is set to zero. Upon failure, `SbiRet.value` is set to the
+/// array index of the failing trigger configuration.
+///
+/// # Return value
+///
+/// | Error code                  | Description
+/// |:----------------------------|:---------------------------------
+/// | `SbiRet::success()`         | Triggers installed successfully.
+/// | `SbiRet::no_shmem()`        | Shared memory for debug triggers is disabled.
+/// | `SbiRet::bad_range()`       | `trig_count >= trig_max`.
+/// | `SbiRet::invalid_param()`   | One of the trigger configuration words `trig_tdata1`, `trig_tdata2`, or `trig_tdata3` has an invalid value.
+/// | `SbiRet::failed()`          | Failed to assign `trig_idx` or HW debug trigger for one of the trigger configurations.
+/// | `SbiRet::not_supported()`   | One of the trigger configuration can't be programmed due to unimplemented optional bits in `tdata1`, `tdata2`, or `tdata3` CSRs.
+#[doc(alias = "sbi_debug_install_triggers")]
+#[inline]
+pub fn debug_install_triggers(trig_count: usize) -> SbiRet {
+    sbi_call_1(EID_DBTR, INSTALL_TRIGGERS, trig_count)
+}
+
+/// Update already installed debug triggers based on a trigger configuration array in the
+/// shared memory of the calling hart.
+///
+/// The `trig_count` parameter represents the number of trigger configuration entries in
+/// the shared memory at offset `0x0`.
+///
+/// The i'th trigger configuration at `offset = i * (XLEN / 2)` in the shared memory
+/// consists of four consecutive XLEN-bit words in little-endian format as follows:
+///
+/// ```text
+/// word[0] = `trig_idx` read by the SBI implementation
+/// word[1] = `trig_tdata1` read by the SBI implementation
+/// word[2] = `trig_tdata2` read by the SBI implementation
+/// word[3] = `trig_tdata3` read by the SBI implementation
+/// ```
+/// The SBI implementation MUST consider trigger configurations in the increasing order of
+/// the array index and starting with array index `0`. To install a debug trigger for the
+/// trigger configuration at array index `i` in the shared memory, the SBI implementation
+/// MUST do the following:
+///
+/// - Map an unused HW debug trigger which matches the trigger configuration to an
+///   an unused `trig_idx`.
+/// - Save a copy of the `trig_tdata1.vs`, `trig_tdata1.vu`, `trig_tdata1.s`, and
+///   `trig_tdata.u` bits in `trig_state`.
+/// - Update the `tdata1`, `tdata2`, and `tdata3` CSRs of the HW debug trigger.
+/// - Write `trig_idx` at `offset = i * (XLEN / 2)` in the shared memory.
+///
+/// Additionally for each trigger configuration chain in the shared memory, the SBI
+/// implementation MUST assign contiguous `trig_idx` values and contiguous HW debug
+/// triggers when installing the trigger configuration chain.
+///
+/// The last trigger configuration in the shared memory MUST not have `trig_tdata1.chain == 1`
+/// for `trig_tdata1.type = 2 or 6` to prevent incomplete trigger configuration chain
+/// in the shared memory.
+///
+/// The `SbiRet.value` is set to zero upon success or if shared memory is disabled whereas
+/// `SbiRet.value` is set to the array index `i` of the failing trigger configuration upon
+/// other failures.
+///
+/// # Return value
+///
+/// | Error code                  | Description
+/// |:----------------------------|:---------------------------------
+/// | `SbiRet::success()`         | Triggers updated successfully.
+/// | `SbiRet::no_shmem()`        | Shared memory for debug triggers is disabled.
+/// | `SbiRet::bad_range()`       | `trig_count >= trig_max`.
+/// | `SbiRet::invalid_param()`   | One of the trigger configuration in the shared memory has an invalid of `trig_idx` (i.e. `trig_idx >= trig_max`), `trig_tdata1`, `trig_tdata2`, or `trig_tdata3`.
+/// | `SbiRet::failed()`          | One of the trigger configurations has valid `trig_idx` but the corresponding debug trigger is not mapped to any HW debug trigger.
+/// | `SbiRet::not_supported()`   | One of the trigger configuration can't be programmed due to unimplemented optional bits in `tdata1`, `tdata2`, or `tdata3` CSRs.
+#[doc(alias = "sbi_debug_update_triggers")]
+#[inline]
+pub fn debug_update_triggers(trig_count: usize) -> SbiRet {
+    sbi_call_1(EID_DBTR, UPDATE_TRIGGERS, trig_count)
+}
+
+/// Uninstall a set of debug triggers specified by the `triggers` mask parameter on the calling hart.
+///
+/// The `triggers` specifies which triggers are to be uninstalled.
+/// Each bit in the mask corresponds to a specific trigger, allowing for batch operations
+/// on multiple triggers simultaneously.
+///
+/// For each debug trigger in the specified set of debug triggers, the SBI implementation MUST:
+/// 1. Clear the `tdata1`, `tdata2`, and `tdata3` CSRs of the mapped HW debug trigger.
+/// 2. Clear the `trig_state` of the debug trigger.
+/// 3. Unmap and free the HW debug trigger and corresponding `trig_idx` for re-use in
+///    the future trigger installations.
+///
+/// # Return value
+///
+/// | Error code                  | Description
+/// |:----------------------------|:---------------------------------
+/// | `SbiRet::success()`         | Triggers uninstalled successfully.
+/// | `SbiRet::invalid_param()`   | One of the debug triggers with index `trig_idx` in the specified set of debug triggers either not mapped to any HW debug trigger OR has `trig_idx >= trig_max`.
+#[doc(alias = "sbi_debug_uninstall_triggers")]
+#[inline]
+pub fn debug_uninstall_triggers(triggers: TriggerMask) -> SbiRet {
+    let (trig_idx_mask, trig_idx_base) = triggers.into_inner();
+    sbi_call_2(EID_DBTR, UNINSTALL_TRIGGERS, trig_idx_base, trig_idx_mask)
+}
+
+/// Enable a set of debug triggers specified by the `triggers` mask parameter on the calling hart.
+///
+/// The `triggers` specifies which triggers are to be enabled.
+/// Each bit in the mask corresponds to a specific trigger, allowing for batch operations
+/// on multiple triggers simultaneously.
+///
+/// To enable a debug trigger in the specified set of debug triggers, the SBI implementation
+/// MUST restore the `vs`, `vu`, `s`, and `u` bits of the mapped HW debug trigger from their
+/// saved copy in `trig_state`.
+///
+/// # Return value
+///
+/// | Error code                  | Description
+/// |:----------------------------|:---------------------------------
+/// | `SbiRet::success()`         | Triggers enabled successfully.
+/// | `SbiRet::invalid_param()`   | One of the debug triggers with index `trig_idx` in the specified set of debug triggers either not mapped to any HW debug trigger OR has `trig_idx >= trig_max`.
+#[doc(alias = "sbi_debug_enable_triggers")]
+#[inline]
+pub fn debug_enable_triggers(triggers: TriggerMask) -> SbiRet {
+    let (trig_idx_mask, trig_idx_base) = triggers.into_inner();
+    sbi_call_2(EID_DBTR, ENABLE_TRIGGERS, trig_idx_base, trig_idx_mask)
+}
+
+/// Disable a set of debug triggers specified by the `triggers` mask parameter on the calling hart.
+///
+/// The `triggers` specifies which triggers are to be disabled.
+/// Each bit in the mask corresponds to a specific trigger, allowing for batch operations
+/// on multiple triggers simultaneously.
+///
+/// To disable a debug trigger in the specified set of debug triggers, the SBI implementation
+/// MUST clear the `vs`, `vu`, `s`, and `u` bits of the mapped HW debug trigger.
+///
+/// # Return value
+///
+/// | Error code                  | Description
+/// |:----------------------------|:---------------------------------
+/// | `SbiRet::success()`         | Triggers disabled successfully.
+/// | `SbiRet::invalid_param()`   | One of the debug triggers with index `trig_idx` in the specified set of debug triggers either not mapped to any HW debug trigger OR has `trig_idx >= trig_max`.
+#[doc(alias = "sbi_debug_disable_triggers")]
+#[inline]
+pub fn debug_disable_triggers(triggers: TriggerMask) -> SbiRet {
+    let (trig_idx_mask, trig_idx_base) = triggers.into_inner();
+    sbi_call_2(EID_DBTR, DISABLE_TRIGGERS, trig_idx_base, trig_idx_mask)
+}

+ 59 - 0
library/sbi-rt/src/fwft.rs

@@ -0,0 +1,59 @@
+//! Chapter 18. SBI Firmware Features Extension (EID #0x46574654 "FWFT").
+
+use crate::binary::{sbi_call_1, sbi_call_3};
+use sbi_spec::{
+    binary::SbiRet,
+    fwft::{EID_FWFT, GET, SET},
+};
+
+/// Set the configuration value of a specific firmware feature.
+///
+/// # Parameters
+///
+/// - `feature`: The identifier of the feature to set.
+/// - `value`: The value to set for the feature.
+/// - `flags`: Flags to modify the behavior of the set operation.
+///
+/// # Return value
+///
+/// A successful return results in the requested firmware feature to be set according to the `value` and `flags` parameters. In case of failure, `feature` value is not modified and the possible error codes returned in `SbiRet.error` are shown in the table below:
+///
+/// | Error code                  | Description
+/// |:----------------------------|:---------------------------------
+/// | `SbiRet::success()`         | `feature` was set successfully.
+/// | `SbiRet::not_supported()`   | `feature` is not reserved and valid, but the platform does not support it due to one or more missing dependencies (Hardware or SBI implementation).
+/// | `SbiRet::invalid_param()`   | Provided `value` or `flags` parameter is invalid.
+/// | `SbiRet::denied()`          | `feature` set operation failed because either it was denied by the SBI implementation, or`feature` is reserved or is platform-specific and unimplemented.
+/// | `SbiRet::denied_locked()`   | `feature` set operation failed because the `feature` is locked.
+/// | `SbiRet::failed()`          | The set operation failed for unspecified or unknown other reasons.
+///
+/// This function is defined in RISC-V SBI Specification chapter 18.1.
+#[inline]
+#[doc(alias = "sbi_fwft_set")]
+pub fn fwft_set(feature: u32, value: usize, flags: usize) -> SbiRet {
+    sbi_call_3(EID_FWFT, SET, feature as _, value, flags)
+}
+
+/// Get the configuration value of a specific firmware feature.
+///
+/// # Parameters
+///
+/// - `feature`: The identifier of the feature to get.
+///
+/// # Return value
+///
+/// A successful return results in the firmware feature configuration value to be returned in `SbiRet.value`. In case of failure, the content of `SbiRet.value` is zero and the possible error codes returned in `SbiRet.error` are shown in the table below:
+///
+/// | Error code                  | Description
+/// |:----------------------------|:---------------------------------
+/// | `SbiRet::success()`         | Feature status was retrieved successfully.
+/// | `SbiRet::not_supported()`   | `feature` is not reserved and valid, but the platform does not support it due to one or more missing dependencies (Hardware or SBI implementation).
+/// | `SbiRet::denied()`          | `feature` is reserved or is platform-specific and unimplemented.
+/// | `SbiRet::failed()`          | The get operation failed for unspecified or unknown other reasons.
+///
+/// This function is defined in RISC-V SBI Specification chapter 18.2.
+#[inline]
+#[doc(alias = "sbi_fwft_get")]
+pub fn fwft_get(feature: u32) -> SbiRet {
+    sbi_call_1(EID_FWFT, GET, feature as _)
+}

+ 5 - 1
library/sbi-rt/src/hsm.rs

@@ -58,6 +58,7 @@ use sbi_spec::{
 ///
 /// This function is defined in RISC-V SBI Specification chapter 9.1.
 #[inline]
+#[doc(alias = "sbi_hart_start")]
 pub fn hart_start(hartid: usize, start_addr: usize, opaque: usize) -> SbiRet {
     sbi_call_3(EID_HSM, HART_START, hartid, start_addr, opaque)
 }
@@ -80,6 +81,7 @@ pub fn hart_start(hartid: usize, start_addr: usize, opaque: usize) -> SbiRet {
 ///
 /// This function is defined in RISC-V SBI Specification chapter 9.2.
 #[inline]
+#[doc(alias = "sbi_hart_stop")]
 pub fn hart_stop() -> SbiRet {
     sbi_call_0(EID_HSM, HART_STOP)
 }
@@ -113,13 +115,14 @@ pub fn hart_stop() -> SbiRet {
 ///
 /// This function is defined in RISC-V SBI Specification chapter 9.3.
 #[inline]
+#[doc(alias = "sbi_hart_get_status")]
 pub fn hart_get_status(hartid: usize) -> SbiRet {
     sbi_call_1(EID_HSM, HART_GET_STATUS, hartid)
 }
 
 /// Put the calling hart into suspend or platform specific lower power states.
 ///
-/// This function requests the SBI implementation to put the calling hart in a platform specfic suspend
+/// This function requests the SBI implementation to put the calling hart in a platform specific suspend
 /// (or low power) state specified by the `suspend_type` parameter.
 ///
 /// The hart will automatically come out of suspended state and resume normal execution
@@ -189,6 +192,7 @@ pub fn hart_get_status(hartid: usize) -> SbiRet {
 ///
 /// This function is defined in RISC-V SBI Specification chapter 9.4.
 #[inline]
+#[doc(alias = "sbi_hart_suspend")]
 pub fn hart_suspend<T>(suspend_type: T, resume_addr: usize, opaque: usize) -> SbiRet
 where
     T: SuspendType,

+ 12 - 0
library/sbi-rt/src/lib.rs

@@ -40,6 +40,14 @@ mod cppc;
 mod nacl;
 // §16
 mod sta;
+// §17
+mod sse;
+// §18
+mod fwft;
+// §19
+mod dbtr;
+// §20
+mod mpxy;
 
 pub use sbi_spec::{
     base::Version,
@@ -52,12 +60,16 @@ pub use sbi_spec::{
 pub use base::*;
 pub use cppc::*;
 pub use dbcn::*;
+pub use dbtr::*;
+pub use fwft::*;
 pub use hsm::*;
+pub use mpxy::*;
 pub use nacl::*;
 pub use pmu::*;
 pub use rfnc::*;
 pub use spi::*;
 pub use srst::*;
+pub use sse::*;
 pub use sta::*;
 pub use susp::*;
 pub use time::*;

+ 352 - 0
library/sbi-rt/src/mpxy.rs

@@ -0,0 +1,352 @@
+//! Chapter 20. Message Proxy Extension (EID #0x4D505859 “MPXY”)
+
+use crate::binary::{sbi_call_0, sbi_call_1, sbi_call_3};
+use sbi_spec::{
+    binary::{SbiRet, SharedPtr},
+    mpxy::*,
+};
+
+/// Get the shared memory size in number of bytes for sending and receiving messages.
+///
+/// The shared memory size returned by the SBI implementation MUST satisfy the following requirements:
+/// - The shared memory size MUST be same for all HARTs.
+/// - The shared memory size MUST be at least 4096 bytes.
+/// - The shared memory size MUST be multiple of 4096 bytes.
+/// - The shared memory size MUST not be less than the biggest MSG_DATA_MAX_LEN attribute value across all MPXY channels.
+///
+/// # Return value
+///
+/// This function always returns `SbiRet::success()` in `SbiRet.error` and it will return the shared memory size.
+#[doc(alias = "sbi_mpxy_get_shmem_size")]
+#[inline]
+pub fn mpxy_get_shmem_size() -> usize {
+    sbi_call_0(EID_MPXY, GET_SHMEM_SIZE).value
+}
+
+/// Set the shared memory for sending and receiving messages on the calling hart.
+///
+/// # Parameters
+///
+/// - `shmem`: shared memory pointer.
+///
+/// If `shmem` is not all-ones bitwise then `shmem` specifies the bits of the shared memory physical base address.
+/// The `shmem` MUST be `4096` bytes aligned and the size of shared memory is assumed to be same as returned by the Get shared memory size function: mpxy_get_shmem_size().
+/// If `shmem` is all-ones bitwise then shared memory is disabled.
+///
+/// - `flags`: the parameter specifies configuration for shared memory setup and it is encoded as follows:
+///
+/// ```text
+/// flags[XLEN-1:2]: Reserved for future use and must be zero.
+/// flags[1:0]: Shared memory setup mode (Refer table below).
+/// ```
+///
+/// | Mode             | flags[1:0]  | Description
+/// |:-----------------|:------------|:---------------------------------
+/// | OVERWRITE        | 0b00        | Ignore the current shared memory state and force setup the new shared memory based on the passed parameters.
+/// | OVERWRITE-RETURN | 0b01        | Same as `OVERWRITE` mode and additionally after the new shared memory state is enabled, the old shared memory are written in the same order to the new shared memory at offset `0x0`. This flag provide provision to software layers in the supervisor software that want to send messages using the shared memory but do not know the shared memory details that has already been setup. Those software layers can temporarily setup their own shared memory on the calling hart, send messages and then restore back the previous shared memory with the SBI implementation.
+/// | RESERVED         | 0b10 - 0b11 | Reserved for future use. Must be initialized to `0`.
+///
+/// # Return value
+///
+/// | Error code                    | Description
+/// |:------------------------------|:---------------------------------
+/// | `SbiRet::success()`           | Shared memory was set or cleared successfully.
+/// | `SbiRet::invalid_param()`     | The `flags` parameter has invalid value or the bits set are within the reserved range. Or the `shmem` parameter is not `4096` bytes aligned.
+/// | `SbiRet::invalid_address()`   | The shared memory pointed to by the `shmem` parameter does not satisfy the requirements.
+/// | `SbiRet::failed()`            | The request failed for unspecified or unknown other reasons.
+#[doc(alias = "sbi_mpxy_set_shmem")]
+#[inline]
+pub fn mpxy_set_shmem(shmem: SharedPtr<u8>, flags: usize) -> SbiRet {
+    sbi_call_3(
+        EID_MPXY,
+        SET_SHMEM,
+        shmem.phys_addr_lo(),
+        shmem.phys_addr_hi(),
+        flags,
+    )
+}
+
+/// Get channel IDs of the message channels accessible to the supervisor software in the shared memory of the calling hart.
+///
+/// The channel IDs are returned as an array of 32 bits unsigned integers where the `start_index` parameter specifies the array index
+/// of the first channel ID to be returned in the shared memory.
+///
+/// The SBI implementation will return channel IDs in the shared memory of the calling hart as specified by the table below:
+///
+/// | Offset            | Field                            | Description
+/// |:------------------|:---------------------------------|:---------------------------------
+/// | 0x0               | REMAINING                        | Remaining number of channel IDs.
+/// | 0x4               | RETURNED                         | Number of channel IDs (N) returned in the shared memory.
+/// | 0x8               | CHANNEL_ID [start_index + 0]     | Channel ID
+/// | 0xC               | CHANNEL_ID [start_index + 1]     | Channel ID
+/// | 0x8 + ((N-1) * 4) | CHANNEL_ID [start_index + N - 1] | Channel ID
+///
+/// The number of channel IDs returned in the shared memory are specified by the `RETURNED` field whereas the `REMAINING` field specifies the
+/// number of remaining channel IDs. If the `REMAINING` is not `0` then supervisor software can call this function again to get remaining channel
+/// IDs with `start_index` passed accordingly. The supervisor software may require multiple SBI calls to get the complete list of channel IDs
+/// depending on the `RETURNED` and `REMAINING` fields.
+///
+/// # Parameters
+///
+/// - `start_index`: specifies the array index of the first channel ID to be returned in the shared memory.
+///
+/// # Return value
+///
+/// The `SbiRet.value` is always set to zero whereas the possible error codes returned in `SbiRet.error` are below.
+///
+/// | Error code                    | Description
+/// |:------------------------------|:---------------------------------
+/// | `SbiRet::success()`           | The channel ID array has been written successfully.
+/// | `SbiRet::invalid_param()`     | `start_index` is invalid.
+/// | `SbiRet::no_shmem()`          | The shared memory setup is not done or disabled for the calling hart.
+/// | `SbiRet::denied()`            | Getting channel ID array is not allowed on the calling hart.
+/// | `SbiRet::failed()`            | The request failed for unspecified or unknown other reasons.
+#[doc(alias = "sbi_mpxy_get_channel_ids")]
+#[inline]
+pub fn mpxy_get_channel_ids(start_index: u32) -> SbiRet {
+    sbi_call_1(EID_MPXY, GET_CHANNEL_IDS, start_index as usize)
+}
+
+/// Read message channel attributes.
+///
+/// Supervisor software MUST call this function for the contiguous attribute range where the `base_attribute_id` is the starting index of that
+/// range and `attribute_count` is the number of attributes in the contiguous range. If there are multiple such attribute ranges then multiple
+/// calls of this function may be done from supervisor software. Supervisor software MUST read the message protocol specific attributes via
+/// separate call to this function with `base_attribute_id` and `attribute_count` without any overlap with the MPXY standard attributes.
+///
+/// # Parameters
+///
+/// - `channel_id`: specifies the message channel whereas `base_attribute_id` and `attribute_count` parameters specify the range of attribute ids to be read.
+/// - `base_attribute_id`: specifies the range of attribute ids to be read.
+/// - `attribute_count`: specifies the range of attribute ids to be read.
+///
+/// # Return value
+///
+/// Upon calling this function the message channel attribute values are returned starting from the offset `0x0` in the shared memory of the
+/// calling hart where the value of the attribute with `attribute_id = base_attribute_id + i` is available at the shared memory offset `4 * i`.
+///
+/// The possible error codes returned in `SbiRet.error` are shown below.
+///
+/// | Error code                    | Description
+/// |:------------------------------|:---------------------------------
+/// | `SbiRet::success()`           | Message channel attributes has been read successfully.
+/// | `SbiRet::invalid_param()`     | `attribute_count` is 0. Or the `attribute_count > (shared memory size)/4`. Or the `base_attribute_id` is not valid.
+/// | `SbiRet::not_supported()`     | `channel_id` is not supported or invalid.
+/// | `SbiRet::bad_range()`         | One of the attributes in the range specified by the `base_attribute_id` and `attribute_count` do not exist.
+/// | `SbiRet::no_shmem()`          | The shared memory setup is not done or disabled for calling hart.
+/// | `SbiRet::failed()`            | The request failed for unspecified or unknown other reasons.
+#[doc(alias = "sbi_mpxy_read_attributes")]
+#[inline]
+pub fn mpxy_read_attributes(
+    channel_id: u32,
+    base_attribute_id: u32,
+    attribute_count: u32,
+) -> SbiRet {
+    sbi_call_3(
+        EID_MPXY,
+        READ_ATTRIBUTE,
+        channel_id as usize,
+        base_attribute_id as usize,
+        attribute_count as usize,
+    )
+}
+
+/// Write message channel attributes.
+///
+/// Supervisor software MUST call this function for the contiguous attribute range where the `base_attribute_id` is the starting index of that
+/// range and `attribute_count` is the number of attributes in the contiguous range. If there are multiple such attribute ranges then multiple
+/// calls of this function may be done from supervisor software. Apart from contiguous attribute indices, supervisor software MUST also
+/// consider the attribute access permissions and attributes with RO (Read Only) access MUST be excluded from the attribute range.
+/// Supervisor software MUST write the message protocol specific attributes via separate call to this function with `base_attribute_id` and
+/// `attribute_count` without any overlap with the MPXY standard attributes.
+///
+/// Before calling this function, the supervisor software must populate the shared memory of the calling hart starting from offset `0x0` with the
+/// message channel attribute values. For each attribute with `attribute_id = base_attribute_id + i`, the corresponding value MUST be placed at
+/// the shared memory offset `4 * i`.
+///
+/// # Parameters
+///
+/// - `channel_id`: specifies the message channel whereas `base_attribute_id` and `attribute_count` parameters specify the range of attribute ids.
+/// - `base_attribute_id`: specifies the range of attribute ids.
+/// - `attribute_count`: specifies the range of attribute ids.
+///
+/// # Return value
+///
+/// The possible error codes returned in `SbiRet.error` are shown below.
+///
+/// | Error code                    | Description
+/// |:------------------------------|:---------------------------------
+/// | `SbiRet::success()`           | Message channel attributes has been written successfully.
+/// | `SbiRet::invalid_param()`     | `attribute_count` is 0. Or the `attribute_count > (shared memory size)/4`. Or the `base_attribute_id` is not valid.
+/// | `SbiRet::not_supported()`     | `channel_id` is not supported or invalid.
+/// | `SbiRet::bad_range()`         | One of the attributes in the range specified by the `base_attribute_id` and `attribute_count` do not exist or the attribute is read-only (RO). Or `base_attribute_id` and `attribute_count` result into a range which overlaps with standard and message protocol specific attributes.
+/// | `SbiRet::no_shmem()`          | The shared memory setup is not done or disabled for calling hart.
+/// | `SbiRet::denied()`            | If any attribute write dependency is not satisfied.
+/// | `SbiRet::failed()`            | The request failed for unspecified or unknown other reasons.
+#[doc(alias = "sbi_mpxy_write_attributes")]
+#[inline]
+pub fn mpxy_write_attributes(
+    channel_id: u32,
+    base_attribute_id: u32,
+    attribute_count: u32,
+) -> SbiRet {
+    sbi_call_3(
+        EID_MPXY,
+        WRITE_ATTRIBUTE,
+        channel_id as usize,
+        base_attribute_id as usize,
+        attribute_count as usize,
+    )
+}
+
+/// Send a message to the MPXY channel specified by the `channel_id` parameter and wait until a message response is received from the MPXY channel.
+///
+/// This function only succeeds upon receipt of a message response from the MPXY channel. In cases where complete data transfer requires
+/// multiple transmissions, the supervisor software shall send multiple messages as necessary. Details of such cases can be found in
+/// respective message protocol specifications.
+///
+/// This function is optional. If this function is implemented, the corresponding bit in the `CHANNEL_CAPABILITY` attribute is set to `1`.
+///
+/// # Parameters
+///
+/// - `channel_id`: specifies the MPXY channel.
+/// - `message_id`: specifies the message protocol specific identification of the message to be sent.
+/// - `message_data_len`: represents the length of message data in bytes which is located at the offset `0x0` in the shared memory setup by the calling hart.
+///
+/// # Return value
+///
+/// Upon calling this function the SBI implementation MUST write the response message data at the offset `0x0` in the shared memory setup by
+/// the calling hart and the number of bytes written will be returned through `SbiRet.value`. The layout of data in case of both request and
+/// response is according to the respective message protocol specification message format.
+///
+/// Upon success, this function:
+/// - Writes the message response data at offset `0x0` of the shared memory setup by the calling hart.
+/// - Returns `SbiRet::success()` in `SbiRet.error`.
+/// - Returns message response data length in `SbiRet.value`.
+///
+/// The possible error codes returned in `SbiRet.error` are shown below.
+///
+/// | Error code                    | Description
+/// |:------------------------------|:---------------------------------
+/// | `SbiRet::success()`           | Message sent and response received successfully.
+/// | `SbiRet::invalid_param()`     | The `message_data_len > MSG_DATA_MAX_LEN` for specified `channel_id`. Or the `message_data_len` is greater than the size of shared memory on the calling hart.
+/// | `SbiRet::not_supported()`     | `channel_id` is not supported or invalid. Or the message represented by the `message_id` is not supported or invalid. Or this function is not supported.
+/// | `SbiRet::no_shmem()`          | The shared memory setup is not done or disabled for calling hart.
+/// | `SbiRet::timeout()`           | Waiting for response timeout.
+/// | `SbiRet::io()`                | Failed due to I/O error.
+/// | `SbiRet::failed()`            | The request failed for unspecified or unknown other reasons.
+#[doc(alias = "sbi_mpxy_send_message_with_response")]
+#[inline]
+pub fn mpxy_send_message_with_response(
+    channel_id: u32,
+    message_id: u32,
+    message_data_len: usize,
+) -> SbiRet {
+    sbi_call_3(
+        EID_MPXY,
+        SEND_MESSAGE_WITH_RESPONSE,
+        channel_id as usize,
+        message_id as usize,
+        message_data_len,
+    )
+}
+
+/// Send a message to the MPXY channel specified by the `channel_id` parameter without waiting for a message response from the MPXY channel.
+///
+/// This function does not wait for message response from the channel and returns after successful message transmission. In cases where
+/// complete data transfer requires multiple transmissions, the supervisor software shall send multiple messages as necessary. Details of such
+/// cases can be found in the respective message protocol specification.
+///
+/// This function is optional. If this function is implemented, the corresponding bit in the `CHANNEL_CAPABILITY` attribute is set to `1`.
+///
+/// # Parameters
+///
+/// - `channel_id`: specifies the MPXY channel.
+/// - `message_id`: specifies the message protocol specific identification of the message to be sent.
+/// - `message_data_len`: represents the length of message data in bytes which is located at the offset `0x0` in the shared memory setup by the calling hart.
+///
+/// # Return value
+///
+/// The possible error codes returned in `SbiRet.error` are shown below.
+///
+/// | Error code                    | Description
+/// |:------------------------------|:---------------------------------
+/// | `SbiRet::success()`           | Message sent successfully.
+/// | `SbiRet::invalid_param()`     | The `message_data_len > MSG_DATA_MAX_LEN` for specified `channel_id`. Or the `message_data_len` is greater than the size of shared memory on the calling hart.
+/// | `SbiRet::not_supported()`     | `channel_id` is not supported or invalid. Or the message represented by the `message_id` is not supported or invalid. Or this function is not supported.
+/// | `SbiRet::no_shmem()`          | The shared memory setup is not done or disabled for calling hart.
+/// | `SbiRet::timeout()`           | Message send timeout.
+/// | `SbiRet::io()`                | Failed due to I/O error.
+/// | `SbiRet::failed()`            | The request failed for unspecified or unknown other reasons.
+#[doc(alias = "sbi_mpxy_send_message_without_response")]
+#[inline]
+pub fn mpxy_send_message_without_response(
+    channel_id: u32,
+    message_id: u32,
+    message_data_len: usize,
+) -> SbiRet {
+    sbi_call_3(
+        EID_MPXY,
+        SEND_MESSAGE_WITHOUT_RESPONSE,
+        channel_id as usize,
+        message_id as usize,
+        message_data_len,
+    )
+}
+
+/// Get the message protocol specific notification events on the MPXY channel specified by the `channel_id` parameter.
+///
+/// The events are message protocol specific and MUST be defined in the respective message protocol specification.
+/// The SBI implementation may support indication mechanisms like MSI or SSE to inform the supervisor software about the availability of events.
+///
+/// Depending on the message protocol implementation, a channel may support events state which includes data like number of events
+/// `RETURNED`, `REMAINING` and `LOST`. Events state data is optional, and if the message protocol implementation supports it, then the channel
+/// will have the corresponding bit set in the `CHANNEL_CAPABILITY` attribute. By default the events state is disabled and supervisor software
+/// can explicitly enable it through the `EVENTS_STATE_CONTROL` attribute.
+///
+/// This function is optional. If this function is implemented, the corresponding bit in the `CHANNEL_CAPABILITY` attribute is set to 1.
+///
+/// # Parameters
+///
+/// - `channel_id`: specifies the MPXY channel.
+///
+/// # Return value
+///
+/// In the shared memory, 16 bytes starting from offset 0x0 are used to return this state data.
+/// Shared memory layout with events state data (each field is of 4 bytes):
+///
+/// ```text
+/// Offset 0x0: REMAINING
+/// Offset 0x4: RETURNED
+/// Offset 0x8: LOST
+/// Offset 0xC: RESERVED
+/// Offset 0x10: Start of message protocol specific notification events data
+/// ```
+///
+/// The `RETURNED` field represents the number of events which are returned in the shared memory when this function is called. The `REMAINING`
+/// field represents the number of events still remaining with SBI implementation. The supervisor software may need to call this function again
+/// until the `REMAINING` field becomes `0`.
+///
+/// The `LOST` field represents the number of events which are lost due to limited buffer size managed by the message protocol
+/// implementation. Details of buffering/caching of events is specific to message protocol implementation.
+///
+/// Upon calling this function the received notification events are written by the SBI implementation at the offset `0x10` in the shared memory
+/// setup by the calling hart irrespective of events state data reporting. If events state data reporting is disabled or not supported, then the
+/// values in events state fields are undefined. The number of the bytes written to the shared memory will be returned through `SbiRet.value`
+/// which is the number of bytes starting from offset `0x10`. The layout and encoding of notification events are defined by the message
+/// protocol specification associated with the message proxy channel (`channel_id`).
+///
+/// The possible error codes returned in `SbiRet.error` are shown below.
+///
+/// | Error code                    | Description
+/// |:------------------------------|:---------------------------------
+/// | `SbiRet::success()`           | Notifications received successfully.
+/// | `SbiRet::not_supported()`     | `channel_id` is not supported or invalid. Or this function is not supported.
+/// | `SbiRet::no_shmem()`          | The shared memory setup is not done or disabled for calling hart.
+/// | `SbiRet::io()`                | Failed due to I/O error.
+/// | `SbiRet::failed()`            | The request failed for unspecified or unknown other reasons.
+#[doc(alias = "sbi_mpxy_get_notification_events")]
+#[inline]
+pub fn mpxy_get_notification_events(channel_id: u32) -> SbiRet {
+    sbi_call_1(EID_MPXY, GET_NOTIFICATION_EVENTS, channel_id as usize)
+}

+ 5 - 0
library/sbi-rt/src/nacl.rs

@@ -24,6 +24,7 @@ use sbi_spec::{
 ///
 /// This function is defined in RISC-V SBI Specification chapter 15.5.
 #[inline]
+#[doc(alias = "sbi_nacl_probe_feature")]
 pub fn nacl_probe_feature(feature_id: u32) -> SbiRet {
     sbi_call_1(EID_NACL, PROBE_FEATURE, feature_id as _)
 }
@@ -53,6 +54,7 @@ pub fn nacl_probe_feature(feature_id: u32) -> SbiRet {
 ///
 /// This function is defined in RISC-V SBI Specification chapter 15.6.
 #[inline]
+#[doc(alias = "sbi_nacl_set_shmem")]
 pub fn nacl_set_shmem(shmem: SharedPtr<[u8; shmem_size::NATIVE]>, flags: usize) -> SbiRet {
     sbi_call_3(
         EID_NACL,
@@ -89,6 +91,7 @@ pub fn nacl_set_shmem(shmem: SharedPtr<[u8; shmem_size::NATIVE]>, flags: usize)
 ///
 /// This function is defined in RISC-V SBI Specification chapter 15.7.
 #[inline]
+#[doc(alias = "sbi_nacl_sync_csr")]
 pub fn nacl_sync_csr(csr_num: usize) -> SbiRet {
     sbi_call_1(EID_NACL, SYNC_CSR, csr_num)
 }
@@ -118,6 +121,7 @@ pub fn nacl_sync_csr(csr_num: usize) -> SbiRet {
 ///
 /// This function is defined in RISC-V SBI Specification chapter 15.8.
 #[inline]
+#[doc(alias = "sbi_nacl_sync_hfence")]
 pub fn nacl_sync_hfence(entry_index: usize) -> SbiRet {
     sbi_call_1(EID_NACL, SYNC_HFENCE, entry_index)
 }
@@ -141,6 +145,7 @@ pub fn nacl_sync_hfence(entry_index: usize) -> SbiRet {
 ///
 /// This function is defined in RISC-V SBI Specification chapter 15.9.
 #[inline]
+#[doc(alias = "sbi_nacl_sync_sret")]
 pub fn nacl_sync_sret() -> SbiRet {
     sbi_call_0(EID_NACL, SYNC_SRET)
 }

+ 8 - 0
library/sbi-rt/src/pmu.rs

@@ -16,6 +16,7 @@ use sbi_spec::{
 ///
 /// This function is defined in RISC-V SBI Specification chapter 11.6.
 #[inline]
+#[doc(alias = "sbi_pmu_num_counters")]
 pub fn pmu_num_counters() -> usize {
     sbi_call_0(EID_PMU, NUM_COUNTERS).value
 }
@@ -48,6 +49,7 @@ pub fn pmu_num_counters() -> usize {
 ///
 /// This function is defined in RISC-V SBI Specification chapter 11.7.
 #[inline]
+#[doc(alias = "sbi_pmu_counter_get_info")]
 pub fn pmu_counter_get_info(counter_idx: usize) -> SbiRet {
     sbi_call_1(EID_PMU, COUNTER_GET_INFO, counter_idx)
 }
@@ -103,6 +105,7 @@ pub fn pmu_counter_get_info(counter_idx: usize) -> SbiRet {
 ///
 /// This function is defined in RISC-V SBI Specification chapter 11.8.
 #[inline]
+#[doc(alias = "sbi_pmu_counter_config_matching")]
 pub fn pmu_counter_config_matching<T>(
     counter_idx: CounterMask,
     config_flags: T,
@@ -167,6 +170,7 @@ where
 ///
 /// This function is defined in RISC-V SBI Specification chapter 11.9.
 #[inline]
+#[doc(alias = "sbi_pmu_counter_start")]
 pub fn pmu_counter_start<T>(counter_idx: CounterMask, start_flags: T, initial_value: u64) -> SbiRet
 where
     T: StartFlags,
@@ -219,6 +223,7 @@ where
 ///
 /// This function is defined in RISC-V SBI Specification chapter 11.10.
 #[inline]
+#[doc(alias = "sbi_pmu_counter_stop")]
 pub fn pmu_counter_stop<T>(counter_idx: CounterMask, stop_flags: T) -> SbiRet
 where
     T: StopFlags,
@@ -254,6 +259,7 @@ where
 ///
 /// This function is defined in RISC-V SBI Specification chapter 11.11.
 #[inline]
+#[doc(alias = "sbi_pmu_counter_fw_read")]
 pub fn pmu_counter_fw_read(counter_idx: usize) -> SbiRet {
     sbi_call_1(EID_PMU, COUNTER_FW_READ, counter_idx)
 }
@@ -273,6 +279,7 @@ pub fn pmu_counter_fw_read(counter_idx: usize) -> SbiRet {
 ///
 /// This function is defined in RISC-V SBI Specification chapter 11.12.
 #[inline]
+#[doc(alias = "sbi_pmu_counter_fw_read_hi")]
 pub fn pmu_counter_fw_read_hi(counter_idx: usize) -> SbiRet {
     sbi_call_1(EID_PMU, COUNTER_FW_READ_HI, counter_idx)
 }
@@ -306,6 +313,7 @@ pub fn pmu_counter_fw_read_hi(counter_idx: usize) -> SbiRet {
 ///
 /// This function is defined in RISC-V SBI Specification chapter 11.13.
 #[inline]
+#[doc(alias = "sbi_pmu_snapshot_set_shmem")]
 pub fn pmu_snapshot_set_shmem(shmem: SharedPtr<[u8; SIZE]>, flags: usize) -> SbiRet {
     sbi_call_3(
         EID_PMU,

+ 26 - 1
library/sbi-rt/src/rfnc.rs

@@ -14,10 +14,17 @@ use sbi_spec::{
 ///
 /// # Return value
 ///
-/// Returns `SbiRet::success()` when a remote fence was sent to all the targeted harts successfully.
+/// The possible return error codes returned in `SbiRet.error` are shown in the table below:
+///
+/// | Return code               | Description
+/// |:--------------------------|:----------------------------------------------
+/// | `SbiRet::success()`       | A remote fence was sent to all the targeted harts successfully.
+/// | `SbiRet::invalid_param()` | At least one hartid constructed from `hart_mask`, is not valid, i.e. either the hartid is not enabled by the platform or is not available to the supervisor.
+/// | `SbiRet::failed()`        | The request failed for unspecified or unknown other reasons.
 ///
 /// This function is defined in RISC-V SBI Specification chapter 8.1.
 #[inline]
+#[doc(alias = "sbi_remote_fence_i")]
 pub fn remote_fence_i(hart_mask: HartMask) -> SbiRet {
     let (hart_mask, hart_mask_base) = hart_mask.into_inner();
     sbi_call_2(EID_RFNC, REMOTE_FENCE_I, hart_mask, hart_mask_base)
@@ -36,9 +43,12 @@ pub fn remote_fence_i(hart_mask: HartMask) -> SbiRet {
 /// |:----------------------------|:----------------------------------------------
 /// | `SbiRet::success()`         | A remote fence was sent to all the targeted harts successfully.
 /// | `SbiRet::invalid_address()` | `start_addr` or `size` is not valid.
+/// | `SbiRet::invalid_param()`   | At least one hartid constructed from `hart_mask`, is not valid, i.e. either the hartid is not enabled by the platform or is not available to the supervisor.
+/// | `SbiRet::failed()`          | The request failed for unspecified or unknown other reasons.
 ///
 /// This function is defined in RISC-V SBI Specification chapter 8.2.
 #[inline]
+#[doc(alias = "sbi_remote_sfence_vma")]
 pub fn remote_sfence_vma(hart_mask: HartMask, start_addr: usize, size: usize) -> SbiRet {
     let (hart_mask, hart_mask_base) = hart_mask.into_inner();
     sbi_call_4(
@@ -65,9 +75,12 @@ pub fn remote_sfence_vma(hart_mask: HartMask, start_addr: usize, size: usize) ->
 /// |:----------------------------|:----------------------------------------------
 /// | `SbiRet::success()`         | A remote fence was sent to all the targeted harts successfully.
 /// | `SbiRet::invalid_address()` | `start_addr` or `size` is not valid.
+/// | `SbiRet::invalid_param()`   | Either `asid`, or at least one hartid constructed from `hart_mask`, is not valid, i.e. either the hartid is not enabled by the platform or is not available to the supervisor.
+/// | `SbiRet::failed()`          | The request failed for unspecified or unknown other reasons.
 ///
 /// This function is defined in RISC-V SBI Specification chapter 8.3.
 #[inline]
+#[doc(alias = "sbi_remote_sfence_vma_asid")]
 pub fn remote_sfence_vma_asid(
     hart_mask: HartMask,
     start_addr: usize,
@@ -103,9 +116,12 @@ pub fn remote_sfence_vma_asid(
 /// | `SbiRet::success()`         | A remote fence was sent to all the targeted harts successfully.
 /// | `SbiRet::not_supported()`   | This function is not supported as it is not implemented or one of the target hart doesn’t support hypervisor extension.
 /// | `SbiRet::invalid_address()` | `start_addr` or `size` is not valid.
+/// | `SbiRet::invalid_param()`   | Either `vmid`, or at least one hartid constructed from `hart_mask`, is not valid, i.e. either the hartid is not enabled by the platform or is not available to the supervisor.
+/// | `SbiRet::failed()`          | The request failed for unspecified or unknown other reasons.
 ///
 /// This function is defined in RISC-V SBI Specification chapter 8.4.
 #[inline]
+#[doc(alias = "sbi_remote_hfence_gvma_vmid")]
 pub fn remote_hfence_gvma_vmid(
     hart_mask: HartMask,
     start_addr: usize,
@@ -141,9 +157,12 @@ pub fn remote_hfence_gvma_vmid(
 /// | `SbiRet::success()`         | A remote fence was sent to all the targeted harts successfully.
 /// | `SbiRet::not_supported()`   | This function is not supported as it is not implemented or one of the target hart does not support hypervisor extension.
 /// | `SbiRet::invalid_address()` | `start_addr` or `size` is not valid.
+/// | `SbiRet::invalid_param()`   | At least one hartid constructed from `hart_mask`, is not valid, i.e. either the hartid is not enabled by the platform or is not available to the supervisor.
+/// | `SbiRet::failed()`          | The request failed for unspecified or unknown other reasons.
 ///
 /// This function is defined in RISC-V SBI Specification chapter 8.5.
 #[inline]
+#[doc(alias = "sbi_remote_hfence_gvma")]
 pub fn remote_hfence_gvma(hart_mask: HartMask, start_addr: usize, size: usize) -> SbiRet {
     let (hart_mask, hart_mask_base) = hart_mask.into_inner();
     sbi_call_4(
@@ -174,9 +193,12 @@ pub fn remote_hfence_gvma(hart_mask: HartMask, start_addr: usize, size: usize) -
 /// | `SbiRet::success()`         | A remote fence was sent to all the targeted harts successfully.
 /// | `SbiRet::not_supported()`   | This function is not supported as it is not implemented or one of the target hart does not support hypervisor extension.
 /// | `SbiRet::invalid_address()` | `start_addr` or `size` is not valid.
+/// | `SbiRet::invalid_param()`   | Either `asid`, or at least one hartid constructed from `hart_mask`, is not valid, i.e. either the hartid is not enabled by the platform or is not available to the supervisor.
+/// | `SbiRet::failed()`          | The request failed for unspecified or unknown other reasons.
 ///
 /// This function is defined in RISC-V SBI Specification chapter 8.6.
 #[inline]
+#[doc(alias = "sbi_remote_hfence_vvma_asid")]
 pub fn remote_hfence_vvma_asid(
     hart_mask: HartMask,
     start_addr: usize,
@@ -212,9 +234,12 @@ pub fn remote_hfence_vvma_asid(
 /// | `SbiRet::success()`         | A remote fence was sent to all the targeted harts successfully.
 /// | `SbiRet::not_supported()`   | This function is not supported as it is not implemented or one of the target hart doesn’t support hypervisor extension.
 /// | `SbiRet::invalid_address()` | `start_addr` or `size` is not valid.
+/// | `SbiRet::invalid_param()`   | At least one hartid constructed from `hart_mask`, is not valid, i.e. either the hartid is not enabled by the platform or is not available to the supervisor.
+/// | `SbiRet::failed()`          | The request failed for unspecified or unknown other reasons.
 ///
 /// This function is defined in RISC-V SBI Specification chapter 8.7.
 #[inline]
+#[doc(alias = "sbi_remote_hfence_vvma")]
 pub fn remote_hfence_vvma(hart_mask: HartMask, start_addr: usize, size: usize) -> SbiRet {
     let (hart_mask, hart_mask_base) = hart_mask.into_inner();
     sbi_call_4(

+ 8 - 1
library/sbi-rt/src/spi.rs

@@ -13,10 +13,17 @@ use sbi_spec::{
 ///
 /// # Return value
 ///
-/// Should return `SbiRet::success()` if IPI was sent to all the targeted harts successfully.
+/// The possible return error codes returned in `SbiRet.error` are shown in the table below:
+///
+/// | Return code               | Description
+/// |:--------------------------|:----------------------------------------------
+/// | `SbiRet::success()`       | IPI was sent to all the targeted harts successfully.
+/// | `SbiRet::invalid_param()` | At least one hartid constructed from `hart_mask`, is not valid, i.e. either the hartid is not enabled by the platform or is not available to the supervisor.
+/// | `SbiRet::failed()`        | The request failed for unspecified or unknown other reasons.
 ///
 /// This function is defined in RISC-V SBI Specification chapter 7.1.
 #[inline]
+#[doc(alias = "sbi_send_ipi")]
 pub fn send_ipi(hart_mask: HartMask) -> SbiRet {
     let (hart_mask, hart_mask_base) = hart_mask.into_inner();
     sbi_call_2(EID_SPI, SEND_IPI, hart_mask, hart_mask_base)

+ 1 - 0
library/sbi-rt/src/srst.rs

@@ -31,6 +31,7 @@ use sbi_spec::{
 ///
 /// This function is defined in RISC-V SBI Specification chapter 10.1.
 #[inline]
+#[doc(alias = "sbi_system_reset")]
 pub fn system_reset<T, R>(reset_type: T, reset_reason: R) -> SbiRet
 where
     T: ResetType,

+ 283 - 0
library/sbi-rt/src/sse.rs

@@ -0,0 +1,283 @@
+//! Chapter 17. Supervisor Software Events Extension (EID #0x535345 "SSE").
+
+use crate::binary::{sbi_call_0, sbi_call_1, sbi_call_2, sbi_call_3, sbi_call_5};
+use sbi_spec::{
+    binary::{SbiRet, SharedPtr},
+    sse::*,
+};
+
+/// Read a range of event attribute values from a software event.
+///
+/// The `event_id` parameter specifies the software event ID whereas `base_attr_id`
+/// and `attr_count` parameters specifies the range of event attribute IDs.
+///
+/// The event attribute values are written to a output shared memory which is specified
+/// by the `output` parameter where:
+///
+/// - The `output` parameter MUST be `XLEN / 8` bytes aligned
+/// - The size of output shared memory is assumed to be `(XLEN / 8) * attr_count`
+/// - The value of event attribute with ID `base_attr_id + i` should be read from offset `(XLEN / 8) * (base_attr_id + i)`
+///
+/// The possible error codes returned in sbiret.error are shown below.
+///
+/// # Return value
+///
+/// | Error code                  | Description
+/// |:----------------------------|:---------------------------------
+/// | `SbiRet::success()`         | Event attribute values read successfully.
+/// | `SbiRet::not_supported()`   | `event_id` is not reserved and valid, but the platform does not support it due to one or more missing dependencies (Hardware or SBI implementation).
+/// | `SbiRet::invalid_param()`   | `event_id` is invalid or `attr_count` is zero.
+/// | `SbiRet::bad_range()`       | One of the event attribute IDs in the range specified by `base_attr_id` and `attr_count` is reserved.
+/// | `SbiRet::invalid_address()` | The shared memory pointed to by the `output` parameter does not satisfy the requirements.
+/// | `SbiRet::failed()`          | The read failed for unspecified or unknown other reasons.
+#[doc(alias = "sbi_sse_read_attrs")]
+#[inline]
+pub fn sse_read_attrs(
+    event_id: u32,
+    base_attr_id: u32,
+    attr_count: u32,
+    output: SharedPtr<u8>,
+) -> SbiRet {
+    sbi_call_5(
+        EID_SSE,
+        READ_ATTRS,
+        event_id as _,
+        base_attr_id as _,
+        attr_count as _,
+        output.phys_addr_lo(),
+        output.phys_addr_hi(),
+    )
+}
+
+/// Write a range of event attribute values to a software event.
+///
+/// The event_id parameter specifies the software event ID whereas `base_attr_id` and
+/// `attr_count` parameters specifies the range of event attribute IDs.
+///
+/// The event attribute values are read from a input shared memory which is specified
+/// by the `input` parameter where:
+///
+/// - The `input` parameter MUST be `XLEN / 8` bytes aligned
+/// - The size of input shared memory is assumed to be `(XLEN / 8) * attr_count`
+/// - The value of event attribute with ID `base_attr_id + i` should be read from offset `(XLEN / 8) * (base_attr_id + i)`
+///
+/// For local events, the event attributes are updated only for the calling hart.
+/// For global events, the event attributes are updated for all the harts.
+/// The possible error codes returned in sbiret.error are shown below.
+///
+/// # Return value
+///
+/// | Error code                  | Description
+/// |:----------------------------|:---------------------------------
+/// | `SbiRet::success()`         | Event attribute values written successfully.
+/// | `SbiRet::not_supported()`   | `event_id` is not reserved and valid, but the platform does not support it due to one or more missing dependencies (Hardware or SBI implementation).
+/// | `SbiRet::invalid_param()`   | `event_id` is invalid or `attr_count` is zero.
+/// | `SbiRet::bad_range()`       | One of the event attribute IDs in the range specified by `base_attr_id` and `attr_count` is reserved or is read-only.
+/// | `SbiRet::invalid_address()` | The shared memory pointed to by the `input` parameter does not satisfy the requirements.
+/// | `SbiRet::failed()`          | The write failed for unspecified or unknown other reasons.
+#[doc(alias = "sbi_sse_write_attrs")]
+#[inline]
+pub fn sse_write_attrs(
+    event_id: u32,
+    base_attr_id: u32,
+    attr_count: u32,
+    input: SharedPtr<u8>,
+) -> SbiRet {
+    sbi_call_5(
+        EID_SSE,
+        WRITE_ATTRS,
+        event_id as _,
+        base_attr_id as _,
+        attr_count as _,
+        input.phys_addr_lo(),
+        input.phys_addr_hi(),
+    )
+}
+
+/// Register an event handler for the software event.
+///
+/// The `event_id` parameter specifies the event ID for which an event handler is being registered.
+/// The `handler_entry_pc` parameter MUST be 2-bytes aligned and specifies the `ENTRY_PC` event
+/// attribute of the software event whereas the `handler_entry_arg` parameter specifies the
+/// `ENTRY_ARG` event attribute of the software event.
+///
+/// For local events, the event is registered only for the calling hart.
+/// For global events, the event is registered for all the harts.
+///
+/// The event MUST be in `UNUSED` state otherwise this function will fail.
+///
+/// Upon success, the event state moves from `UNUSED` to `REGISTERED`. In case of an error, possible error codes are listed below.
+///
+/// # Return value
+///
+/// | Error code                  | Description
+/// |:----------------------------|:---------------------------------
+/// | `SbiRet::success()`         | Event handler is registered successfully.
+/// | `SbiRet::not_supported()`   | `event_id` is not reserved and valid, but the platform does not support it due to one or more missing dependencies (Hardware or SBI implementation).
+/// | `SbiRet::invalid_state()`   | `event_id` is valid but the event is not in `UNUSED` state.
+/// | `SbiRet::invalid_param()`   | `event_id` is invalid or `handler_entry_pc` is not 2-bytes aligned.    
+#[doc(alias = "sbi_sse_register")]
+#[inline]
+pub fn sse_register(event_id: u32, handler_entry_pc: usize, handler_entry_arg: usize) -> SbiRet {
+    sbi_call_3(
+        EID_SSE,
+        REGISTER,
+        event_id as _,
+        handler_entry_pc,
+        handler_entry_arg,
+    )
+}
+
+/// Unregister the event handler for given `event_id`.
+///
+/// For local events, the event is unregistered only for the calling hart.
+/// For global events, the event is unregistered for all the harts.
+///
+/// The event MUST be in `REGISTERED` state otherwise this function will fail.
+///
+/// Upon success, the event state moves from `REGISTERED` to `UNUSED`. In case of an error, possible error codes are listed below.
+///
+/// # Return value
+///
+/// | Error code                  | Description
+/// |:----------------------------|:---------------------------------
+/// | `SbiRet::success()`         | Event handler is unregistered successfully.
+/// | `SbiRet::not_supported()`   | `event_id` is not reserved and valid, but the platform does not support it due to one or more missing dependencies (Hardware or SBI implementation).
+/// | `SbiRet::invalid_state()`   | `event_id` is valid but the event is not in `REGISTERED` state.
+/// | `SbiRet::invalid_param()`   | `event_id` is invalid.
+#[doc(alias = "sbi_sse_unregister")]
+#[inline]
+pub fn sse_unregister(event_id: u32) -> SbiRet {
+    sbi_call_1(EID_SSE, UNREGISTER, event_id as _)
+}
+
+/// Enable the software event specified by the `event_id` parameter.
+///
+/// For local events, the event is enabled only for the calling hart.
+/// For global events, the event is enabled for all the harts.
+///
+/// The event MUST be in `REGISTERED` state otherwise this function will fail.
+///
+/// Upon success, the event state moves from `REGISTERED` to `ENABLED`. In case of an error, possible error codes are listed below.
+///
+/// # Return value
+///
+/// | Error code                  | Description
+/// |:----------------------------|:---------------------------------
+/// | `SbiRet::success()`         | Event is successfully enabled.
+/// | `SbiRet::not_supported()`   | `event_id` is not reserved and valid, but the platform does not support it due to one or more missing dependencies (Hardware or SBI implementation).
+/// | `SbiRet::invalid_param()`   | `event_id` is invalid.
+/// | `SbiRet::invalid_state()`   | `event_id` is valid but the event is not in `REGISTERED` state.
+#[doc(alias = "sbi_sse_enable")]
+#[inline]
+pub fn sse_enable(event_id: u32) -> SbiRet {
+    sbi_call_1(EID_SSE, ENABLE, event_id as _)
+}
+
+/// Disable the software event specified by the `event_id` parameter.
+///
+/// For local events, the event is disabled only for the calling hart.
+/// For global events, the event is disabled for all the harts.
+///
+/// The event MUST be in `ENABLED` state otherwise this function will fail.
+///
+/// Upon success, the event state moves from `ENABLED` to `REGISTERED`. In case of an error, possible error codes are listed below.
+///
+/// # Return value
+///
+/// | Error code                  | Description
+/// |:----------------------------|:---------------------------------
+/// | `SbiRet::success()`         | Event is successfully disabled.
+/// | `SbiRet::not_supported()`   | `event_id` is not reserved and valid, but the platform does not support it due to one or more missing dependencies (Hardware or SBI implementation).
+/// | `SbiRet::invalid_param()`   | `event_id` is invalid.
+/// | `SbiRet::invalid_state()`   | `event_id` is valid but the event is not in `ENABLED` state.
+#[doc(alias = "sbi_sse_disable")]
+#[inline]
+pub fn sse_disable(event_id: u32) -> SbiRet {
+    sbi_call_1(EID_SSE, DISABLE, event_id as _)
+}
+
+/// Complete the supervisor event handling for the highest priority event in `RUNNING` state on the calling hart.
+///
+/// If there were no events in `RUNNING` state on the calling hart then this function does nothing and returns `SBI_SUCCESS`
+/// otherwise it moves the highest priority event in `RUNNING` state to:
+///
+/// - `REGISTERED` if the event is configured as one-shot
+/// - `ENABLED` state otherwise
+///
+/// It then resumes the interrupted supervisor state.
+#[doc(alias = "sbi_sse_complete")]
+#[inline]
+pub fn sse_complete() -> SbiRet {
+    sbi_call_0(EID_SSE, COMPLETE)
+}
+
+/// The supervisor software can inject a software event with this function.
+///
+/// The `event_id` parameter refers to the ID of the event to be injected.
+///
+/// For local events, the `hart_id` parameter refers to the hart on which the event is to be injected.
+/// For global events, the `hart_id` parameter is ignored.
+///
+/// An event can only be injected if it is allowed by the event attribute.
+///
+/// If an event is injected from within an SSE event handler, if it is ready to be run,
+/// it will be handled according to the priority rules
+///
+/// - If it has a higher priority than the one currently running, then it will be handled immediately, effectively preempting the currently running one.
+/// - If it has a lower priority, it will be run after the one that is currently running completes.
+///
+/// In case of an error, possible error codes are listed below.
+///
+/// # Return value
+///
+/// | Error code                  | Description
+/// |:----------------------------|:---------------------------------
+/// | `SbiRet::success()`         | Event is successfully injected.
+/// | `SbiRet::not_supported()`   | `event_id` is not reserved and valid, but the platform does not support it due to one or more missing dependencies (Hardware or SBI implementation).
+/// | `SbiRet::invalid_param()`   | `event_id` is invalid or `hart_id` is invalid.
+/// | `SbiRet::failed()`          | The injection failed for unspecified or unknown other reasons.
+#[doc(alias = "sbi_sse_inject")]
+#[inline]
+pub fn sse_inject(event_id: u32, hart_id: usize) -> SbiRet {
+    sbi_call_2(EID_SSE, INJECT, event_id as _, hart_id)
+}
+
+/// Start receiving (or unmask) software events on the calling hart.
+/// In other words, the calling hart is ready to receive software events from the SBI implementation.
+///
+/// The software events are masked initially on all harts so the supervisor software must
+/// explicitly unmask software events on relevant harts at boot-time.
+///
+/// In case of an error, possible error codes are listed below.
+///
+/// # Return value
+///
+/// | Error code                  | Description
+/// |:----------------------------|:---------------------------------
+/// | `SbiRet::success()`         | Software events unmasked successfully on the calling hart.
+/// | `SbiRet::already_started()` | Software events were already unmasked on the calling hart.
+/// | `SbiRet::failed()`          | The request failed for unspecified or unknown other reasons.
+#[doc(alias = "sbi_sse_hart_unmask")]
+#[inline]
+pub fn sse_hart_unmask() -> SbiRet {
+    sbi_call_0(EID_SSE, HART_UNMASK)
+}
+
+/// Stop receiving (or mask) software events on the calling hart.
+/// In other words, the calling hart will no longer be ready to receive software events from the SBI implementation.
+///
+/// In case of an error, possible error codes are listed below.
+///
+/// # Return value
+///
+/// | Error code                  | Description
+/// |:----------------------------|:---------------------------------
+/// | `SbiRet::success()`         | Software events masked successfully on the calling hart.
+/// | `SbiRet::already_stopped()` | Software events were already masked on the calling hart.
+/// | `SbiRet::failed()`          | The request failed for unspecified or unknown other reasons.
+#[doc(alias = "sbi_sse_hart_mask")]
+#[inline]
+pub fn sse_hart_mask() -> SbiRet {
+    sbi_call_0(EID_SSE, HART_MASK)
+}

+ 1 - 0
library/sbi-rt/src/sta.rs

@@ -56,6 +56,7 @@ use sbi_spec::{
 ///
 /// This function is defined in RISC-V SBI Specification chapter 16.1.
 #[inline]
+#[doc(alias = "sbi_sta_set_shmem")]
 pub fn sta_set_shmem(shmem: SharedPtr<[u8; 64]>, flags: usize) -> SbiRet {
     sbi_call_3(
         EID_STA,

+ 1 - 0
library/sbi-rt/src/susp.rs

@@ -41,6 +41,7 @@ use sbi_spec::{
 ///
 /// This function is defined in RISC-V SBI Specification chapter 13.1.
 #[inline]
+#[doc(alias = "sbi_system_suspend")]
 pub fn system_suspend<T>(sleep_type: T, resume_addr: usize, opaque: usize) -> SbiRet
 where
     T: SleepType,

+ 1 - 0
library/sbi-rt/src/time.rs

@@ -16,6 +16,7 @@ use sbi_spec::{
 ///
 /// This function is defined in RISC-V SBI Specification chapter 6.1.
 #[inline]
+#[doc(alias = "sbi_set_timer")]
 pub fn set_timer(stime_value: u64) -> SbiRet {
     match () {
         #[cfg(target_pointer_width = "32")]

+ 10 - 2
library/sbi-spec/CHANGELOG.md

@@ -21,6 +21,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
 - examples: an SBI version example for usage of the Version structure
 - base: add special constant `V1_0` and `V2_0` for structure `Version`
 - examples: add an example on non-usize `HartMask` structure
+- examples: add an example for custom SBI error code
+- binary: add `TriggerMask` structure, it would be used in SBI DBTR extension
+- binary: add `SbiRet::denied_locked()` error code
 
 ### Modified
 
@@ -29,9 +32,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
 - base: refactor `SbiRet` to be generic of registers and introduce the `SbiRegister` trait
 - base: implement `SbiRegister` for `i32`, `i64`, `i128` and `isize` primitive types
 - base: make HartMask and CounterMask generic over SBI registers
+- Add C language naming alias tags to all constants in the sbi-spec package
+- binary: refactor code to split binary structures into modules
 
 ### Fixed
 
+- Remove redundant license file on module path; the `sbi-spec` module inherits workspace level license files.
+- Fix typos.
+
 ## [0.0.8] - 2024-10-25
 
 ### Added
@@ -107,7 +115,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
 ### Modified
 
 - Rename `SbiRet::ok` to `SbiRet::success`
-- Rename `SbiSpecVersion` to struct `Version` 
+- Rename `SbiSpecVersion` to struct `Version`
 
 ## [0.0.3] - 2022-10-06
 
@@ -117,7 +125,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
   check implementations during compilation, and provide an item list for developers
 - denied: warnings and unsafe code
 - a github workflow to check building
-- `SbiSpecVersion` type defination for sbi base
+- `SbiSpecVersion` type definition for sbi base
 
 ### Modified
 

+ 0 - 8
library/sbi-spec/LICENSE-MIT

@@ -1,8 +0,0 @@
-Copyright 2020 Luo Jia
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-

+ 0 - 101
library/sbi-spec/LICENSE-MULAN

@@ -1,101 +0,0 @@
-木兰宽松许可证, 第2版
-
-2020年1月 http://license.coscl.org.cn/MulanPSL2
-
-您对“软件”的复制、使用、修改及分发受木兰宽松许可证,第2版(“本许可证”)的如下条款的约束:
-
-0. 定义
-
-“软件”是指由“贡献”构成的许可在“本许可证”下的程序和相关文档的集合。
-
-“贡献”是指由任一“贡献者”许可在“本许可证”下的受版权法保护的作品。
-
-“贡献者”是指将受版权法保护的作品许可在“本许可证”下的自然人或“法人实体”。
-
-“法人实体”是指提交贡献的机构及其“关联实体”。
-
-“关联实体”是指,对“本许可证”下的行为方而言,控制、受控制或与其共同受控制的机构,此处的控制是指有受控方或共同受控方至少50%直接或间接的投票权、资金或其他有价证券。
-
-1. 授予版权许可
-
-每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的版权许可,您可以复制、使用、修改、分发其“贡献”,不论修改与否。
-
-2. 授予专利许可
-
-每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的(根据本条规定撤销除外)专利许可,供您制造、委托制造、使用、许诺销售、销售、进口其“贡献”或以其他方式转移其“贡献”。前述专利许可仅限于“贡献者”现在或将来拥有或控制的其“贡献”本身或其“贡献”与许可“贡献”时的“软件”结合而将必然会侵犯的专利权利要求,不包括对“贡献”的修改或包含“贡献”的其他结合。如果您或您的“关联实体”直接或间接地,就“软件”或其中的“贡献”对任何人发起专利侵权诉讼(包括反诉或交叉诉讼)或其他专利维权行动,指控其侵犯专利权,则“本许可证”授予您对“软件”的专利许可自您提起诉讼或发起维权行动之日终止。
-
-3. 无商标许可
-
-“本许可证”不提供对“贡献者”的商品名称、商标、服务标志或产品名称的商标许可,但您为满足第4条规定的声明义务而必须使用除外。
-
-4. 分发限制
-
-您可以在任何媒介中将“软件”以源程序形式或可执行形式重新分发,不论修改与否,但您必须向接收者提供“本许可证”的副本,并保留“软件”中的版权、商标、专利及免责声明。
-
-5. 免责声明与责任限制
-
-“软件”及其中的“贡献”在提供时不带任何明示或默示的担保。在任何情况下,“贡献者”或版权所有者不对任何人因使用“软件”或其中的“贡献”而引发的任何直接或间接损失承担责任,不论因何种原因导致或者基于何种法律理论,即使其曾被建议有此种损失的可能性。
-
-6. 语言
-
-“本许可证”以中英文双语表述,中英文版本具有同等法律效力。如果中英文版本存在任何冲突不一致,以中文版为准。
-
-条款结束
-
-如何将木兰宽松许可证,第2版,应用到您的软件
-
-如果您希望将木兰宽松许可证,第2版,应用到您的新软件,为了方便接收者查阅,建议您完成如下三步:
-
-1,请您补充如下声明中的空白,包括软件名、软件的首次发表年份以及您作为版权人的名字;
-
-2,请您在软件包的一级目录下创建以“LICENSE”为名的文件,将整个许可证文本放入该文件中;
-
-3,请将如下声明文本放入每个源文件的头部注释中。
-
-Copyright (c) 2020 Luo Jia
-
-RustSBI is licensed under Mulan PSL v2. You can use this software according to the terms and conditions of the Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at: http://license.coscl.org.cn/MulanPSL2 
-
-THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. See the Mulan PSL v2 for more details.
-
-January 2020 http://license.coscl.org.cn/MulanPSL2
-
-Your reproduction, use, modification and distribution of the Software shall be subject to Mulan PSL v2 (this License) with the following terms and conditions:
-
-0. Definition
-
-Software means the program and related documents which are licensed under this License and comprise all Contribution(s).
-
-Contribution means the copyrightable work licensed by a particular Contributor under this License.
-
-Contributor means the Individual or Legal Entity who licenses its copyrightable work under this License.
-
-Legal Entity means the entity making a Contribution and all its Affiliates.
-
-Affiliates means entities that control, are controlled by, or are under common control with the acting entity under this License, ‘control’ means direct or indirect ownership of at least fifty percent (50%) of the voting power, capital or other securities of controlled or commonly controlled entity.
-
-1. Grant of Copyright License
-
-Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable copyright license to reproduce, use, modify, or distribute its Contribution, with modification or not.
-
-2. Grant of Patent License
-
-Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable (except for revocation under this Section) patent license to make, have made, use, offer for sale, sell, import or otherwise transfer its Contribution, where such patent license is only limited to the patent claims owned or controlled by such Contributor now or in future which will be necessarily infringed by its Contribution alone, or by combination of the Contribution with the Software to which the Contribution was contributed. The patent license shall not apply to any modification of the Contribution, and any other combination which includes the Contribution. If you or your Affiliates directly or indirectly institute patent litigation (including a cross claim or counterclaim in a litigation) or other patent enforcement activities against any individual or entity by alleging that the Software or any Contribution in it infringes patents, then any patent license granted to you under this License for the Software shall terminate as of the date such litigation or activity is filed or taken.
-
-3. No Trademark License
-
-No trademark license is granted to use the trade names, trademarks, service marks, or product names of Contributor, except as required to fulfill notice requirements in Section 4.
-
-4. Distribution Restriction
-
-You may distribute the Software in any medium with or without modification, whether in source or executable forms, provided that you provide recipients with a copy of this License and retain copyright, patent, trademark and disclaimer statements in the Software.
-
-5. Disclaimer of Warranty and Limitation of Liability
-
-THE SOFTWARE AND CONTRIBUTION IN IT ARE PROVIDED WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED. IN NO EVENT SHALL ANY CONTRIBUTOR OR COPYRIGHT HOLDER BE LIABLE TO YOU FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO ANY DIRECT, OR INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING FROM YOUR USE OR INABILITY TO USE THE SOFTWARE OR THE CONTRIBUTION IN IT, NO MATTER HOW IT’S CAUSED OR BASED ON WHICH LEGAL THEORY, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-6. Language
-
-THIS LICENSE IS WRITTEN IN BOTH CHINESE AND ENGLISH, AND THE CHINESE VERSION AND ENGLISH VERSION SHALL HAVE THE SAME LEGAL EFFECT. IN THE CASE OF DIVERGENCE BETWEEN THE CHINESE AND ENGLISH VERSIONS, THE CHINESE VERSION SHALL PREVAIL.
-
-END OF THE TERMS AND CONDITIONS

+ 71 - 0
library/sbi-spec/examples/custom-sbi-error.rs

@@ -0,0 +1,71 @@
+/// This example demonstrates how to define custom error types and map SBI error codes.
+///
+/// The code shows:
+/// - How to create a custom error enum that wraps standard SBI errors and adds custom error codes
+/// - Conversion logic between standard SBI errors and custom error types
+/// - Error handling patterns using Rust's Result type and error matching
+use sbi_spec::binary::{Error, SbiRet};
+
+/// Custom error type for SBI-related operations
+///
+/// Contains three possible variants:
+/// - Standard: Wraps standard SBI errors from the sbi_spec crate
+/// - MyErrorCode1/MyErrorCode2: Custom error codes specific to this implementation
+pub enum MyError {
+    /// Wrapper for standard SBI errors defined in sbi_spec
+    Standard(sbi_spec::binary::Error),
+    /// First custom error code (maps to value 1001)
+    MyErrorCode1,
+    /// Second custom error code (maps to value 1002)
+    MyErrorCode2,
+}
+
+/// Numeric value for first custom error code
+const MY_ERROR_CODE_1: usize = 1001;
+/// Numeric value for second custom error code
+const MY_ERROR_CODE_2: usize = 1002;
+
+/// Conversion implementation from standard SBI errors to our custom error type
+///
+/// This allows automatic conversion when using the ? operator in functions returning MyError.
+/// The conversion logic:
+/// - Maps specific custom error codes to their corresponding MyError variants
+/// - Wraps all other standard errors in the Standard variant
+impl From<sbi_spec::binary::Error> for MyError {
+    fn from(value: Error) -> Self {
+        match value {
+            // Handle custom error code mappings
+            Error::Custom(MY_ERROR_CODE_1) => MyError::MyErrorCode1,
+            Error::Custom(MY_ERROR_CODE_2) => MyError::MyErrorCode2,
+            // Wrap all other standard errors
+            _ => MyError::Standard(value),
+        }
+    }
+}
+
+/// Main function demonstrating error handling workflow
+///
+/// The execution flow:
+/// 1. Creates an SbiRet structure with success status
+/// 2. Manually sets an error code for demonstration
+/// 3. Converts the SbiRet to Result type with custom error mapping
+/// 4. Pattern matches on the result to handle different error cases
+fn main() {
+    // Create a base SbiRet with success status (value = 0)
+    let mut ret = SbiRet::success(0);
+    // Override the error code for demonstration purposes
+    ret.error = 1001;
+
+    // Convert SbiRet to Result, mapping error codes to MyError variants
+    let ans = ret.map_err(MyError::from);
+
+    // Handle different error cases through pattern matching
+    match ans {
+        Ok(_) => println!("Okay"),
+        // Match specific custom error codes
+        Err(MyError::MyErrorCode1) => println!("Custom error code 1"),
+        Err(MyError::MyErrorCode2) => println!("Custom error code 2"),
+        // Handle wrapped standard SBI errors
+        Err(MyError::Standard(err)) => println!("Standard error: {:?}", err),
+    }
+}

+ 9 - 1
library/sbi-spec/src/base.rs

@@ -1,6 +1,7 @@
 //! Chapter 4. Base Extension (EID #0x10).
 
 /// Extension ID for RISC-V SBI Base extension.
+#[doc(alias = "SBI_EXT_BASE")]
 pub const EID_BASE: usize = 0x10;
 pub use fid::*;
 
@@ -74,30 +75,37 @@ mod fid {
     /// Function ID to get the current SBI specification version.
     ///
     /// Declared in §4.1.
+    #[doc(alias = "SBI_EXT_BASE_GET_SPEC_VERSION")]
     pub const GET_SBI_SPEC_VERSION: usize = 0x0;
     /// Function ID to get the current SBI implementation ID.
     ///
     /// Declared in §4.2.
+    #[doc(alias = "SBI_EXT_BASE_GET_IMP_ID")]
     pub const GET_SBI_IMPL_ID: usize = 0x1;
     /// Function ID to get the current SBI implementation version.
     ///
     /// Declared in §4.3.
+    #[doc(alias = "SBI_EXT_BASE_GET_IMP_VERSION")]
     pub const GET_SBI_IMPL_VERSION: usize = 0x2;
     /// Function ID to probe information about one SBI extension from the current environment.
     ///
     /// Declared in §4.4.
+    #[doc(alias = "SBI_EXT_BASE_PROBE_EXT")]
     pub const PROBE_EXTENSION: usize = 0x3;
     /// Function ID to get the value of `mvendorid` register in the current environment.
     ///
     /// Declared in §4.5.
+    #[doc(alias = "SBI_EXT_BASE_GET_MVENDORID")]
     pub const GET_MVENDORID: usize = 0x4;
     /// Function ID to get the value of `marchid` register in the current environment.
     ///
     /// Declared in §4.6.
+    #[doc(alias = "SBI_EXT_BASE_GET_MARCHID")]
     pub const GET_MARCHID: usize = 0x5;
     /// Function ID to get the value of `mimpid` register in the current environment.
     ///
     /// Declared in §4.7.
+    #[doc(alias = "SBI_EXT_BASE_GET_MIMPID")]
     pub const GET_MIMPID: usize = 0x6;
 }
 
@@ -105,7 +113,7 @@ mod fid {
 ///
 /// Declared in §4.9.
 pub mod impl_id {
-    /// Berkley Bootloader.
+    /// Berkeley Bootloader.
     pub const BBL: usize = 0;
     /// OpenSBI.
     pub const OPEN_SBI: usize = 1;

+ 20 - 1893
library/sbi-spec/src/binary.rs

@@ -1,1895 +1,22 @@
 //! Chapter 3. Binary Encoding.
 
-use core::marker::PhantomData;
-
-/// SBI functions return type.
-///
-/// > SBI functions must return a pair of values in a0 and a1,
-/// > with a0 returning an error code.
-/// > This is analogous to returning the C structure `SbiRet`.
-///
-/// Note: if this structure is used in function return on conventional
-/// Rust code, it would not require pinning memory representation as
-/// extern C. The `repr(C)` is set in case that some users want to use
-/// this structure in FFI code.
-#[derive(Clone, Copy, PartialEq, Eq)]
-#[repr(C)]
-pub struct SbiRet<T = usize> {
-    /// Error number.
-    pub error: T,
-    /// Result value.
-    pub value: T,
-}
-
-/// SBI success state return value.
-pub const RET_SUCCESS: usize = <usize as SbiRegister>::RET_SUCCESS;
-/// Error for SBI call failed for unknown reasons.
-pub const RET_ERR_FAILED: usize = <usize as SbiRegister>::RET_ERR_FAILED;
-/// Error for target operation not supported.
-pub const RET_ERR_NOT_SUPPORTED: usize = <usize as SbiRegister>::RET_ERR_NOT_SUPPORTED;
-/// Error for invalid parameter.
-pub const RET_ERR_INVALID_PARAM: usize = <usize as SbiRegister>::RET_ERR_INVALID_PARAM;
-/// Error for denied.
-pub const RET_ERR_DENIED: usize = <usize as SbiRegister>::RET_ERR_DENIED;
-/// Error for invalid address.
-pub const RET_ERR_INVALID_ADDRESS: usize = <usize as SbiRegister>::RET_ERR_INVALID_ADDRESS;
-/// Error for resource already available.
-pub const RET_ERR_ALREADY_AVAILABLE: usize = <usize as SbiRegister>::RET_ERR_ALREADY_AVAILABLE;
-/// Error for resource already started.
-pub const RET_ERR_ALREADY_STARTED: usize = <usize as SbiRegister>::RET_ERR_ALREADY_STARTED;
-/// Error for resource already stopped.
-pub const RET_ERR_ALREADY_STOPPED: usize = <usize as SbiRegister>::RET_ERR_ALREADY_STOPPED;
-/// Error for shared memory not available.
-pub const RET_ERR_NO_SHMEM: usize = <usize as SbiRegister>::RET_ERR_NO_SHMEM;
-/// Error for invalid state.
-pub const RET_ERR_INVALID_STATE: usize = <usize as SbiRegister>::RET_ERR_INVALID_STATE;
-/// Error for bad or invalid range.
-pub const RET_ERR_BAD_RANGE: usize = <usize as SbiRegister>::RET_ERR_BAD_RANGE;
-/// Error for failed due to timeout.
-pub const RET_ERR_TIMEOUT: usize = <usize as SbiRegister>::RET_ERR_TIMEOUT;
-/// Error for input or output error.
-pub const RET_ERR_IO: usize = <usize as SbiRegister>::RET_ERR_IO;
-
-/// Data type of register that can be passed to the RISC-V SBI ABI.
-///
-/// This trait defines the requirements for types that are used as the underlying
-/// representation for both the `value` and `error` fields in the `SbiRet` structure.
-/// In most cases, this trait is implemented for primitive integer types (e.g., `usize`),
-/// but it can also be implemented for other types that satisfy the constraints.
-///
-/// # Examples
-///
-/// Implemented automatically for all types that satisfy `Copy`, `Eq`, and `Debug`.
-pub trait SbiRegister: Copy + Eq + Ord + core::fmt::Debug {
-    /// SBI success state return value.
-    const RET_SUCCESS: Self;
-    /// Error for SBI call failed for unknown reasons.
-    const RET_ERR_FAILED: Self;
-    /// Error for target operation not supported.
-    const RET_ERR_NOT_SUPPORTED: Self;
-    /// Error for invalid parameter.
-    const RET_ERR_INVALID_PARAM: Self;
-    /// Error for denied.
-    const RET_ERR_DENIED: Self;
-    /// Error for invalid address.
-    const RET_ERR_INVALID_ADDRESS: Self;
-    /// Error for resource already available.
-    const RET_ERR_ALREADY_AVAILABLE: Self;
-    /// Error for resource already started.
-    const RET_ERR_ALREADY_STARTED: Self;
-    /// Error for resource already stopped.
-    const RET_ERR_ALREADY_STOPPED: Self;
-    /// Error for shared memory not available.
-    const RET_ERR_NO_SHMEM: Self;
-    /// Error for invalid state.
-    const RET_ERR_INVALID_STATE: Self;
-    /// Error for bad or invalid range.
-    const RET_ERR_BAD_RANGE: Self;
-    /// Error for failed due to timeout.
-    const RET_ERR_TIMEOUT: Self;
-    /// Error for input or output error.
-    const RET_ERR_IO: Self;
-
-    /// Zero value for this type; this is used on `value` fields once `SbiRet` returns an error.
-    const ZERO: Self;
-    /// Full-ones value for this type; this is used on SBI mask structures like `CounterMask`
-    /// and `HartMask`.
-    const FULL_MASK: Self;
-
-    /// Converts an `SbiRet` of this type to a `Result` of self and `Error`.
-    fn into_result(ret: SbiRet<Self>) -> Result<Self, Error<Self>>;
-}
-
-macro_rules! impl_sbi_register {
-    ($ty:ty, $signed:ty) => {
-        impl SbiRegister for $ty {
-            const RET_SUCCESS: Self = 0;
-            const RET_ERR_FAILED: Self = -1 as $signed as $ty;
-            const RET_ERR_NOT_SUPPORTED: Self = -2 as $signed as $ty;
-            const RET_ERR_INVALID_PARAM: Self = -3 as $signed as $ty;
-            const RET_ERR_DENIED: Self = -4 as $signed as $ty;
-            const RET_ERR_INVALID_ADDRESS: Self = -5 as $signed as $ty;
-            const RET_ERR_ALREADY_AVAILABLE: Self = -6 as $signed as $ty;
-            const RET_ERR_ALREADY_STARTED: Self = -7 as $signed as $ty;
-            const RET_ERR_ALREADY_STOPPED: Self = -8 as $signed as $ty;
-            const RET_ERR_NO_SHMEM: Self = -9 as $signed as $ty;
-            const RET_ERR_INVALID_STATE: Self = -10 as $signed as $ty;
-            const RET_ERR_BAD_RANGE: Self = -11 as $signed as $ty;
-            const RET_ERR_TIMEOUT: Self = -12 as $signed as $ty;
-            const RET_ERR_IO: Self = -13 as $signed as $ty;
-            const ZERO: Self = 0;
-            const FULL_MASK: Self = !0;
-
-            fn into_result(ret: SbiRet<Self>) -> Result<Self, Error<Self>> {
-                match ret.error {
-                    Self::RET_SUCCESS => Ok(ret.value),
-                    Self::RET_ERR_FAILED => Err(Error::Failed),
-                    Self::RET_ERR_NOT_SUPPORTED => Err(Error::NotSupported),
-                    Self::RET_ERR_INVALID_PARAM => Err(Error::InvalidParam),
-                    Self::RET_ERR_DENIED => Err(Error::Denied),
-                    Self::RET_ERR_INVALID_ADDRESS => Err(Error::InvalidAddress),
-                    Self::RET_ERR_ALREADY_AVAILABLE => Err(Error::AlreadyAvailable),
-                    Self::RET_ERR_ALREADY_STARTED => Err(Error::AlreadyStarted),
-                    Self::RET_ERR_ALREADY_STOPPED => Err(Error::AlreadyStopped),
-                    Self::RET_ERR_NO_SHMEM => Err(Error::NoShmem),
-                    Self::RET_ERR_INVALID_STATE => Err(Error::InvalidState),
-                    Self::RET_ERR_BAD_RANGE => Err(Error::BadRange),
-                    Self::RET_ERR_TIMEOUT => Err(Error::Timeout),
-                    Self::RET_ERR_IO => Err(Error::Io),
-                    unknown => Err(Error::Custom(unknown as _)),
-                }
-            }
-        }
-    };
-}
-
-impl_sbi_register!(usize, isize);
-impl_sbi_register!(isize, isize);
-impl_sbi_register!(u32, i32);
-impl_sbi_register!(i32, i32);
-impl_sbi_register!(u64, i64);
-impl_sbi_register!(i64, i64);
-impl_sbi_register!(u128, i128);
-impl_sbi_register!(i128, i128);
-
-impl<T: SbiRegister + core::fmt::LowerHex> core::fmt::Debug for SbiRet<T> {
-    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
-        match T::into_result(*self) {
-            Ok(value) => write!(f, "{:?}", value),
-            Err(err) => match err {
-                Error::Failed => write!(f, "<SBI call failed>"),
-                Error::NotSupported => write!(f, "<SBI feature not supported>"),
-                Error::InvalidParam => write!(f, "<SBI invalid parameter>"),
-                Error::Denied => write!(f, "<SBI denied>"),
-                Error::InvalidAddress => write!(f, "<SBI invalid address>"),
-                Error::AlreadyAvailable => write!(f, "<SBI already available>"),
-                Error::AlreadyStarted => write!(f, "<SBI already started>"),
-                Error::AlreadyStopped => write!(f, "<SBI already stopped>"),
-                Error::NoShmem => write!(f, "<SBI shared memory not available>"),
-                Error::InvalidState => write!(f, "<SBI invalid state>"),
-                Error::BadRange => write!(f, "<SBI bad range>"),
-                Error::Timeout => write!(f, "<SBI timeout>"),
-                Error::Io => write!(f, "<SBI input/output error>"),
-                Error::Custom(unknown) => write!(f, "[SBI Unknown error: {:#x}]", unknown),
-            },
-        }
-    }
-}
-
-/// RISC-V SBI error in enumeration.
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum Error<T = usize> {
-    /// Error for SBI call failed for unknown reasons.
-    Failed,
-    /// Error for target operation not supported.
-    NotSupported,
-    /// Error for invalid parameter.
-    InvalidParam,
-    /// Error for denied.
-    Denied,
-    /// Error for invalid address.
-    InvalidAddress,
-    /// Error for resource already available.
-    AlreadyAvailable,
-    /// Error for resource already started.
-    AlreadyStarted,
-    /// Error for resource already stopped.
-    AlreadyStopped,
-    /// Error for shared memory not available.
-    NoShmem,
-    /// Error for invalid state.
-    InvalidState,
-    /// Error for bad or invalid range.
-    BadRange,
-    /// Error for failed due to timeout.
-    Timeout,
-    /// Error for input or output error.
-    Io,
-    /// Custom error code.
-    Custom(T),
-}
-
-impl<T: SbiRegister> SbiRet<T> {
-    /// Returns success SBI state with given `value`.
-    #[inline]
-    pub const fn success(value: T) -> Self {
-        Self {
-            error: T::RET_SUCCESS,
-            value,
-        }
-    }
-
-    /// The SBI call request failed for unknown reasons.
-    #[inline]
-    pub const fn failed() -> Self {
-        Self {
-            error: T::RET_ERR_FAILED,
-            value: T::ZERO,
-        }
-    }
-
-    /// SBI call failed due to not supported by target ISA,
-    /// operation type not supported,
-    /// or target operation type not implemented on purpose.
-    #[inline]
-    pub const fn not_supported() -> Self {
-        Self {
-            error: T::RET_ERR_NOT_SUPPORTED,
-            value: T::ZERO,
-        }
-    }
-
-    /// SBI call failed due to invalid hart mask parameter,
-    /// invalid target hart id,
-    /// invalid operation type,
-    /// or invalid resource index.
-    #[inline]
-    pub const fn invalid_param() -> Self {
-        Self {
-            error: T::RET_ERR_INVALID_PARAM,
-            value: T::ZERO,
-        }
-    }
-    /// SBI call denied for unsatisfied entry criteria, or insufficient access
-    /// permission to debug console or CPPC register.
-    #[inline]
-    pub const fn denied() -> Self {
-        Self {
-            error: T::RET_ERR_DENIED,
-            value: T::ZERO,
-        }
-    }
-
-    /// SBI call failed for invalid mask start address,
-    /// not a valid physical address parameter,
-    /// or the target address is prohibited by PMP to run in supervisor mode.
-    #[inline]
-    pub const fn invalid_address() -> Self {
-        Self {
-            error: T::RET_ERR_INVALID_ADDRESS,
-            value: T::ZERO,
-        }
-    }
-
-    /// SBI call failed for the target resource is already available,
-    /// e.g., the target hart is already started when caller still requests it to start.
-    #[inline]
-    pub const fn already_available() -> Self {
-        Self {
-            error: T::RET_ERR_ALREADY_AVAILABLE,
-            value: T::ZERO,
-        }
-    }
-
-    /// SBI call failed for the target resource is already started,
-    /// e.g., target performance counter is started.
-    #[inline]
-    pub const fn already_started() -> Self {
-        Self {
-            error: T::RET_ERR_ALREADY_STARTED,
-            value: T::ZERO,
-        }
-    }
-
-    /// SBI call failed for the target resource is already stopped,
-    /// e.g., target performance counter is stopped.
-    #[inline]
-    pub const fn already_stopped() -> Self {
-        Self {
-            error: T::RET_ERR_ALREADY_STOPPED,
-            value: T::ZERO,
-        }
-    }
-
-    /// SBI call failed for shared memory is not available,
-    /// e.g. nested acceleration shared memory is not available.
-    #[inline]
-    pub const fn no_shmem() -> Self {
-        Self {
-            error: T::RET_ERR_NO_SHMEM,
-            value: T::ZERO,
-        }
-    }
-
-    /// SBI call failed for invalid state,
-    /// e.g. register a software event but the event is not in unused state.
-    #[inline]
-    pub const fn invalid_state() -> Self {
-        Self {
-            error: T::RET_ERR_INVALID_STATE,
-            value: T::ZERO,
-        }
-    }
-
-    /// SBI call failed for bad or invalid range,
-    /// e.g. the software event is not exist in the specified range.
-    #[inline]
-    pub const fn bad_range() -> Self {
-        Self {
-            error: T::RET_ERR_BAD_RANGE,
-            value: T::ZERO,
-        }
-    }
-
-    /// SBI call failed for timeout,
-    /// e.g. message send timeout.
-    #[inline]
-    pub const fn timeout() -> Self {
-        Self {
-            error: T::RET_ERR_TIMEOUT,
-            value: T::ZERO,
-        }
-    }
-
-    /// SBI call failed for input or output error.
-    #[inline]
-    pub const fn io() -> Self {
-        Self {
-            error: T::RET_ERR_IO,
-            value: T::ZERO,
-        }
-    }
-}
-
-impl<T: SbiRegister> From<Error<T>> for SbiRet<T> {
-    #[inline]
-    fn from(value: Error<T>) -> Self {
-        match value {
-            Error::Failed => SbiRet::failed(),
-            Error::NotSupported => SbiRet::not_supported(),
-            Error::InvalidParam => SbiRet::invalid_param(),
-            Error::Denied => SbiRet::denied(),
-            Error::InvalidAddress => SbiRet::invalid_address(),
-            Error::AlreadyAvailable => SbiRet::already_available(),
-            Error::AlreadyStarted => SbiRet::already_started(),
-            Error::AlreadyStopped => SbiRet::already_stopped(),
-            Error::NoShmem => SbiRet::no_shmem(),
-            Error::InvalidState => SbiRet::invalid_state(),
-            Error::BadRange => SbiRet::bad_range(),
-            Error::Timeout => SbiRet::timeout(),
-            Error::Io => SbiRet::io(),
-            Error::Custom(error) => SbiRet {
-                error,
-                value: T::ZERO,
-            },
-        }
-    }
-}
-
-impl SbiRet {
-    /// Converts to a [`Result`] of value and error.
-    #[inline]
-    pub const fn into_result(self) -> Result<usize, Error> {
-        match self.error {
-            RET_SUCCESS => Ok(self.value),
-            RET_ERR_FAILED => Err(Error::Failed),
-            RET_ERR_NOT_SUPPORTED => Err(Error::NotSupported),
-            RET_ERR_INVALID_PARAM => Err(Error::InvalidParam),
-            RET_ERR_DENIED => Err(Error::Denied),
-            RET_ERR_INVALID_ADDRESS => Err(Error::InvalidAddress),
-            RET_ERR_ALREADY_AVAILABLE => Err(Error::AlreadyAvailable),
-            RET_ERR_ALREADY_STARTED => Err(Error::AlreadyStarted),
-            RET_ERR_ALREADY_STOPPED => Err(Error::AlreadyStopped),
-            RET_ERR_NO_SHMEM => Err(Error::NoShmem),
-            RET_ERR_INVALID_STATE => Err(Error::InvalidState),
-            RET_ERR_BAD_RANGE => Err(Error::BadRange),
-            RET_ERR_TIMEOUT => Err(Error::Timeout),
-            RET_ERR_IO => Err(Error::Io),
-            unknown => Err(Error::Custom(unknown as _)),
-        }
-    }
-
-    /// Returns `true` if current SBI return succeeded.
-    ///
-    /// # Examples
-    ///
-    /// Basic usage:
-    ///
-    /// ```
-    /// # use sbi_spec::binary::SbiRet;
-    /// let x = SbiRet::success(0);
-    /// assert_eq!(x.is_ok(), true);
-    ///
-    /// let x = SbiRet::failed();
-    /// assert_eq!(x.is_ok(), false);
-    /// ```
-    #[must_use = "if you intended to assert that this is ok, consider `.unwrap()` instead"]
-    #[inline]
-    pub const fn is_ok(&self) -> bool {
-        matches!(self.error, RET_SUCCESS)
-    }
-
-    /// Returns `true` if the SBI call succeeded and the value inside of it matches a predicate.
-    ///
-    /// # Examples
-    ///
-    /// Basic usage:
-    ///
-    /// ```
-    /// # use sbi_spec::binary::SbiRet;
-    /// let x = SbiRet::success(2);
-    /// assert_eq!(x.is_ok_and(|x| x > 1), true);
-    ///
-    /// let x = SbiRet::success(0);
-    /// assert_eq!(x.is_ok_and(|x| x > 1), false);
-    ///
-    /// let x = SbiRet::no_shmem();
-    /// assert_eq!(x.is_ok_and(|x| x > 1), false);
-    /// ```
-    #[must_use]
-    #[inline]
-    pub fn is_ok_and(self, f: impl FnOnce(usize) -> bool) -> bool {
-        self.into_result().is_ok_and(f)
-    }
-
-    /// Returns `true` if current SBI return is an error.
-    ///
-    /// # Examples
-    ///
-    /// Basic usage:
-    ///
-    /// ```
-    /// # use sbi_spec::binary::SbiRet;
-    /// let x = SbiRet::success(0);
-    /// assert_eq!(x.is_err(), false);
-    ///
-    /// let x = SbiRet::not_supported();
-    /// assert_eq!(x.is_err(), true);
-    /// ```
-    #[must_use = "if you intended to assert that this is err, consider `.unwrap_err()` instead"]
-    #[inline]
-    pub const fn is_err(&self) -> bool {
-        !self.is_ok()
-    }
-
-    /// Returns `true` if the result is an error and the value inside of it matches a predicate.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use sbi_spec::binary::{SbiRet, Error};
-    /// let x = SbiRet::denied();
-    /// assert_eq!(x.is_err_and(|x| x == Error::Denied), true);
-    ///
-    /// let x = SbiRet::invalid_address();
-    /// assert_eq!(x.is_err_and(|x| x == Error::Denied), false);
-    ///
-    /// let x = SbiRet::success(0);
-    /// assert_eq!(x.is_err_and(|x| x == Error::Denied), false);
-    /// ```
-    #[must_use]
-    #[inline]
-    pub fn is_err_and(self, f: impl FnOnce(Error) -> bool) -> bool {
-        self.into_result().is_err_and(f)
-    }
-
-    /// Converts from `SbiRet` to [`Option<usize>`].
-    ///
-    /// Converts `self` into an [`Option<usize>`], consuming `self`,
-    /// and discarding the error, if any.
-    ///
-    /// # Examples
-    ///
-    /// Basic usage:
-    ///
-    /// ```
-    /// # use sbi_spec::binary::SbiRet;
-    /// let x = SbiRet::success(2);
-    /// assert_eq!(x.ok(), Some(2));
-    ///
-    /// let x = SbiRet::invalid_param();
-    /// assert_eq!(x.ok(), None);
-    /// ```
-    // fixme: should be pub const fn once this function in Result is stablized in constant
-    #[inline]
-    pub fn ok(self) -> Option<usize> {
-        self.into_result().ok()
-    }
-
-    /// Converts from `SbiRet` to [`Option<Error>`].
-    ///
-    /// Converts `self` into an [`Option<Error>`], consuming `self`,
-    /// and discarding the success value, if any.
-    ///
-    /// # Examples
-    ///
-    /// Basic usage:
-    ///
-    /// ```
-    /// # use sbi_spec::binary::{SbiRet, Error};
-    /// let x = SbiRet::success(2);
-    /// assert_eq!(x.err(), None);
-    ///
-    /// let x = SbiRet::denied();
-    /// assert_eq!(x.err(), Some(Error::Denied));
-    /// ```
-    // fixme: should be pub const fn once this function in Result is stablized in constant
-    #[inline]
-    pub fn err(self) -> Option<Error> {
-        self.into_result().err()
-    }
-
-    /// Maps a `SbiRet` to `Result<U, Error>` by applying a function to a
-    /// contained success value, leaving an error value untouched.
-    ///
-    /// This function can be used to compose the results of two functions.
-    ///
-    /// # Examples
-    ///
-    /// Gets detail of a PMU counter and judge if it is a firmware counter.
-    ///
-    /// ```
-    /// # use sbi_spec::binary::SbiRet;
-    /// # use core::mem::size_of;
-    /// # mod sbi_rt {
-    /// #     use sbi_spec::binary::SbiRet;
-    /// #     const TYPE_MASK: usize = 1 << (core::mem::size_of::<usize>() - 1);
-    /// #     pub fn pmu_counter_get_info(_: usize) -> SbiRet { SbiRet::success(TYPE_MASK) }
-    /// # }
-    /// // We assume that counter index 42 is a firmware counter.
-    /// let counter_idx = 42;
-    /// // Masks PMU counter type by setting highest bit in `usize`.
-    /// const TYPE_MASK: usize = 1 << (size_of::<usize>() - 1);
-    /// // Highest bit of returned `counter_info` represents whether it's
-    /// // a firmware counter or a hardware counter.
-    /// let is_firmware_counter = sbi_rt::pmu_counter_get_info(counter_idx)
-    ///     .map(|counter_info| counter_info & TYPE_MASK != 0);
-    /// // If that bit is set, it is a firmware counter.
-    /// assert_eq!(is_firmware_counter, Ok(true));
-    /// ```
-    #[inline]
-    pub fn map<U, F: FnOnce(usize) -> U>(self, op: F) -> Result<U, Error> {
-        self.into_result().map(op)
-    }
-
-    /// Returns the provided default (if error),
-    /// or applies a function to the contained value (if success).
-    ///
-    /// Arguments passed to `map_or` are eagerly evaluated;
-    /// if you are passing the result of a function call,
-    /// it is recommended to use [`map_or_else`],
-    /// which is lazily evaluated.
-    ///
-    /// [`map_or_else`]: SbiRet::map_or_else
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use sbi_spec::binary::SbiRet;
-    /// let x = SbiRet::success(3);
-    /// assert_eq!(x.map_or(42, |v| v & 0b1), 1);
-    ///
-    /// let x = SbiRet::invalid_address();
-    /// assert_eq!(x.map_or(42, |v| v & 0b1), 42);
-    /// ```
-    #[inline]
-    pub fn map_or<U, F: FnOnce(usize) -> U>(self, default: U, f: F) -> U {
-        self.into_result().map_or(default, f)
-    }
-
-    /// Maps a `SbiRet` to `usize` value by applying fallback function `default` to
-    /// a contained error, or function `f` to a contained success value.
-    ///
-    /// This function can be used to unpack a successful result
-    /// while handling an error.
-    ///
-    /// # Examples
-    ///
-    /// Basic usage:
-    ///
-    /// ```
-    /// # use sbi_spec::binary::SbiRet;
-    /// let k = 21;
-    ///
-    /// let x = SbiRet::success(3);
-    /// assert_eq!(x.map_or_else(|e| k * 2, |v| v & 0b1), 1);
-    ///
-    /// let x = SbiRet::already_available();
-    /// assert_eq!(x.map_or_else(|e| k * 2, |v| v & 0b1), 42);
-    /// ```
-    #[inline]
-    pub fn map_or_else<U, D: FnOnce(Error) -> U, F: FnOnce(usize) -> U>(
-        self,
-        default: D,
-        f: F,
-    ) -> U {
-        self.into_result().map_or_else(default, f)
-    }
-
-    /// Maps a `SbiRet` to `Result<T, F>` by applying a function to a
-    /// contained error as [`Error`] struct, leaving success value untouched.
-    ///
-    /// This function can be used to pass through a successful result while handling
-    /// an error.
-    ///
-    /// # Examples
-    ///
-    /// Basic usage:
-    ///
-    /// ```
-    /// # use sbi_spec::binary::{SbiRet, Error};
-    /// fn stringify(x: Error) -> String {
-    ///     if x == Error::AlreadyStarted {
-    ///         "error: already started!".to_string()
-    ///     } else {
-    ///         "error: other error!".to_string()
-    ///     }
-    /// }
-    ///
-    /// let x = SbiRet::success(2);
-    /// assert_eq!(x.map_err(stringify), Ok(2));
-    ///
-    /// let x = SbiRet::already_started();
-    /// assert_eq!(x.map_err(stringify), Err("error: already started!".to_string()));
-    /// ```
-    #[inline]
-    pub fn map_err<F, O: FnOnce(Error) -> F>(self, op: O) -> Result<usize, F> {
-        self.into_result().map_err(op)
-    }
-
-    /// Calls a function with a reference to the contained value if current SBI call succeeded.
-    ///
-    /// Returns the original result.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use sbi_spec::binary::SbiRet;
-    /// // Assume that SBI debug console have read 512 bytes into a buffer.
-    /// let ret = SbiRet::success(512);
-    /// // Inspect the SBI DBCN call result.
-    /// let idx = ret
-    ///     .inspect(|x| println!("bytes written: {x}"))
-    ///     .map(|x| x - 1)
-    ///     .expect("SBI DBCN call failed");
-    /// assert_eq!(idx, 511);
-    /// ```
-    #[inline]
-    pub fn inspect<F: FnOnce(&usize)>(self, f: F) -> Self {
-        if let Ok(ref t) = self.into_result() {
-            f(t);
-        }
-
-        self
-    }
-
-    /// Calls a function with a reference to the contained value if current SBI result is an error.
-    ///
-    /// Returns the original result.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use sbi_spec::binary::SbiRet;
-    /// // Assume that SBI debug console write operation failed for invalid parameter.
-    /// let ret = SbiRet::invalid_param();
-    /// // Print the error if SBI DBCN call failed.
-    /// let ret = ret.inspect_err(|e| eprintln!("failed to read from SBI console: {e:?}"));
-    /// ```
-    #[inline]
-    pub fn inspect_err<F: FnOnce(&Error)>(self, f: F) -> Self {
-        if let Err(ref e) = self.into_result() {
-            f(e);
-        }
-
-        self
-    }
-
-    // TODO: pub fn iter(&self) -> Iter
-    // TODO: pub fn iter_mut(&mut self) -> IterMut
-
-    /// Returns the contained success value, consuming the `self` value.
-    ///
-    /// # Panics
-    ///
-    /// Panics if self is an SBI error with a panic message including the
-    /// passed message, and the content of the SBI state.
-    ///
-    /// # Examples
-    ///
-    /// Basic usage:
-    ///
-    /// ```should_panic
-    /// # use sbi_spec::binary::SbiRet;
-    /// let x = SbiRet::already_stopped();
-    /// x.expect("Testing expect"); // panics with `Testing expect`
-    /// ```
-    #[inline]
-    pub fn expect(self, msg: &str) -> usize {
-        self.into_result().expect(msg)
-    }
-
-    /// Returns the contained success value, consuming the `self` value.
-    ///
-    /// # Panics
-    ///
-    /// Panics if self is an SBI error, with a panic message provided by the
-    /// SBI error converted into [`Error`] struct.
-    ///
-    /// # Examples
-    ///
-    /// Basic usage:
-    ///
-    /// ```
-    /// # use sbi_spec::binary::SbiRet;
-    /// let x = SbiRet::success(2);
-    /// assert_eq!(x.unwrap(), 2);
-    /// ```
-    ///
-    /// ```should_panic
-    /// # use sbi_spec::binary::SbiRet;
-    /// let x = SbiRet::failed();
-    /// x.unwrap(); // panics
-    /// ```
-    #[inline]
-    pub fn unwrap(self) -> usize {
-        self.into_result().unwrap()
-    }
-
-    // Note: No unwrap_or_default as we cannot determine a meaningful default value for a successful SbiRet.
-
-    /// Returns the contained error as [`Error`] struct, consuming the `self` value.
-    ///
-    /// # Panics
-    ///
-    /// Panics if the self is SBI success value, with a panic message
-    /// including the passed message, and the content of the success value.
-    ///
-    /// # Examples
-    ///
-    /// Basic usage:
-    ///
-    /// ```should_panic
-    /// # use sbi_spec::binary::SbiRet;
-    /// let x = SbiRet::success(10);
-    /// x.expect_err("Testing expect_err"); // panics with `Testing expect_err`
-    /// ```
-    #[inline]
-    pub fn expect_err(self, msg: &str) -> Error {
-        self.into_result().expect_err(msg)
-    }
-
-    /// Returns the contained error as [`Error`] struct, consuming the `self` value.
-    ///
-    /// # Panics
-    ///
-    /// Panics if the self is SBI success value, with a custom panic message provided
-    /// by the success value.
-    ///
-    /// # Examples
-    ///
-    /// ```should_panic
-    /// # use sbi_spec::binary::SbiRet;
-    /// let x = SbiRet::success(2);
-    /// x.unwrap_err(); // panics with `2`
-    /// ```
-    ///
-    /// ```
-    /// # use sbi_spec::binary::{SbiRet, Error};
-    /// let x = SbiRet::not_supported();
-    /// assert_eq!(x.unwrap_err(), Error::NotSupported);
-    /// ```
-    #[inline]
-    pub fn unwrap_err(self) -> Error {
-        self.into_result().unwrap_err()
-    }
-
-    // TODO: pub fn into_ok(self) -> usize and pub fn into_err(self) -> Error
-    // once `unwrap_infallible` is stablized
-
-    /// Returns `res` if self is success value, otherwise otherwise returns the contained error
-    /// of `self` as [`Error`] struct.
-    ///
-    /// Arguments passed to `and` are eagerly evaluated; if you are passing the
-    /// result of a function call, it is recommended to use [`and_then`], which is
-    /// lazily evaluated.
-    ///
-    /// [`and_then`]: SbiRet::and_then
-    ///
-    /// # Examples
-    ///
-    /// Basic usage:
-    ///
-    /// ```
-    /// # use sbi_spec::binary::{SbiRet, Error};
-    /// let x = SbiRet::success(2);
-    /// let y = SbiRet::invalid_param().into_result();
-    /// assert_eq!(x.and(y), Err(Error::InvalidParam));
-    ///
-    /// let x = SbiRet::denied();
-    /// let y = SbiRet::success(3).into_result();
-    /// assert_eq!(x.and(y), Err(Error::Denied));
-    ///
-    /// let x = SbiRet::invalid_address();
-    /// let y = SbiRet::already_available().into_result();
-    /// assert_eq!(x.and(y), Err(Error::InvalidAddress));
-    ///
-    /// let x = SbiRet::success(4);
-    /// let y = SbiRet::success(5).into_result();
-    /// assert_eq!(x.and(y), Ok(5));
-    /// ```
-    // fixme: should be pub const fn once this function in Result is stablized in constant
-    // fixme: should parameter be `res: SbiRet`?
-    #[inline]
-    pub fn and<U>(self, res: Result<U, Error>) -> Result<U, Error> {
-        self.into_result().and(res)
-    }
-
-    /// Calls `op` if self is success value, otherwise returns the contained error
-    /// as [`Error`] struct.
-    ///
-    /// This function can be used for control flow based on `SbiRet` values.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use sbi_spec::binary::{SbiRet, Error};
-    /// fn sq_then_to_string(x: usize) -> Result<String, Error> {
-    ///     x.checked_mul(x).map(|sq| sq.to_string()).ok_or(Error::Failed)
-    /// }
-    ///
-    /// assert_eq!(SbiRet::success(2).and_then(sq_then_to_string), Ok(4.to_string()));
-    /// assert_eq!(SbiRet::success(1_000_000_000_000).and_then(sq_then_to_string), Err(Error::Failed));
-    /// assert_eq!(SbiRet::invalid_param().and_then(sq_then_to_string), Err(Error::InvalidParam));
-    /// ```
-    #[inline]
-    pub fn and_then<U, F: FnOnce(usize) -> Result<U, Error>>(self, op: F) -> Result<U, Error> {
-        self.into_result().and_then(op)
-    }
-
-    /// Returns `res` if self is SBI error, otherwise returns the success value of `self`.
-    ///
-    /// Arguments passed to `or` are eagerly evaluated; if you are passing the
-    /// result of a function call, it is recommended to use [`or_else`], which is
-    /// lazily evaluated.
-    ///
-    /// [`or_else`]: Result::or_else
-    ///
-    /// # Examples
-    ///
-    /// Basic usage:
-    ///
-    /// ```
-    /// # use sbi_spec::binary::{SbiRet, Error};
-    /// let x = SbiRet::success(2);
-    /// let y = SbiRet::invalid_param().into_result();
-    /// assert_eq!(x.or(y), Ok(2));
-    ///
-    /// let x = SbiRet::denied();
-    /// let y = SbiRet::success(3).into_result();
-    /// assert_eq!(x.or(y), Ok(3));
-    ///
-    /// let x = SbiRet::invalid_address();
-    /// let y = SbiRet::already_available().into_result();
-    /// assert_eq!(x.or(y), Err(Error::AlreadyAvailable));
-    ///
-    /// let x = SbiRet::success(4);
-    /// let y = SbiRet::success(100).into_result();
-    /// assert_eq!(x.or(y), Ok(4));
-    /// ```
-    // fixme: should be pub const fn once this function in Result is stablized in constant
-    // fixme: should parameter be `res: SbiRet`?
-    #[inline]
-    pub fn or<F>(self, res: Result<usize, F>) -> Result<usize, F> {
-        self.into_result().or(res)
-    }
-
-    /// Calls `op` if self is SBI error, otherwise returns the success value of `self`.
-    ///
-    /// This function can be used for control flow based on result values.
-    ///
-    ///
-    /// # Examples
-    ///
-    /// Basic usage:
-    ///
-    /// ```
-    /// # use sbi_spec::binary::{SbiRet, Error};
-    /// fn is_failed(x: Error) -> Result<usize, bool> { Err(x == Error::Failed) }
-    ///
-    /// assert_eq!(SbiRet::success(2).or_else(is_failed), Ok(2));
-    /// assert_eq!(SbiRet::failed().or_else(is_failed), Err(true));
-    /// ```
-    #[inline]
-    pub fn or_else<F, O: FnOnce(Error) -> Result<usize, F>>(self, op: O) -> Result<usize, F> {
-        self.into_result().or_else(op)
-    }
-
-    /// Returns the contained success value or a provided default.
-    ///
-    /// Arguments passed to `unwrap_or` are eagerly evaluated; if you are passing
-    /// the result of a function call, it is recommended to use [`unwrap_or_else`],
-    /// which is lazily evaluated.
-    ///
-    /// [`unwrap_or_else`]: SbiRet::unwrap_or_else
-    ///
-    /// # Examples
-    ///
-    /// Basic usage:
-    ///
-    /// ```
-    /// # use sbi_spec::binary::SbiRet;
-    /// let default = 2;
-    /// let x = SbiRet::success(9);
-    /// assert_eq!(x.unwrap_or(default), 9);
-    ///
-    /// let x = SbiRet::invalid_param();
-    /// assert_eq!(x.unwrap_or(default), default);
-    /// ```
-    // fixme: should be pub const fn once this function in Result is stablized in constant
-    #[inline]
-    pub fn unwrap_or(self, default: usize) -> usize {
-        self.into_result().unwrap_or(default)
-    }
-
-    /// Returns the contained success value or computes it from a closure.
-    ///
-    /// # Examples
-    ///
-    /// Basic usage:
-    ///
-    /// ```
-    /// # use sbi_spec::binary::{SbiRet, Error};
-    /// fn invalid_use_zero(x: Error) -> usize { if x == Error::InvalidParam { 0 } else { 3 } }
-    ///
-    /// assert_eq!(SbiRet::success(2).unwrap_or_else(invalid_use_zero), 2);
-    /// assert_eq!(SbiRet::invalid_param().unwrap_or_else(invalid_use_zero), 0);
-    /// ```
-    #[inline]
-    pub fn unwrap_or_else<F: FnOnce(Error) -> usize>(self, op: F) -> usize {
-        self.into_result().unwrap_or_else(op)
-    }
-
-    /// Returns the contained success value, consuming the `self` value,
-    /// without checking that the `SbiRet` contains an error value.
-    ///
-    /// # Safety
-    ///
-    /// Calling this method on an `SbiRet` containing an error value results
-    /// in *undefined behavior*.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use sbi_spec::binary::{SbiRet, Error};
-    /// let x = SbiRet::success(3);
-    /// assert_eq!(unsafe { x.unwrap_unchecked() }, 3);
-    /// ```
-    ///
-    /// ```no_run
-    /// # use sbi_spec::binary::SbiRet;
-    /// let x = SbiRet::no_shmem();
-    /// unsafe { x.unwrap_unchecked(); } // Undefined behavior!
-    /// ```
-    #[inline]
-    pub unsafe fn unwrap_unchecked(self) -> usize {
-        unsafe { self.into_result().unwrap_unchecked() }
-    }
-
-    /// Returns the contained `Error` value, consuming the `self` value,
-    /// without checking that the `SbiRet` does not contain a success value.
-    ///
-    /// # Safety
-    ///
-    /// Calling this method on an `SbiRet` containing a success value results
-    /// in *undefined behavior*.
-    ///
-    /// # Examples
-    ///
-    /// ```no_run
-    /// # use sbi_spec::binary::SbiRet;
-    /// let x = SbiRet::success(4);
-    /// unsafe { x.unwrap_unchecked(); } // Undefined behavior!
-    /// ```
-    ///
-    /// ```
-    /// # use sbi_spec::binary::{SbiRet, Error};
-    /// let x = SbiRet::failed();
-    /// assert_eq!(unsafe { x.unwrap_err_unchecked() }, Error::Failed);
-    /// ```
-    #[inline]
-    pub unsafe fn unwrap_err_unchecked(self) -> Error {
-        unsafe { self.into_result().unwrap_err_unchecked() }
-    }
-}
-
-impl IntoIterator for SbiRet {
-    type Item = usize;
-    type IntoIter = core::result::IntoIter<usize>;
-
-    /// Returns a consuming iterator over the possibly contained value.
-    ///
-    /// The iterator yields one value if the result contains a success value, otherwise none.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use sbi_spec::binary::SbiRet;
-    /// let x = SbiRet::success(5);
-    /// let v: Vec<usize> = x.into_iter().collect();
-    /// assert_eq!(v, [5]);
-    ///
-    /// let x = SbiRet::not_supported();
-    /// let v: Vec<usize> = x.into_iter().collect();
-    /// assert_eq!(v, []);
-    /// ```
-    #[inline]
-    fn into_iter(self) -> Self::IntoIter {
-        self.into_result().into_iter()
-    }
-}
-
-// TODO: implement Try and FromResidual for SbiRet once those traits are stablized
-/*
-impl core::ops::Try for SbiRet {
-    type Output = usize;
-    type Residual = Result<core::convert::Infallible, Error>;
-
-    #[inline]
-    fn from_output(output: Self::Output) -> Self {
-        SbiRet::success(output)
-    }
-
-    #[inline]
-    fn branch(self) -> core::ops::ControlFlow<Self::Residual, Self::Output> {
-        self.into_result().branch()
-    }
-}
-
-impl core::ops::FromResidual<Result<core::convert::Infallible, Error>> for SbiRet {
-    #[inline]
-    #[track_caller]
-    fn from_residual(residual: Result<core::convert::Infallible, Error>) -> Self {
-        match residual {
-            Err(e) => e.into(),
-        }
-    }
-}
-
-/// ```
-/// # use sbi_spec::binary::SbiRet;
-/// fn test() -> SbiRet {
-///     let value = SbiRet::failed()?;
-///     SbiRet::success(0)
-/// }
-/// assert_eq!(test(), SbiRet::failed());
-/// ```
-mod test_try_trait_for_sbiret {}
-*/
-
-/// Check if the implementation can contains the provided `bit`.
-#[inline]
-pub(crate) const fn valid_bit(base: usize, bit: usize) -> bool {
-    if bit < base {
-        // invalid index, under minimum range.
-        false
-    } else if (bit - base) >= usize::BITS as usize {
-        // invalid index, over max range.
-        false
-    } else {
-        true
-    }
-}
-
-/// Check if the implementation contains the provided `bit`.
-///
-/// ## Parameters
-///
-/// - `mask`: bitmask defining the range of bits.
-/// - `base`: the starting bit index. (default: `0`)
-/// - `ignore`: if `base` is equal to this value, ignore the `mask` parameter, and consider all `bit`s set.
-/// - `bit`: the bit index to check for membership in the `mask`.
-#[inline]
-pub(crate) const fn has_bit(mask: usize, base: usize, ignore: usize, bit: usize) -> bool {
-    if base == ignore {
-        // ignore the `mask`, consider all `bit`s as set.
-        true
-    } else if !valid_bit(base, bit) {
-        false
-    } else {
-        // index is in range, check if it is set in the mask.
-        mask & (1 << (bit - base)) != 0
-    }
-}
-
-/// Hart mask structure in SBI function calls.
-#[repr(C)]
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub struct HartMask<T = usize> {
-    hart_mask: T,
-    hart_mask_base: T,
-}
-
-impl<T: SbiRegister> HartMask<T> {
-    /// Special value to ignore the `mask`, and consider all `bit`s as set.
-    pub const IGNORE_MASK: T = T::FULL_MASK;
-
-    /// Construct a [HartMask] from mask value and base hart id.
-    #[inline]
-    pub const fn from_mask_base(hart_mask: T, hart_mask_base: T) -> Self {
-        Self {
-            hart_mask,
-            hart_mask_base,
-        }
-    }
-
-    /// Construct a [HartMask] that selects all available harts on the current environment.
-    ///
-    /// According to the RISC-V SBI Specification, `hart_mask_base` can be set to `-1` (i.e. `usize::MAX`)
-    /// to indicate that `hart_mask` shall be ignored and all available harts must be considered.
-    /// In case of this function in the `sbi-spec` crate, we fill in `usize::MAX` in `hart_mask_base`
-    /// parameter to match the RISC-V SBI standard, while choosing 0 as the ignored `hart_mask` value.
-    #[inline]
-    pub const fn all() -> Self {
-        Self {
-            hart_mask: T::ZERO,
-            hart_mask_base: T::FULL_MASK,
-        }
-    }
-
-    /// Gets the special value for ignoring the `mask` parameter.
-    #[inline]
-    pub const fn ignore_mask(&self) -> T {
-        Self::IGNORE_MASK
-    }
-
-    /// Returns `mask` and `base` parameters from the [HartMask].
-    #[inline]
-    pub const fn into_inner(self) -> (T, T) {
-        (self.hart_mask, self.hart_mask_base)
-    }
-}
-
-// FIXME: implement for T: SbiRegister once we can implement this using const traits.
-// Ref: https://rust-lang.github.io/rust-project-goals/2024h2/const-traits.html
-impl HartMask<usize> {
-    /// Returns whether the [HartMask] contains the provided `hart_id`.
-    #[inline]
-    pub const fn has_bit(self, hart_id: usize) -> bool {
-        has_bit(
-            self.hart_mask,
-            self.hart_mask_base,
-            Self::IGNORE_MASK,
-            hart_id,
-        )
-    }
-
-    /// Insert a hart id into this [HartMask].
-    ///
-    /// Returns error when `hart_id` is invalid.
-    #[inline]
-    pub const fn insert(&mut self, hart_id: usize) -> Result<(), MaskError> {
-        if self.hart_mask_base == Self::IGNORE_MASK {
-            Ok(())
-        } else if valid_bit(self.hart_mask_base, hart_id) {
-            self.hart_mask |= 1usize << (hart_id - self.hart_mask_base);
-            Ok(())
-        } else {
-            Err(MaskError::InvalidBit)
-        }
-    }
-
-    /// Remove a hart id from this [HartMask].
-    ///
-    /// Returns error when `hart_id` is invalid, or it has been ignored.
-    #[inline]
-    pub const fn remove(&mut self, hart_id: usize) -> Result<(), MaskError> {
-        if self.hart_mask_base == Self::IGNORE_MASK {
-            Err(MaskError::Ignored)
-        } else if valid_bit(self.hart_mask_base, hart_id) {
-            self.hart_mask &= !(1usize << (hart_id - self.hart_mask_base));
-            Ok(())
-        } else {
-            Err(MaskError::InvalidBit)
-        }
-    }
-
-    /// Returns [HartIds] of self.
-    #[inline]
-    pub const fn iter(&self) -> HartIds {
-        HartIds {
-            inner: match self.hart_mask_base {
-                Self::IGNORE_MASK => UnvisitedMask::Range(0, usize::MAX),
-                _ => UnvisitedMask::MaskBase(self.hart_mask, self.hart_mask_base),
-            },
-        }
-    }
-}
-
-impl IntoIterator for HartMask {
-    type Item = usize;
-
-    type IntoIter = HartIds;
-
-    #[inline]
-    fn into_iter(self) -> Self::IntoIter {
-        self.iter()
-    }
-}
-
-/// Iterator structure for `HartMask`.
-///
-/// It will iterate hart id from low to high.
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub struct HartIds {
-    inner: UnvisitedMask,
-}
-
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-enum UnvisitedMask {
-    MaskBase(usize, usize),
-    Range(usize, usize),
-}
-
-impl Iterator for HartIds {
-    type Item = usize;
-
-    #[inline]
-    fn next(&mut self) -> Option<Self::Item> {
-        match &mut self.inner {
-            UnvisitedMask::MaskBase(0, _base) => None,
-            UnvisitedMask::MaskBase(unvisited_mask, base) => {
-                let low_bit = unvisited_mask.trailing_zeros();
-                let hart_id = usize::try_from(low_bit).unwrap() + *base;
-                *unvisited_mask &= !(1usize << low_bit);
-                Some(hart_id)
-            }
-            UnvisitedMask::Range(start, end) => {
-                assert!(start <= end);
-                if *start < *end {
-                    let ans = *start;
-                    *start += 1;
-                    Some(ans)
-                } else {
-                    None
-                }
-            }
-        }
-    }
-
-    #[inline]
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        match self.inner {
-            UnvisitedMask::MaskBase(unvisited_mask, _base) => {
-                let exact_popcnt = usize::try_from(unvisited_mask.count_ones()).unwrap();
-                (exact_popcnt, Some(exact_popcnt))
-            }
-            UnvisitedMask::Range(start, end) => {
-                assert!(start <= end);
-                let exact_num_harts = end - start;
-                (exact_num_harts, Some(exact_num_harts))
-            }
-        }
-    }
-
-    #[inline]
-    fn count(self) -> usize {
-        self.size_hint().0
-    }
-
-    #[inline]
-    fn last(mut self) -> Option<Self::Item> {
-        self.next_back()
-    }
-
-    #[inline]
-    fn min(mut self) -> Option<Self::Item> {
-        self.next()
-    }
-
-    #[inline]
-    fn max(mut self) -> Option<Self::Item> {
-        self.next_back()
-    }
-
-    #[inline]
-    fn is_sorted(self) -> bool {
-        true
-    }
-
-    // TODO: implement fn advance_by once it's stablized: https://github.com/rust-lang/rust/issues/77404
-    // #[inline]
-    // fn advance_by(&mut self, n: usize) -> Result<(), core::num::NonZero<usize>> { ... }
-}
-
-impl DoubleEndedIterator for HartIds {
-    #[inline]
-    fn next_back(&mut self) -> Option<Self::Item> {
-        match &mut self.inner {
-            UnvisitedMask::MaskBase(0, _base) => None,
-            UnvisitedMask::MaskBase(unvisited_mask, base) => {
-                let high_bit = unvisited_mask.leading_zeros();
-                let hart_id = usize::try_from(usize::BITS - high_bit - 1).unwrap() + *base;
-                *unvisited_mask &= !(1usize << (usize::BITS - high_bit - 1));
-                Some(hart_id)
-            }
-            UnvisitedMask::Range(start, end) => {
-                assert!(start <= end);
-                if *start < *end {
-                    let ans = *end;
-                    *end -= 1;
-                    Some(ans)
-                } else {
-                    None
-                }
-            }
-        }
-    }
-
-    // TODO: implement advance_back_by once stablized.
-    // #[inline]
-    // fn advance_back_by(&mut self, n: usize) -> Result<(), core::num::NonZero<usize>> { ... }
-}
-
-impl ExactSizeIterator for HartIds {}
-
-impl core::iter::FusedIterator for HartIds {}
-
-/// Error of mask modification.
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub enum MaskError {
-    /// This mask has been ignored.
-    Ignored,
-    /// Request bit is invalid.
-    InvalidBit,
-}
-
-/// Counter index mask structure in SBI function calls for the `PMU` extension §11.
-#[repr(C)]
-#[derive(Debug, Copy, Clone, Eq, PartialEq)]
-pub struct CounterMask<T = usize> {
-    counter_idx_mask: T,
-    counter_idx_base: T,
-}
-
-impl<T: SbiRegister> CounterMask<T> {
-    /// Special value to ignore the `mask`, and consider all `bit`s as set.
-    pub const IGNORE_MASK: T = T::FULL_MASK;
-
-    /// Construct a [CounterMask] from mask value and base counter index.
-    #[inline]
-    pub const fn from_mask_base(counter_idx_mask: T, counter_idx_base: T) -> Self {
-        Self {
-            counter_idx_mask,
-            counter_idx_base,
-        }
-    }
-
-    /// Gets the special value for ignoring the `mask` parameter.
-    #[inline]
-    pub const fn ignore_mask(&self) -> T {
-        Self::IGNORE_MASK
-    }
-
-    /// Returns `mask` and `base` parameters from the [CounterMask].
-    #[inline]
-    pub const fn into_inner(self) -> (T, T) {
-        (self.counter_idx_mask, self.counter_idx_base)
-    }
-}
-
-// FIXME: implement for T: SbiRegister once we can implement this using const traits.
-// Ref: https://rust-lang.github.io/rust-project-goals/2024h2/const-traits.html
-impl CounterMask<usize> {
-    /// Returns whether the [CounterMask] contains the provided `counter`.
-    #[inline]
-    pub const fn has_bit(self, counter: usize) -> bool {
-        has_bit(
-            self.counter_idx_mask,
-            self.counter_idx_base,
-            Self::IGNORE_MASK,
-            counter,
-        )
-    }
-}
-
-/// Physical slice wrapper with type annotation.
-///
-/// This struct wraps slices in RISC-V physical memory by low and high part of the
-/// physical base address as well as its length. It is usually used by SBI extensions
-/// as parameter types to pass base address and length parameters on physical memory
-/// other than a virtual one.
-///
-/// Generic parameter `P` represents a hint of how this physical slice would be used.
-/// For example, `Physical<&[u8]>` represents an immutable reference to physical byte slice,
-/// while `Physical<&mut [u8]>` represents a mutable one.
-///
-/// An SBI implementation should load or store memory using both `phys_addr_lo` and
-/// `phys_addr_hi` combined as base address. A supervisor program (kernels etc.)
-/// should provide continuous physical memory, wrapping its reference using this structure
-/// before passing into SBI runtime.
-#[derive(Clone, Copy)]
-pub struct Physical<P> {
-    num_bytes: usize,
-    phys_addr_lo: usize,
-    phys_addr_hi: usize,
-    _marker: PhantomData<P>,
-}
-
-impl<P> Physical<P> {
-    /// Create a physical memory slice by length and physical address.
-    #[inline]
-    pub const fn new(num_bytes: usize, phys_addr_lo: usize, phys_addr_hi: usize) -> Self {
-        Self {
-            num_bytes,
-            phys_addr_lo,
-            phys_addr_hi,
-            _marker: core::marker::PhantomData,
-        }
-    }
-
-    /// Returns length of the physical memory slice.
-    #[inline]
-    pub const fn num_bytes(&self) -> usize {
-        self.num_bytes
-    }
-
-    /// Returns low-part base address of physical memory slice.
-    #[inline]
-    pub const fn phys_addr_lo(&self) -> usize {
-        self.phys_addr_lo
-    }
-
-    /// Returns high-part base address of physical memory slice.
-    #[inline]
-    pub const fn phys_addr_hi(&self) -> usize {
-        self.phys_addr_hi
-    }
-}
-
-/// Shared memory physical address raw pointer with type annotation.
-///
-/// This is a structure wrapping a raw pointer to the value of the type `T` without
-/// a pointer metadata. `SharedPtr`'s are _thin_; they won't include metadata
-/// as RISC-V SBI does not provide an approach to pass them via SBI calls,
-/// thus the length of type `T` should be decided independently of raw
-/// pointer structure.
-///
-/// `SharedPtr` can be used as a parameter to pass the shared memory physical pointer
-///  with a given base address in RISC-V SBI calls. For example, a `SharedPtr<[u8; 64]>`
-/// would represent a fixed-size 64 byte array on a RISC-V SBI function argument
-/// type.
-///
-/// This structure cannot be dereferenced directly with physical addresses,
-/// because on RISC-V systems the physical address space could be larger than the
-/// virtual ones. Hence, this structure describes the physical memory range by
-/// two `usize` values: the upper `phys_addr_hi` and lower `phys_addr_lo`.
-///
-/// RISC-V SBI extensions may declare special pointer values for shared memory
-/// raw pointers. For example, SBI STA declares that steal-time information
-/// should stop from reporting when the SBI call is invoked using all-ones
-/// bitwise shared pointer, i.e. `phys_addr_hi` and `phys_addr_lo` both equals
-/// `usize::MAX`. `SharedPtr` can be constructed using such special values
-/// by providing them to the `SharedPtr::new` function.
-///
-/// # Requirements
-///
-/// If an SBI function needs to pass a shared memory physical address range to
-/// the SBI implementation (or higher privilege mode), then this physical memory
-/// address range MUST satisfy the following requirements:
-///
-/// * The SBI implementation MUST check that the supervisor-mode software is
-///   allowed to access the specified physical memory range with the access
-///   type requested (read and/or write).
-/// * The SBI implementation MUST access the specified physical memory range
-///   using the PMA attributes.
-/// * The data in the shared memory MUST follow little-endian byte ordering.
-///
-/// *NOTE:* If the supervisor-mode software accesses the same physical memory
-/// range using a memory type different from the PMA, then a loss of coherence
-/// or unexpected memory ordering may occur. The invoking software should
-/// follow the rules and sequences defined in the RISC-V Svpbmt specification
-/// to prevent the loss of coherence and memory ordering.
-///
-/// It is recommended that a memory physical address passed to an SBI function
-/// should use at least two `usize` parameters to support platforms
-/// which have memory physical addresses wider than `XLEN` bits.
-// FIXME: should constrain with `T: Thin` once ptr_metadata feature is stabled;
-// RISC-V SBI does not provide an approach to pass pointer metadata by SBI calls.
-pub struct SharedPtr<T> {
-    phys_addr_lo: usize,
-    phys_addr_hi: usize,
-    _marker: PhantomData<*mut T>,
-}
-
-// FIXME: we should consider strict provenance rules for this pointer-like structure
-// once feature strict_provenance is stabled.
-impl<T> SharedPtr<T> {
-    /// Create a shared physical memory pointer by physical address.
-    #[inline]
-    pub const fn new(phys_addr_lo: usize, phys_addr_hi: usize) -> Self {
-        Self {
-            phys_addr_lo,
-            phys_addr_hi,
-            _marker: PhantomData,
-        }
-    }
-
-    /// Returns low-part physical address of the shared physical memory pointer.
-    #[inline]
-    pub const fn phys_addr_lo(self) -> usize {
-        self.phys_addr_lo
-    }
-
-    /// Returns high-part physical address of the shared physical memory pointer.
-    #[inline]
-    pub const fn phys_addr_hi(self) -> usize {
-        self.phys_addr_hi
-    }
-}
-
-impl<T> Clone for SharedPtr<T> {
-    #[inline(always)]
-    fn clone(&self) -> Self {
-        *self
-    }
-}
-
-impl<T> Copy for SharedPtr<T> {}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    #[test]
-    #[rustfmt::skip]
-    fn rustsbi_sbi_ret_constructors() {
-        assert_eq!(SbiRet::success(0), SbiRet { value: 0, error: 0 });
-        assert_eq!(SbiRet::success(1037), SbiRet { value: 1037, error: 0 });
-        assert_eq!(SbiRet::success(usize::MAX), SbiRet { value: usize::MAX, error: 0 });
-
-        assert_eq!(SbiRet::failed(), SbiRet { value: 0, error: usize::MAX - 1 + 1 });
-        assert_eq!(SbiRet::not_supported(), SbiRet { value: 0, error: usize::MAX - 2 + 1 });
-        assert_eq!(SbiRet::invalid_param(), SbiRet { value: 0, error: usize::MAX - 3 + 1 });
-        assert_eq!(SbiRet::denied(), SbiRet { value: 0, error: usize::MAX - 4 + 1 });
-        assert_eq!(SbiRet::invalid_address(), SbiRet { value: 0, error: usize::MAX - 5 + 1 });
-        assert_eq!(SbiRet::already_available(), SbiRet { value: 0, error: usize::MAX - 6 + 1 });
-        assert_eq!(SbiRet::already_started(), SbiRet { value: 0, error: usize::MAX - 7 + 1 });
-        assert_eq!(SbiRet::already_stopped(), SbiRet { value: 0, error: usize::MAX - 8 + 1 });
-        assert_eq!(SbiRet::no_shmem(), SbiRet { value: 0, error: usize::MAX - 9 + 1 });
-        assert_eq!(SbiRet::invalid_state(), SbiRet { value: 0, error: usize::MAX - 10 + 1 });
-        assert_eq!(SbiRet::bad_range(), SbiRet { value: 0, error: usize::MAX - 11 + 1 });
-        assert_eq!(SbiRet::timeout(), SbiRet { value: 0, error: usize::MAX - 12 + 1 });
-        assert_eq!(SbiRet::io(), SbiRet { value: 0, error: usize::MAX - 13 + 1 });
-    }
-
-    #[test]
-    fn rustsbi_hart_mask() {
-        let mask = HartMask::from_mask_base(0b1, 400);
-        assert!(!mask.has_bit(0));
-        assert!(mask.has_bit(400));
-        assert!(!mask.has_bit(401));
-        let mask = HartMask::from_mask_base(0b110, 500);
-        assert!(!mask.has_bit(0));
-        assert!(!mask.has_bit(500));
-        assert!(mask.has_bit(501));
-        assert!(mask.has_bit(502));
-        assert!(!mask.has_bit(500 + (usize::BITS as usize)));
-        let max_bit = 1 << (usize::BITS - 1);
-        let mask = HartMask::from_mask_base(max_bit, 600);
-        assert!(mask.has_bit(600 + (usize::BITS as usize) - 1));
-        assert!(!mask.has_bit(600 + (usize::BITS as usize)));
-        let mask = HartMask::from_mask_base(0b11, usize::MAX - 1);
-        assert!(!mask.has_bit(usize::MAX - 2));
-        assert!(mask.has_bit(usize::MAX - 1));
-        assert!(mask.has_bit(usize::MAX));
-        assert!(!mask.has_bit(0));
-        // hart_mask_base == usize::MAX is special, it means hart_mask should be ignored
-        // and this hart mask contains all harts available
-        let mask = HartMask::from_mask_base(0, usize::MAX);
-        for i in 0..5 {
-            assert!(mask.has_bit(i));
-        }
-        assert!(mask.has_bit(usize::MAX));
-
-        let mut mask = HartMask::from_mask_base(0, 1);
-        assert!(!mask.has_bit(1));
-        assert!(mask.insert(1).is_ok());
-        assert!(mask.has_bit(1));
-        assert!(mask.remove(1).is_ok());
-        assert!(!mask.has_bit(1));
-    }
-
-    #[test]
-    fn rustsbi_hart_ids_iterator() {
-        let mask = HartMask::from_mask_base(0b101011, 1);
-        // Test the `next` method of `HartIds` structure.
-        let mut hart_ids = mask.iter();
-        assert_eq!(hart_ids.next(), Some(1));
-        assert_eq!(hart_ids.next(), Some(2));
-        assert_eq!(hart_ids.next(), Some(4));
-        assert_eq!(hart_ids.next(), Some(6));
-        assert_eq!(hart_ids.next(), None);
-        // `HartIds` structures are fused, meaning they return `None` forever once iteration finished.
-        assert_eq!(hart_ids.next(), None);
-
-        // Test `for` loop on mask (`HartMask`) as `IntoIterator`.
-        let mut ans = [0; 4];
-        let mut idx = 0;
-        for hart_id in mask {
-            ans[idx] = hart_id;
-            idx += 1;
-        }
-        assert_eq!(ans, [1, 2, 4, 6]);
-
-        // Test `Iterator` methods on `HartIds`.
-        let mut hart_ids = mask.iter();
-        assert_eq!(hart_ids.size_hint(), (4, Some(4)));
-        let _ = hart_ids.next();
-        assert_eq!(hart_ids.size_hint(), (3, Some(3)));
-        let _ = hart_ids.next();
-        let _ = hart_ids.next();
-        assert_eq!(hart_ids.size_hint(), (1, Some(1)));
-        let _ = hart_ids.next();
-        assert_eq!(hart_ids.size_hint(), (0, Some(0)));
-        let _ = hart_ids.next();
-        assert_eq!(hart_ids.size_hint(), (0, Some(0)));
-
-        let mut hart_ids = mask.iter();
-        assert_eq!(hart_ids.count(), 4);
-        let _ = hart_ids.next();
-        assert_eq!(hart_ids.count(), 3);
-        let _ = hart_ids.next();
-        let _ = hart_ids.next();
-        let _ = hart_ids.next();
-        assert_eq!(hart_ids.count(), 0);
-        let _ = hart_ids.next();
-        assert_eq!(hart_ids.count(), 0);
-
-        let hart_ids = mask.iter();
-        assert_eq!(hart_ids.last(), Some(6));
-
-        let mut hart_ids = mask.iter();
-        assert_eq!(hart_ids.nth(2), Some(4));
-        let mut hart_ids = mask.iter();
-        assert_eq!(hart_ids.nth(0), Some(1));
-
-        let mut iter = mask.iter().step_by(2);
-        assert_eq!(iter.next(), Some(1));
-        assert_eq!(iter.next(), Some(4));
-        assert_eq!(iter.next(), None);
-
-        let mask_2 = HartMask::from_mask_base(0b1001101, 64);
-        let mut iter = mask.iter().chain(mask_2);
-        assert_eq!(iter.next(), Some(1));
-        assert_eq!(iter.next(), Some(2));
-        assert_eq!(iter.next(), Some(4));
-        assert_eq!(iter.next(), Some(6));
-        assert_eq!(iter.next(), Some(64));
-        assert_eq!(iter.next(), Some(66));
-        assert_eq!(iter.next(), Some(67));
-        assert_eq!(iter.next(), Some(70));
-        assert_eq!(iter.next(), None);
-
-        let mut iter = mask.iter().zip(mask_2);
-        assert_eq!(iter.next(), Some((1, 64)));
-        assert_eq!(iter.next(), Some((2, 66)));
-        assert_eq!(iter.next(), Some((4, 67)));
-        assert_eq!(iter.next(), Some((6, 70)));
-        assert_eq!(iter.next(), None);
-
-        fn to_plic_context_id(hart_id_machine: usize) -> usize {
-            hart_id_machine * 2
-        }
-        let mut iter = mask.iter().map(to_plic_context_id);
-        assert_eq!(iter.next(), Some(2));
-        assert_eq!(iter.next(), Some(4));
-        assert_eq!(iter.next(), Some(8));
-        assert_eq!(iter.next(), Some(12));
-        assert_eq!(iter.next(), None);
-
-        let mut channel_received = [0; 4];
-        let mut idx = 0;
-        let mut channel_send = |hart_id| {
-            channel_received[idx] = hart_id;
-            idx += 1;
-        };
-        mask.iter().for_each(|value| channel_send(value));
-        assert_eq!(channel_received, [1, 2, 4, 6]);
-
-        let is_in_cluster_1 = |hart_id: &usize| *hart_id >= 4 && *hart_id < 7;
-        let mut iter = mask.iter().filter(is_in_cluster_1);
-        assert_eq!(iter.next(), Some(4));
-        assert_eq!(iter.next(), Some(6));
-        assert_eq!(iter.next(), None);
-
-        let if_in_cluster_1_get_plic_context_id = |hart_id: usize| {
-            if hart_id >= 4 && hart_id < 7 {
-                Some(hart_id * 2)
-            } else {
-                None
-            }
-        };
-        let mut iter = mask.iter().filter_map(if_in_cluster_1_get_plic_context_id);
-        assert_eq!(iter.next(), Some(8));
-        assert_eq!(iter.next(), Some(12));
-        assert_eq!(iter.next(), None);
-
-        let mut iter = mask.iter().enumerate();
-        assert_eq!(iter.next(), Some((0, 1)));
-        assert_eq!(iter.next(), Some((1, 2)));
-        assert_eq!(iter.next(), Some((2, 4)));
-        assert_eq!(iter.next(), Some((3, 6)));
-        assert_eq!(iter.next(), None);
-        let mut ans = [(0, 0); 4];
-        let mut idx = 0;
-        for (i, hart_id) in mask.iter().enumerate() {
-            ans[idx] = (i, hart_id);
-            idx += 1;
-        }
-        assert_eq!(ans, [(0, 1), (1, 2), (2, 4), (3, 6)]);
-
-        let mut iter = mask.iter().peekable();
-        assert_eq!(iter.peek(), Some(&1));
-        assert_eq!(iter.next(), Some(1));
-        assert_eq!(iter.peek(), Some(&2));
-        assert_eq!(iter.next(), Some(2));
-        assert_eq!(iter.peek(), Some(&4));
-        assert_eq!(iter.next(), Some(4));
-        assert_eq!(iter.peek(), Some(&6));
-        assert_eq!(iter.next(), Some(6));
-        assert_eq!(iter.peek(), None);
-        assert_eq!(iter.next(), None);
-
-        // TODO: other iterator tests.
-
-        assert!(mask.iter().is_sorted());
-        assert!(mask.iter().is_sorted_by(|a, b| a <= b));
-
-        // Reverse iterator as `DoubleEndedIterator`.
-        let mut iter = mask.iter().rev();
-        assert_eq!(iter.next(), Some(6));
-        assert_eq!(iter.next(), Some(4));
-        assert_eq!(iter.next(), Some(2));
-        assert_eq!(iter.next(), Some(1));
-        assert_eq!(iter.next(), None);
-
-        // Special iterator values.
-        let nothing = HartMask::from_mask_base(0, 1000);
-        assert!(nothing.iter().eq([]));
-
-        let all_mask_bits_set = HartMask::from_mask_base(usize::MAX, 1000);
-        let range = 1000..(1000 + usize::BITS as usize);
-        assert!(all_mask_bits_set.iter().eq(range));
-
-        let all_harts = HartMask::all();
-        let mut iter = all_harts.iter();
-        assert_eq!(iter.size_hint(), (usize::MAX, Some(usize::MAX)));
-        // Don't use `Iterator::eq` here; it would literally run `Iterator::try_for_each` from 0 to usize::MAX
-        // which will cost us forever to run the test.
-        assert_eq!(iter.next(), Some(0));
-        assert_eq!(iter.size_hint(), (usize::MAX - 1, Some(usize::MAX - 1)));
-        assert_eq!(iter.next(), Some(1));
-        assert_eq!(iter.next(), Some(2));
-        // skip 500 elements
-        let _ = iter.nth(500 - 1);
-        assert_eq!(iter.next(), Some(503));
-        assert_eq!(iter.size_hint(), (usize::MAX - 504, Some(usize::MAX - 504)));
-        assert_eq!(iter.next_back(), Some(usize::MAX));
-        assert_eq!(iter.next_back(), Some(usize::MAX - 1));
-        assert_eq!(iter.size_hint(), (usize::MAX - 506, Some(usize::MAX - 506)));
-
-        // A common usage of `HartMask::all`, we assume that this platform filters out hart 0..=3.
-        let environment_available_hart_ids = 4..128;
-        // `hart_mask_iter` contains 64..=usize::MAX.
-        let hart_mask_iter = all_harts.iter().skip(64);
-        let filtered_iter = environment_available_hart_ids.filter(|&x| {
-            hart_mask_iter
-                .clone()
-                .find(|&y| y >= x)
-                .map_or(false, |y| y == x)
-        });
-        assert!(filtered_iter.eq(64..128));
-
-        // The following operations should have O(1) complexity.
-        let all_harts = HartMask::all();
-        assert_eq!(all_harts.iter().count(), usize::MAX);
-        assert_eq!(all_harts.iter().last(), Some(usize::MAX));
-        assert_eq!(all_harts.iter().min(), Some(0));
-        assert_eq!(all_harts.iter().max(), Some(usize::MAX));
-        assert!(all_harts.iter().is_sorted());
-
-        let partial_all_harts = {
-            let mut ans = HartMask::all().iter();
-            let _ = ans.nth(65536 - 1);
-            let _ = ans.nth_back(4096 - 1);
-            ans
-        };
-        assert_eq!(partial_all_harts.clone().count(), usize::MAX - 65536 - 4096);
-        assert_eq!(partial_all_harts.clone().last(), Some(usize::MAX - 4096));
-        assert_eq!(partial_all_harts.clone().min(), Some(65536));
-        assert_eq!(partial_all_harts.clone().max(), Some(usize::MAX - 4096));
-        assert!(partial_all_harts.is_sorted());
-
-        let nothing = HartMask::from_mask_base(0, 1000);
-        assert_eq!(nothing.iter().count(), 0);
-        assert_eq!(nothing.iter().last(), None);
-        assert_eq!(nothing.iter().min(), None);
-        assert_eq!(nothing.iter().max(), None);
-        assert!(nothing.iter().is_sorted());
-
-        let mask = HartMask::from_mask_base(0b101011, 1);
-        assert_eq!(mask.iter().count(), 4);
-        assert_eq!(mask.iter().last(), Some(6));
-        assert_eq!(mask.iter().min(), Some(1));
-        assert_eq!(mask.iter().max(), Some(6));
-        assert!(mask.iter().is_sorted());
-
-        let all_mask_bits_set = HartMask::from_mask_base(usize::MAX, 1000);
-        let last = 1000 + usize::BITS as usize - 1;
-        assert_eq!(all_mask_bits_set.iter().count(), usize::BITS as usize);
-        assert_eq!(all_mask_bits_set.iter().last(), Some(last));
-        assert_eq!(all_mask_bits_set.iter().min(), Some(1000));
-        assert_eq!(all_mask_bits_set.iter().max(), Some(last));
-        assert!(all_mask_bits_set.iter().is_sorted());
-    }
-
-    #[test]
-    fn rustsbi_counter_index_mask() {
-        let mask = CounterMask::from_mask_base(0b1, 400);
-        assert!(!mask.has_bit(0));
-        assert!(mask.has_bit(400));
-        assert!(!mask.has_bit(401));
-        let mask = CounterMask::from_mask_base(0b110, 500);
-        assert!(!mask.has_bit(0));
-        assert!(!mask.has_bit(500));
-        assert!(mask.has_bit(501));
-        assert!(mask.has_bit(502));
-        assert!(!mask.has_bit(500 + (usize::BITS as usize)));
-        let max_bit = 1 << (usize::BITS - 1);
-        let mask = CounterMask::from_mask_base(max_bit, 600);
-        assert!(mask.has_bit(600 + (usize::BITS as usize) - 1));
-        assert!(!mask.has_bit(600 + (usize::BITS as usize)));
-        let mask = CounterMask::from_mask_base(0b11, usize::MAX - 1);
-        assert!(!mask.has_bit(usize::MAX - 2));
-        assert!(mask.has_bit(usize::MAX - 1));
-        assert!(mask.has_bit(usize::MAX));
-        assert!(!mask.has_bit(0));
-        let mask = CounterMask::from_mask_base(0, usize::MAX);
-        let null_mask = CounterMask::from_mask_base(0, 0);
-        (0..=usize::BITS as usize).for_each(|i| {
-            assert!(mask.has_bit(i));
-            assert!(!null_mask.has_bit(i));
-        });
-        assert!(mask.has_bit(usize::MAX));
-    }
-
-    #[test]
-    fn rustsbi_mask_non_usize() {
-        assert_eq!(CounterMask::<i32>::IGNORE_MASK, -1);
-        assert_eq!(CounterMask::<i64>::IGNORE_MASK, -1);
-        assert_eq!(CounterMask::<i128>::IGNORE_MASK, -1);
-        assert_eq!(CounterMask::<u32>::IGNORE_MASK, u32::MAX);
-        assert_eq!(CounterMask::<u64>::IGNORE_MASK, u64::MAX);
-        assert_eq!(CounterMask::<u128>::IGNORE_MASK, u128::MAX);
-
-        assert_eq!(HartMask::<i32>::IGNORE_MASK, -1);
-        assert_eq!(HartMask::<i64>::IGNORE_MASK, -1);
-        assert_eq!(HartMask::<i128>::IGNORE_MASK, -1);
-        assert_eq!(HartMask::<u32>::IGNORE_MASK, u32::MAX);
-        assert_eq!(HartMask::<u64>::IGNORE_MASK, u64::MAX);
-        assert_eq!(HartMask::<u128>::IGNORE_MASK, u128::MAX);
-
-        assert_eq!(HartMask::<i32>::all(), HartMask::from_mask_base(0, -1));
-    }
-}
+// SBI return value and error codes.
+mod sbi_ret;
+pub use sbi_ret::{Error, SbiRegister, SbiRet, id::*};
+
+// Masks.
+mod mask_commons;
+pub use mask_commons::MaskError;
+
+mod counter_mask;
+mod hart_mask;
+mod trigger_mask;
+pub use counter_mask::CounterMask;
+pub use hart_mask::{HartIds, HartMask};
+pub use trigger_mask::TriggerMask;
+
+// Pointers.
+mod physical_slice;
+mod shared_physical_ptr;
+pub use physical_slice::Physical;
+pub use shared_physical_ptr::SharedPtr;

+ 95 - 0
library/sbi-spec/src/binary/counter_mask.rs

@@ -0,0 +1,95 @@
+use super::{mask_commons::has_bit, sbi_ret::SbiRegister};
+
+/// Counter index mask structure in SBI function calls for the `PMU` extension §11.
+#[repr(C)]
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub struct CounterMask<T = usize> {
+    counter_idx_mask: T,
+    counter_idx_base: T,
+}
+
+impl<T: SbiRegister> CounterMask<T> {
+    /// Special value to ignore the `mask`, and consider all `bit`s as set.
+    pub const IGNORE_MASK: T = T::FULL_MASK;
+
+    /// Construct a [CounterMask] from mask value and base counter index.
+    #[inline]
+    pub const fn from_mask_base(counter_idx_mask: T, counter_idx_base: T) -> Self {
+        Self {
+            counter_idx_mask,
+            counter_idx_base,
+        }
+    }
+
+    /// Gets the special value for ignoring the `mask` parameter.
+    #[inline]
+    pub const fn ignore_mask(&self) -> T {
+        Self::IGNORE_MASK
+    }
+
+    /// Returns `mask` and `base` parameters from the [CounterMask].
+    #[inline]
+    pub const fn into_inner(self) -> (T, T) {
+        (self.counter_idx_mask, self.counter_idx_base)
+    }
+}
+
+// FIXME: implement for T: SbiRegister once we can implement this using const traits.
+// Ref: https://rust-lang.github.io/rust-project-goals/2024h2/const-traits.html
+impl CounterMask<usize> {
+    /// Returns whether the [CounterMask] contains the provided `counter`.
+    #[inline]
+    pub const fn has_bit(self, counter: usize) -> bool {
+        has_bit(
+            self.counter_idx_mask,
+            self.counter_idx_base,
+            Self::IGNORE_MASK,
+            counter,
+        )
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn rustsbi_counter_index_mask() {
+        let mask = CounterMask::from_mask_base(0b1, 400);
+        assert!(!mask.has_bit(0));
+        assert!(mask.has_bit(400));
+        assert!(!mask.has_bit(401));
+        let mask = CounterMask::from_mask_base(0b110, 500);
+        assert!(!mask.has_bit(0));
+        assert!(!mask.has_bit(500));
+        assert!(mask.has_bit(501));
+        assert!(mask.has_bit(502));
+        assert!(!mask.has_bit(500 + (usize::BITS as usize)));
+        let max_bit = 1 << (usize::BITS - 1);
+        let mask = CounterMask::from_mask_base(max_bit, 600);
+        assert!(mask.has_bit(600 + (usize::BITS as usize) - 1));
+        assert!(!mask.has_bit(600 + (usize::BITS as usize)));
+        let mask = CounterMask::from_mask_base(0b11, usize::MAX - 1);
+        assert!(!mask.has_bit(usize::MAX - 2));
+        assert!(mask.has_bit(usize::MAX - 1));
+        assert!(mask.has_bit(usize::MAX));
+        assert!(!mask.has_bit(0));
+        let mask = CounterMask::from_mask_base(0, usize::MAX);
+        let null_mask = CounterMask::from_mask_base(0, 0);
+        (0..=usize::BITS as usize).for_each(|i| {
+            assert!(mask.has_bit(i));
+            assert!(!null_mask.has_bit(i));
+        });
+        assert!(mask.has_bit(usize::MAX));
+    }
+
+    #[test]
+    fn rustsbi_counter_mask_non_usize() {
+        assert_eq!(CounterMask::<i32>::IGNORE_MASK, -1);
+        assert_eq!(CounterMask::<i64>::IGNORE_MASK, -1);
+        assert_eq!(CounterMask::<i128>::IGNORE_MASK, -1);
+        assert_eq!(CounterMask::<u32>::IGNORE_MASK, u32::MAX);
+        assert_eq!(CounterMask::<u64>::IGNORE_MASK, u64::MAX);
+        assert_eq!(CounterMask::<u128>::IGNORE_MASK, u128::MAX);
+    }
+}

+ 525 - 0
library/sbi-spec/src/binary/hart_mask.rs

@@ -0,0 +1,525 @@
+use super::{
+    mask_commons::{MaskError, has_bit, valid_bit},
+    sbi_ret::SbiRegister,
+};
+
+/// Hart mask structure in SBI function calls.
+#[repr(C)]
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
+pub struct HartMask<T = usize> {
+    hart_mask: T,
+    hart_mask_base: T,
+}
+
+impl<T: SbiRegister> HartMask<T> {
+    /// Special value to ignore the `mask`, and consider all `bit`s as set.
+    pub const IGNORE_MASK: T = T::FULL_MASK;
+
+    /// Construct a [HartMask] from mask value and base hart id.
+    #[inline]
+    pub const fn from_mask_base(hart_mask: T, hart_mask_base: T) -> Self {
+        Self {
+            hart_mask,
+            hart_mask_base,
+        }
+    }
+
+    /// Construct a [HartMask] that selects all available harts on the current environment.
+    ///
+    /// According to the RISC-V SBI Specification, `hart_mask_base` can be set to `-1` (i.e. `usize::MAX`)
+    /// to indicate that `hart_mask` shall be ignored and all available harts must be considered.
+    /// In case of this function in the `sbi-spec` crate, we fill in `usize::MAX` in `hart_mask_base`
+    /// parameter to match the RISC-V SBI standard, while choosing 0 as the ignored `hart_mask` value.
+    #[inline]
+    pub const fn all() -> Self {
+        Self {
+            hart_mask: T::ZERO,
+            hart_mask_base: T::FULL_MASK,
+        }
+    }
+
+    /// Gets the special value for ignoring the `mask` parameter.
+    #[inline]
+    pub const fn ignore_mask(&self) -> T {
+        Self::IGNORE_MASK
+    }
+
+    /// Returns `mask` and `base` parameters from the [HartMask].
+    #[inline]
+    pub const fn into_inner(self) -> (T, T) {
+        (self.hart_mask, self.hart_mask_base)
+    }
+}
+
+// FIXME: implement for T: SbiRegister once we can implement this using const traits.
+// Ref: https://rust-lang.github.io/rust-project-goals/2024h2/const-traits.html
+impl HartMask<usize> {
+    /// Returns whether the [HartMask] contains the provided `hart_id`.
+    #[inline]
+    pub const fn has_bit(self, hart_id: usize) -> bool {
+        has_bit(
+            self.hart_mask,
+            self.hart_mask_base,
+            Self::IGNORE_MASK,
+            hart_id,
+        )
+    }
+
+    /// Insert a hart id into this [HartMask].
+    ///
+    /// Returns error when `hart_id` is invalid.
+    #[inline]
+    pub const fn insert(&mut self, hart_id: usize) -> Result<(), MaskError> {
+        if self.hart_mask_base == Self::IGNORE_MASK {
+            Ok(())
+        } else if valid_bit(self.hart_mask_base, hart_id) {
+            self.hart_mask |= 1usize << (hart_id - self.hart_mask_base);
+            Ok(())
+        } else {
+            Err(MaskError::InvalidBit)
+        }
+    }
+
+    /// Remove a hart id from this [HartMask].
+    ///
+    /// Returns error when `hart_id` is invalid, or it has been ignored.
+    #[inline]
+    pub const fn remove(&mut self, hart_id: usize) -> Result<(), MaskError> {
+        if self.hart_mask_base == Self::IGNORE_MASK {
+            Err(MaskError::Ignored)
+        } else if valid_bit(self.hart_mask_base, hart_id) {
+            self.hart_mask &= !(1usize << (hart_id - self.hart_mask_base));
+            Ok(())
+        } else {
+            Err(MaskError::InvalidBit)
+        }
+    }
+
+    /// Returns [HartIds] of self.
+    #[inline]
+    pub const fn iter(&self) -> HartIds {
+        HartIds {
+            inner: match self.hart_mask_base {
+                Self::IGNORE_MASK => UnvisitedMask::Range(0, usize::MAX),
+                _ => UnvisitedMask::MaskBase(self.hart_mask, self.hart_mask_base),
+            },
+        }
+    }
+}
+
+impl IntoIterator for HartMask {
+    type Item = usize;
+
+    type IntoIter = HartIds;
+
+    #[inline]
+    fn into_iter(self) -> Self::IntoIter {
+        self.iter()
+    }
+}
+
+/// Iterator structure for `HartMask`.
+///
+/// It will iterate hart id from low to high.
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
+pub struct HartIds {
+    inner: UnvisitedMask,
+}
+
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
+enum UnvisitedMask {
+    MaskBase(usize, usize),
+    Range(usize, usize),
+}
+
+impl Iterator for HartIds {
+    type Item = usize;
+
+    #[inline]
+    fn next(&mut self) -> Option<Self::Item> {
+        match &mut self.inner {
+            UnvisitedMask::MaskBase(0, _base) => None,
+            UnvisitedMask::MaskBase(unvisited_mask, base) => {
+                let low_bit = unvisited_mask.trailing_zeros();
+                let hart_id = usize::try_from(low_bit).unwrap() + *base;
+                *unvisited_mask &= !(1usize << low_bit);
+                Some(hart_id)
+            }
+            UnvisitedMask::Range(start, end) => {
+                assert!(start <= end);
+                if *start < *end {
+                    let ans = *start;
+                    *start += 1;
+                    Some(ans)
+                } else {
+                    None
+                }
+            }
+        }
+    }
+
+    #[inline]
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        match self.inner {
+            UnvisitedMask::MaskBase(unvisited_mask, _base) => {
+                let exact_popcnt = usize::try_from(unvisited_mask.count_ones()).unwrap();
+                (exact_popcnt, Some(exact_popcnt))
+            }
+            UnvisitedMask::Range(start, end) => {
+                assert!(start <= end);
+                let exact_num_harts = end - start;
+                (exact_num_harts, Some(exact_num_harts))
+            }
+        }
+    }
+
+    #[inline]
+    fn count(self) -> usize {
+        self.size_hint().0
+    }
+
+    #[inline]
+    fn last(mut self) -> Option<Self::Item> {
+        self.next_back()
+    }
+
+    #[inline]
+    fn min(mut self) -> Option<Self::Item> {
+        self.next()
+    }
+
+    #[inline]
+    fn max(mut self) -> Option<Self::Item> {
+        self.next_back()
+    }
+
+    #[inline]
+    fn is_sorted(self) -> bool {
+        true
+    }
+
+    // TODO: implement fn advance_by once it's stabilized: https://github.com/rust-lang/rust/issues/77404
+    // #[inline]
+    // fn advance_by(&mut self, n: usize) -> Result<(), core::num::NonZero<usize>> { ... }
+}
+
+impl DoubleEndedIterator for HartIds {
+    #[inline]
+    fn next_back(&mut self) -> Option<Self::Item> {
+        match &mut self.inner {
+            UnvisitedMask::MaskBase(0, _base) => None,
+            UnvisitedMask::MaskBase(unvisited_mask, base) => {
+                let high_bit = unvisited_mask.leading_zeros();
+                let hart_id = usize::try_from(usize::BITS - high_bit - 1).unwrap() + *base;
+                *unvisited_mask &= !(1usize << (usize::BITS - high_bit - 1));
+                Some(hart_id)
+            }
+            UnvisitedMask::Range(start, end) => {
+                assert!(start <= end);
+                if *start < *end {
+                    let ans = *end;
+                    *end -= 1;
+                    Some(ans)
+                } else {
+                    None
+                }
+            }
+        }
+    }
+
+    // TODO: implement advance_back_by once stabilized.
+    // #[inline]
+    // fn advance_back_by(&mut self, n: usize) -> Result<(), core::num::NonZero<usize>> { ... }
+}
+
+impl ExactSizeIterator for HartIds {}
+
+impl core::iter::FusedIterator for HartIds {}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn rustsbi_hart_mask() {
+        let mask = HartMask::from_mask_base(0b1, 400);
+        assert!(!mask.has_bit(0));
+        assert!(mask.has_bit(400));
+        assert!(!mask.has_bit(401));
+        let mask = HartMask::from_mask_base(0b110, 500);
+        assert!(!mask.has_bit(0));
+        assert!(!mask.has_bit(500));
+        assert!(mask.has_bit(501));
+        assert!(mask.has_bit(502));
+        assert!(!mask.has_bit(500 + (usize::BITS as usize)));
+        let max_bit = 1 << (usize::BITS - 1);
+        let mask = HartMask::from_mask_base(max_bit, 600);
+        assert!(mask.has_bit(600 + (usize::BITS as usize) - 1));
+        assert!(!mask.has_bit(600 + (usize::BITS as usize)));
+        let mask = HartMask::from_mask_base(0b11, usize::MAX - 1);
+        assert!(!mask.has_bit(usize::MAX - 2));
+        assert!(mask.has_bit(usize::MAX - 1));
+        assert!(mask.has_bit(usize::MAX));
+        assert!(!mask.has_bit(0));
+        // hart_mask_base == usize::MAX is special, it means hart_mask should be ignored
+        // and this hart mask contains all harts available
+        let mask = HartMask::from_mask_base(0, usize::MAX);
+        for i in 0..5 {
+            assert!(mask.has_bit(i));
+        }
+        assert!(mask.has_bit(usize::MAX));
+
+        let mut mask = HartMask::from_mask_base(0, 1);
+        assert!(!mask.has_bit(1));
+        assert!(mask.insert(1).is_ok());
+        assert!(mask.has_bit(1));
+        assert!(mask.remove(1).is_ok());
+        assert!(!mask.has_bit(1));
+    }
+
+    #[test]
+    fn rustsbi_hart_ids_iterator() {
+        let mask = HartMask::from_mask_base(0b101011, 1);
+        // Test the `next` method of `HartIds` structure.
+        let mut hart_ids = mask.iter();
+        assert_eq!(hart_ids.next(), Some(1));
+        assert_eq!(hart_ids.next(), Some(2));
+        assert_eq!(hart_ids.next(), Some(4));
+        assert_eq!(hart_ids.next(), Some(6));
+        assert_eq!(hart_ids.next(), None);
+        // `HartIds` structures are fused, meaning they return `None` forever once iteration finished.
+        assert_eq!(hart_ids.next(), None);
+
+        // Test `for` loop on mask (`HartMask`) as `IntoIterator`.
+        let mut ans = [0; 4];
+        let mut idx = 0;
+        for hart_id in mask {
+            ans[idx] = hart_id;
+            idx += 1;
+        }
+        assert_eq!(ans, [1, 2, 4, 6]);
+
+        // Test `Iterator` methods on `HartIds`.
+        let mut hart_ids = mask.iter();
+        assert_eq!(hart_ids.size_hint(), (4, Some(4)));
+        let _ = hart_ids.next();
+        assert_eq!(hart_ids.size_hint(), (3, Some(3)));
+        let _ = hart_ids.next();
+        let _ = hart_ids.next();
+        assert_eq!(hart_ids.size_hint(), (1, Some(1)));
+        let _ = hart_ids.next();
+        assert_eq!(hart_ids.size_hint(), (0, Some(0)));
+        let _ = hart_ids.next();
+        assert_eq!(hart_ids.size_hint(), (0, Some(0)));
+
+        let mut hart_ids = mask.iter();
+        assert_eq!(hart_ids.count(), 4);
+        let _ = hart_ids.next();
+        assert_eq!(hart_ids.count(), 3);
+        let _ = hart_ids.next();
+        let _ = hart_ids.next();
+        let _ = hart_ids.next();
+        assert_eq!(hart_ids.count(), 0);
+        let _ = hart_ids.next();
+        assert_eq!(hart_ids.count(), 0);
+
+        let hart_ids = mask.iter();
+        assert_eq!(hart_ids.last(), Some(6));
+
+        let mut hart_ids = mask.iter();
+        assert_eq!(hart_ids.nth(2), Some(4));
+        let mut hart_ids = mask.iter();
+        assert_eq!(hart_ids.nth(0), Some(1));
+
+        let mut iter = mask.iter().step_by(2);
+        assert_eq!(iter.next(), Some(1));
+        assert_eq!(iter.next(), Some(4));
+        assert_eq!(iter.next(), None);
+
+        let mask_2 = HartMask::from_mask_base(0b1001101, 64);
+        let mut iter = mask.iter().chain(mask_2);
+        assert_eq!(iter.next(), Some(1));
+        assert_eq!(iter.next(), Some(2));
+        assert_eq!(iter.next(), Some(4));
+        assert_eq!(iter.next(), Some(6));
+        assert_eq!(iter.next(), Some(64));
+        assert_eq!(iter.next(), Some(66));
+        assert_eq!(iter.next(), Some(67));
+        assert_eq!(iter.next(), Some(70));
+        assert_eq!(iter.next(), None);
+
+        let mut iter = mask.iter().zip(mask_2);
+        assert_eq!(iter.next(), Some((1, 64)));
+        assert_eq!(iter.next(), Some((2, 66)));
+        assert_eq!(iter.next(), Some((4, 67)));
+        assert_eq!(iter.next(), Some((6, 70)));
+        assert_eq!(iter.next(), None);
+
+        fn to_plic_context_id(hart_id_machine: usize) -> usize {
+            hart_id_machine * 2
+        }
+        let mut iter = mask.iter().map(to_plic_context_id);
+        assert_eq!(iter.next(), Some(2));
+        assert_eq!(iter.next(), Some(4));
+        assert_eq!(iter.next(), Some(8));
+        assert_eq!(iter.next(), Some(12));
+        assert_eq!(iter.next(), None);
+
+        let mut channel_received = [0; 4];
+        let mut idx = 0;
+        let mut channel_send = |hart_id| {
+            channel_received[idx] = hart_id;
+            idx += 1;
+        };
+        mask.iter().for_each(|value| channel_send(value));
+        assert_eq!(channel_received, [1, 2, 4, 6]);
+
+        let is_in_cluster_1 = |hart_id: &usize| *hart_id >= 4 && *hart_id < 7;
+        let mut iter = mask.iter().filter(is_in_cluster_1);
+        assert_eq!(iter.next(), Some(4));
+        assert_eq!(iter.next(), Some(6));
+        assert_eq!(iter.next(), None);
+
+        let if_in_cluster_1_get_plic_context_id = |hart_id: usize| {
+            if hart_id >= 4 && hart_id < 7 {
+                Some(hart_id * 2)
+            } else {
+                None
+            }
+        };
+        let mut iter = mask.iter().filter_map(if_in_cluster_1_get_plic_context_id);
+        assert_eq!(iter.next(), Some(8));
+        assert_eq!(iter.next(), Some(12));
+        assert_eq!(iter.next(), None);
+
+        let mut iter = mask.iter().enumerate();
+        assert_eq!(iter.next(), Some((0, 1)));
+        assert_eq!(iter.next(), Some((1, 2)));
+        assert_eq!(iter.next(), Some((2, 4)));
+        assert_eq!(iter.next(), Some((3, 6)));
+        assert_eq!(iter.next(), None);
+        let mut ans = [(0, 0); 4];
+        let mut idx = 0;
+        for (i, hart_id) in mask.iter().enumerate() {
+            ans[idx] = (i, hart_id);
+            idx += 1;
+        }
+        assert_eq!(ans, [(0, 1), (1, 2), (2, 4), (3, 6)]);
+
+        let mut iter = mask.iter().peekable();
+        assert_eq!(iter.peek(), Some(&1));
+        assert_eq!(iter.next(), Some(1));
+        assert_eq!(iter.peek(), Some(&2));
+        assert_eq!(iter.next(), Some(2));
+        assert_eq!(iter.peek(), Some(&4));
+        assert_eq!(iter.next(), Some(4));
+        assert_eq!(iter.peek(), Some(&6));
+        assert_eq!(iter.next(), Some(6));
+        assert_eq!(iter.peek(), None);
+        assert_eq!(iter.next(), None);
+
+        // TODO: other iterator tests.
+
+        assert!(mask.iter().is_sorted());
+        assert!(mask.iter().is_sorted_by(|a, b| a <= b));
+
+        // Reverse iterator as `DoubleEndedIterator`.
+        let mut iter = mask.iter().rev();
+        assert_eq!(iter.next(), Some(6));
+        assert_eq!(iter.next(), Some(4));
+        assert_eq!(iter.next(), Some(2));
+        assert_eq!(iter.next(), Some(1));
+        assert_eq!(iter.next(), None);
+
+        // Special iterator values.
+        let nothing = HartMask::from_mask_base(0, 1000);
+        assert!(nothing.iter().eq([]));
+
+        let all_mask_bits_set = HartMask::from_mask_base(usize::MAX, 1000);
+        let range = 1000..(1000 + usize::BITS as usize);
+        assert!(all_mask_bits_set.iter().eq(range));
+
+        let all_harts = HartMask::all();
+        let mut iter = all_harts.iter();
+        assert_eq!(iter.size_hint(), (usize::MAX, Some(usize::MAX)));
+        // Don't use `Iterator::eq` here; it would literally run `Iterator::try_for_each` from 0 to usize::MAX
+        // which will cost us forever to run the test.
+        assert_eq!(iter.next(), Some(0));
+        assert_eq!(iter.size_hint(), (usize::MAX - 1, Some(usize::MAX - 1)));
+        assert_eq!(iter.next(), Some(1));
+        assert_eq!(iter.next(), Some(2));
+        // skip 500 elements
+        let _ = iter.nth(500 - 1);
+        assert_eq!(iter.next(), Some(503));
+        assert_eq!(iter.size_hint(), (usize::MAX - 504, Some(usize::MAX - 504)));
+        assert_eq!(iter.next_back(), Some(usize::MAX));
+        assert_eq!(iter.next_back(), Some(usize::MAX - 1));
+        assert_eq!(iter.size_hint(), (usize::MAX - 506, Some(usize::MAX - 506)));
+
+        // A common usage of `HartMask::all`, we assume that this platform filters out hart 0..=3.
+        let environment_available_hart_ids = 4..128;
+        // `hart_mask_iter` contains 64..=usize::MAX.
+        let hart_mask_iter = all_harts.iter().skip(64);
+        let filtered_iter = environment_available_hart_ids.filter(|&x| {
+            hart_mask_iter
+                .clone()
+                .find(|&y| y >= x)
+                .map_or(false, |y| y == x)
+        });
+        assert!(filtered_iter.eq(64..128));
+
+        // The following operations should have O(1) complexity.
+        let all_harts = HartMask::all();
+        assert_eq!(all_harts.iter().count(), usize::MAX);
+        assert_eq!(all_harts.iter().last(), Some(usize::MAX));
+        assert_eq!(all_harts.iter().min(), Some(0));
+        assert_eq!(all_harts.iter().max(), Some(usize::MAX));
+        assert!(all_harts.iter().is_sorted());
+
+        let partial_all_harts = {
+            let mut ans = HartMask::all().iter();
+            let _ = ans.nth(65536 - 1);
+            let _ = ans.nth_back(4096 - 1);
+            ans
+        };
+        assert_eq!(partial_all_harts.clone().count(), usize::MAX - 65536 - 4096);
+        assert_eq!(partial_all_harts.clone().last(), Some(usize::MAX - 4096));
+        assert_eq!(partial_all_harts.clone().min(), Some(65536));
+        assert_eq!(partial_all_harts.clone().max(), Some(usize::MAX - 4096));
+        assert!(partial_all_harts.is_sorted());
+
+        let nothing = HartMask::from_mask_base(0, 1000);
+        assert_eq!(nothing.iter().count(), 0);
+        assert_eq!(nothing.iter().last(), None);
+        assert_eq!(nothing.iter().min(), None);
+        assert_eq!(nothing.iter().max(), None);
+        assert!(nothing.iter().is_sorted());
+
+        let mask = HartMask::from_mask_base(0b101011, 1);
+        assert_eq!(mask.iter().count(), 4);
+        assert_eq!(mask.iter().last(), Some(6));
+        assert_eq!(mask.iter().min(), Some(1));
+        assert_eq!(mask.iter().max(), Some(6));
+        assert!(mask.iter().is_sorted());
+
+        let all_mask_bits_set = HartMask::from_mask_base(usize::MAX, 1000);
+        let last = 1000 + usize::BITS as usize - 1;
+        assert_eq!(all_mask_bits_set.iter().count(), usize::BITS as usize);
+        assert_eq!(all_mask_bits_set.iter().last(), Some(last));
+        assert_eq!(all_mask_bits_set.iter().min(), Some(1000));
+        assert_eq!(all_mask_bits_set.iter().max(), Some(last));
+        assert!(all_mask_bits_set.iter().is_sorted());
+    }
+
+    #[test]
+    fn rustsbi_hart_mask_non_usize() {
+        assert_eq!(HartMask::<i32>::IGNORE_MASK, -1);
+        assert_eq!(HartMask::<i64>::IGNORE_MASK, -1);
+        assert_eq!(HartMask::<i128>::IGNORE_MASK, -1);
+        assert_eq!(HartMask::<u32>::IGNORE_MASK, u32::MAX);
+        assert_eq!(HartMask::<u64>::IGNORE_MASK, u64::MAX);
+        assert_eq!(HartMask::<u128>::IGNORE_MASK, u128::MAX);
+
+        assert_eq!(HartMask::<i32>::all(), HartMask::from_mask_base(0, -1));
+    }
+}

+ 45 - 0
library/sbi-spec/src/binary/mask_commons.rs

@@ -0,0 +1,45 @@
+//! Common SBI mask operations and structures.
+
+/// Check if the implementation can contains the provided `bit`.
+#[inline]
+pub(crate) const fn valid_bit(base: usize, bit: usize) -> bool {
+    if bit < base {
+        // invalid index, under minimum range.
+        false
+    } else if (bit - base) >= usize::BITS as usize {
+        // invalid index, over max range.
+        false
+    } else {
+        true
+    }
+}
+
+/// Check if the implementation contains the provided `bit`.
+///
+/// ## Parameters
+///
+/// - `mask`: bitmask defining the range of bits.
+/// - `base`: the starting bit index. (default: `0`)
+/// - `ignore`: if `base` is equal to this value, ignore the `mask` parameter, and consider all `bit`s set.
+/// - `bit`: the bit index to check for membership in the `mask`.
+#[inline]
+pub(crate) const fn has_bit(mask: usize, base: usize, ignore: usize, bit: usize) -> bool {
+    if base == ignore {
+        // ignore the `mask`, consider all `bit`s as set.
+        true
+    } else if !valid_bit(base, bit) {
+        false
+    } else {
+        // index is in range, check if it is set in the mask.
+        mask & (1 << (bit - base)) != 0
+    }
+}
+
+/// Error of mask modification.
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
+pub enum MaskError {
+    /// This mask has been ignored.
+    Ignored,
+    /// Request bit is invalid.
+    InvalidBit,
+}

+ 55 - 0
library/sbi-spec/src/binary/physical_slice.rs

@@ -0,0 +1,55 @@
+use core::marker::PhantomData;
+
+/// Physical slice wrapper with type annotation.
+///
+/// This struct wraps slices in RISC-V physical memory by low and high part of the
+/// physical base address as well as its length. It is usually used by SBI extensions
+/// as parameter types to pass base address and length parameters on physical memory
+/// other than a virtual one.
+///
+/// Generic parameter `P` represents a hint of how this physical slice would be used.
+/// For example, `Physical<&[u8]>` represents an immutable reference to physical byte slice,
+/// while `Physical<&mut [u8]>` represents a mutable one.
+///
+/// An SBI implementation should load or store memory using both `phys_addr_lo` and
+/// `phys_addr_hi` combined as base address. A supervisor program (kernels etc.)
+/// should provide continuous physical memory, wrapping its reference using this structure
+/// before passing into SBI runtime.
+#[derive(Clone, Copy)]
+pub struct Physical<P> {
+    num_bytes: usize,
+    phys_addr_lo: usize,
+    phys_addr_hi: usize,
+    _marker: PhantomData<P>,
+}
+
+impl<P> Physical<P> {
+    /// Create a physical memory slice by length and physical address.
+    #[inline]
+    pub const fn new(num_bytes: usize, phys_addr_lo: usize, phys_addr_hi: usize) -> Self {
+        Self {
+            num_bytes,
+            phys_addr_lo,
+            phys_addr_hi,
+            _marker: core::marker::PhantomData,
+        }
+    }
+
+    /// Returns length of the physical memory slice.
+    #[inline]
+    pub const fn num_bytes(&self) -> usize {
+        self.num_bytes
+    }
+
+    /// Returns low-part base address of physical memory slice.
+    #[inline]
+    pub const fn phys_addr_lo(&self) -> usize {
+        self.phys_addr_lo
+    }
+
+    /// Returns high-part base address of physical memory slice.
+    #[inline]
+    pub const fn phys_addr_hi(&self) -> usize {
+        self.phys_addr_hi
+    }
+}

+ 1146 - 0
library/sbi-spec/src/binary/sbi_ret.rs

@@ -0,0 +1,1146 @@
+/// SBI functions return type.
+///
+/// > SBI functions must return a pair of values in a0 and a1,
+/// > with a0 returning an error code.
+/// > This is analogous to returning the C structure `SbiRet`.
+///
+/// Note: if this structure is used in function return on conventional
+/// Rust code, it would not require pinning memory representation as
+/// extern C. The `repr(C)` is set in case that some users want to use
+/// this structure in FFI code.
+#[derive(Clone, Copy, PartialEq, Eq)]
+#[repr(C)]
+pub struct SbiRet<T = usize> {
+    /// Error number.
+    pub error: T,
+    /// Result value.
+    pub value: T,
+}
+
+/// Standard RISC-V SBI error IDs in `usize`.
+pub mod id {
+    use super::SbiRegister;
+
+    /// SBI success state return value.
+    #[doc(alias = "SBI_SUCCESS")]
+    pub const RET_SUCCESS: usize = <usize as SbiRegister>::RET_SUCCESS;
+    /// Error for SBI call failed for unknown reasons.
+    #[doc(alias = "SBI_ERR_FAILED")]
+    pub const RET_ERR_FAILED: usize = <usize as SbiRegister>::RET_ERR_FAILED;
+    /// Error for target operation not supported.
+    #[doc(alias = "SBI_ERR_NOT_SUPPORTED")]
+    pub const RET_ERR_NOT_SUPPORTED: usize = <usize as SbiRegister>::RET_ERR_NOT_SUPPORTED;
+    /// Error for invalid parameter.
+    #[doc(alias = "SBI_ERR_INVALID_PARAM")]
+    pub const RET_ERR_INVALID_PARAM: usize = <usize as SbiRegister>::RET_ERR_INVALID_PARAM;
+    /// Error for denied.
+    #[doc(alias = "SBI_ERR_DENIED")]
+    pub const RET_ERR_DENIED: usize = <usize as SbiRegister>::RET_ERR_DENIED;
+    /// Error for invalid address.
+    #[doc(alias = "SBI_ERR_INVALID_ADDRESS")]
+    pub const RET_ERR_INVALID_ADDRESS: usize = <usize as SbiRegister>::RET_ERR_INVALID_ADDRESS;
+    /// Error for resource already available.
+    #[doc(alias = "SBI_ERR_ALREADY_AVAILABLE")]
+    pub const RET_ERR_ALREADY_AVAILABLE: usize = <usize as SbiRegister>::RET_ERR_ALREADY_AVAILABLE;
+    /// Error for resource already started.
+    #[doc(alias = "SBI_ERR_ALREADY_STARTED")]
+    pub const RET_ERR_ALREADY_STARTED: usize = <usize as SbiRegister>::RET_ERR_ALREADY_STARTED;
+    /// Error for resource already stopped.
+    #[doc(alias = "SBI_ERR_ALREADY_STOPPED")]
+    pub const RET_ERR_ALREADY_STOPPED: usize = <usize as SbiRegister>::RET_ERR_ALREADY_STOPPED;
+    /// Error for shared memory not available.
+    #[doc(alias = "SBI_ERR_NO_SHMEM")]
+    pub const RET_ERR_NO_SHMEM: usize = <usize as SbiRegister>::RET_ERR_NO_SHMEM;
+    /// Error for invalid state.
+    #[doc(alias = "SBI_ERR_INVALID_STATE")]
+    pub const RET_ERR_INVALID_STATE: usize = <usize as SbiRegister>::RET_ERR_INVALID_STATE;
+    /// Error for bad or invalid range.
+    #[doc(alias = "SBI_ERR_BAD_RANGE")]
+    pub const RET_ERR_BAD_RANGE: usize = <usize as SbiRegister>::RET_ERR_BAD_RANGE;
+    /// Error for failed due to timeout.
+    #[doc(alias = "SBI_ERR_TIMEOUT")]
+    pub const RET_ERR_TIMEOUT: usize = <usize as SbiRegister>::RET_ERR_TIMEOUT;
+    /// Error for input or output error.
+    #[doc(alias = "SBI_ERR_IO")]
+    pub const RET_ERR_IO: usize = <usize as SbiRegister>::RET_ERR_IO;
+    /// Error for denied or not allowed due to lock status.
+    #[doc(alias = "SBI_ERR_DENIED_LOCKED")]
+    pub const RET_ERR_DENIED_LOCKED: usize = <usize as SbiRegister>::RET_ERR_DENIED_LOCKED;
+    // ^^ Note: remember to add a test case in `rustsbi_sbi_ret_constructors` in this file,
+    // and `test_binary` in lib.rs after adding an error number!
+}
+// Use each constants in `id` module, so that any `match` operations will not treat constant
+// names (`RET_ERR_*`) as newly defined variable names.
+use id::*;
+
+/// Data type of register that can be passed to the RISC-V SBI ABI.
+///
+/// This trait defines the requirements for types that are used as the underlying
+/// representation for both the `value` and `error` fields in the `SbiRet` structure.
+/// In most cases, this trait is implemented for primitive integer types (e.g., `usize`),
+/// but it can also be implemented for other types that satisfy the constraints.
+///
+/// # Examples
+///
+/// Implemented automatically for all types that satisfy `Copy`, `Eq`, and `Debug`.
+pub trait SbiRegister: Copy + Eq + Ord + core::fmt::Debug {
+    /// SBI success state return value.
+    const RET_SUCCESS: Self;
+    /// Error for SBI call failed for unknown reasons.
+    const RET_ERR_FAILED: Self;
+    /// Error for target operation not supported.
+    const RET_ERR_NOT_SUPPORTED: Self;
+    /// Error for invalid parameter.
+    const RET_ERR_INVALID_PARAM: Self;
+    /// Error for denied.
+    const RET_ERR_DENIED: Self;
+    /// Error for invalid address.
+    const RET_ERR_INVALID_ADDRESS: Self;
+    /// Error for resource already available.
+    const RET_ERR_ALREADY_AVAILABLE: Self;
+    /// Error for resource already started.
+    const RET_ERR_ALREADY_STARTED: Self;
+    /// Error for resource already stopped.
+    const RET_ERR_ALREADY_STOPPED: Self;
+    /// Error for shared memory not available.
+    const RET_ERR_NO_SHMEM: Self;
+    /// Error for invalid state.
+    const RET_ERR_INVALID_STATE: Self;
+    /// Error for bad or invalid range.
+    const RET_ERR_BAD_RANGE: Self;
+    /// Error for failed due to timeout.
+    const RET_ERR_TIMEOUT: Self;
+    /// Error for input or output error.
+    const RET_ERR_IO: Self;
+    /// Error for denied or not allowed due to lock status.
+    const RET_ERR_DENIED_LOCKED: Self;
+
+    /// Zero value for this type; this is used on `value` fields once `SbiRet` returns an error.
+    const ZERO: Self;
+    /// Full-ones value for this type; this is used on SBI mask structures like `CounterMask`
+    /// and `HartMask`.
+    const FULL_MASK: Self;
+
+    /// Converts an `SbiRet` of this type to a `Result` of self and `Error`.
+    fn into_result(ret: SbiRet<Self>) -> Result<Self, Error<Self>>;
+}
+
+macro_rules! impl_sbi_register {
+    ($ty:ty, $signed:ty) => {
+        impl SbiRegister for $ty {
+            const RET_SUCCESS: Self = 0;
+            const RET_ERR_FAILED: Self = -1 as $signed as $ty;
+            const RET_ERR_NOT_SUPPORTED: Self = -2 as $signed as $ty;
+            const RET_ERR_INVALID_PARAM: Self = -3 as $signed as $ty;
+            const RET_ERR_DENIED: Self = -4 as $signed as $ty;
+            const RET_ERR_INVALID_ADDRESS: Self = -5 as $signed as $ty;
+            const RET_ERR_ALREADY_AVAILABLE: Self = -6 as $signed as $ty;
+            const RET_ERR_ALREADY_STARTED: Self = -7 as $signed as $ty;
+            const RET_ERR_ALREADY_STOPPED: Self = -8 as $signed as $ty;
+            const RET_ERR_NO_SHMEM: Self = -9 as $signed as $ty;
+            const RET_ERR_INVALID_STATE: Self = -10 as $signed as $ty;
+            const RET_ERR_BAD_RANGE: Self = -11 as $signed as $ty;
+            const RET_ERR_TIMEOUT: Self = -12 as $signed as $ty;
+            const RET_ERR_IO: Self = -13 as $signed as $ty;
+            const RET_ERR_DENIED_LOCKED: Self = -14 as $signed as $ty;
+            const ZERO: Self = 0;
+            const FULL_MASK: Self = !0;
+
+            fn into_result(ret: SbiRet<Self>) -> Result<Self, Error<Self>> {
+                match ret.error {
+                    Self::RET_SUCCESS => Ok(ret.value),
+                    Self::RET_ERR_FAILED => Err(Error::Failed),
+                    Self::RET_ERR_NOT_SUPPORTED => Err(Error::NotSupported),
+                    Self::RET_ERR_INVALID_PARAM => Err(Error::InvalidParam),
+                    Self::RET_ERR_DENIED => Err(Error::Denied),
+                    Self::RET_ERR_INVALID_ADDRESS => Err(Error::InvalidAddress),
+                    Self::RET_ERR_ALREADY_AVAILABLE => Err(Error::AlreadyAvailable),
+                    Self::RET_ERR_ALREADY_STARTED => Err(Error::AlreadyStarted),
+                    Self::RET_ERR_ALREADY_STOPPED => Err(Error::AlreadyStopped),
+                    Self::RET_ERR_NO_SHMEM => Err(Error::NoShmem),
+                    Self::RET_ERR_INVALID_STATE => Err(Error::InvalidState),
+                    Self::RET_ERR_BAD_RANGE => Err(Error::BadRange),
+                    Self::RET_ERR_TIMEOUT => Err(Error::Timeout),
+                    Self::RET_ERR_IO => Err(Error::Io),
+                    Self::RET_ERR_DENIED_LOCKED => Err(Error::DeniedLocked),
+                    unknown => Err(Error::Custom(unknown as _)),
+                }
+            }
+        }
+    };
+}
+
+impl_sbi_register!(usize, isize);
+impl_sbi_register!(isize, isize);
+impl_sbi_register!(u32, i32);
+impl_sbi_register!(i32, i32);
+impl_sbi_register!(u64, i64);
+impl_sbi_register!(i64, i64);
+impl_sbi_register!(u128, i128);
+impl_sbi_register!(i128, i128);
+
+impl<T: SbiRegister + core::fmt::LowerHex> core::fmt::Debug for SbiRet<T> {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        match T::into_result(*self) {
+            Ok(value) => write!(f, "{:?}", value),
+            Err(err) => match err {
+                Error::Failed => write!(f, "<SBI call failed>"),
+                Error::NotSupported => write!(f, "<SBI feature not supported>"),
+                Error::InvalidParam => write!(f, "<SBI invalid parameter>"),
+                Error::Denied => write!(f, "<SBI denied>"),
+                Error::InvalidAddress => write!(f, "<SBI invalid address>"),
+                Error::AlreadyAvailable => write!(f, "<SBI already available>"),
+                Error::AlreadyStarted => write!(f, "<SBI already started>"),
+                Error::AlreadyStopped => write!(f, "<SBI already stopped>"),
+                Error::NoShmem => write!(f, "<SBI shared memory not available>"),
+                Error::InvalidState => write!(f, "<SBI invalid state>"),
+                Error::BadRange => write!(f, "<SBI bad range>"),
+                Error::Timeout => write!(f, "<SBI timeout>"),
+                Error::Io => write!(f, "<SBI input/output error>"),
+                Error::DeniedLocked => write!(f, "<SBI denied due to locked status>"),
+                Error::Custom(unknown) => write!(f, "[SBI Unknown error: {:#x}]", unknown),
+            },
+        }
+    }
+}
+
+/// RISC-V SBI error in enumeration.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum Error<T = usize> {
+    /// Error for SBI call failed for unknown reasons.
+    Failed,
+    /// Error for target operation not supported.
+    NotSupported,
+    /// Error for invalid parameter.
+    InvalidParam,
+    /// Error for denied.
+    Denied,
+    /// Error for invalid address.
+    InvalidAddress,
+    /// Error for resource already available.
+    AlreadyAvailable,
+    /// Error for resource already started.
+    AlreadyStarted,
+    /// Error for resource already stopped.
+    AlreadyStopped,
+    /// Error for shared memory not available.
+    NoShmem,
+    /// Error for invalid state.
+    InvalidState,
+    /// Error for bad or invalid range.
+    BadRange,
+    /// Error for failed due to timeout.
+    Timeout,
+    /// Error for input or output error.
+    Io,
+    /// Error for denied or not allowed due to lock status.
+    DeniedLocked,
+    /// Custom error code.
+    Custom(T),
+}
+
+impl<T: SbiRegister> SbiRet<T> {
+    /// Returns success SBI state with given `value`.
+    #[inline]
+    pub const fn success(value: T) -> Self {
+        Self {
+            error: T::RET_SUCCESS,
+            value,
+        }
+    }
+
+    /// The SBI call request failed for unknown reasons.
+    #[inline]
+    pub const fn failed() -> Self {
+        Self {
+            error: T::RET_ERR_FAILED,
+            value: T::ZERO,
+        }
+    }
+
+    /// SBI call failed due to not supported by target ISA,
+    /// operation type not supported,
+    /// or target operation type not implemented on purpose.
+    #[inline]
+    pub const fn not_supported() -> Self {
+        Self {
+            error: T::RET_ERR_NOT_SUPPORTED,
+            value: T::ZERO,
+        }
+    }
+
+    /// SBI call failed due to invalid hart mask parameter,
+    /// invalid target hart id,
+    /// invalid operation type,
+    /// or invalid resource index.
+    #[inline]
+    pub const fn invalid_param() -> Self {
+        Self {
+            error: T::RET_ERR_INVALID_PARAM,
+            value: T::ZERO,
+        }
+    }
+    /// SBI call denied for unsatisfied entry criteria, or insufficient access
+    /// permission to debug console or CPPC register.
+    #[inline]
+    pub const fn denied() -> Self {
+        Self {
+            error: T::RET_ERR_DENIED,
+            value: T::ZERO,
+        }
+    }
+
+    /// SBI call failed for invalid mask start address,
+    /// not a valid physical address parameter,
+    /// or the target address is prohibited by PMP to run in supervisor mode.
+    #[inline]
+    pub const fn invalid_address() -> Self {
+        Self {
+            error: T::RET_ERR_INVALID_ADDRESS,
+            value: T::ZERO,
+        }
+    }
+
+    /// SBI call failed for the target resource is already available,
+    /// e.g., the target hart is already started when caller still requests it to start.
+    #[inline]
+    pub const fn already_available() -> Self {
+        Self {
+            error: T::RET_ERR_ALREADY_AVAILABLE,
+            value: T::ZERO,
+        }
+    }
+
+    /// SBI call failed for the target resource is already started,
+    /// e.g., target performance counter is started.
+    #[inline]
+    pub const fn already_started() -> Self {
+        Self {
+            error: T::RET_ERR_ALREADY_STARTED,
+            value: T::ZERO,
+        }
+    }
+
+    /// SBI call failed for the target resource is already stopped,
+    /// e.g., target performance counter is stopped.
+    #[inline]
+    pub const fn already_stopped() -> Self {
+        Self {
+            error: T::RET_ERR_ALREADY_STOPPED,
+            value: T::ZERO,
+        }
+    }
+
+    /// SBI call failed for shared memory is not available,
+    /// e.g. nested acceleration shared memory is not available.
+    #[inline]
+    pub const fn no_shmem() -> Self {
+        Self {
+            error: T::RET_ERR_NO_SHMEM,
+            value: T::ZERO,
+        }
+    }
+
+    /// SBI call failed for invalid state,
+    /// e.g. register a software event but the event is not in unused state.
+    #[inline]
+    pub const fn invalid_state() -> Self {
+        Self {
+            error: T::RET_ERR_INVALID_STATE,
+            value: T::ZERO,
+        }
+    }
+
+    /// SBI call failed for bad or invalid range,
+    /// e.g. the software event is not exist in the specified range.
+    #[inline]
+    pub const fn bad_range() -> Self {
+        Self {
+            error: T::RET_ERR_BAD_RANGE,
+            value: T::ZERO,
+        }
+    }
+
+    /// SBI call failed for timeout,
+    /// e.g. message send timeout.
+    #[inline]
+    pub const fn timeout() -> Self {
+        Self {
+            error: T::RET_ERR_TIMEOUT,
+            value: T::ZERO,
+        }
+    }
+
+    /// SBI call failed for input or output error.
+    #[inline]
+    pub const fn io() -> Self {
+        Self {
+            error: T::RET_ERR_IO,
+            value: T::ZERO,
+        }
+    }
+    /// SBI call failed for denied or not allowed due to lock status.
+    #[inline]
+    pub const fn denied_locked() -> Self {
+        Self {
+            error: T::RET_ERR_DENIED_LOCKED,
+            value: T::ZERO,
+        }
+    }
+}
+
+impl<T: SbiRegister> From<Error<T>> for SbiRet<T> {
+    #[inline]
+    fn from(value: Error<T>) -> Self {
+        match value {
+            Error::Failed => SbiRet::failed(),
+            Error::NotSupported => SbiRet::not_supported(),
+            Error::InvalidParam => SbiRet::invalid_param(),
+            Error::Denied => SbiRet::denied(),
+            Error::InvalidAddress => SbiRet::invalid_address(),
+            Error::AlreadyAvailable => SbiRet::already_available(),
+            Error::AlreadyStarted => SbiRet::already_started(),
+            Error::AlreadyStopped => SbiRet::already_stopped(),
+            Error::NoShmem => SbiRet::no_shmem(),
+            Error::InvalidState => SbiRet::invalid_state(),
+            Error::BadRange => SbiRet::bad_range(),
+            Error::Timeout => SbiRet::timeout(),
+            Error::Io => SbiRet::io(),
+            Error::DeniedLocked => SbiRet::denied_locked(),
+            Error::Custom(error) => SbiRet {
+                error,
+                value: T::ZERO,
+            },
+        }
+    }
+}
+
+impl SbiRet {
+    /// Converts to a [`Result`] of value and error.
+    #[inline]
+    pub const fn into_result(self) -> Result<usize, Error> {
+        match self.error {
+            RET_SUCCESS => Ok(self.value),
+            RET_ERR_FAILED => Err(Error::Failed),
+            RET_ERR_NOT_SUPPORTED => Err(Error::NotSupported),
+            RET_ERR_INVALID_PARAM => Err(Error::InvalidParam),
+            RET_ERR_DENIED => Err(Error::Denied),
+            RET_ERR_INVALID_ADDRESS => Err(Error::InvalidAddress),
+            RET_ERR_ALREADY_AVAILABLE => Err(Error::AlreadyAvailable),
+            RET_ERR_ALREADY_STARTED => Err(Error::AlreadyStarted),
+            RET_ERR_ALREADY_STOPPED => Err(Error::AlreadyStopped),
+            RET_ERR_NO_SHMEM => Err(Error::NoShmem),
+            RET_ERR_INVALID_STATE => Err(Error::InvalidState),
+            RET_ERR_BAD_RANGE => Err(Error::BadRange),
+            RET_ERR_TIMEOUT => Err(Error::Timeout),
+            RET_ERR_IO => Err(Error::Io),
+            RET_ERR_DENIED_LOCKED => Err(Error::DeniedLocked),
+            unknown => Err(Error::Custom(unknown as _)),
+        }
+    }
+
+    /// Returns `true` if current SBI return succeeded.
+    ///
+    /// # Examples
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// # use sbi_spec::binary::SbiRet;
+    /// let x = SbiRet::success(0);
+    /// assert_eq!(x.is_ok(), true);
+    ///
+    /// let x = SbiRet::failed();
+    /// assert_eq!(x.is_ok(), false);
+    /// ```
+    #[must_use = "if you intended to assert that this is ok, consider `.unwrap()` instead"]
+    #[inline]
+    pub const fn is_ok(&self) -> bool {
+        matches!(self.error, RET_SUCCESS)
+    }
+
+    /// Returns `true` if the SBI call succeeded and the value inside of it matches a predicate.
+    ///
+    /// # Examples
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// # use sbi_spec::binary::SbiRet;
+    /// let x = SbiRet::success(2);
+    /// assert_eq!(x.is_ok_and(|x| x > 1), true);
+    ///
+    /// let x = SbiRet::success(0);
+    /// assert_eq!(x.is_ok_and(|x| x > 1), false);
+    ///
+    /// let x = SbiRet::no_shmem();
+    /// assert_eq!(x.is_ok_and(|x| x > 1), false);
+    /// ```
+    #[must_use]
+    #[inline]
+    pub fn is_ok_and(self, f: impl FnOnce(usize) -> bool) -> bool {
+        self.into_result().is_ok_and(f)
+    }
+
+    /// Returns `true` if current SBI return is an error.
+    ///
+    /// # Examples
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// # use sbi_spec::binary::SbiRet;
+    /// let x = SbiRet::success(0);
+    /// assert_eq!(x.is_err(), false);
+    ///
+    /// let x = SbiRet::not_supported();
+    /// assert_eq!(x.is_err(), true);
+    /// ```
+    #[must_use = "if you intended to assert that this is err, consider `.unwrap_err()` instead"]
+    #[inline]
+    pub const fn is_err(&self) -> bool {
+        !self.is_ok()
+    }
+
+    /// Returns `true` if the result is an error and the value inside of it matches a predicate.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use sbi_spec::binary::{SbiRet, Error};
+    /// let x = SbiRet::denied();
+    /// assert_eq!(x.is_err_and(|x| x == Error::Denied), true);
+    ///
+    /// let x = SbiRet::invalid_address();
+    /// assert_eq!(x.is_err_and(|x| x == Error::Denied), false);
+    ///
+    /// let x = SbiRet::success(0);
+    /// assert_eq!(x.is_err_and(|x| x == Error::Denied), false);
+    /// ```
+    #[must_use]
+    #[inline]
+    pub fn is_err_and(self, f: impl FnOnce(Error) -> bool) -> bool {
+        self.into_result().is_err_and(f)
+    }
+
+    /// Converts from `SbiRet` to [`Option<usize>`].
+    ///
+    /// Converts `self` into an [`Option<usize>`], consuming `self`,
+    /// and discarding the error, if any.
+    ///
+    /// # Examples
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// # use sbi_spec::binary::SbiRet;
+    /// let x = SbiRet::success(2);
+    /// assert_eq!(x.ok(), Some(2));
+    ///
+    /// let x = SbiRet::invalid_param();
+    /// assert_eq!(x.ok(), None);
+    /// ```
+    // fixme: should be pub const fn once this function in Result is stabilized in constant
+    #[inline]
+    pub fn ok(self) -> Option<usize> {
+        self.into_result().ok()
+    }
+
+    /// Converts from `SbiRet` to [`Option<Error>`].
+    ///
+    /// Converts `self` into an [`Option<Error>`], consuming `self`,
+    /// and discarding the success value, if any.
+    ///
+    /// # Examples
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// # use sbi_spec::binary::{SbiRet, Error};
+    /// let x = SbiRet::success(2);
+    /// assert_eq!(x.err(), None);
+    ///
+    /// let x = SbiRet::denied();
+    /// assert_eq!(x.err(), Some(Error::Denied));
+    /// ```
+    // fixme: should be pub const fn once this function in Result is stabilized in constant
+    #[inline]
+    pub fn err(self) -> Option<Error> {
+        self.into_result().err()
+    }
+
+    /// Maps a `SbiRet` to `Result<U, Error>` by applying a function to a
+    /// contained success value, leaving an error value untouched.
+    ///
+    /// This function can be used to compose the results of two functions.
+    ///
+    /// # Examples
+    ///
+    /// Gets detail of a PMU counter and judge if it is a firmware counter.
+    ///
+    /// ```
+    /// # use sbi_spec::binary::SbiRet;
+    /// # use core::mem::size_of;
+    /// # mod sbi_rt {
+    /// #     use sbi_spec::binary::SbiRet;
+    /// #     const TYPE_MASK: usize = 1 << (core::mem::size_of::<usize>() - 1);
+    /// #     pub fn pmu_counter_get_info(_: usize) -> SbiRet { SbiRet::success(TYPE_MASK) }
+    /// # }
+    /// // We assume that counter index 42 is a firmware counter.
+    /// let counter_idx = 42;
+    /// // Masks PMU counter type by setting highest bit in `usize`.
+    /// const TYPE_MASK: usize = 1 << (size_of::<usize>() - 1);
+    /// // Highest bit of returned `counter_info` represents whether it's
+    /// // a firmware counter or a hardware counter.
+    /// let is_firmware_counter = sbi_rt::pmu_counter_get_info(counter_idx)
+    ///     .map(|counter_info| counter_info & TYPE_MASK != 0);
+    /// // If that bit is set, it is a firmware counter.
+    /// assert_eq!(is_firmware_counter, Ok(true));
+    /// ```
+    #[inline]
+    pub fn map<U, F: FnOnce(usize) -> U>(self, op: F) -> Result<U, Error> {
+        self.into_result().map(op)
+    }
+
+    /// Returns the provided default (if error),
+    /// or applies a function to the contained value (if success).
+    ///
+    /// Arguments passed to `map_or` are eagerly evaluated;
+    /// if you are passing the result of a function call,
+    /// it is recommended to use [`map_or_else`],
+    /// which is lazily evaluated.
+    ///
+    /// [`map_or_else`]: SbiRet::map_or_else
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use sbi_spec::binary::SbiRet;
+    /// let x = SbiRet::success(3);
+    /// assert_eq!(x.map_or(42, |v| v & 0b1), 1);
+    ///
+    /// let x = SbiRet::invalid_address();
+    /// assert_eq!(x.map_or(42, |v| v & 0b1), 42);
+    /// ```
+    #[inline]
+    pub fn map_or<U, F: FnOnce(usize) -> U>(self, default: U, f: F) -> U {
+        self.into_result().map_or(default, f)
+    }
+
+    /// Maps a `SbiRet` to `usize` value by applying fallback function `default` to
+    /// a contained error, or function `f` to a contained success value.
+    ///
+    /// This function can be used to unpack a successful result
+    /// while handling an error.
+    ///
+    /// # Examples
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// # use sbi_spec::binary::SbiRet;
+    /// let k = 21;
+    ///
+    /// let x = SbiRet::success(3);
+    /// assert_eq!(x.map_or_else(|e| k * 2, |v| v & 0b1), 1);
+    ///
+    /// let x = SbiRet::already_available();
+    /// assert_eq!(x.map_or_else(|e| k * 2, |v| v & 0b1), 42);
+    /// ```
+    #[inline]
+    pub fn map_or_else<U, D: FnOnce(Error) -> U, F: FnOnce(usize) -> U>(
+        self,
+        default: D,
+        f: F,
+    ) -> U {
+        self.into_result().map_or_else(default, f)
+    }
+
+    /// Maps a `SbiRet` to `Result<T, F>` by applying a function to a
+    /// contained error as [`Error`] struct, leaving success value untouched.
+    ///
+    /// This function can be used to pass through a successful result while handling
+    /// an error.
+    ///
+    /// # Examples
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// # use sbi_spec::binary::{SbiRet, Error};
+    /// fn stringify(x: Error) -> String {
+    ///     if x == Error::AlreadyStarted {
+    ///         "error: already started!".to_string()
+    ///     } else {
+    ///         "error: other error!".to_string()
+    ///     }
+    /// }
+    ///
+    /// let x = SbiRet::success(2);
+    /// assert_eq!(x.map_err(stringify), Ok(2));
+    ///
+    /// let x = SbiRet::already_started();
+    /// assert_eq!(x.map_err(stringify), Err("error: already started!".to_string()));
+    /// ```
+    #[inline]
+    pub fn map_err<F, O: FnOnce(Error) -> F>(self, op: O) -> Result<usize, F> {
+        self.into_result().map_err(op)
+    }
+
+    /// Calls a function with a reference to the contained value if current SBI call succeeded.
+    ///
+    /// Returns the original result.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use sbi_spec::binary::SbiRet;
+    /// // Assume that SBI debug console have read 512 bytes into a buffer.
+    /// let ret = SbiRet::success(512);
+    /// // Inspect the SBI DBCN call result.
+    /// let idx = ret
+    ///     .inspect(|x| println!("bytes written: {x}"))
+    ///     .map(|x| x - 1)
+    ///     .expect("SBI DBCN call failed");
+    /// assert_eq!(idx, 511);
+    /// ```
+    #[inline]
+    pub fn inspect<F: FnOnce(&usize)>(self, f: F) -> Self {
+        if let Ok(ref t) = self.into_result() {
+            f(t);
+        }
+
+        self
+    }
+
+    /// Calls a function with a reference to the contained value if current SBI result is an error.
+    ///
+    /// Returns the original result.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use sbi_spec::binary::SbiRet;
+    /// // Assume that SBI debug console write operation failed for invalid parameter.
+    /// let ret = SbiRet::invalid_param();
+    /// // Print the error if SBI DBCN call failed.
+    /// let ret = ret.inspect_err(|e| eprintln!("failed to read from SBI console: {e:?}"));
+    /// ```
+    #[inline]
+    pub fn inspect_err<F: FnOnce(&Error)>(self, f: F) -> Self {
+        if let Err(ref e) = self.into_result() {
+            f(e);
+        }
+
+        self
+    }
+
+    // TODO: pub fn iter(&self) -> Iter
+    // TODO: pub fn iter_mut(&mut self) -> IterMut
+
+    /// Returns the contained success value, consuming the `self` value.
+    ///
+    /// # Panics
+    ///
+    /// Panics if self is an SBI error with a panic message including the
+    /// passed message, and the content of the SBI state.
+    ///
+    /// # Examples
+    ///
+    /// Basic usage:
+    ///
+    /// ```should_panic
+    /// # use sbi_spec::binary::SbiRet;
+    /// let x = SbiRet::already_stopped();
+    /// x.expect("Testing expect"); // panics with `Testing expect`
+    /// ```
+    #[inline]
+    pub fn expect(self, msg: &str) -> usize {
+        self.into_result().expect(msg)
+    }
+
+    /// Returns the contained success value, consuming the `self` value.
+    ///
+    /// # Panics
+    ///
+    /// Panics if self is an SBI error, with a panic message provided by the
+    /// SBI error converted into [`Error`] struct.
+    ///
+    /// # Examples
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// # use sbi_spec::binary::SbiRet;
+    /// let x = SbiRet::success(2);
+    /// assert_eq!(x.unwrap(), 2);
+    /// ```
+    ///
+    /// ```should_panic
+    /// # use sbi_spec::binary::SbiRet;
+    /// let x = SbiRet::failed();
+    /// x.unwrap(); // panics
+    /// ```
+    #[inline]
+    pub fn unwrap(self) -> usize {
+        self.into_result().unwrap()
+    }
+
+    // Note: No unwrap_or_default as we cannot determine a meaningful default value for a successful SbiRet.
+
+    /// Returns the contained error as [`Error`] struct, consuming the `self` value.
+    ///
+    /// # Panics
+    ///
+    /// Panics if the self is SBI success value, with a panic message
+    /// including the passed message, and the content of the success value.
+    ///
+    /// # Examples
+    ///
+    /// Basic usage:
+    ///
+    /// ```should_panic
+    /// # use sbi_spec::binary::SbiRet;
+    /// let x = SbiRet::success(10);
+    /// x.expect_err("Testing expect_err"); // panics with `Testing expect_err`
+    /// ```
+    #[inline]
+    pub fn expect_err(self, msg: &str) -> Error {
+        self.into_result().expect_err(msg)
+    }
+
+    /// Returns the contained error as [`Error`] struct, consuming the `self` value.
+    ///
+    /// # Panics
+    ///
+    /// Panics if the self is SBI success value, with a custom panic message provided
+    /// by the success value.
+    ///
+    /// # Examples
+    ///
+    /// ```should_panic
+    /// # use sbi_spec::binary::SbiRet;
+    /// let x = SbiRet::success(2);
+    /// x.unwrap_err(); // panics with `2`
+    /// ```
+    ///
+    /// ```
+    /// # use sbi_spec::binary::{SbiRet, Error};
+    /// let x = SbiRet::not_supported();
+    /// assert_eq!(x.unwrap_err(), Error::NotSupported);
+    /// ```
+    #[inline]
+    pub fn unwrap_err(self) -> Error {
+        self.into_result().unwrap_err()
+    }
+
+    // TODO: pub fn into_ok(self) -> usize and pub fn into_err(self) -> Error
+    // once `unwrap_infallible` is stabilized
+
+    /// Returns `res` if self is success value, otherwise otherwise returns the contained error
+    /// of `self` as [`Error`] struct.
+    ///
+    /// Arguments passed to `and` are eagerly evaluated; if you are passing the
+    /// result of a function call, it is recommended to use [`and_then`], which is
+    /// lazily evaluated.
+    ///
+    /// [`and_then`]: SbiRet::and_then
+    ///
+    /// # Examples
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// # use sbi_spec::binary::{SbiRet, Error};
+    /// let x = SbiRet::success(2);
+    /// let y = SbiRet::invalid_param().into_result();
+    /// assert_eq!(x.and(y), Err(Error::InvalidParam));
+    ///
+    /// let x = SbiRet::denied();
+    /// let y = SbiRet::success(3).into_result();
+    /// assert_eq!(x.and(y), Err(Error::Denied));
+    ///
+    /// let x = SbiRet::invalid_address();
+    /// let y = SbiRet::already_available().into_result();
+    /// assert_eq!(x.and(y), Err(Error::InvalidAddress));
+    ///
+    /// let x = SbiRet::success(4);
+    /// let y = SbiRet::success(5).into_result();
+    /// assert_eq!(x.and(y), Ok(5));
+    /// ```
+    // fixme: should be pub const fn once this function in Result is stabilized in constant
+    // fixme: should parameter be `res: SbiRet`?
+    #[inline]
+    pub fn and<U>(self, res: Result<U, Error>) -> Result<U, Error> {
+        self.into_result().and(res)
+    }
+
+    /// Calls `op` if self is success value, otherwise returns the contained error
+    /// as [`Error`] struct.
+    ///
+    /// This function can be used for control flow based on `SbiRet` values.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use sbi_spec::binary::{SbiRet, Error};
+    /// fn sq_then_to_string(x: usize) -> Result<String, Error> {
+    ///     x.checked_mul(x).map(|sq| sq.to_string()).ok_or(Error::Failed)
+    /// }
+    ///
+    /// assert_eq!(SbiRet::success(2).and_then(sq_then_to_string), Ok(4.to_string()));
+    /// assert_eq!(SbiRet::success(1_000_000_000_000).and_then(sq_then_to_string), Err(Error::Failed));
+    /// assert_eq!(SbiRet::invalid_param().and_then(sq_then_to_string), Err(Error::InvalidParam));
+    /// ```
+    #[inline]
+    pub fn and_then<U, F: FnOnce(usize) -> Result<U, Error>>(self, op: F) -> Result<U, Error> {
+        self.into_result().and_then(op)
+    }
+
+    /// Returns `res` if self is SBI error, otherwise returns the success value of `self`.
+    ///
+    /// Arguments passed to `or` are eagerly evaluated; if you are passing the
+    /// result of a function call, it is recommended to use [`or_else`], which is
+    /// lazily evaluated.
+    ///
+    /// [`or_else`]: Result::or_else
+    ///
+    /// # Examples
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// # use sbi_spec::binary::{SbiRet, Error};
+    /// let x = SbiRet::success(2);
+    /// let y = SbiRet::invalid_param().into_result();
+    /// assert_eq!(x.or(y), Ok(2));
+    ///
+    /// let x = SbiRet::denied();
+    /// let y = SbiRet::success(3).into_result();
+    /// assert_eq!(x.or(y), Ok(3));
+    ///
+    /// let x = SbiRet::invalid_address();
+    /// let y = SbiRet::already_available().into_result();
+    /// assert_eq!(x.or(y), Err(Error::AlreadyAvailable));
+    ///
+    /// let x = SbiRet::success(4);
+    /// let y = SbiRet::success(100).into_result();
+    /// assert_eq!(x.or(y), Ok(4));
+    /// ```
+    // fixme: should be pub const fn once this function in Result is stabilized in constant
+    // fixme: should parameter be `res: SbiRet`?
+    #[inline]
+    pub fn or<F>(self, res: Result<usize, F>) -> Result<usize, F> {
+        self.into_result().or(res)
+    }
+
+    /// Calls `op` if self is SBI error, otherwise returns the success value of `self`.
+    ///
+    /// This function can be used for control flow based on result values.
+    ///
+    ///
+    /// # Examples
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// # use sbi_spec::binary::{SbiRet, Error};
+    /// fn is_failed(x: Error) -> Result<usize, bool> { Err(x == Error::Failed) }
+    ///
+    /// assert_eq!(SbiRet::success(2).or_else(is_failed), Ok(2));
+    /// assert_eq!(SbiRet::failed().or_else(is_failed), Err(true));
+    /// ```
+    #[inline]
+    pub fn or_else<F, O: FnOnce(Error) -> Result<usize, F>>(self, op: O) -> Result<usize, F> {
+        self.into_result().or_else(op)
+    }
+
+    /// Returns the contained success value or a provided default.
+    ///
+    /// Arguments passed to `unwrap_or` are eagerly evaluated; if you are passing
+    /// the result of a function call, it is recommended to use [`unwrap_or_else`],
+    /// which is lazily evaluated.
+    ///
+    /// [`unwrap_or_else`]: SbiRet::unwrap_or_else
+    ///
+    /// # Examples
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// # use sbi_spec::binary::SbiRet;
+    /// let default = 2;
+    /// let x = SbiRet::success(9);
+    /// assert_eq!(x.unwrap_or(default), 9);
+    ///
+    /// let x = SbiRet::invalid_param();
+    /// assert_eq!(x.unwrap_or(default), default);
+    /// ```
+    // fixme: should be pub const fn once this function in Result is stabilized in constant
+    #[inline]
+    pub fn unwrap_or(self, default: usize) -> usize {
+        self.into_result().unwrap_or(default)
+    }
+
+    /// Returns the contained success value or computes it from a closure.
+    ///
+    /// # Examples
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// # use sbi_spec::binary::{SbiRet, Error};
+    /// fn invalid_use_zero(x: Error) -> usize { if x == Error::InvalidParam { 0 } else { 3 } }
+    ///
+    /// assert_eq!(SbiRet::success(2).unwrap_or_else(invalid_use_zero), 2);
+    /// assert_eq!(SbiRet::invalid_param().unwrap_or_else(invalid_use_zero), 0);
+    /// ```
+    #[inline]
+    pub fn unwrap_or_else<F: FnOnce(Error) -> usize>(self, op: F) -> usize {
+        self.into_result().unwrap_or_else(op)
+    }
+
+    /// Returns the contained success value, consuming the `self` value,
+    /// without checking that the `SbiRet` contains an error value.
+    ///
+    /// # Safety
+    ///
+    /// Calling this method on an `SbiRet` containing an error value results
+    /// in *undefined behavior*.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use sbi_spec::binary::{SbiRet, Error};
+    /// let x = SbiRet::success(3);
+    /// assert_eq!(unsafe { x.unwrap_unchecked() }, 3);
+    /// ```
+    ///
+    /// ```no_run
+    /// # use sbi_spec::binary::SbiRet;
+    /// let x = SbiRet::no_shmem();
+    /// unsafe { x.unwrap_unchecked(); } // Undefined behavior!
+    /// ```
+    #[inline]
+    pub unsafe fn unwrap_unchecked(self) -> usize {
+        unsafe { self.into_result().unwrap_unchecked() }
+    }
+
+    /// Returns the contained `Error` value, consuming the `self` value,
+    /// without checking that the `SbiRet` does not contain a success value.
+    ///
+    /// # Safety
+    ///
+    /// Calling this method on an `SbiRet` containing a success value results
+    /// in *undefined behavior*.
+    ///
+    /// # Examples
+    ///
+    /// ```no_run
+    /// # use sbi_spec::binary::SbiRet;
+    /// let x = SbiRet::success(4);
+    /// unsafe { x.unwrap_unchecked(); } // Undefined behavior!
+    /// ```
+    ///
+    /// ```
+    /// # use sbi_spec::binary::{SbiRet, Error};
+    /// let x = SbiRet::failed();
+    /// assert_eq!(unsafe { x.unwrap_err_unchecked() }, Error::Failed);
+    /// ```
+    #[inline]
+    pub unsafe fn unwrap_err_unchecked(self) -> Error {
+        unsafe { self.into_result().unwrap_err_unchecked() }
+    }
+}
+
+impl IntoIterator for SbiRet {
+    type Item = usize;
+    type IntoIter = core::result::IntoIter<usize>;
+
+    /// Returns a consuming iterator over the possibly contained value.
+    ///
+    /// The iterator yields one value if the result contains a success value, otherwise none.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use sbi_spec::binary::SbiRet;
+    /// let x = SbiRet::success(5);
+    /// let v: Vec<usize> = x.into_iter().collect();
+    /// assert_eq!(v, [5]);
+    ///
+    /// let x = SbiRet::not_supported();
+    /// let v: Vec<usize> = x.into_iter().collect();
+    /// assert_eq!(v, []);
+    /// ```
+    #[inline]
+    fn into_iter(self) -> Self::IntoIter {
+        self.into_result().into_iter()
+    }
+}
+
+// TODO: implement Try and FromResidual for SbiRet once those traits are stabilized
+/*
+impl core::ops::Try for SbiRet {
+    type Output = usize;
+    type Residual = Result<core::convert::Infallible, Error>;
+
+    #[inline]
+    fn from_output(output: Self::Output) -> Self {
+        SbiRet::success(output)
+    }
+
+    #[inline]
+    fn branch(self) -> core::ops::ControlFlow<Self::Residual, Self::Output> {
+        self.into_result().branch()
+    }
+}
+
+impl core::ops::FromResidual<Result<core::convert::Infallible, Error>> for SbiRet {
+    #[inline]
+    #[track_caller]
+    fn from_residual(residual: Result<core::convert::Infallible, Error>) -> Self {
+        match residual {
+            Err(e) => e.into(),
+        }
+    }
+}
+
+/// ```
+/// # use sbi_spec::binary::SbiRet;
+/// fn test() -> SbiRet {
+///     let value = SbiRet::failed()?;
+///     SbiRet::success(0)
+/// }
+/// assert_eq!(test(), SbiRet::failed());
+/// ```
+mod test_try_trait_for_sbiret {}
+*/
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    #[rustfmt::skip]
+    fn rustsbi_sbi_ret_constructors() {
+        assert_eq!(SbiRet::success(0), SbiRet { value: 0, error: 0 });
+        assert_eq!(SbiRet::success(1037), SbiRet { value: 1037, error: 0 });
+        assert_eq!(SbiRet::success(usize::MAX), SbiRet { value: usize::MAX, error: 0 });
+
+        assert_eq!(SbiRet::failed(), SbiRet { value: 0, error: usize::MAX - 1 + 1 });
+        assert_eq!(SbiRet::not_supported(), SbiRet { value: 0, error: usize::MAX - 2 + 1 });
+        assert_eq!(SbiRet::invalid_param(), SbiRet { value: 0, error: usize::MAX - 3 + 1 });
+        assert_eq!(SbiRet::denied(), SbiRet { value: 0, error: usize::MAX - 4 + 1 });
+        assert_eq!(SbiRet::invalid_address(), SbiRet { value: 0, error: usize::MAX - 5 + 1 });
+        assert_eq!(SbiRet::already_available(), SbiRet { value: 0, error: usize::MAX - 6 + 1 });
+        assert_eq!(SbiRet::already_started(), SbiRet { value: 0, error: usize::MAX - 7 + 1 });
+        assert_eq!(SbiRet::already_stopped(), SbiRet { value: 0, error: usize::MAX - 8 + 1 });
+        assert_eq!(SbiRet::no_shmem(), SbiRet { value: 0, error: usize::MAX - 9 + 1 });
+        assert_eq!(SbiRet::invalid_state(), SbiRet { value: 0, error: usize::MAX - 10 + 1 });
+        assert_eq!(SbiRet::bad_range(), SbiRet { value: 0, error: usize::MAX - 11 + 1 });
+        assert_eq!(SbiRet::timeout(), SbiRet { value: 0, error: usize::MAX - 12 + 1 });
+        assert_eq!(SbiRet::io(), SbiRet { value: 0, error: usize::MAX - 13 + 1 });
+        assert_eq!(SbiRet::denied_locked(), SbiRet { value: 0, error: usize::MAX - 14 + 1 });
+    }
+}

+ 91 - 0
library/sbi-spec/src/binary/shared_physical_ptr.rs

@@ -0,0 +1,91 @@
+use core::marker::PhantomData;
+
+/// Shared memory physical address raw pointer with type annotation.
+///
+/// This is a structure wrapping a raw pointer to the value of the type `T` without
+/// a pointer metadata. `SharedPtr`'s are _thin_; they won't include metadata
+/// as RISC-V SBI does not provide an approach to pass them via SBI calls,
+/// thus the length of type `T` should be decided independently of raw
+/// pointer structure.
+///
+/// `SharedPtr` can be used as a parameter to pass the shared memory physical pointer
+///  with a given base address in RISC-V SBI calls. For example, a `SharedPtr<[u8; 64]>`
+/// would represent a fixed-size 64 byte array on a RISC-V SBI function argument
+/// type.
+///
+/// This structure cannot be dereferenced directly with physical addresses,
+/// because on RISC-V systems the physical address space could be larger than the
+/// virtual ones. Hence, this structure describes the physical memory range by
+/// two `usize` values: the upper `phys_addr_hi` and lower `phys_addr_lo`.
+///
+/// RISC-V SBI extensions may declare special pointer values for shared memory
+/// raw pointers. For example, SBI STA declares that steal-time information
+/// should stop from reporting when the SBI call is invoked using all-ones
+/// bitwise shared pointer, i.e. `phys_addr_hi` and `phys_addr_lo` both equals
+/// `usize::MAX`. `SharedPtr` can be constructed using such special values
+/// by providing them to the `SharedPtr::new` function.
+///
+/// # Requirements
+///
+/// If an SBI function needs to pass a shared memory physical address range to
+/// the SBI implementation (or higher privilege mode), then this physical memory
+/// address range MUST satisfy the following requirements:
+///
+/// * The SBI implementation MUST check that the supervisor-mode software is
+///   allowed to access the specified physical memory range with the access
+///   type requested (read and/or write).
+/// * The SBI implementation MUST access the specified physical memory range
+///   using the PMA attributes.
+/// * The data in the shared memory MUST follow little-endian byte ordering.
+///
+/// *NOTE:* If the supervisor-mode software accesses the same physical memory
+/// range using a memory type different from the PMA, then a loss of coherence
+/// or unexpected memory ordering may occur. The invoking software should
+/// follow the rules and sequences defined in the RISC-V Svpbmt specification
+/// to prevent the loss of coherence and memory ordering.
+///
+/// It is recommended that a memory physical address passed to an SBI function
+/// should use at least two `usize` parameters to support platforms
+/// which have memory physical addresses wider than `XLEN` bits.
+// FIXME: should constrain with `T: Thin` once ptr_metadata feature is stabled;
+// RISC-V SBI does not provide an approach to pass pointer metadata by SBI calls.
+pub struct SharedPtr<T> {
+    phys_addr_lo: usize,
+    phys_addr_hi: usize,
+    _marker: PhantomData<*mut T>,
+}
+
+// FIXME: we should consider strict provenance rules for this pointer-like structure
+// once feature strict_provenance is stabled.
+impl<T> SharedPtr<T> {
+    /// Create a shared physical memory pointer by physical address.
+    #[inline]
+    pub const fn new(phys_addr_lo: usize, phys_addr_hi: usize) -> Self {
+        Self {
+            phys_addr_lo,
+            phys_addr_hi,
+            _marker: PhantomData,
+        }
+    }
+
+    /// Returns low-part physical address of the shared physical memory pointer.
+    #[inline]
+    pub const fn phys_addr_lo(self) -> usize {
+        self.phys_addr_lo
+    }
+
+    /// Returns high-part physical address of the shared physical memory pointer.
+    #[inline]
+    pub const fn phys_addr_hi(self) -> usize {
+        self.phys_addr_hi
+    }
+}
+
+impl<T> Clone for SharedPtr<T> {
+    #[inline(always)]
+    fn clone(&self) -> Self {
+        *self
+    }
+}
+
+impl<T> Copy for SharedPtr<T> {}

+ 29 - 0
library/sbi-spec/src/binary/trigger_mask.rs

@@ -0,0 +1,29 @@
+use super::sbi_ret::SbiRegister;
+
+/// Debug trigger mask structure for the `DBTR` extension §19.
+#[repr(C)]
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub struct TriggerMask<T = usize> {
+    trig_idx_base: T,
+    trig_idx_mask: T,
+}
+
+impl<T: SbiRegister> TriggerMask<T> {
+    /// Construct a [TriggerMask] from mask value and base counter index.
+    ///
+    /// The `trig_idx_base` specifies the starting trigger index, while the `trig_idx_mask` is a
+    /// bitmask indicating which triggers, relative to the base, are to be operated.
+    #[inline]
+    pub const fn from_mask_base(trig_idx_mask: T, trig_idx_base: T) -> Self {
+        Self {
+            trig_idx_mask,
+            trig_idx_base,
+        }
+    }
+
+    /// Returns `mask` and `base` parameters from the [TriggerMask].
+    #[inline]
+    pub const fn into_inner(self) -> (T, T) {
+        (self.trig_idx_mask, self.trig_idx_base)
+    }
+}

+ 5 - 0
library/sbi-spec/src/cppc.rs

@@ -1,6 +1,7 @@
 //! Chapter 14. CPPC Extension (EID #0x43505043 "CPPC").
 
 /// Extension ID for CPPC Extension.
+#[doc(alias = "SBI_EXT_CPPC")]
 pub const EID_CPPC: usize = crate::eid_from_str("CPPC") as _;
 pub use fid::*;
 
@@ -9,17 +10,21 @@ mod fid {
     /// Function ID to probe a CPPC register.
     ///
     /// Declared in §14.1.
+    #[doc(alias = "SBI_EXT_CPPC_PROBE")]
     pub const PROBE: usize = 0;
     /// Function ID to read CPPC register bits.
     ///
     /// Declared in §14.2.
+    #[doc(alias = "SBI_EXT_CPPC_READ")]
     pub const READ: usize = 1;
     /// Function ID to read high bits of a CPPC register.
     ///
     /// Declared in §14.3.
+    #[doc(alias = "SBI_EXT_CPPC_READ_HI")]
     pub const READ_HI: usize = 2;
     /// Function ID to write to a CPPC register.
     ///
     /// Declared in §14.4.
+    #[doc(alias = "SBI_EXT_CPPC_WRITE")]
     pub const WRITE: usize = 3;
 }

+ 4 - 0
library/sbi-spec/src/dbcn.rs

@@ -1,6 +1,7 @@
 //! Chapter 12. Debug Console Extension (EID #0x4442434E "DBCN").
 
 /// Extension ID for Debug Console Extension.
+#[doc(alias = "SBI_EXT_DBCN")]
 pub const EID_DBCN: usize = crate::eid_from_str("DBCN") as _;
 pub use fid::*;
 
@@ -9,13 +10,16 @@ mod fid {
     /// Function ID to write bytes to the debug console from input memory.
     ///
     /// Declared in §12.1.
+    #[doc(alias = "SBI_EXT_DBCN_CONSOLE_WRITE")]
     pub const CONSOLE_WRITE: usize = 0;
     /// Function ID to read bytes from the debug console into an output memory.
     ///
     /// Declared in §12.2.
+    #[doc(alias = "SBI_EXT_DBCN_CONSOLE_READ")]
     pub const CONSOLE_READ: usize = 1;
     /// Function ID to write a single byte to the debug console.
     ///
     /// Declared in §12.3.
+    #[doc(alias = "SBI_EXT_DBCN_CONSOLE_WRITE_BYTE")]
     pub const CONSOLE_WRITE_BYTE: usize = 2;
 }

+ 9 - 0
library/sbi-spec/src/dbtr.rs

@@ -1,6 +1,7 @@
 //! Chapter 19. Debug Triggers Extension (EID #0x44425452 "DBTR")
 
 /// Extension ID for Debug Triggers Extension.
+#[doc(alias = "SBI_EXT_DBTR")]
 pub const EID_DBTR: usize = crate::eid_from_str("DBTR") as _;
 pub use fid::*;
 
@@ -9,33 +10,41 @@ mod fid {
     /// Function ID to get the number of debug triggers on the calling hart.
     ///
     /// Declared in §19.1.
+    #[doc(alias = "SBI_EXT_DBTR_NUM_TRIGGERS")]
     pub const NUM_TRIGGERS: usize = 0;
     /// Function ID to set and enable the shared memory for debug trigger configuration on the calling hart.
     ///
     /// Declared in §19.2.
+    #[doc(alias = "SBI_EXT_DBTR_SET_SHMEM")]
     pub const SET_SHMEM: usize = 1;
     /// Function ID to read the debug trigger state and configuration into shared memory.
     ///
     /// Declared in §19.3.
+    #[doc(alias = "SBI_EXT_DBTR_TRIGGER_READ")]
     pub const READ_TRIGGERS: usize = 2;
     /// Function ID to install debug triggers based on an array of trigger configurations.
     ///
     /// Declared in §19.4.
+    #[doc(alias = "SBI_EXT_DBTR_TRIGGER_INSTALL")]
     pub const INSTALL_TRIGGERS: usize = 3;
     /// Function ID to update already installed debug triggers based on a trigger configuration array.
     ///
     /// Declared in 19.5.
+    #[doc(alias = "SBI_EXT_DBTR_TRIGGER_UPDATE")]
     pub const UPDATE_TRIGGERS: usize = 4;
     /// Function ID to uninstall a set of debug triggers.
     ///
     /// Declared in 19.6.
+    #[doc(alias = "SBI_EXT_DBTR_TRIGGER_UNINSTALL")]
     pub const UNINSTALL_TRIGGERS: usize = 5;
     /// Function ID to enable a set of debug triggers.
     ///
     /// Declared in 19.7.
+    #[doc(alias = "SBI_EXT_DBTR_TRIGGER_ENABLE")]
     pub const ENABLE_TRIGGERS: usize = 6;
     /// Function ID to disable a set of debug triggers.
     ///
     /// Declared in 19.8.
+    #[doc(alias = "SBI_EXT_DBTR_TRIGGER_DISABLE")]
     pub const DISABLE_TRIGGERS: usize = 7;
 }

+ 3 - 0
library/sbi-spec/src/fwft.rs

@@ -1,6 +1,7 @@
 //! Chapter 18. Firmware Features Extension (EID #0x46574654 "FWFT").
 
 /// Extension ID for Firmware Features Extension.
+#[doc(alias = "SBI_EXT_FWFT")]
 pub const EID_FWFT: usize = crate::eid_from_str("FWFT") as _;
 pub use fid::*;
 
@@ -9,10 +10,12 @@ mod fid {
     /// Set the firmware function of the request based on Value and Flags parameters.
     ///
     /// Declared in §18.1.
+    #[doc(alias = "SBI_EXT_FWFT_SET")]
     pub const SET: usize = 0;
     /// Return to the firmware function configuration value.
     ///
     /// Declared in §18.2.
+    #[doc(alias = "SBI_EXT_FWFT_GET")]
     pub const GET: usize = 1;
 }
 

+ 14 - 0
library/sbi-spec/src/hsm.rs

@@ -1,6 +1,7 @@
 //! Chapter 9. Hart State Management Extension (EID #0x48534D "HSM").
 
 /// Extension ID for Hart State Management extension.
+#[doc(alias = "SBI_EXT_HSM")]
 pub const EID_HSM: usize = crate::eid_from_str("HSM") as _;
 pub use fid::*;
 
@@ -9,43 +10,52 @@ pub use fid::*;
 /// Declared in Table 1 at §9.
 pub mod hart_state {
     /// The hart is physically powered-up and executing normally.
+    #[doc(alias = "SBI_HSM_STATE_STARTED")]
     pub const STARTED: usize = 0;
     /// 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.
+    #[doc(alias = "SBI_HSM_STATE_STOPPED")]
     pub const STOPPED: usize = 1;
     /// The hart is pending before being started
     ///
     /// 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.
+    #[doc(alias = "SBI_HSM_STATE_START_PENDING")]
     pub const START_PENDING: usize = 2;
     /// The hart is pending before being stopped.
     ///
     /// 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.
+    #[doc(alias = "SBI_HSM_STATE_STOP_PENDING")]
     pub const STOP_PENDING: usize = 3;
     /// The hart is in a platform-specific suspend (or low-power) state.
+    #[doc(alias = "SBI_HSM_STATE_SUSPENDED")]
     pub const SUSPENDED: usize = 4;
     /// The hart is pending before being suspended.
     ///
     /// The hart has requested 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.
+    #[doc(alias = "SBI_HSM_STATE_SUSPEND_PENDING")]
     pub const SUSPEND_PENDING: usize = 5;
     /// The hart is pending before being resumed.
     ///
     /// 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.
+    #[doc(alias = "SBI_HSM_STATE_RESUME_PENDING")]
     pub const RESUME_PENDING: usize = 6;
 }
 
 /// Hart suspend types.
 pub mod suspend_type {
     /// Default retentive hart suspend type.
+    #[doc(alias = "SBI_HSM_SUSPEND_RET_DEFAULT")]
     pub const RETENTIVE: u32 = 0;
     /// Default non-retentive hart suspend type.
+    #[doc(alias = "SBI_HSM_SUSP_NON_RET_BIT")]
     pub const NON_RETENTIVE: u32 = 0x8000_0000;
 }
 
@@ -54,17 +64,21 @@ mod fid {
     /// Function ID to start executing the given hart at specified address in supervisor-mode.
     ///
     /// Declared in §9.1.
+    #[doc(alias = "SBI_EXT_HSM_HART_START")]
     pub const HART_START: usize = 0;
     /// Function ID to stop executing the calling hart in supervisor-mode.
     ///
     /// Declared in §9.2.
+    #[doc(alias = "SBI_EXT_HSM_HART_STOP")]
     pub const HART_STOP: usize = 1;
     /// Function ID to get the current status (or HSM state id) of the given hart.
     ///
     /// Declared in §9.3.
+    #[doc(alias = "SBI_EXT_HSM_HART_GET_STATUS")]
     pub const HART_GET_STATUS: usize = 2;
     /// Function ID to put the calling hart into suspend or platform-specific lower power states.
     ///
     /// Declared in §9.4.
+    #[doc(alias = "SBI_EXT_HSM_HART_SUSPEND")]
     pub const HART_SUSPEND: usize = 3;
 }

+ 13 - 7
library/sbi-spec/src/lib.rs

@@ -92,6 +92,11 @@ mod tests {
         const_assert_eq!(-7, RET_ERR_ALREADY_STARTED as isize);
         const_assert_eq!(-8, RET_ERR_ALREADY_STOPPED as isize);
         const_assert_eq!(-9, RET_ERR_NO_SHMEM as isize);
+        const_assert_eq!(-10, RET_ERR_INVALID_STATE as isize);
+        const_assert_eq!(-11, RET_ERR_BAD_RANGE as isize);
+        const_assert_eq!(-12, RET_ERR_TIMEOUT as isize);
+        const_assert_eq!(-13, RET_ERR_IO as isize);
+        const_assert_eq!(-14, RET_ERR_DENIED_LOCKED as isize);
     }
     // §4
     #[test]
@@ -386,12 +391,13 @@ mod tests {
     fn test_mpxy() {
         use crate::mpxy::*;
         const_assert_eq!(0x4D505859, EID_MPXY);
-        const_assert_eq!(0, SET_SHMEM);
-        const_assert_eq!(1, GET_CHANNEL_IDS);
-        const_assert_eq!(2, READ_ATTRIBUTE);
-        const_assert_eq!(3, WRITE_ATTRIBUTE);
-        const_assert_eq!(4, SEND_MESSAGE_WITH_RESPONSE);
-        const_assert_eq!(5, SEND_MESSAGE_WITHOUT_RESPONSE);
-        const_assert_eq!(6, GET_NOTIFICATION_EVENTS);
+        const_assert_eq!(0, GET_SHMEM_SIZE);
+        const_assert_eq!(1, SET_SHMEM);
+        const_assert_eq!(2, GET_CHANNEL_IDS);
+        const_assert_eq!(3, READ_ATTRIBUTE);
+        const_assert_eq!(4, WRITE_ATTRIBUTE);
+        const_assert_eq!(5, SEND_MESSAGE_WITH_RESPONSE);
+        const_assert_eq!(6, SEND_MESSAGE_WITHOUT_RESPONSE);
+        const_assert_eq!(7, GET_NOTIFICATION_EVENTS);
     }
 }

+ 27 - 14
library/sbi-spec/src/mpxy.rs

@@ -1,37 +1,50 @@
 //! Chapter 20. Message Proxy Extension (EID #0x4D505859 "MPXY")
 
 /// Extension ID for Message Proxy Extension.
+#[doc(alias = "SBI_EXT_MPXY")]
 pub const EID_MPXY: usize = crate::eid_from_str("MPXY") as _;
 pub use fid::*;
 
 /// Declared in §20.12.
 mod fid {
-    /// Function ID to set the shared memory for sending and receiving messages on the calling hart.
+    /// Function ID to get the version of the message proxy extension.
     ///
     /// Declared in §20.5.
-    pub const SET_SHMEM: usize = 0;
-    /// Function ID to get channel ids of the message channels accessible to the supervisor software in the shared memory of the calling hart.
+    #[doc(alias = "SBI_EXT_MPXY_GET_SHMEM_SIZE")]
+    pub const GET_SHMEM_SIZE: usize = 0;
+    /// Function ID to set the shared memory for sending and receiving messages on the calling hart.
     ///
     /// Declared in §20.6.
-    pub const GET_CHANNEL_IDS: usize = 1;
-    /// Function ID to read message channel attributes.
+    #[doc(alias = "SBI_EXT_MPXY_SET_SHMEM")]
+    pub const SET_SHMEM: usize = 1;
+    /// Function ID to get channel ids of the message channels accessible to the supervisor software in the shared memory of the calling hart.
     ///
     /// Declared in §20.7.
-    pub const READ_ATTRIBUTE: usize = 2;
-    /// Function ID to write message channel attributes.
+    #[doc(alias = "SBI_EXT_MPXY_GET_CHANNEL_IDS")]
+    pub const GET_CHANNEL_IDS: usize = 2;
+    /// Function ID to read message channel attributes.
     ///
     /// Declared in §20.8.
-    pub const WRITE_ATTRIBUTE: usize = 3;
+    #[doc(alias = "SBI_EXT_MPXY_READ_ATTRS")]
+    pub const READ_ATTRIBUTE: usize = 3;
+    /// Function ID to write message channel attributes.
+    ///
+    /// Declared in §20.9.
+    #[doc(alias = "SBI_EXT_MPXY_WRITE_ATTRS")]
+    pub const WRITE_ATTRIBUTE: usize = 4;
     /// Function ID to send a message to the mpxy channel and waits for sbi implementation for the message response.
     ///
-    /// Declared in 20.9.
-    pub const SEND_MESSAGE_WITH_RESPONSE: usize = 4;
+    /// Declared in 20.10.
+    #[doc(alias = "SBI_EXT_MPXY_SEND_MSG_WITH_RESP")]
+    pub const SEND_MESSAGE_WITH_RESPONSE: usize = 5;
     /// Function ID to send a message to the mpxy channel and does not waits for response.
     ///
-    /// Declared in 20.10.
-    pub const SEND_MESSAGE_WITHOUT_RESPONSE: usize = 5;
+    /// Declared in 20.11.
+    #[doc(alias = "SBI_EXT_MPXY_SEND_MSG_WITHOUT_RESP")]
+    pub const SEND_MESSAGE_WITHOUT_RESPONSE: usize = 6;
     /// Function ID to get the message protocol specific notification events on the mpxy channel.
     ///
-    /// Declared in 20.11.
-    pub const GET_NOTIFICATION_EVENTS: usize = 6;
+    /// Declared in 20.12.
+    #[doc(alias = "SBI_EXT_MPXY_GET_NOTIFICATION_EVENTS")]
+    pub const GET_NOTIFICATION_EVENTS: usize = 7;
 }

+ 10 - 0
library/sbi-spec/src/pmu.rs

@@ -1,6 +1,7 @@
 //! Chapter 11. Performance Monitoring Unit Extension (EID #0x504D55 "PMU").
 
 /// Extension ID for Performance Monitoring Unit extension.
+#[doc(alias = "SBI_EXT_PMU")]
 pub const EID_PMU: usize = crate::eid_from_str("PMU") as _;
 pub use fid::*;
 
@@ -9,38 +10,47 @@ mod fid {
     /// Function ID to get the number of counters, both hardware and firmware.
     ///
     /// Declared in §11.6.
+    #[doc(alias = "SBI_EXT_PMU_NUM_COUNTERS")]
     pub const NUM_COUNTERS: usize = 0;
     /// Function ID to get details about the specified counter.
     ///
     /// Declared in §11.7.
+    #[doc(alias = "SBI_EXT_PMU_COUNTER_GET_INFO")]
     pub const COUNTER_GET_INFO: usize = 1;
     /// Function ID to find and configure a counter from a set of counters.
     ///
     /// Declared in §11.8.
+    #[doc(alias = "SBI_EXT_PMU_COUNTER_CFG_MATCH")]
     pub const COUNTER_CONFIG_MATCHING: usize = 2;
     /// Function ID to start or enable a set of counters on the calling hart with the specified initial value.
     ///
     /// Declared in §11.9.
+    #[doc(alias = "SBI_EXT_PMU_COUNTER_START")]
     pub const COUNTER_START: usize = 3;
     /// Function ID to stop or disable a set of counters on the calling hart.
     ///
     /// Declared in §11.10.
+    #[doc(alias = "SBI_EXT_PMU_COUNTER_STOP")]
     pub const COUNTER_STOP: usize = 4;
     /// Function ID to provide the current value of a firmware counter.
     ///
     /// Declared in §11.11.
+    #[doc(alias = "SBI_EXT_PMU_COUNTER_FW_READ")]
     pub const COUNTER_FW_READ: usize = 5;
     /// Function ID to provide the upper 32 bits from the value of the current firmware counter.
     ///
     /// Declared in §11.12.
+    #[doc(alias = "SBI_EXT_PMU_COUNTER_FW_READ_HI")]
     pub const COUNTER_FW_READ_HI: usize = 6;
     /// Function ID to set and enable the PMU snapshot shared memory.
     ///
     /// Declared in §11.13.
+    #[doc(alias = "SBI_EXT_PMU_SNAPSHOT_SET_SHMEM")]
     pub const SNAPSHOT_SET_SHMEM: usize = 7;
     /// Function ID to get details about any PMU event via shared memory.
     ///
     /// Declared in §11.14.
+    #[doc(alias = "SBI_EXT_PMU_EVENT_GET_INFO")]
     pub const EVENT_GET_INFO: usize = 8;
 }
 

+ 8 - 0
library/sbi-spec/src/rfnc.rs

@@ -1,6 +1,7 @@
 //! Chapter 8. RFENCE Extension (EID #0x52464E43 "RFNC").
 
 /// Extension ID for Remote Fence extension.
+#[doc(alias = "SBI_EXT_RFENCE")]
 pub const EID_RFNC: usize = crate::eid_from_str("RFNC") as _;
 pub use fid::*;
 
@@ -9,29 +10,36 @@ mod fid {
     /// Function ID to `FENCE.I` instruction on remote harts.
     ///
     /// Declared in §8.1.
+    #[doc(alias = "SBI_EXT_RFENCE_REMOTE_FENCE_I")]
     pub const REMOTE_FENCE_I: usize = 0;
     /// Function ID to `SFENCE.VMA` for all address spaces on remote harts.
     ///
     /// Declared in §8.2.
+    #[doc(alias = "SBI_EXT_RFENCE_REMOTE_SFENCE_VMA")]
     pub const REMOTE_SFENCE_VMA: usize = 1;
     /// Function ID to address space based `SFENCE.VMA` on remote harts.
     ///
     /// Declared in §8.3.
+    #[doc(alias = "SBI_EXT_RFENCE_REMOTE_SFENCE_VMA_ASID")]
     pub const REMOTE_SFENCE_VMA_ASID: usize = 2;
     /// Function ID to virtual machine id based `HFENCE.GVMA` on remote harts.
     ///
     /// Declared in §8.4.
+    #[doc(alias = "SBI_EXT_RFENCE_REMOTE_HFENCE_GVMA_VMID")]
     pub const REMOTE_HFENCE_GVMA_VMID: usize = 3;
     /// Function ID to `HFENCE.GVMA` for all virtual machines on remote harts.
     ///
     /// Declared in §8.5.
+    #[doc(alias = "SBI_EXT_RFENCE_REMOTE_HFENCE_GVMA")]
     pub const REMOTE_HFENCE_GVMA: usize = 4;
     /// Function ID to address space based `HFENCE.VVMA` for current virtual machine on remote harts.
     ///
     /// Declared in §8.6.
+    #[doc(alias = "SBI_EXT_RFENCE_REMOTE_HFENCE_VVMA_ASID")]
     pub const REMOTE_HFENCE_VVMA_ASID: usize = 5;
     /// Function ID to `HFENCE.VVMA` for all address spaces in the current virtual machine on remote harts.
     ///
     /// Declared in §8.7.
+    #[doc(alias = "SBI_EXT_RFENCE_REMOTE_HFENCE_VVMA")]
     pub const REMOTE_HFENCE_VVMA: usize = 6;
 }

+ 2 - 0
library/sbi-spec/src/spi.rs

@@ -1,6 +1,7 @@
 //! Chapter 7. IPI Extension (EID #0x735049 "sPI: s-mode IPI").
 
 /// Extension ID for Inter-processor Interrupt extension.
+#[doc(alias = "SBI_EXT_IPI")]
 pub const EID_SPI: usize = crate::eid_from_str("sPI") as _;
 pub use fid::*;
 
@@ -9,5 +10,6 @@ mod fid {
     /// Function ID to send an inter-processor interrupt to all harts defined in hart mask.
     ///
     /// Declared in §7.1.
+    #[doc(alias = "SBI_EXT_IPI_SEND_IPI")]
     pub const SEND_IPI: usize = 0;
 }

+ 7 - 0
library/sbi-spec/src/srst.rs

@@ -1,19 +1,25 @@
 //! Chapter 10. System Reset Extension (EID #0x53525354 "SRST").
 
 /// Extension ID for System Reset extension.
+#[doc(alias = "SBI_EXT_SRST")]
 pub const EID_SRST: usize = crate::eid_from_str("SRST") as _;
 pub use fid::*;
 
 /// Shutdown as a reset type.
+#[doc(alias = "SBI_SRST_RESET_TYPE_SHUTDOWN")]
 pub const RESET_TYPE_SHUTDOWN: u32 = 0;
 /// Cold Reboot as a reset type.
+#[doc(alias = "SBI_SRST_RESET_TYPE_COLD_REBOOT")]
 pub const RESET_TYPE_COLD_REBOOT: u32 = 1;
 /// Warm Reboot as a reset type.
+#[doc(alias = "SBI_SRST_RESET_TYPE_WARM_REBOOT")]
 pub const RESET_TYPE_WARM_REBOOT: u32 = 2;
 
 /// No Reason as reset reason.
+#[doc(alias = "SBI_SRST_RESET_REASON_NONE")]
 pub const RESET_REASON_NO_REASON: u32 = 0;
 /// System Failure as reset reason.
+#[doc(alias = "SBI_SRST_RESET_REASON_SYSFAIL")]
 pub const RESET_REASON_SYSTEM_FAILURE: u32 = 1;
 
 /// Declared in §10.2.
@@ -21,5 +27,6 @@ mod fid {
     /// Function ID to reset the system based on provided reset type and reason.
     ///
     /// Declared in §10.1.
+    #[doc(alias = "SBI_EXT_SRST_RESET")]
     pub const SYSTEM_RESET: usize = 0;
 }

+ 11 - 0
library/sbi-spec/src/sse.rs

@@ -1,6 +1,7 @@
 //! Chapter 17. Supervisor Software Events Extension (EID #0x535345 "SSE").
 
 /// Extension ID for Supervisor Software Events Extension.
+#[doc(alias = "SBI_EXT_SSE")]
 pub const EID_SSE: usize = crate::eid_from_str("SSE") as _;
 pub use fid::*;
 
@@ -9,41 +10,51 @@ mod fid {
     /// Function ID to read software event attributes.
     ///
     /// Declared in §17.7
+    #[doc(alias = "SBI_EXT_SSE_READ_ATTR")]
     pub const READ_ATTRS: usize = 0;
     /// Function ID to write software event attributes.
     ///
     /// Declared in §17.8
+    #[doc(alias = "SBI_EXT_SSE_WRITE_ATTR")]
     pub const WRITE_ATTRS: usize = 1;
     /// Function ID to register a software event.
     ///
     /// Declared in §17.9.
+    #[doc(alias = "SBI_EXT_SSE_REGISTER")]
     pub const REGISTER: usize = 2;
     /// Function ID to unregister a software event.
     ///
     /// Declared in §17.10.
+    #[doc(alias = "SBI_EXT_SSE_UNREGISTER")]
     pub const UNREGISTER: usize = 3;
     /// Function ID to enable a software event.
     ///
     /// Declared in §17.11.
+    #[doc(alias = "SBI_EXT_SSE_ENABLE")]
     pub const ENABLE: usize = 4;
     /// Function ID to disable a software event.
     ///
     /// Declared in §17.12.
+    #[doc(alias = "SBI_EXT_SSE_DISABLE")]
     pub const DISABLE: usize = 5;
     /// Function ID to complete software event handling.
     ///
     /// Declared in §17.13.
+    #[doc(alias = "SBI_EXT_SSE_COMPLETE")]
     pub const COMPLETE: usize = 6;
     /// Function ID to inject a software event.
     ///
     /// Declared in §17.14.
+    #[doc(alias = "SBI_EXT_SSE_INJECT")]
     pub const INJECT: usize = 7;
     /// Function ID to unmask software events on the calling hart.
     ///
     /// Declared in §17.15.
+    #[doc(alias = "SBI_EXT_SSE_HART_UNMASK")]
     pub const HART_UNMASK: usize = 8;
     /// Function ID to mask software events on the calling hart.
     ///
     /// Declared in §17.16.
+    #[doc(alias = "SBI_EXT_SSE_HART_MASK")]
     pub const HART_MASK: usize = 9;
 }

+ 2 - 0
library/sbi-spec/src/susp.rs

@@ -1,6 +1,7 @@
 //! Chapter 13. System Suspend Extension (EID #0x53555350 "SUSP").
 
 /// Extension ID for System Suspend Extension.
+#[doc(alias = "SBI_EXT_SUSP")]
 pub const EID_SUSP: usize = crate::eid_from_str("SUSP") as _;
 pub use fid::*;
 
@@ -9,5 +10,6 @@ mod fid {
     /// Function ID to suspend under system-level sleep states.
     ///
     /// Declared in §13.1.
+    #[doc(alias = "SBI_EXT_SUSP_SUSPEND")]
     pub const SUSPEND: usize = 0;
 }

+ 2 - 0
library/sbi-spec/src/time.rs

@@ -1,6 +1,7 @@
 //! Chapter 6. Timer Extension (EID #0x54494D45 "TIME").
 
 /// Extension ID for Timer extension.
+#[doc(alias = "SBI_EXT_TIME")]
 pub const EID_TIME: usize = crate::eid_from_str("TIME") as _;
 pub use fid::*;
 
@@ -9,5 +10,6 @@ mod fid {
     /// Function ID to program the clock for the next event after an absolute time.
     ///
     /// Declared in §6.1.
+    #[doc(alias = "SBI_EXT_TIME_SET_TIMER")]
     pub const SET_TIMER: usize = 0;
 }

+ 1 - 0
library/sbi-testing/CHANGELOG.md

@@ -19,6 +19,7 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 - Rename `MArchId` and `MVendorId` into `MarchId` and `MvendorId` in `BaseCase`
 
 ### Fixed
+- Fix typos.
 
 ## [0.0.2] - 2023-01-20
 

+ 1 - 1
library/sbi-testing/README_EN.md

@@ -8,7 +8,7 @@
 
 - [中文自述文件](README.md)
 
-This library provides a set of test cases for supervisors to verify functions of the supervisor executation environment.
+This library provides a set of test cases for supervisors to verify functions of the supervisor execution environment.
 
 Characters implementation of SBI 1.0.0 specification:
 

+ 1 - 1
library/sbi-testing/src/base.rs

@@ -6,7 +6,7 @@ use sbi_spec::base::impl_id;
 /// Base extension test cases.
 #[derive(Clone, Debug)]
 pub enum Case {
-    /// Can't procceed test for base extension does not exist.
+    /// Can't proceed test for base extension does not exist.
     NotExist,
     /// Test begin.
     Begin,

+ 1 - 1
library/sbi-testing/src/dbcn.rs

@@ -6,7 +6,7 @@ use sbi_spec::binary::Physical;
 /// Debug console extension test cases.
 #[derive(Clone, Debug)]
 pub enum Case {
-    /// Can't procceed test for debug console extension does not exist.
+    /// Can't proceed test for debug console extension does not exist.
     NotExist,
     /// Test begin.
     Begin,

+ 1 - 1
library/sbi-testing/src/hsm.rs

@@ -7,7 +7,7 @@ use sbi_spec::hsm::hart_state;
 /// Hart state monitor extension test cases.
 #[derive(Clone, Debug)]
 pub enum Case<'a> {
-    /// Can't procceed test for Hart state monitor extension does not exist.
+    /// Can't proceed test for Hart state monitor extension does not exist.
     NotExist,
     /// Test begin.
     Begin,

+ 1 - 1
library/sbi-testing/src/spi.rs

@@ -13,7 +13,7 @@ use sbi::HartMask;
 /// Inter-processor Interrupt extension test cases.
 #[derive(Clone, Debug)]
 pub enum Case {
-    /// Can't procceed test for inter-processor interrupt extension does not exist.
+    /// Can't proceed test for inter-processor interrupt extension does not exist.
     NotExist,
     /// Test begin.
     Begin,

+ 2 - 2
library/sbi-testing/src/thread.rs

@@ -75,9 +75,9 @@ impl Thread {
             // 设置线程仍在 S 态并打开中断
             let mut sstatus: usize;
             core::arch::asm!("csrr {}, sstatus", out(reg) sstatus);
-            const PREVILEGE_BIT: usize = 1 << 8;
+            const PRIVILEGE_BIT: usize = 1 << 8;
             const INTERRUPT_BIT: usize = 1 << 5;
-            sstatus |= PREVILEGE_BIT | INTERRUPT_BIT;
+            sstatus |= PRIVILEGE_BIT | INTERRUPT_BIT;
             // 执行线程
             // TODO support RV32 instruction set
             core::arch::asm!(

+ 1 - 1
library/sbi-testing/src/time.rs

@@ -12,7 +12,7 @@ use riscv::{
 /// Timer programmer extension test cases.
 #[derive(Clone, Debug)]
 pub enum Case {
-    /// Can't procceed test for Timer extension does not exist.
+    /// Can't proceed test for Timer extension does not exist.
     NotExist,
     /// Test begin.
     Begin,

+ 149 - 15
prototyper/README.md

@@ -2,50 +2,184 @@
 
 RustSBI Prototyper is a developing RISC-V Secure Bootloader solution. It can be integrated with the Rust or C language ecosystem to form a complete RISC-V bootloader ecosystem.
 
-## Setting Up the Development Environment
+## Usage
+
+### Basic Usage
 
-### Packages to be installed
+#### Required Dependencies:  
+
+Before compiling, ensure the following packages are installed:  
 
 ```bash
 cargo install cargo-binutils
 sudo apt install u-boot-tools
+```  
+
+These are necessary for building the firmware and handling RISC-V binary outputs.
+
+#### Compilation Command:
+
+The following command compiles the RustSBI Prototyper bootloader with optional settings:
+
+```bash
+cargo prototyper [OPTIONS]
 ```
 
+This builds the firmware based on the provided options (or defaults if none are specified). The resulting files—such as `.elf` executables and `.bin` binaries—are generated in the `target/riscv64imac-unknown-none-elf/release/` directory under your project root. See the "Firmware Compilation" section for specific outputs and modes.
+
+These are necessary for building the firmware and handling RISC-V binary outputs.
+
+#### Options
 
-### Optional Tools
+- `-f, --features <FEATURES>`  
+  Enable specific features during the build (supports multiple values, e.g., `--features "feat1,feat2"`).
+- `--fdt <PATH>`  
+  Specify the path to a Flattened Device Tree (FDT) file.  
+  [Environment Variable: `PROTOTYPER_FDT_PATH`]
+- `--payload <PATH>`  
+  Specify the path to the payload ELF file.  
+  [Environment Variable: `PROTOTYPER_PAYLOAD_PATH`]
+- `--jump`  
+  Enable jump mode.
+- `-c, --config-file <PATH>`  
+  Specify the path to a custom configuration file.
+- `-v, --verbose`  
+  Increase logging verbosity (more detailed output).
+- `-q, --quiet`  
+  Decrease logging verbosity (less output).
+- `-h, --help`  
+  Display help information.
 
-The following tools are not mandatory but can be useful for enhancing your development experience.
+> #### Note on FDT Files
+> 
+> Regardless of the mode (Dynamic Firmware, Payload Firmware, or Jump Firmware), specifying an FDT file with `--fdt` ensures it is used to initialize the hardware platform configuration. The FDT file provides essential hardware setup details and overrides the bootloader’s default settings.
 
-#### Install pre-commit
+### Firmware Compilation
 
-pre-commit is a tool that runs code checks before you commit your code.
+#### 1. Dynamic Firmware
+
+**Compilation Command:**  
+Use this command to compile firmware that dynamically loads payloads:
 
 ```bash
-pipx install pre-commit
+cargo prototyper
+```
+
+**Output:**  
+Once compiled, the firmware files will be located in the `target/riscv64imac-unknown-none-elf/release/` directory under your project root:  
+- `rustsbi-prototyper-dynamic.elf` (ELF executable)  
+- `rustsbi-prototyper-dynamic.bin` (Binary file)
+
+#### 2. Payload Firmware
+
+**Compilation Command:**  
+Build firmware with an embedded payload:
+
+```bash
+cargo prototyper --payload <PAYLOAD_PATH>
+```
+
+**Output:**  
+After compilation, the resulting firmware files are generated in the `target/riscv64imac-unknown-none-elf/release/` directory:  
+- `rustsbi-prototyper-payload.elf`  
+- `rustsbi-prototyper-payload.bin`
+
+#### 3. Jump Firmware
+
+**Compilation Command:**  
+Build firmware for jump mode:
+
+```bash
+cargo prototyper --jump
+```
+
+**Output:**  
+After compilation, the resulting firmware files are generated in the `target/riscv64imac-unknown-none-elf/release/` directory:  
+- `rustsbi-prototyper-jump.elf`  
+- `rustsbi-prototyper-jump.bin`
 
-# After installation, run pre-commit install to set it up for your project.
-pre-commit install
+### Configuration File
+
+Customize bootloader parameters by editing `default.toml` located at `prototyper/config/default.toml`. Example:
+
+```toml
+num_hart_max = 8
+stack_size_per_hart = 16384  # 16 KiB (16 * 1024)
+heap_size = 32768            # 32 KiB (32 * 1024)
+page_size = 4096             # 4 KiB
+log_level = "INFO"
+jump_address = 0x80200000
+tlb_flush_limit = 16384      # 16 KiB (page_size * 4)
+```
+
+#### Configuration Options
+
+- `num_hart_max`: Maximum number of supported harts (hardware threads).
+- `stack_size_per_hart`: Stack size per hart, in bytes.
+- `heap_size`: Heap size, in bytes.
+- `page_size`: Page size, in bytes.
+- `log_level`: Logging level (`TRACE`, `DEBUG`, `INFO`, `WARN`, `ERROR`).
+- `jump_address`: Target address for jump mode.
+- `tlb_flush_limit`: TLB flush limit, in bytes.
+
+To use a custom configuration file, specify it with:
+
+```bash
+cargo prototyper -c /path/to/custom_config.toml
+```
+
+### Running an Example
+
+Run the generated firmware in QEMU:
+
+```bash
+qemu-system-riscv64 \
+  -machine virt \
+  -bios target/riscv64imac-unknown-none-elf/release/rustsbi-prototyper-dynamic.elf \
+  -display none \
+  -serial stdio
+```
+
+For additional examples, see the [docs](/prototyper/docs/) directory.
+
+## Setting Up the Development Environment
+
+### Required Packages
+
+See the **[Required Dependencies](#required-dependencies)** under **Usage** above for the packages needed to compile the RustSBI Prototyper.
+
+### Optional Development Tools
+
+These tools are optional but recommended to enhance your development workflow:
+
+#### pre-commit
+
+A tool to run code checks before committing:
+
+```bash
+pipx install pre-commit
+pre-commit install  # Set up pre-commit for the project
 ```
 
-#### Install Cargo Deny
+#### Cargo Deny
 
-Cargo deny is a Cargo plugin used to check the security of your dependencies.
+A Cargo plugin to audit dependency security:
 
 ```bash
 cargo install --locked cargo-deny
 ```
 
-#### Install typos
+#### typos
 
-typos is a spell-checking tool.
+A spell-checking tool for code and documentation:
 
 ```bash
 cargo install typos-cli
 ```
 
-#### Install git cliff
+#### git-cliff
 
-git cliff is a tool for generating changelogs.
+A changelog generation tool:
 
 ```bash
 cargo install git-cliff

+ 1 - 1
prototyper/bench-kernel/Cargo.toml

@@ -11,7 +11,7 @@ publish = false
 [dependencies]
 sbi-testing = { path = "../../library/sbi-testing", features = ["log"] }
 sbi-spec = { version = "0.0.8", path = "../../library/sbi-spec" }
-serde-device-tree = { git = "https://github.com/rustsbi/serde-device-tree", default-features = false }
+serde-device-tree = { git = "https://github.com/rustsbi/serde-device-tree", rev= "e7f9404f", default-features = false }
 serde = { version = "1.0.202", default-features = false, features = ["derive"] }
 log = "0.4"
 riscv = "0.12.1"

+ 150 - 0
prototyper/docs/booting-in-visionfive2-using-uboot-and-rustsbi.md

@@ -0,0 +1,150 @@
+# 在 VisionFive2 中使用 RustSBI 和 U-Boot 启动
+
+本教程给出了使用 RustSBI 和 U-Boot 在 VisionFive2 中启动的基本流程。
+
+本教程使用软件版本如下:
+
+|         软件          |  版本   |
+| :-------------------: | :-----: |
+|          gcc          | **10.5.0**  |
+|  RustSBI Prototyper   |  0.0.0  |
+
+## 烧录 Payload 镜像
+
+> 你可以通过[ 这个链接 ](https://github.com/rustsbi/rustsbi/releases/download/v0.0.0-oscomp-2020/rustsbi_visionfive2_fw_payload.img)下载预编译的 `visionfive2_fw_payload.img`。如果你希望自行编译相关内容,请先跳过此节。
+
+VisionFive2 支持从 1-bit QSPI Nor Flash、SDIO3.0、eMMC 中启动,对于不同启动模式,烧录方式有所不同。
+
+你可以参照[ 昉·星光 2启动模式设置 ](https://doc.rvspace.org/VisionFive2/SDK_Quick_Start_Guide/VisionFive2_SDK_QSG/boot_mode_settings.html)来修改启动模式。
+
+### 从 Flash 中启动
+
+参照 [更新Flash中的SPL和U-Boot](https://doc.rvspace.org/VisionFive2/SDK_Quick_Start_Guide/VisionFive2_SDK_QSG/updating_spl_and_u_boot%20-%20vf2.html) 或 [恢复Bootloader](https://doc.rvspace.org/VisionFive2/SDK_Quick_Start_Guide/VisionFive2_SDK_QSG/recovering_bootloader%20-%20vf2.html) 来更新 Flash 中的 U-Boot 部分。
+
+### 从 EMMC 或 SD 卡中启动
+
+> 非 Flash 的启动方式并不被 VisionFive2 官方文档建议。
+
+首先,你需要确保你的对应磁盘(如 SD 卡)按照[ 启动地址分配 -  昉·惊鸿-7110启动手册 ](https://doc.rvspace.org/VisionFive2/Developing_and_Porting_Guide/JH7110_Boot_UG/JH7110_SDK/boot_address_allocation.html)进行分区。
+
+如果你不清楚如何对磁盘进行分区,你可以选择先完成[烧录 Debian 镜像](#烧录-debian-镜像)一节,这样你对应磁盘的分区表就应已满足上述要求。
+
+之后,将上文得到的 `visionfive2_fw_payload.img` 写入表中所指向的 2 号分区即可。
+
+**本命令仅为参考,请根据自己的磁盘路径修改**
+```shell
+$ cd workshop
+$ dd if=./visionfive2_fw_payload.img of=/dev/sda2 status=progress
+```
+
+
+## 准备工作
+
+创建工作目录
+
+``` shell
+$ mkdir workshop 
+```
+
+### 下载 VisionFive2 Debian 镜像
+
+> 这里仅使用 Debian 镜像提供分区表。如果需要启动其他操作系统,可以在进入 U-Boot 后自行启动。
+
+前往 <https://debian.starfivetech.com/> 下载最新 Debian 镜像。
+
+假设下载下来的文件名为 `debian_image.img.bz2`。
+
+```shell
+$ bzip2 -dk debian_image.img.bz2
+```
+
+移动 `debian_image.img` 到 `workshop` 目录下。
+
+### Clone VisionFive2 SDK
+
+```shell
+$ cd workshop
+$ git clone git@github.com:starfive-tech/VisionFive2.git
+$ cd VisionFive2
+$ git checkout JH7110_VisionFive2_devel
+$ git submodule update --init --recursive
+```
+
+### Clone RustSBI
+
+```shell
+$ cd workshop/VisionFive2
+$ git clone https://github.com/rustsbi/rustsbi
+```
+
+## 编译 SDK 和 RustSBI
+
+编译 SDK,编译产物应在 `work` 目录下。
+
+``` shell
+$ cd workshop/VisionFive2
+$ make -j$(nproc)
+```
+
+
+编译 RustSBI Prototyper,以 U-Boot 作为 Payload。
+
+``` shell
+$ cd workshop/VisionFive2/rustsbi 
+$ cargo prototyper --payload ../work/u-boot/u-boot.bin --fdt ../work/u-boot/arch/riscv/dts/starfive_visionfive2.dtb 
+```
+
+## 生成 Payload 镜像
+
+创建 `workshop/VisionFive2/payload_image.its`:
+
+```plain
+/dts-v1/;
+
+/ {
+	description = "U-boot-spl FIT image for JH7110 VisionFive2";
+	#address-cells = <2>;
+
+	images {
+		firmware {
+			description = "u-boot";
+			data = /incbin/("./rustsbi/target/riscv64imac-unknown-none-elf/release/rustsbi-prototyper-payload.bin");
+			type = "firmware";
+			arch = "riscv";
+			os = "u-boot";
+			load = <0x0 0x40000000>;
+			entry = <0x0 0x40000000>;
+			compression = "none";
+		};
+	};
+
+	configurations {
+		default = "config-1";
+
+		config-1 {
+			description = "U-boot-spl FIT config for JH7110 VisionFive2";
+			firmware = "firmware";
+		};
+	};
+};
+```
+
+生成 `visionfive2_fw_payload.img`:
+```shell
+$ cd workshop/VisionFive2
+$ mkimage -f payload_image.its -A riscv -O u-boot -T firmware visionfive2_fw_payload.img
+```
+
+## 烧录 Debian 镜像
+
+假设你的对应磁盘路径为 `/dev/sda`,按如下命令使用 `dd` 工具进行烧写即可:
+
+```shell
+$ cd workshop
+$ dd if=./debian_image.img of=/dev/sda
+```
+
+## SEE ALSO
+
+- [昉·星光 2 SDK快速参考手册](https://doc.rvspace.org/VisionFive2/SDK_Quick_Start_Guide/index.html)
+- [昉·惊鸿-7110启动手册](https://doc.rvspace.org/VisionFive2/Developing_and_Porting_Guide/JH7110_Boot_UG/)

+ 15 - 7
prototyper/prototyper/Cargo.toml

@@ -16,15 +16,23 @@ uart16550 = "0.0.1"
 riscv-decode = "0.2.1"
 cfg-if = "1.0.0"
 buddy_system_allocator = "0.11.0"
-rustsbi = { version = "0.4.0", features = ["machine"], path = "../../library/rustsbi" }
-sbi-spec = { version = "0.0.8", features = ["legacy"], path = "../../library/sbi-spec" }
+rustsbi = { version = "0.4.0", features = [
+    "machine",
+], path = "../../library/rustsbi" }
+sbi-spec = { version = "0.0.8", features = [
+    "legacy",
+], path = "../../library/sbi-spec" }
 serde = { version = "1.0.202", default-features = false, features = ["derive"] }
-fast-trap = { version = "0.1.0",  features = ["riscv-m"] }
-serde-device-tree = { git = "https://github.com/rustsbi/serde-device-tree", default-features = false }
-uart_xilinx = { git = "https://github.com/duskmoon314/uart-rs/" }
-xuantie-riscv = { git= "https://github.com/rustsbi/xuantie" }
-bouffalo-hal = { git = "https://github.com/rustsbi/bouffalo-hal", rev = "968b949", features = ["bl808"] }
+fast-trap = { version = "0.1.0", features = ["riscv-m"] }
+serde-device-tree = { git = "https://github.com/rustsbi/serde-device-tree", rev = "2a5d6ab7", default-features = false }
+uart_xilinx = { git = "https://github.com/duskmoon314/uart-rs/", rev = "12be9142" }
+xuantie-riscv = { git = "https://github.com/rustsbi/xuantie", rev = "7a521c04" }
+bouffalo-hal = { git = "https://github.com/rustsbi/bouffalo-hal", rev = "968b949", features = [
+    "bl808",
+] }
 static-toml = "1"
+seq-macro = "0.3.5"
+pastey = "0.1.0"
 
 [[bin]]
 name = "rustsbi-prototyper"

+ 12 - 5
prototyper/prototyper/build.rs

@@ -17,15 +17,17 @@ SECTIONS {
     . = 0x80000000;
 
     . = ALIGN(0x1000); /* Need this to create proper sections */
-
     sbi_start = .;
+
     .text : ALIGN(0x1000) { 
         *(.text.entry)
         *(.text .text.*)
     }
 
+    . = ALIGN(0x1000);
+    sbi_rodata_start = .;
+
     .rodata : ALIGN(0x1000) { 
-        sbi_rodata_start = .;
         *(.rodata .rodata.*)
         *(.srodata .srodata.*)
         . = ALIGN(0x1000);  
@@ -41,14 +43,15 @@ SECTIONS {
         __rel_dyn_end = .;
     }
 
+    . = ALIGN(0x1000);
     sbi_rodata_end = .;
 
 	/*
 	 * PMP regions must be to be power-of-2. RX/RW will have separate
 	 * regions, so ensure that the split is power-of-2.
 	 */
-	. = ALIGN(1 << LOG2CEIL((SIZEOF(.rodata) + SIZEOF(.text)
-				+ SIZEOF(.dynsym) + SIZEOF(.rela.dyn))));
+	/* . = ALIGN(1 << LOG2CEIL((SIZEOF(.rodata) + SIZEOF(.text)
+				+ SIZEOF(.dynsym) + SIZEOF(.rela.dyn)))); */
 
     .data : ALIGN(0x1000) { 
         sbi_data_start = .;
@@ -73,9 +76,13 @@ SECTIONS {
         *(.eh_frame)
     }
 
-	. = ALIGN(0x1000); /* Need this to create proper sections */
+    . = ALIGN(0x1000);
     sbi_end = .;
 
+    .text : ALIGN(0x1000) {
+        *(.fdt)
+    }
+
     .text 0x80200000 : ALIGN(0x1000) {
         *(.payload)
     }

+ 22 - 0
prototyper/prototyper/src/devicetree.rs

@@ -2,6 +2,7 @@ use serde::Deserialize;
 use serde_device_tree::{
     Dtb, DtbPtr,
     buildin::{Node, NodeSeq, Reg, StrSeq},
+    value::riscv_pmu::{EventToMhpmcounters, EventToMhpmevent, RawEventToMhpcounters},
 };
 
 use core::ops::Range;
@@ -52,6 +53,16 @@ pub struct Memory<'a> {
     pub reg: Reg<'a>,
 }
 
+#[derive(Deserialize)]
+pub struct Pmu<'a> {
+    #[serde(rename = "riscv,event-to-mhpmevent")]
+    pub event_to_mhpmevent: Option<EventToMhpmevent<'a>>,
+    #[serde(rename = "riscv,event-to-mhpmcounters")]
+    pub event_to_mhpmcounters: Option<EventToMhpmcounters<'a>>,
+    #[serde(rename = "riscv,raw-event-to-mhpmcounters")]
+    pub raw_event_to_mhpmcounters: Option<RawEventToMhpcounters<'a>>,
+}
+
 /// Errors that can occur during device tree parsing.
 pub enum ParseDeviceTreeError {
     /// Invalid device tree format.
@@ -90,3 +101,14 @@ pub fn get_compatible_and_range<'de>(node: &Node) -> Option<(StrSeq<'de>, Range<
         None
     }
 }
+
+pub fn get_compatible<'de>(node: &Node) -> Option<StrSeq<'de>> {
+    let compatible = node
+        .get_prop("compatible")
+        .map(|prop_item| prop_item.deserialize::<StrSeq<'de>>());
+    if let Some(compatible) = compatible {
+        Some(compatible)
+    } else {
+        None
+    }
+}

+ 21 - 9
prototyper/prototyper/src/firmware/mod.rs

@@ -27,7 +27,7 @@ pub struct BootHart {
 }
 
 #[naked]
-#[unsafe(link_section = ".rodata.fdt")]
+#[unsafe(link_section = ".fdt")]
 #[repr(align(16))]
 #[cfg(feature = "fdt")]
 pub extern "C" fn raw_fdt() {
@@ -67,13 +67,13 @@ static mut RODATA_END_ADDRESS: usize = 0;
 
 pub fn set_pmp(memory_range: &Range<usize>) {
     unsafe {
-        // [0..memory_range.start] RW
+        // [0..memory_range.start] RWX
         // [memory_range.start..sbi_start] RWX
         // [sbi_start..sbi_rodata_start] NONE
         // [sbi_rodata_start..sbi_rodata_end] NONE
         // [sbi_rodata_end..sbi_end] NONE
         // [sbi_end..memory_range.end] RWX
-        // [memory_range.end..INF] RW
+        // [memory_range.end..INF] RWX
         use riscv::register::*;
 
         asm!("la {}, sbi_start", out(reg) SBI_START_ADDRESS, options(nomem));
@@ -81,21 +81,28 @@ pub fn set_pmp(memory_range: &Range<usize>) {
         asm!("la {}, sbi_rodata_start", out(reg) RODATA_START_ADDRESS, options(nomem));
         asm!("la {}, sbi_rodata_end", out(reg) RODATA_END_ADDRESS, options(nomem));
 
+        assert_eq!(memory_range.start & 0x3, 0);
+        assert_eq!(memory_range.end & 0x3, 0);
+        assert_eq!(SBI_START_ADDRESS & 0x3, 0);
+        assert_eq!(SBI_END_ADDRESS & 0x3, 0);
+        assert_eq!(RODATA_START_ADDRESS & 0x3, 0);
+        assert_eq!(RODATA_END_ADDRESS & 0x3, 0);
+
         pmpcfg0::set_pmp(0, Range::OFF, Permission::NONE, false);
         pmpaddr0::write(0);
-        pmpcfg0::set_pmp(1, Range::TOR, Permission::RW, false);
+        pmpcfg0::set_pmp(1, Range::TOR, Permission::RWX, false);
         pmpaddr1::write(memory_range.start >> 2);
         pmpcfg0::set_pmp(2, Range::TOR, Permission::RWX, false);
         pmpaddr2::write(SBI_START_ADDRESS >> 2);
         pmpcfg0::set_pmp(3, Range::TOR, Permission::NONE, false);
         pmpaddr3::write(RODATA_START_ADDRESS >> 2);
-        pmpcfg0::set_pmp(4, Range::TOR, Permission::RW, false);
+        pmpcfg0::set_pmp(4, Range::TOR, Permission::NONE, false);
         pmpaddr4::write(RODATA_END_ADDRESS >> 2);
         pmpcfg0::set_pmp(5, Range::TOR, Permission::NONE, false);
         pmpaddr5::write(SBI_END_ADDRESS >> 2);
         pmpcfg0::set_pmp(6, Range::TOR, Permission::RWX, false);
         pmpaddr6::write(memory_range.end >> 2);
-        pmpcfg0::set_pmp(7, Range::TOR, Permission::RW, false);
+        pmpcfg0::set_pmp(7, Range::TOR, Permission::RWX, false);
         pmpaddr7::write(usize::MAX >> 2);
     }
 }
@@ -112,11 +119,16 @@ pub fn log_pmp_cfg(memory_range: &Range<usize>) {
         info!("{:<10} {:<10} {:<15} 0x{:08x}", "PMP 0:", "OFF", "NONE", 0);
         info!(
             "{:<10} {:<10} {:<15} 0x{:08x} - 0x{:08x}",
-            "PMP 1-2:", "TOR", "RW/RWX", memory_range.start, SBI_START_ADDRESS
+            "PMP 1-2:", "TOR", "RWX/RWX", memory_range.start, SBI_START_ADDRESS
         );
         info!(
             "{:<10} {:<10} {:<15} 0x{:08x} - 0x{:08x} - 0x{:08x}",
-            "PMP 3-5:", "TOR", "NONE/RW", RODATA_START_ADDRESS, RODATA_END_ADDRESS, SBI_END_ADDRESS
+            "PMP 3-5:",
+            "TOR",
+            "NONE/NONE",
+            RODATA_START_ADDRESS,
+            RODATA_END_ADDRESS,
+            SBI_END_ADDRESS
         );
         info!(
             "{:<10} {:<10} {:<15} 0x{:08x}",
@@ -126,7 +138,7 @@ pub fn log_pmp_cfg(memory_range: &Range<usize>) {
             "{:<10} {:<10} {:<15} 0x{:08x}",
             "PMP 7:",
             "TOR",
-            "RW",
+            "RWX",
             usize::MAX
         );
     }

+ 2 - 2
prototyper/prototyper/src/macros.rs

@@ -28,13 +28,13 @@ macro_rules! has_csr {
     ($($x: expr)*) => {{
             use core::arch::asm;
             use riscv::register::mtvec;
-            use crate::sbi::early_trap::expected_trap;
+            use crate::sbi::early_trap::light_expected_trap;
             let res: usize;
             unsafe {
                 // Backup old mtvec
                 let mtvec = mtvec::read().bits();
                 // Write expected_trap
-                mtvec::write(expected_trap as _, mtvec::TrapMode::Direct);
+                mtvec::write(light_expected_trap as _, mtvec::TrapMode::Direct);
                 asm!("addi a0, zero, 0",
                     "addi a1, zero, 0",
                     "csrr a2, {}",

+ 58 - 33
prototyper/prototyper/src/main.rs

@@ -24,9 +24,10 @@ use core::arch::{asm, naked_asm};
 use crate::platform::PLATFORM;
 use crate::riscv::csr::menvcfg;
 use crate::riscv::current_hartid;
-use crate::sbi::extensions::{
-    Extension, PrivilegedVersion, hart_extension_probe, hart_privileged_version,
-    privileged_version_detection,
+use crate::sbi::features::hart_mhpm_mask;
+use crate::sbi::features::{
+    Extension, PrivilegedVersion, hart_extension_probe, hart_features_detection,
+    hart_privileged_version,
 };
 use crate::sbi::hart_context::NextStage;
 use crate::sbi::heap::sbi_heap_init;
@@ -66,10 +67,17 @@ extern "C" fn rust_main(_hart_id: usize, opaque: usize, nonstandard_a2: usize) {
         let hart_id = current_hartid();
         info!("{:<30}: {}", "Boot HART ID", hart_id);
 
-        // Detection Priv Version
-        privileged_version_detection();
+        // Detection Hart Features
+        hart_features_detection();
+        // Other harts task entry.
+        trap_stack::prepare_for_trap();
         let priv_version = hart_privileged_version(hart_id);
-        info!("{:<30}: {:?}", "Boot HART Privileged Version", priv_version);
+        let mhpm_mask = hart_mhpm_mask(hart_id);
+        info!(
+            "{:<30}: {:?}",
+            "Boot HART Privileged Version:", priv_version
+        );
+        info!("{:<30}: {:#08x}", "Boot HART MHPM Mask:", mhpm_mask);
 
         // Start kernel.
         local_remote_hsm().start(NextStage {
@@ -79,12 +87,14 @@ extern "C" fn rust_main(_hart_id: usize, opaque: usize, nonstandard_a2: usize) {
         });
 
         info!(
-            "Redirecting hart {} to 0x{:0>16x} in {:?} mode.",
+            "Redirecting hart {} to {:#016x} in {:?} mode.",
             current_hartid(),
             next_addr,
             mpp
         );
     } else {
+        // Detection Hart feature
+        hart_features_detection();
         // Other harts task entry.
         trap_stack::prepare_for_trap();
 
@@ -94,8 +104,6 @@ extern "C" fn rust_main(_hart_id: usize, opaque: usize, nonstandard_a2: usize) {
         }
 
         firmware::set_pmp(unsafe { PLATFORM.info.memory_range.as_ref().unwrap() });
-        // Detection Priv Version
-        privileged_version_detection();
     }
     // Clear all pending IPIs.
     ipi::clear_all();
@@ -110,8 +118,15 @@ extern "C" fn rust_main(_hart_id: usize, opaque: usize, nonstandard_a2: usize) {
         use ::riscv::register::{medeleg, mtvec};
         // Keep supervisor environment calls and illegal instructions in M-mode.
         medeleg::clear_supervisor_env_call();
+        medeleg::clear_load_misaligned();
+        medeleg::clear_store_misaligned();
         medeleg::clear_illegal_instruction();
-        if hart_privileged_version(current_hartid()) >= PrivilegedVersion::Version1_12 {
+
+        let hart_priv_version = hart_privileged_version(current_hartid());
+        if hart_priv_version >= PrivilegedVersion::Version1_11 {
+            asm!("csrw mcountinhibit, {}", in(reg) !0b10);
+        }
+        if hart_priv_version >= PrivilegedVersion::Version1_12 {
             // Configure environment features based on available extensions.
             if hart_extension_probe(current_hartid(), Extension::Sstc) {
                 menvcfg::set_bits(
@@ -134,38 +149,47 @@ unsafe extern "C" fn start() -> ! {
         naked_asm!(
             ".option arch, +a",
             // 1. Turn off interrupt.
-            "   csrw    mie, zero",
+            "
+            csrw    mie, zero",
             // 2. Initialize programming language runtime.
             // only clear bss if hartid matches preferred boot hart id.
-            "   csrr    t0, mhartid",
-            "   bne     t0, zero, 4f",
-            "   call    {relocation_update}",
-            "1:",
-            // 3. Hart 0 clear bss segment.
-            "   lla     t0, sbi_bss_start
-            lla     t1, sbi_bss_end
-         2: bgeu    t0, t1, 3f
+            // Race
+            "
+            lla      t0, 6f
+            li       t1, 1
+            amoadd.w t0, t1, 0(t0)
+            bnez     t0, 4f
+            call     {relocation_update}",
+            // 3. Boot hart clear bss segment.
+            "1:
+            lla     t0, sbi_bss_start
+            lla     t1, sbi_bss_end",
+            "2:
+            bgeu    t0, t1, 3f
             sd      zero, 0(t0)
             addi    t0, t0, 8
             j       2b",
-            "3: ", // Hart 0 set bss ready signal.
-            "   lla     t0, 6f
+            // 3.1 Boot hart set bss ready signal.
+            "3:
+            lla     t0, 7f
             li      t1, 1
             amoadd.w t0, t1, 0(t0)
             j       5f",
-            "4:", // Other harts are waiting for bss ready signal.
-            "   li      t1, 1
-            lla     t0, 6f
+            // 3.2 Other harts are waiting for bss ready signal.
+            "4:
+            lla     t0, 7f
             lw      t0, 0(t0)
-            bne     t0, t1, 4b",
-            "5:",
-             // 4. Prepare stack for each hart.
-            "   call    {locate_stack}",
-            "   call    {main}",
-            "   csrw    mscratch, sp",
-            "   j       {hart_boot}",
-            "  .balign  4",
-            "6:",  // bss ready signal.
+            beqz    t0, 4b",
+            // 4. Prepare stack for each hart.
+            "5:
+            call    {locate_stack}
+            call    {main}
+            csrw    mscratch, sp
+            j       {hart_boot}
+            .balign  4",
+            "6:", // boot hart race signal.
+            "  .word    0",
+            "7:", // bss ready signal.
             "  .word    0",
             relocation_update = sym relocation_update,
             locate_stack = sym trap_stack::locate,
@@ -200,6 +224,7 @@ unsafe extern "C" fn relocation_update() {
             "   addi t0, t0, 24", // Get next rela item
             "2:",
             "   blt t0, t1, 1b",
+            "   fence.i",
 
             // Return
             "   ret",

+ 143 - 67
prototyper/prototyper/src/platform/mod.rs

@@ -22,13 +22,13 @@ use crate::platform::console::{
 use crate::platform::reset::SIFIVETEST_COMPATIBLE;
 use crate::sbi::SBI;
 use crate::sbi::console::SbiConsole;
-use crate::sbi::extensions;
+use crate::sbi::features::extension_detection;
 use crate::sbi::hsm::SbiHsm;
 use crate::sbi::ipi::SbiIpi;
 use crate::sbi::logger;
+use crate::sbi::pmu::{EventToCounterMap, RawEventToCounterMap};
 use crate::sbi::reset::SbiReset;
 use crate::sbi::rfence::SbiRFence;
-use crate::sbi::trap_stack;
 
 mod clint;
 mod console;
@@ -78,13 +78,6 @@ impl Platform {
     }
 
     pub fn init(&mut self, fdt_address: usize) {
-        self.info_init(fdt_address);
-        self.sbi_init();
-        trap_stack::prepare_for_trap();
-        self.ready.swap(true, Ordering::Release);
-    }
-
-    fn info_init(&mut self, fdt_address: usize) {
         let dtb = parse_device_tree(fdt_address).unwrap_or_else(fail::device_tree_format);
         let dtb = dtb.share();
 
@@ -92,9 +85,50 @@ impl Platform {
             .unwrap_or_else(fail::device_tree_deserialize_root);
         let tree: Tree = root.deserialize();
 
-        // Get console device, init sbi console and logger
+        // Get console device, init sbi console and logger.
         self.sbi_find_and_init_console(&root);
+        // Get clint and reset device, init sbi ipi, reset, hsm and rfence.
+        self.sbi_init_ipi_reset_hsm_rfence(&root);
+        // Initialize pmu extension
+        self.sbi_init_pmu(&root);
+        // Get other info
+        self.sbi_misc_init(&tree);
+
+        self.ready.swap(true, Ordering::Release);
+    }
+
+    fn sbi_find_and_init_console(&mut self, root: &serde_device_tree::buildin::Node) {
+        //  Get console device info
+        if let Some(stdout_path) = root.chosen_stdout_path() {
+            if let Some(node) = root.find(stdout_path) {
+                let info = get_compatible_and_range(&node);
+                if let Some((compatible, regs)) = info {
+                    for device_id in compatible.iter() {
+                        if UART16650U8_COMPATIBLE.contains(&device_id) {
+                            self.info.console = Some((regs.start, MachineConsoleType::Uart16550U8));
+                        }
+                        if UART16650U32_COMPATIBLE.contains(&device_id) {
+                            self.info.console =
+                                Some((regs.start, MachineConsoleType::Uart16550U32));
+                        }
+                        if UARTAXILITE_COMPATIBLE.contains(&device_id) {
+                            self.info.console = Some((regs.start, MachineConsoleType::UartAxiLite));
+                        }
+                        if UARTBFLB_COMPATIBLE.contains(&device_id) {
+                            self.info.console = Some((regs.start, MachineConsoleType::UartBflb));
+                        }
+                    }
+                }
+            }
+        }
 
+        // init console and logger
+        self.sbi_console_init();
+        logger::Logger::init().unwrap();
+        info!("Hello RustSBI!");
+    }
+
+    fn sbi_init_ipi_reset_hsm_rfence(&mut self, root: &serde_device_tree::buildin::Node) {
         // Get ipi and reset device info
         let mut find_device = |node: &serde_device_tree::buildin::Node| {
             let info = get_compatible_and_range(node);
@@ -120,7 +154,73 @@ impl Platform {
             }
         };
         root.search(&mut find_device);
+        self.sbi_ipi_init();
+        self.sbi_hsm_init();
+        self.sbi_reset_init();
+        self.sbi_rfence_init();
+    }
 
+    fn sbi_init_pmu(&mut self, root: &serde_device_tree::buildin::Node) {
+        let mut pmu_node: Option<Pmu> = None;
+        let mut find_pmu = |node: &serde_device_tree::buildin::Node| {
+            let info = get_compatible(node);
+            if let Some(compatible_strseq) = info {
+                let compatible_iter = compatible_strseq.iter();
+                for compatible in compatible_iter {
+                    if compatible == "riscv,pmu" {
+                        pmu_node = Some(node.deserialize::<Pmu>());
+                    }
+                }
+            }
+        };
+        root.search(&mut find_pmu);
+
+        if let Some(ref pmu) = pmu_node {
+            let sbi_pmu = self.sbi.pmu.get_or_insert_default();
+            if let Some(ref event_to_mhpmevent) = pmu.event_to_mhpmevent {
+                let len = event_to_mhpmevent.len();
+                for idx in 0..len {
+                    let event = event_to_mhpmevent.get_event_id(idx);
+                    let mhpmevent = event_to_mhpmevent.get_selector_value(idx);
+                    sbi_pmu.insert_event_to_mhpmevent(event, mhpmevent);
+                    debug!(
+                        "pmu: insert event: 0x{:08x}, mhpmevent: {:#016x}",
+                        event, mhpmevent
+                    );
+                }
+            }
+
+            if let Some(ref event_to_mhpmcounters) = pmu.event_to_mhpmcounters {
+                let len = event_to_mhpmcounters.len();
+                for idx in 0..len {
+                    let events = event_to_mhpmcounters.get_event_idx_range(idx);
+                    let mhpmcounters = event_to_mhpmcounters.get_counter_bitmap(idx);
+                    let event_to_counter =
+                        EventToCounterMap::new(mhpmcounters, *events.start(), *events.end());
+                    debug!("pmu: insert event_to_mhpmcounter: {:x?}", event_to_counter);
+                    sbi_pmu.insert_event_to_mhpmcounter(event_to_counter);
+                }
+            }
+
+            if let Some(ref raw_event_to_mhpmcounters) = pmu.raw_event_to_mhpmcounters {
+                let len = raw_event_to_mhpmcounters.len();
+                for idx in 0..len {
+                    let raw_event_select = raw_event_to_mhpmcounters.get_event_idx_base(idx);
+                    let select_mask = raw_event_to_mhpmcounters.get_event_idx_mask(idx);
+                    let counters_mask = raw_event_to_mhpmcounters.get_counter_bitmap(idx);
+                    let raw_event_to_counter =
+                        RawEventToCounterMap::new(counters_mask, raw_event_select, select_mask);
+                    debug!(
+                        "pmu: insert raw_event_to_mhpmcounter: {:x?}",
+                        raw_event_to_counter
+                    );
+                    sbi_pmu.insert_raw_event_to_mhpmcounter(raw_event_to_counter);
+                }
+            }
+        }
+    }
+
+    fn sbi_misc_init(&mut self, tree: &Tree) {
         // Get memory info
         // TODO: More than one memory node or range?
         let memory_reg = tree
@@ -137,7 +237,7 @@ impl Platform {
         self.info.cpu_num = Some(tree.cpus.cpu.len());
 
         // Get model info
-        if let Some(model) = tree.model {
+        if let Some(ref model) = tree.model {
             let model = model.iter().next().unwrap_or("<unspecified>");
             self.info.model = model.to_string();
         } else {
@@ -146,7 +246,7 @@ impl Platform {
         }
 
         // TODO: Need a better extension initialization method
-        extensions::init(&tree.cpus.cpu);
+        extension_detection(&tree.cpus.cpu);
 
         // Find which hart is enabled by fdt
         let mut cpu_list: CpuEnableList = [false; NUM_HART_MAX];
@@ -160,44 +260,6 @@ impl Platform {
         self.info.cpu_enabled = Some(cpu_list);
     }
 
-    fn sbi_init(&mut self) {
-        self.sbi_ipi_init();
-        self.sbi_hsm_init();
-        self.sbi_reset_init();
-        self.sbi_rfence_init();
-    }
-
-    fn sbi_find_and_init_console(&mut self, root: &serde_device_tree::buildin::Node) {
-        //  Get console device info
-        if let Some(stdout_path) = root.chosen_stdout_path() {
-            if let Some(node) = root.find(stdout_path) {
-                let info = get_compatible_and_range(&node);
-                if let Some((compatible, regs)) = info {
-                    for device_id in compatible.iter() {
-                        if UART16650U8_COMPATIBLE.contains(&device_id) {
-                            self.info.console = Some((regs.start, MachineConsoleType::Uart16550U8));
-                        }
-                        if UART16650U32_COMPATIBLE.contains(&device_id) {
-                            self.info.console =
-                                Some((regs.start, MachineConsoleType::Uart16550U32));
-                        }
-                        if UARTAXILITE_COMPATIBLE.contains(&device_id) {
-                            self.info.console = Some((regs.start, MachineConsoleType::UartAxiLite));
-                        }
-                        if UARTBFLB_COMPATIBLE.contains(&device_id) {
-                            self.info.console = Some((regs.start, MachineConsoleType::UartBflb));
-                        }
-                    }
-                }
-            }
-        }
-
-        // init console and logger
-        self.sbi_console_init();
-        logger::Logger::init().unwrap();
-        info!("Hello RustSBI!");
-    }
-
     fn sbi_console_init(&mut self) {
         if let Some((base, console_type)) = self.info.console {
             self.sbi.console = match console_type {
@@ -310,6 +372,7 @@ impl Platform {
         self.print_reset_info();
         self.print_hsm_info();
         self.print_rfence_info();
+        self.print_pmu_info();
     }
 
     #[inline]
@@ -318,7 +381,7 @@ impl Platform {
             Some((base, device)) => {
                 info!(
                     "{:<30}: {:?} (Base Address: 0x{:x})",
-                    "Platform IPI Device", device, base
+                    "Platform IPI Extension", device, base
                 );
             }
             None => warn!("{:<30}: Not Available", "Platform IPI Device"),
@@ -331,7 +394,7 @@ impl Platform {
             Some((base, device)) => {
                 info!(
                     "{:<30}: {:?} (Base Address: 0x{:x})",
-                    "Platform Console Device", device, base
+                    "Platform Console Extension", device, base
                 );
             }
             None => warn!("{:<30}: Not Available", "Platform Console Device"),
@@ -343,30 +406,18 @@ impl Platform {
         if let Some(base) = self.info.reset {
             info!(
                 "{:<30}: Available (Base Address: 0x{:x})",
-                "Platform Reset Device", base
+                "Platform Reset Extension", base
             );
         } else {
             warn!("{:<30}: Not Available", "Platform Reset Device");
         }
     }
 
-    #[inline]
-    fn print_memory_info(&self) {
-        if let Some(memory_range) = &self.info.memory_range {
-            info!(
-                "{:<30}: 0x{:x} - 0x{:x}",
-                "Memory range", memory_range.start, memory_range.end
-            );
-        } else {
-            warn!("{:<30}: Not Available", "Memory range");
-        }
-    }
-
     #[inline]
     fn print_hsm_info(&self) {
         info!(
             "{:<30}: {}",
-            "Platform HSM Device",
+            "Platform HSM Extension",
             if self.have_hsm() {
                 "Available"
             } else {
@@ -379,7 +430,7 @@ impl Platform {
     fn print_rfence_info(&self) {
         info!(
             "{:<30}: {}",
-            "Platform RFence Device",
+            "Platform RFence Extension",
             if self.have_rfence() {
                 "Available"
             } else {
@@ -388,6 +439,27 @@ impl Platform {
         );
     }
 
+    #[inline]
+    fn print_pmu_info(&self) {
+        if self.have_pmu() {
+            info!("{:<30}: {}", "Platform PMU Extension", "Available");
+        } else {
+            warn!("{:<30}: {}", "Platform PMU Extension", "Not Available");
+        }
+    }
+
+    #[inline]
+    fn print_memory_info(&self) {
+        if let Some(memory_range) = &self.info.memory_range {
+            info!(
+                "{:<30}: 0x{:x} - 0x{:x}",
+                "Memory range", memory_range.start, memory_range.end
+            );
+        } else {
+            warn!("{:<30}: Not Available", "Memory range");
+        }
+    }
+
     #[inline]
     fn print_additional_info(&self) {
         if !self.ready.load(Ordering::Acquire) {
@@ -426,6 +498,10 @@ impl Platform {
         self.sbi.rfence.is_some()
     }
 
+    pub fn have_pmu(&self) -> bool {
+        self.sbi.pmu.is_some()
+    }
+
     pub fn ready(&self) -> bool {
         self.ready.load(Ordering::Acquire)
     }

+ 59 - 8
prototyper/prototyper/src/riscv/csr.rs

@@ -1,13 +1,46 @@
 #![allow(unused)]
 
-/// CSR addresses for timer registers.
-///
-/// Time value (lower 32 bits).
-pub const CSR_TIME: u32 = 0xc01;
-/// Time value (upper 32 bits).
-pub const CSR_TIMEH: u32 = 0xc81;
-/// Supervisor timer compare value.
-pub const CSR_STIMECMP: u32 = 0x14D;
+use pastey::paste;
+use seq_macro::seq;
+
+/// CSR addresses
+pub const CSR_STIMECMP: u16 = 0x14D;
+pub const CSR_MCOUNTEREN: u16 = 0x306;
+pub const CSR_MENVCFG: u16 = 0x30a;
+pub const CSR_MCYCLE: u16 = 0xb00;
+pub const CSR_MINSTRET: u16 = 0xb02;
+seq!(N in 3..32 {
+    pub const CSR_MHPMCOUNTER~N: u16 = 0xb00 + N;
+});
+pub const CSR_MCYCLEH: u16 = 0xb80;
+pub const CSR_MINSTRETH: u16 = 0xb82;
+seq!(N in 3..32 {
+    paste! {
+        pub const [<CSR_MHPMCOUNTER ~N H>]: u16 = 0xb80 + N;
+    }
+});
+/* User Counters/Timers */
+pub const CSR_CYCLE: u16 = 0xc00;
+pub const CSR_TIME: u16 = 0xc01;
+pub const CSR_INSTRET: u16 = 0xc02;
+seq!(N in 3..32 {
+    pub const CSR_HPMCOUNTER~N: u16 = 0xc00 + N;
+});
+/// MHPMEVENT
+pub const CSR_MCOUNTINHIBIT: u16 = 0x320;
+pub const CSR_MCYCLECFG: u16 = 0x321;
+pub const CSR_MINSTRETCFG: u16 = 0x322;
+seq!(N in 3..32 {
+    pub const CSR_MHPMEVENT~N: u16 = 0x320 + N;
+});
+
+// For RV32
+pub const CSR_CYCLEH: u16 = 0xc80;
+pub const CSR_TIMEH: u16 = 0xc81;
+pub const CSR_INSTRETH: u16 = 0xc82;
+seq!(N in 3..32 {
+    paste!{ pub const [<CSR_HPMCOUNTER ~N H>]: u16 = 0xc80 + N; }
+});
 
 /// Machine environment configuration register (menvcfg) bit fields.
 pub mod menvcfg {
@@ -61,3 +94,21 @@ pub mod stimecmp {
         }
     }
 }
+
+pub mod mcycle {
+    use core::arch::asm;
+    pub fn write(value: u64) {
+        unsafe {
+            asm!("csrrw zero, mcycle, {}", in(reg) value, options(nomem));
+        }
+    }
+}
+
+pub mod minstret {
+    use core::arch::asm;
+    pub fn write(value: u64) {
+        unsafe {
+            asm!("csrrw zero, minstret, {}", in(reg) value, options(nomem));
+        }
+    }
+}

+ 101 - 1
prototyper/prototyper/src/sbi/early_trap.rs

@@ -1,12 +1,15 @@
+use core::arch::asm;
 use core::arch::naked_asm;
+use riscv::register::mtvec;
 
 /// When you expected some insts will cause trap, use this.
 /// If trap happened, a0 will set to 1, otherwise will be 0.
 ///
 /// This function will change a0 and a1 and will NOT change them back.
+// TODO: Support save trap info.
 #[naked]
 #[repr(align(16))]
-pub(crate) unsafe extern "C" fn expected_trap() {
+pub(crate) unsafe extern "C" fn light_expected_trap() {
     unsafe {
         naked_asm!(
             "add a0, zero, zero",
@@ -19,3 +22,100 @@ pub(crate) unsafe extern "C" fn expected_trap() {
         )
     }
 }
+
+#[repr(C)]
+pub struct TrapInfo {
+    pub mepc: usize,
+    pub mcause: usize,
+    pub mtval: usize,
+}
+
+impl Default for TrapInfo {
+    fn default() -> Self {
+        Self {
+            mepc: 0,
+            mcause: 0,
+            mtval: 0,
+        }
+    }
+}
+
+#[naked]
+#[repr(align(16))]
+pub(crate) unsafe extern "C" fn expected_trap() {
+    unsafe {
+        naked_asm!(
+            "csrr a4, mepc",
+            "sd a4, 0*8(a3)",
+            "csrr a4, mcause",
+            "sd a4, 1*8(a3)",
+            "csrr a4, mtval",
+            "sd a4, 2*8(a3)",
+            "csrr a4, mepc",
+            "addi a4, a4, 4",
+            "csrw mepc, a4",
+            "mret",
+        )
+    }
+}
+
+pub(crate) unsafe fn csr_read_allow<const CSR_NUM: u16>(trap_info: *mut TrapInfo) -> usize {
+    let tinfo = trap_info as usize;
+    let mut ret: usize;
+    // Backup old mtvec
+    let mtvec = mtvec::read().bits();
+    unsafe {
+        core::ptr::write_volatile(&mut (*trap_info).mcause, usize::MAX);
+        // Write expected_trap
+        mtvec::write(expected_trap as _, mtvec::TrapMode::Direct);
+
+        asm!(
+            "add a3, {tinfo}, zero",
+            "add a4, {tinfo}, zero",
+            "csrr {ret}, {csr}",
+            tinfo = in(reg) tinfo,
+            ret = out(reg) ret,
+            csr = const CSR_NUM,
+            options(nostack, preserves_flags)
+        );
+        asm!("csrw mtvec, {}", in(reg) mtvec);
+    }
+    ret
+}
+
+pub(crate) unsafe fn csr_write_allow<const CSR_NUM: u16>(trap_info: *mut TrapInfo, value: usize) {
+    let tinfo = trap_info as usize;
+    // Backup old mtvec
+    let mtvec = mtvec::read().bits();
+    unsafe {
+        core::ptr::write_volatile(&mut (*trap_info).mcause, usize::MAX);
+        // Write expected_trap
+        mtvec::write(expected_trap as _, mtvec::TrapMode::Direct);
+
+        asm!(
+            "add a3, {tinfo}, zero",
+            "add a4, {tinfo}, zero",
+            "csrw {csr}, {value}",
+            tinfo = in(reg) tinfo,
+            csr = const CSR_NUM,
+            value = in(reg) value,
+            options(nostack, preserves_flags)
+        );
+        asm!("csrw mtvec, {}", in(reg) mtvec);
+    }
+}
+
+pub(crate) unsafe fn csr_swap<const CSR_NUM: u16>(val: usize) -> usize {
+    let ret: usize;
+
+    unsafe {
+        asm!(
+            "csrrw {ret}, {csr}, {val}",
+            csr = const CSR_NUM,
+            val = in(reg) val,
+            ret = out(reg) ret,
+            options(nostack, preserves_flags)
+        );
+    }
+    ret
+}

+ 63 - 41
prototyper/prototyper/src/sbi/extensions.rs → prototyper/prototyper/src/sbi/features.rs

@@ -1,11 +1,18 @@
+use seq_macro::seq;
 use serde_device_tree::buildin::NodeSeq;
 
+use crate::riscv::csr::*;
 use crate::riscv::current_hartid;
-use crate::sbi::trap_stack::ROOT_STACK;
+use crate::sbi::early_trap::{TrapInfo, csr_read_allow, csr_write_allow};
+use crate::sbi::trap_stack::{hart_context, hart_context_mut};
+
+use super::early_trap::csr_swap;
 
 pub struct HartFeatures {
     extension: [bool; Extension::COUNT],
     privileged_version: PrivilegedVersion,
+    mhpm_mask: u32,
+    mhpm_bits: u32,
 }
 
 #[derive(Copy, Clone)]
@@ -37,26 +44,22 @@ impl Extension {
     }
 }
 
+/// access hart feature
 pub fn hart_extension_probe(hart_id: usize, ext: Extension) -> bool {
-    unsafe {
-        ROOT_STACK
-            .get_mut(hart_id)
-            .map(|x| x.hart_context().features.extension[ext.index()])
-            .unwrap()
-    }
+    hart_context(hart_id).features.extension[ext.index()]
 }
 
 pub fn hart_privileged_version(hart_id: usize) -> PrivilegedVersion {
-    unsafe {
-        ROOT_STACK
-            .get_mut(hart_id)
-            .map(|x| x.hart_context().features.privileged_version)
-            .unwrap()
-    }
+    hart_context(hart_id).features.privileged_version
 }
 
+pub fn hart_mhpm_mask(hart_id: usize) -> u32 {
+    hart_context(hart_id).features.mhpm_mask
+}
+
+/// Hart features detection
 #[cfg(not(feature = "nemu"))]
-pub fn init(cpus: &NodeSeq) {
+pub fn extension_detection(cpus: &NodeSeq) {
     use crate::devicetree::Cpu;
     for cpu_iter in cpus.iter() {
         let cpu = cpu_iter.deserialize::<Cpu>();
@@ -74,23 +77,13 @@ pub fn init(cpus: &NodeSeq) {
                 hart_exts[ext.index()] = isa.contains(ext.as_str());
             })
         }
-
-        unsafe {
-            ROOT_STACK
-                .get_mut(hart_id)
-                .map(|stack| stack.hart_context().features.extension = hart_exts)
-                .unwrap()
-        }
+        hart_context_mut(hart_id).features.extension = hart_exts;
     }
 }
 
-pub fn privileged_version_detection() {
+fn privileged_version_detection() {
     let mut current_priv_ver = PrivilegedVersion::Unknown;
     {
-        const CSR_MCOUNTEREN: u64 = 0x306;
-        const CSR_MCOUNTINHIBIT: u64 = 0x320;
-        const CSR_MENVCFG: u64 = 0x30a;
-
         if has_csr!(CSR_MCOUNTEREN) {
             current_priv_ver = PrivilegedVersion::Version1_10;
             if has_csr!(CSR_MCOUNTINHIBIT) {
@@ -101,12 +94,48 @@ pub fn privileged_version_detection() {
             }
         }
     }
-    unsafe {
-        ROOT_STACK
-            .get_mut(current_hartid())
-            .map(|stack| stack.hart_context().features.privileged_version = current_priv_ver)
-            .unwrap()
+    hart_context_mut(current_hartid())
+        .features
+        .privileged_version = current_priv_ver;
+}
+
+fn mhpm_detection() {
+    // The standard specifies that mcycle,minstret,mtime must be implemented
+    let mut current_mhpm_mask: u32 = 0b111;
+    let mut trap_info: TrapInfo = TrapInfo::default();
+
+    fn check_mhpm_csr<const CSR_NUM: u16>(trap_info: *mut TrapInfo, mhpm_mask: &mut u32) {
+        unsafe {
+            let old_value = csr_read_allow::<CSR_NUM>(trap_info);
+            if (*trap_info).mcause == usize::MAX {
+                csr_write_allow::<CSR_NUM>(trap_info, 1);
+                if (*trap_info).mcause == usize::MAX && csr_swap::<CSR_NUM>(old_value) == 1 {
+                    (*mhpm_mask) |= 1 << (CSR_NUM - CSR_MCYCLE);
+                }
+            }
+        }
+    }
+
+    macro_rules! m_check_mhpm_csr {
+        ($csr_num:expr, $trap_info:expr, $value:expr) => {
+            check_mhpm_csr::<$csr_num>($trap_info, $value)
+        };
     }
+
+    // CSR_MHPMCOUNTER3:   0xb03
+    // CSR_MHPMCOUNTER31:  0xb1f
+    seq!(csr_num in 0xb03..=0xb1f{
+        m_check_mhpm_csr!(csr_num, &mut trap_info, &mut current_mhpm_mask);
+    });
+
+    hart_context_mut(current_hartid()).features.mhpm_mask = current_mhpm_mask;
+    // TODO: at present, rustsbi prptotyper only supports 64bit.
+    hart_context_mut(current_hartid()).features.mhpm_bits = 64;
+}
+
+pub fn hart_features_detection() {
+    privileged_version_detection();
+    mhpm_detection();
 }
 
 #[cfg(feature = "nemu")]
@@ -114,16 +143,9 @@ pub fn init(cpus: &NodeSeq) {
     for hart_id in 0..cpus.len() {
         let mut hart_exts = [false; Extension::COUNT];
         hart_exts[Extension::Sstc.index()] = true;
-        unsafe {
-            ROOT_STACK
-                .get_mut(hart_id)
-                .map(|stack| {
-                    stack.hart_context().features = HartFeatures {
-                        extension: hart_exts,
-                        privileged_version: PrivilegedVersion::Version1_12,
-                    }
-                })
-                .unwrap()
+        hart_context(hart_id).features = HartFeatures {
+            extension: hart_exts,
+            privileged_version: PrivilegedVersion::Version1_12,
         }
     }
 }

+ 6 - 1
prototyper/prototyper/src/sbi/hart_context.rs

@@ -1,4 +1,4 @@
-use crate::sbi::extensions::HartFeatures;
+use crate::sbi::features::HartFeatures;
 use crate::sbi::hsm::HsmCell;
 use crate::sbi::rfence::RFenceCell;
 use core::ptr::NonNull;
@@ -6,6 +6,8 @@ use core::sync::atomic::AtomicU8;
 use fast_trap::FlowContext;
 use riscv::register::mstatus;
 
+use super::pmu::PmuState;
+
 /// Context for managing hart (hardware thread) state and operations.
 pub(crate) struct HartContext {
     /// Trap context for handling exceptions and interrupts.
@@ -18,6 +20,8 @@ pub(crate) struct HartContext {
     pub ipi_type: AtomicU8,
     /// Supported hart features.
     pub features: HartFeatures,
+    /// PMU State
+    pub pmu_state: PmuState,
 }
 
 impl HartContext {
@@ -26,6 +30,7 @@ impl HartContext {
     pub fn init(&mut self) {
         self.hsm = HsmCell::new();
         self.rfence = RFenceCell::new();
+        self.pmu_state = PmuState::new();
     }
 
     /// Get a non-null pointer to the trap context.

+ 4 - 14
prototyper/prototyper/src/sbi/hsm.rs

@@ -11,6 +11,8 @@ use crate::riscv::current_hartid;
 use crate::sbi::hart_context::NextStage;
 use crate::sbi::trap_stack::ROOT_STACK;
 
+use super::trap_stack::hart_context;
+
 /// Special state indicating a hart is in the process of starting.
 const HART_STATE_START_PENDING_EXT: usize = usize::MAX;
 
@@ -152,24 +154,12 @@ impl<T: core::fmt::Debug> RemoteHsmCell<'_, T> {
 
 /// Gets the local HSM cell for the current hart.
 pub(crate) fn local_hsm() -> LocalHsmCell<'static, NextStage> {
-    unsafe {
-        ROOT_STACK
-            .get_unchecked_mut(current_hartid())
-            .hart_context()
-            .hsm
-            .local()
-    }
+    unsafe { hart_context(current_hartid()).hsm.local() }
 }
 
 /// Gets a remote view of the current hart's HSM cell.
 pub(crate) fn local_remote_hsm() -> RemoteHsmCell<'static, NextStage> {
-    unsafe {
-        ROOT_STACK
-            .get_unchecked_mut(current_hartid())
-            .hart_context()
-            .hsm
-            .remote()
-    }
+    hart_context(current_hartid()).hsm.remote()
 }
 
 /// Gets a remote view of any hart's HSM cell.

+ 8 - 16
prototyper/prototyper/src/sbi/ipi.rs

@@ -1,13 +1,15 @@
+use super::pmu::pmu_firmware_counter_increment;
 use crate::platform::PLATFORM;
 use crate::riscv::csr::stimecmp;
 use crate::riscv::current_hartid;
-use crate::sbi::extensions::{Extension, hart_extension_probe};
+use crate::sbi::features::{Extension, hart_extension_probe};
 use crate::sbi::hsm::remote_hsm;
 use crate::sbi::rfence;
-use crate::sbi::trap_stack::ROOT_STACK;
+use crate::sbi::trap_stack::hart_context;
 use alloc::boxed::Box;
 use core::sync::atomic::Ordering::Relaxed;
 use rustsbi::{HartMask, SbiRet};
+use sbi_spec::pmu::firmware_event;
 use spin::Mutex;
 
 /// IPI type for supervisor software interrupt.
@@ -46,6 +48,7 @@ impl rustsbi::Timer for SbiIpi {
     /// Set timer value for current hart.
     #[inline]
     fn set_timer(&self, stime_value: u64) {
+        pmu_firmware_counter_increment(firmware_event::SET_TIMER);
         let hart_id = current_hartid();
         let uses_sstc = hart_extension_probe(hart_id, Extension::Sstc);
 
@@ -69,6 +72,7 @@ impl rustsbi::Ipi for SbiIpi {
     /// Send IPI to specified harts.
     #[inline]
     fn send_ipi(&self, hart_mask: rustsbi::HartMask) -> SbiRet {
+        pmu_firmware_counter_increment(firmware_event::IPI_SENT);
         let mut hart_mask = hart_mask;
 
         for hart_id in 0..=self.max_hart_id {
@@ -230,24 +234,12 @@ impl SbiIpi {
 
 /// Set IPI type for specified hart.
 pub fn set_ipi_type(hart_id: usize, event_id: u8) -> u8 {
-    unsafe {
-        ROOT_STACK
-            .get_unchecked_mut(hart_id)
-            .hart_context()
-            .ipi_type
-            .fetch_or(event_id, Relaxed)
-    }
+    hart_context(hart_id).ipi_type.fetch_or(event_id, Relaxed)
 }
 
 /// Get and reset IPI type for current hart.
 pub fn get_and_reset_ipi_type() -> u8 {
-    unsafe {
-        ROOT_STACK
-            .get_unchecked_mut(current_hartid())
-            .hart_context()
-            .ipi_type
-            .swap(0, Relaxed)
-    }
+    hart_context(current_hartid()).ipi_type.swap(0, Relaxed)
 }
 
 /// Clear machine software interrupt pending for current hart.

+ 6 - 1
prototyper/prototyper/src/sbi/mod.rs

@@ -3,11 +3,12 @@ use rustsbi::RustSBI;
 pub mod console;
 pub mod hsm;
 pub mod ipi;
+pub mod pmu;
 pub mod reset;
 pub mod rfence;
 
 pub mod early_trap;
-pub mod extensions;
+pub mod features;
 pub mod fifo;
 pub mod hart_context;
 pub mod heap;
@@ -18,6 +19,7 @@ pub mod trap_stack;
 use console::SbiConsole;
 use hsm::SbiHsm;
 use ipi::SbiIpi;
+use pmu::SbiPmu;
 use reset::SbiReset;
 use rfence::SbiRFence;
 
@@ -35,6 +37,8 @@ pub struct SBI {
     pub reset: Option<SbiReset>,
     #[rustsbi(fence)]
     pub rfence: Option<SbiRFence>,
+    #[rustsbi(pmu)]
+    pub pmu: Option<SbiPmu>,
 }
 
 impl SBI {
@@ -45,6 +49,7 @@ impl SBI {
             hsm: None,
             reset: None,
             rfence: None,
+            pmu: None,
         }
     }
 }

+ 1112 - 0
prototyper/prototyper/src/sbi/pmu.rs

@@ -0,0 +1,1112 @@
+use alloc::collections::BTreeMap;
+use alloc::vec::Vec;
+use riscv::register::*;
+use rustsbi::{Pmu, SbiRet};
+use sbi_spec::binary::SharedPtr;
+use sbi_spec::pmu::shmem_size::SIZE;
+use sbi_spec::pmu::*;
+
+use crate::riscv::csr::*;
+use crate::{riscv::current_hartid, sbi::features::hart_mhpm_mask};
+
+use super::features::{PrivilegedVersion, hart_privileged_version};
+use super::trap_stack::{hart_context, hart_context_mut};
+
+/// Maximum number of hardware performance counters supported.
+const PMU_HARDWARE_COUNTER_MAX: usize = 32;
+/// Maximum number of firmware-managed counters supported.
+const PMU_FIRMWARE_COUNTER_MAX: usize = 16;
+/// Marker value for inactive/invalid event indices.
+const PMU_EVENT_IDX_INVALID: usize = usize::MAX;
+
+/// PMU state tracking hardware and firmware performance counters
+#[repr(C)]
+pub struct PmuState {
+    active_event: [usize; PMU_HARDWARE_COUNTER_MAX + PMU_FIRMWARE_COUNTER_MAX],
+    /// Bitmap of active firmware counters (1 bit per counter)
+    fw_counter_state: usize,
+    /// Values for firmware-managed counters
+    fw_counter: [u64; PMU_FIRMWARE_COUNTER_MAX],
+    hw_counters_num: usize,
+    total_counters_num: usize,
+}
+
+impl PmuState {
+    /// Creates a new PMU state with default configuration.
+    pub fn new() -> Self {
+        let mhpm_mask = hart_mhpm_mask(current_hartid());
+        let hw_counters_num = mhpm_mask.count_ones() as usize;
+        let total_counters_num = hw_counters_num + PMU_FIRMWARE_COUNTER_MAX;
+
+        let mut active_event =
+            [PMU_EVENT_IDX_INVALID; PMU_HARDWARE_COUNTER_MAX + PMU_FIRMWARE_COUNTER_MAX];
+        // Standard mappings for fixed counters
+        active_event[1] = 0x0; // time (memory-mapped)
+
+        Self {
+            active_event,
+            fw_counter_state: 0,
+            fw_counter: [0; PMU_FIRMWARE_COUNTER_MAX],
+            hw_counters_num,
+            total_counters_num,
+        }
+    }
+
+    /// Returns the number of hardware counters available.
+    #[inline(always)]
+    pub fn get_hw_counter_num(&self) -> usize {
+        self.hw_counters_num
+    }
+
+    /// Returns the total number of counters (hardware + firmware).
+    #[inline(always)]
+    pub fn get_total_counters_num(&self) -> usize {
+        self.total_counters_num
+    }
+
+    /// Gets the event index associated with a counter.
+    #[inline]
+    pub fn get_event_idx(&self, counter_idx: usize, firmware_event: bool) -> Option<EventIdx> {
+        if counter_idx >= self.total_counters_num {
+            return None;
+        }
+        if firmware_event && counter_idx < self.hw_counters_num {
+            return None;
+        }
+
+        Some(EventIdx::new(self.active_event[counter_idx]))
+    }
+
+    /// Gets the value of a firmware counter.
+    #[inline]
+    pub fn get_fw_counter(&self, counter_idx: usize) -> Option<u64> {
+        if counter_idx < self.hw_counters_num || counter_idx >= self.total_counters_num {
+            return None;
+        }
+        let fw_idx = counter_idx - self.hw_counters_num;
+        // Safety: fw_idx is guaranteed to be within bounds (0..FIRMWARE_COUNTER_MAX)
+        unsafe { Some(*self.fw_counter.get_unchecked(fw_idx)) }
+    }
+
+    /// start a firmware counter with a optional new value.
+    #[inline]
+    fn start_fw_counter(
+        &mut self,
+        counter_idx: usize,
+        initial_value: u64,
+        is_update_value: bool,
+    ) -> Result<(), StartCounterErr> {
+        if counter_idx < self.hw_counters_num || counter_idx >= self.total_counters_num {
+            return Err(StartCounterErr::OffsetInvalid);
+        }
+        let fw_idx = counter_idx - self.hw_counters_num;
+
+        if self.fw_counter_state & (1 << fw_idx) != 0 {
+            return Err(StartCounterErr::AlreadyStart);
+        }
+
+        if is_update_value {
+            self.fw_counter[fw_idx] = initial_value;
+        }
+        self.fw_counter_state |= 1 << fw_idx; // Mark as active
+        Ok(())
+    }
+
+    /// stop a firmware counter
+    #[inline]
+    fn stop_fw_counter(
+        &mut self,
+        counter_idx: usize,
+        is_reset: bool,
+    ) -> Result<(), StopCounterErr> {
+        if counter_idx < self.hw_counters_num || counter_idx >= self.total_counters_num {
+            return Err(StopCounterErr::OffsetInvalid);
+        }
+        let fw_idx = counter_idx - self.hw_counters_num;
+
+        if self.fw_counter_state & (1 << fw_idx) == 0 {
+            return Err(StopCounterErr::AlreadyStop);
+        }
+
+        if is_reset {
+            self.active_event[counter_idx] = PMU_EVENT_IDX_INVALID;
+        }
+        self.fw_counter_state &= !(1 << fw_idx); // Mark as stop
+        Ok(())
+    }
+
+    #[inline]
+    pub fn is_firmware_event_start(&self, counter_idx: usize) -> bool {
+        if counter_idx < self.hw_counters_num || counter_idx >= self.total_counters_num {
+            return false;
+        }
+        let fw_idx = counter_idx - self.hw_counters_num;
+        self.fw_counter_state & (1 << fw_idx) != 0
+    }
+}
+
+pub struct SbiPmu {
+    event_to_mhpmevent: Option<BTreeMap<u32, u64>>,
+    event_to_mhpmcounter: Option<Vec<EventToCounterMap>>,
+    raw_event_to_mhpmcounter: Option<Vec<RawEventToCounterMap>>,
+}
+
+impl Pmu for SbiPmu {
+    /// Returns the total number of available performance counters
+    ///
+    /// Implements SBI PMU extension function (FID #0)
+    #[inline]
+    fn num_counters(&self) -> usize {
+        hart_context(current_hartid())
+            .pmu_state
+            .get_total_counters_num()
+    }
+
+    /// DONE:
+    /// Function: Get details of a counter (FID #1)
+    #[inline]
+    fn counter_get_info(&self, counter_idx: usize) -> SbiRet {
+        if counter_idx >= self.num_counters() {
+            return SbiRet::invalid_param();
+        }
+
+        let pmu_state = &hart_context(current_hartid()).pmu_state;
+        if counter_idx < pmu_state.get_hw_counter_num() {
+            let mask = hart_mhpm_mask(current_hartid());
+
+            // Find the corresponding hardware counter using bit manipulation
+            // This is more efficient than iterating through all possible offsets
+            let mut remaining_mask = mask;
+            let mut count = 0;
+
+            while remaining_mask != 0 {
+                if count == counter_idx {
+                    // Found the counter - get its CSR offset
+                    let offset = remaining_mask.trailing_zeros() as u16;
+                    return SbiRet::success(
+                        CounterInfo::with_hardware_info(CSR_CYCLE + offset, 63).inner(),
+                    );
+                }
+                remaining_mask &= remaining_mask - 1;
+                count += 1;
+            }
+            return SbiRet::invalid_param();
+        }
+
+        SbiRet::success(CounterInfo::with_firmware_info().inner())
+    }
+
+    /// Find and configure a matching counter (FID #2)
+    #[inline]
+    fn counter_config_matching(
+        &self,
+        counter_idx_base: usize,
+        counter_idx_mask: usize,
+        config_flags: usize,
+        event_idx: usize,
+        event_data: u64,
+    ) -> SbiRet {
+        let flags = match flags::CounterCfgFlags::from_bits(config_flags) {
+            Some(flags) => flags,
+            None => return SbiRet::invalid_param(), // Reserved bits are set
+        };
+
+        let event = EventIdx::new(event_idx);
+        let pmu_state = &mut hart_context_mut(current_hartid()).pmu_state;
+        let is_firmware_event = event.is_firmware_event();
+
+        if counter_idx_base >= pmu_state.total_counters_num
+            || (counter_idx_mask & ((1 << pmu_state.total_counters_num) - 1)) == 0
+            || !event.check_event_type()
+            || (is_firmware_event && !event.firmware_event_valid())
+        {
+            return SbiRet::invalid_param();
+        }
+
+        let skip_match = flags.contains(flags::CounterCfgFlags::SKIP_MATCH);
+
+        let counter_idx;
+
+        if skip_match {
+            // If SKIP_MATCH is set, use the first counter in the mask without searching
+            if let Some(ctr_idx) = CounterMask::new(counter_idx_base, counter_idx_mask).next() {
+                if pmu_state.active_event[ctr_idx] == PMU_EVENT_IDX_INVALID {
+                    return SbiRet::invalid_param();
+                }
+                counter_idx = ctr_idx;
+            } else {
+                return SbiRet::invalid_param();
+            }
+        } else {
+            let match_result: Result<usize, SbiRet>;
+            if event.is_firmware_event() {
+                match_result = self.find_firmware_counter(
+                    counter_idx_base,
+                    counter_idx_mask,
+                    event_idx,
+                    pmu_state,
+                );
+            } else {
+                match_result = self.find_hardware_counter(
+                    counter_idx_base,
+                    counter_idx_mask,
+                    event_idx,
+                    event_data,
+                    pmu_state,
+                );
+            }
+            match match_result {
+                Ok(ctr_idx) => {
+                    counter_idx = ctr_idx;
+                }
+                Err(err) => {
+                    return err;
+                }
+            }
+            pmu_state.active_event[counter_idx] = event_idx;
+        }
+
+        if configure_counter(pmu_state, counter_idx, event, flags) {
+            return SbiRet::success(counter_idx);
+        }
+
+        return SbiRet::not_supported();
+    }
+
+    /// Start one or more counters (FID #3)
+    /// Note: The next two functions contain redundant logic and should be refactored.
+    #[inline]
+    fn counter_start(
+        &self,
+        counter_idx_base: usize,
+        counter_idx_mask: usize,
+        start_flags: usize,
+        initial_value: u64,
+    ) -> SbiRet {
+        let flags = match flags::CounterStartFlags::from_bits(start_flags) {
+            Some(flags) => flags,
+            None => return SbiRet::invalid_param(),
+        };
+
+        let pmu_state = &mut hart_context_mut(current_hartid()).pmu_state;
+        let is_update_value = flags.contains(flags::CounterStartFlags::INIT_VALUE);
+
+        if counter_idx_base >= pmu_state.total_counters_num
+            || (counter_idx_mask & ((1 << pmu_state.total_counters_num) - 1)) == 0
+        {
+            return SbiRet::invalid_param();
+        }
+
+        if flags.contains(flags::CounterStartFlags::INIT_SNAPSHOT) {
+            return SbiRet::no_shmem();
+        }
+
+        for counter_idx in CounterMask::new(counter_idx_base, counter_idx_mask) {
+            if counter_idx >= pmu_state.total_counters_num {
+                return SbiRet::invalid_param();
+            }
+
+            let start_result = if counter_idx >= pmu_state.get_hw_counter_num() {
+                pmu_state.start_fw_counter(counter_idx, initial_value, is_update_value)
+            } else {
+                let mhpm_offset = get_mhpm_csr_offset(counter_idx).unwrap();
+                start_hardware_counter(mhpm_offset, initial_value, is_update_value)
+            };
+            match start_result {
+                Ok(_) => {}
+                Err(StartCounterErr::AlreadyStart) => {
+                    return SbiRet::already_started();
+                }
+                Err(StartCounterErr::OffsetInvalid) => {
+                    return SbiRet::invalid_param();
+                }
+            }
+        }
+        SbiRet::success(0)
+    }
+
+    /// Stop one or more counters (FID #4)
+    #[inline]
+    fn counter_stop(
+        &self,
+        counter_idx_base: usize,
+        counter_idx_mask: usize,
+        stop_flags: usize,
+    ) -> SbiRet {
+        let flags = match flags::CounterStopFlags::from_bits(stop_flags) {
+            Some(flags) => flags,
+            None => return SbiRet::invalid_param(),
+        };
+
+        let pmu_state = &mut hart_context_mut(current_hartid()).pmu_state;
+        let is_reset = flags.contains(flags::CounterStopFlags::RESET);
+
+        if counter_idx_base >= pmu_state.total_counters_num
+            || (counter_idx_mask & ((1 << pmu_state.total_counters_num) - 1)) == 0
+        {
+            return SbiRet::invalid_param();
+        }
+
+        if flags.contains(flags::CounterStopFlags::TAKE_SNAPSHOT) {
+            return SbiRet::no_shmem();
+        }
+
+        for counter_idx in CounterMask::new(counter_idx_base, counter_idx_mask) {
+            if counter_idx >= pmu_state.total_counters_num {
+                return SbiRet::invalid_param();
+            }
+
+            let stop_result = if counter_idx >= pmu_state.get_hw_counter_num() {
+                pmu_state.stop_fw_counter(counter_idx, is_reset)
+            } else {
+                // If RESET flag is set, mark the counter as inactive
+                if is_reset {
+                    pmu_state.active_event[counter_idx] = PMU_EVENT_IDX_INVALID;
+                }
+                let mhpm_offset = get_mhpm_csr_offset(counter_idx).unwrap();
+                stop_hardware_counter(mhpm_offset, is_reset)
+            };
+            match stop_result {
+                Ok(_) => {}
+                Err(StopCounterErr::OffsetInvalid) => return SbiRet::invalid_param(),
+                Err(StopCounterErr::AlreadyStop) => return SbiRet::already_stopped(),
+            }
+        }
+        SbiRet::success(0)
+    }
+
+    /// Reads a firmware counter value
+    /// Function: Read a firmware counter (FID #5).
+    #[inline]
+    fn counter_fw_read(&self, counter_idx: usize) -> SbiRet {
+        let pmu_state = &hart_context(current_hartid()).pmu_state;
+        match pmu_state.get_event_idx(counter_idx, true) {
+            Some(event_id) if event_id.firmware_event_valid() => {
+                if event_id.event_code() == firmware_event::PLATFORM {
+                    // TODO: Handle platform-specific PMU events
+                    return SbiRet::invalid_param();
+                }
+                match pmu_state.get_fw_counter(counter_idx) {
+                    Some(value) => SbiRet::success(value as usize),
+                    None => SbiRet::invalid_param(),
+                }
+            }
+            _ => SbiRet::invalid_param(),
+        }
+    }
+
+    /// Function: Read a firmware counter high bits (FID #6).
+    #[inline]
+    fn counter_fw_read_hi(&self, _counter_idx: usize) -> SbiRet {
+        // The Specification states the this function always return zero in sbiret.value for RV64 (or higher) systems.
+        // Currently RustSBI Prototyper only supports RV64 systems
+        SbiRet::success(0)
+    }
+
+    /// Function: Set PMU snapshot shared memory (FID #7).
+    #[inline]
+    fn snapshot_set_shmem(&self, shmem: SharedPtr<[u8; SIZE]>, flags: usize) -> SbiRet {
+        // Optional function, `not_supported` is returned if not implemented.
+        let _ = (shmem, flags);
+        SbiRet::not_supported()
+    }
+}
+
+impl Default for SbiPmu {
+    fn default() -> Self {
+        Self {
+            event_to_mhpmevent: None,
+            event_to_mhpmcounter: None,
+            raw_event_to_mhpmcounter: None,
+        }
+    }
+}
+
+impl SbiPmu {
+    fn find_firmware_counter(
+        &self,
+        counter_idx_base: usize,
+        counter_idx_mask: usize,
+        event_idx: usize,
+        pmu_state: &PmuState,
+    ) -> Result<usize, SbiRet> {
+        // TODO: support `PLATFORM` event
+        let event = EventIdx::new(event_idx);
+        if !event.firmware_event_valid() {
+            return Err(SbiRet::not_supported());
+        }
+
+        //  TODO: If all firmware events are implemented,
+        // this condition should be removed.
+        if event.event_code() <= 21 {
+            if !PMU_FIRMWARE_EVENT_SUPPORTED[event.event_code()] {
+                return Err(SbiRet::not_supported());
+            }
+        }
+
+        for counter_idx in CounterMask::new(counter_idx_base, counter_idx_mask) {
+            // If counter idx is not a firmware counter index, skip this index
+            if counter_idx < pmu_state.get_hw_counter_num()
+                || counter_idx >= pmu_state.get_total_counters_num()
+            {
+                continue;
+            }
+            // If the firmware counter at this index is already occupied, skip this index
+            if pmu_state.active_event[counter_idx] != PMU_EVENT_IDX_INVALID {
+                continue;
+            }
+            return Ok(counter_idx);
+        }
+        return Err(SbiRet::not_supported());
+    }
+
+    fn find_hardware_counter(
+        &self,
+        counter_idx_base: usize,
+        counter_idx_mask: usize,
+        event_idx: usize,
+        event_data: u64,
+        pmu_state: &PmuState,
+    ) -> Result<usize, SbiRet> {
+        let event = EventIdx::new(event_idx);
+        let mut hw_counters_mask = 0;
+        // Find the counters available for the event.
+        if event.is_raw_event() {
+            if let Some(ref raw_event_map_vec) = self.raw_event_to_mhpmcounter {
+                for raw_event_map in raw_event_map_vec {
+                    if raw_event_map.have_event(event_data) {
+                        hw_counters_mask = raw_event_map.get_counter_mask();
+                        break;
+                    }
+                }
+            } else {
+                return Err(SbiRet::not_supported());
+            }
+        } else {
+            // event is general event or cache event
+            if let Some(ref sbi_hw_event_map_vec) = self.event_to_mhpmcounter {
+                for sbi_hw_event_map in sbi_hw_event_map_vec {
+                    if sbi_hw_event_map.have_event(event_idx as u32) {
+                        hw_counters_mask = sbi_hw_event_map.get_counter_mask();
+                        break;
+                    }
+                }
+            } else {
+                return Err(SbiRet::not_supported());
+            }
+        }
+        // mcycle, time, minstret cannot be used for other events.
+        let mhpm_mask = hart_mhpm_mask(current_hartid());
+        let can_use_counter_mask = hw_counters_mask & mhpm_mask;
+
+        // Find a counter that meets the conditions from a set of counters
+        for counter_idx in CounterMask::new(counter_idx_base, counter_idx_mask) {
+            if counter_idx >= pmu_state.hw_counters_num {
+                continue;
+            }
+
+            // If the counter idx corresponding to the hardware counter index cannot be used by the event,
+            // or has already been used, skip this counter idx
+            let mhpm_offset = get_mhpm_csr_offset(counter_idx).unwrap();
+            // Find a unused counter
+            if (can_use_counter_mask >> mhpm_offset) & 0x1 == 0
+                || pmu_state.active_event[counter_idx] != PMU_EVENT_IDX_INVALID
+            {
+                continue;
+            }
+            // If mcycle is selected but the event is not SBI_PMU_HW_CPU_CYCLES,
+            // or minstret is selected but the event is not SBI_PMU_HW_INSTRUCTIONS, skip
+            if (mhpm_offset == 0 && event_idx != 1) || (mhpm_offset == 2 && event_idx != 2) {
+                continue;
+            }
+            // If the counter idx corresponding to the hardware counter index has already started counting, skip the counter
+            if hart_privileged_version(current_hartid()) >= PrivilegedVersion::Version1_11 {
+                let inhibit = riscv::register::mcountinhibit::read();
+                if (inhibit.bits() & (1 << mhpm_offset)) == 0 {
+                    continue;
+                }
+            }
+
+            // Found a counter that meets the conditions - write the event value to the corresponding mhpmevent
+            self.pmu_update_hardware_mhpmevent(mhpm_offset, event_idx, event_data)?;
+            return Ok(counter_idx);
+        }
+        Err(SbiRet::not_supported())
+    }
+
+    fn pmu_update_hardware_mhpmevent(
+        &self,
+        mhpm_offset: u16,
+        event_idx: usize,
+        event_data: u64,
+    ) -> Result<(), SbiRet> {
+        // If the event is SBI_PMU_HW_CPU_CYCLES and mcycle is selected,
+        // or the event is SBI_PMU_HW_INSTRUCTIONS and minstret is selected, return directly
+        if (mhpm_offset == 0 && event_idx == 1) || (mhpm_offset == 2 && event_idx == 2) {
+            return Ok(());
+        }
+        // Validate counter offset range (only mhpmcounter3-31 are configurable)
+        if mhpm_offset == 1 || mhpm_offset > 31 {
+            return Err(SbiRet::not_supported());
+        }
+
+        let event = EventIdx::new(event_idx);
+
+        // Determine the value to write to mhpmevent CSR
+        let mhpmevent_val = if event.is_raw_event() {
+            // For raw events, use the provided event_data directly
+            event_data
+        } else if let Some(ref event_to_mhpmevent) = self.event_to_mhpmevent {
+            // For standard events, look up the corresponding mhpmevent value
+            *event_to_mhpmevent
+                .get(&(event_idx as u32))
+                .ok_or(SbiRet::not_supported())?
+        } else if self.event_to_mhpmcounter.is_some() {
+            // Handle QEMU compatibility case:
+            // When only event_to_mhpmcounter is available (like in QEMU),
+            // use the event index directly as the raw event value
+            event_idx as u64
+        } else {
+            // No mapping available for this event
+            return Err(SbiRet::not_supported());
+        };
+
+        write_mhpmevent(mhpm_offset, mhpmevent_val);
+        Ok(())
+    }
+
+    pub fn insert_event_to_mhpmevent(&mut self, event: u32, mhpmevent: u64) {
+        let event_to_mhpmevent_map = self.event_to_mhpmevent.get_or_insert_default();
+
+        //TODO: When https://github.com/rust-lang/rust/issues/82766 is stable, change this to `try_insert`
+        if let Some(mhpmevent_mapped) = event_to_mhpmevent_map.get(&event) {
+            error!(
+                "Try to map event:0x{:08x} to mhpmevent:0x{:016x}, but the event has been mapped to mhpmevent:{}, please check the device tree file",
+                event, mhpmevent, mhpmevent_mapped
+            );
+        } else {
+            event_to_mhpmevent_map.insert(event, mhpmevent);
+        }
+    }
+
+    pub fn insert_event_to_mhpmcounter(&mut self, event_to_counter: EventToCounterMap) {
+        let event_to_mhpmcounter_map = self.event_to_mhpmcounter.get_or_insert_default();
+        for event_to_mhpmcounter in event_to_mhpmcounter_map.iter() {
+            if event_to_mhpmcounter.is_overlop(&event_to_counter) {
+                error!(
+                    "The mapping of event_to_mhpmcounter {:?} and {:?} overlap, please check the device tree file",
+                    event_to_mhpmcounter, event_to_counter
+                );
+                return;
+            }
+        }
+        event_to_mhpmcounter_map.push(event_to_counter);
+    }
+
+    pub fn insert_raw_event_to_mhpmcounter(&mut self, raw_event_to_counter: RawEventToCounterMap) {
+        let raw_event_to_mhpmcounter_map = self.raw_event_to_mhpmcounter.get_or_insert_default();
+        for raw_event_to_mhpmcounter in raw_event_to_mhpmcounter_map.iter() {
+            if raw_event_to_mhpmcounter.is_overlop(&raw_event_to_counter) {
+                error!(
+                    "The mapping of raw_event_to_mhpmcounter {:?} and {:?} overlap, please check the device tree file",
+                    raw_event_to_mhpmcounter, raw_event_to_counter
+                );
+                return;
+            }
+        }
+        raw_event_to_mhpmcounter_map.push(raw_event_to_counter);
+    }
+}
+
+/// Configures a counter to monitor an event based on the given flags.
+///
+/// Returns `true` if configuration succeeds, `false` otherwise.
+#[inline]
+fn configure_counter(
+    pmu_state: &mut PmuState,
+    counter_idx: usize,
+    event: EventIdx,
+    flags: flags::CounterCfgFlags,
+) -> bool {
+    let auto_start = flags.contains(flags::CounterCfgFlags::AUTO_START);
+    let clear_value = flags.contains(flags::CounterCfgFlags::CLEAR_VALUE);
+    if event.is_firmware_event() {
+        let firmware_event_idx = counter_idx - pmu_state.hw_counters_num;
+        if clear_value {
+            pmu_state.fw_counter[firmware_event_idx] = 0;
+        }
+        if auto_start {
+            pmu_state.fw_counter_state |= 1 << firmware_event_idx;
+        }
+    } else {
+        let mhpm_offset = get_mhpm_csr_offset(counter_idx).unwrap();
+        if clear_value {
+            write_mhpmcounter(mhpm_offset, 0);
+        }
+        if auto_start {
+            return start_hardware_counter(mhpm_offset, 0, false).is_ok();
+        }
+    }
+    true
+}
+
+/// Get the offset of the mhpmcounter CSR corresponding to counter_idx relative to mcycle
+fn get_mhpm_csr_offset(counter_idx: usize) -> Option<u16> {
+    let mhpm_mask = hart_mhpm_mask(current_hartid());
+    let mut count = 0;
+    for offset in 0..32 {
+        if (mhpm_mask >> offset) & 1 == 1 {
+            if count == counter_idx {
+                return Some(offset as u16);
+            }
+            count += 1;
+        }
+    }
+    None
+}
+
+/// Start Hardware Counter
+enum StartCounterErr {
+    OffsetInvalid,
+    AlreadyStart,
+}
+
+/// Starts a hardware performance counter specified by the offset.
+fn start_hardware_counter(
+    mhpm_offset: u16,
+    new_value: u64,
+    is_update_value: bool,
+) -> Result<(), StartCounterErr> {
+    if mhpm_offset == 1 || mhpm_offset > 31 {
+        return Err(StartCounterErr::OffsetInvalid);
+    }
+
+    if hart_privileged_version(current_hartid()) < PrivilegedVersion::Version1_11 {
+        if is_update_value {
+            write_mhpmcounter(mhpm_offset, new_value);
+        }
+        return Ok(());
+    }
+
+    // Check if counter is already running by testing the inhibit bit
+    // A zero bit in mcountinhibit means the counter is running
+    if mcountinhibit::read().bits() & (1 << mhpm_offset) == 0 {
+        return Err(StartCounterErr::AlreadyStart);
+    }
+
+    if is_update_value {
+        write_mhpmcounter(mhpm_offset, new_value);
+    }
+
+    unsafe {
+        match mhpm_offset {
+            0 => mcountinhibit::clear_cy(),
+            2 => mcountinhibit::clear_ir(),
+            _ => mcountinhibit::clear_hpm(mhpm_offset as usize),
+        }
+    }
+    Ok(())
+}
+
+/// Stop Hardware Counter
+enum StopCounterErr {
+    OffsetInvalid,
+    AlreadyStop,
+}
+
+/// Stops a hardware performance counter specified by the offset.
+fn stop_hardware_counter(mhpm_offset: u16, is_reset: bool) -> Result<(), StopCounterErr> {
+    if mhpm_offset == 1 || mhpm_offset > 31 {
+        return Err(StopCounterErr::OffsetInvalid);
+    }
+
+    if is_reset && mhpm_offset >= 3 && mhpm_offset <= 31 {
+        write_mhpmevent(mhpm_offset, 0);
+    }
+
+    if hart_privileged_version(current_hartid()) < PrivilegedVersion::Version1_11 {
+        return Ok(());
+    }
+
+    if mcountinhibit::read().bits() & (1 << mhpm_offset) != 0 {
+        return Err(StopCounterErr::AlreadyStop);
+    }
+
+    unsafe {
+        match mhpm_offset {
+            0 => mcountinhibit::set_cy(),
+            2 => mcountinhibit::set_ir(),
+            _ => mcountinhibit::set_hpm(mhpm_offset as usize),
+        }
+    }
+    Ok(())
+}
+
+/// Write MHPMEVENT or MHPMCOUNTER
+fn write_mhpmevent(mhpm_offset: u16, mhpmevent_val: u64) {
+    let csr = CSR_MHPMEVENT3 + mhpm_offset - 3;
+
+    // Handle MHPMEVENT3-31
+    if csr >= CSR_MHPMEVENT3 && csr <= CSR_MHPMEVENT31 {
+        // Convert CSR value to register index (3-31)
+        let idx = csr - CSR_MHPMEVENT3 + 3;
+
+        // Use seq_macro to generate all valid indices from 3 to 31
+        seq_macro::seq!(N in 3..=31 {
+            match idx {
+                #(
+                    N => pastey::paste!{ [<mhpmevent ~N>]::write(mhpmevent_val as usize) },
+                )*
+                _ =>{}
+            }
+        });
+    }
+}
+
+fn write_mhpmcounter(mhpm_offset: u16, mhpmcounter_val: u64) {
+    let counter_idx = mhpm_offset;
+
+    let csr = CSR_MHPMCOUNTER3 + mhpm_offset - 3;
+    // Special cases for cycle and instret
+    if csr == CSR_MCYCLE {
+        crate::riscv::csr::mcycle::write(mhpmcounter_val);
+        return;
+    } else if csr == CSR_MINSTRET {
+        crate::riscv::csr::minstret::write(mhpmcounter_val);
+        return;
+    }
+
+    // Only handle valid counter indices (3-31)
+    if counter_idx >= 3 && counter_idx <= 31 {
+        // Call the macro with all valid indices
+        seq_macro::seq!(N in 3..=31 {
+            match counter_idx {
+                #(
+                    N => pastey::paste!{ [<mhpmcounter ~N>]::write(mhpmcounter_val as usize) },
+                )*
+                _ =>{}
+            }
+        });
+    }
+}
+
+/// Wrap for counter info
+struct CounterInfo {
+    /// Packed representation of counter information:
+    /// - Bits [11:0]: CSR number for hardware counters
+    /// - Bits [17:12]: Counter width (typically 63 for RV64)
+    /// - MSB: Set for firmware counters, clear for hardware counters
+    inner: usize,
+}
+
+#[allow(unused)]
+impl CounterInfo {
+    const CSR_MASK: usize = 0xFFF; // Bits [11:0]
+    const WIDTH_MASK: usize = 0x3F << 12; // Bits [17:12]
+    const FIRMWARE_FLAG: usize = 1 << (size_of::<usize>() * 8 - 1); // MSB
+
+    #[inline]
+    pub const fn new() -> Self {
+        Self { inner: 0 }
+    }
+
+    #[inline]
+    pub fn set_csr(&mut self, csr_num: u16) {
+        self.inner = (self.inner & !Self::CSR_MASK) | ((csr_num as usize) & Self::CSR_MASK);
+    }
+
+    #[inline]
+    pub fn set_width(&mut self, width: u8) {
+        self.inner = (self.inner & !Self::WIDTH_MASK) | (((width as usize) & 0x3F) << 12);
+    }
+
+    #[inline]
+    pub const fn with_hardware_info(csr_num: u16, width: u8) -> Self {
+        Self {
+            inner: ((csr_num as usize) & Self::CSR_MASK) | (((width as usize) & 0x3F) << 12),
+        }
+    }
+
+    #[inline]
+    pub const fn with_firmware_info() -> Self {
+        Self {
+            inner: Self::FIRMWARE_FLAG,
+        }
+    }
+
+    #[inline]
+    pub const fn inner(self) -> usize {
+        self.inner
+    }
+}
+
+impl Default for CounterInfo {
+    #[inline]
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+#[derive(Clone, Copy)]
+pub struct EventIdx {
+    /// Packed representation of event information:
+    /// - Bits [15:0]: Event code
+    /// - Bits [19:16]: Event type
+    inner: usize,
+}
+
+#[allow(unused)]
+impl EventIdx {
+    #[inline]
+    const fn new(event_idx: usize) -> Self {
+        Self { inner: event_idx }
+    }
+
+    #[inline]
+    fn from_firmwarw_event(firmware_event: usize) -> Self {
+        Self {
+            inner: 0xf << 16 | firmware_event,
+        }
+    }
+
+    #[inline]
+    fn raw(&self) -> usize {
+        self.inner
+    }
+
+    #[inline]
+    const fn event_type(&self) -> usize {
+        (self.inner >> 16) & 0xF
+    }
+
+    #[inline]
+    const fn event_code(&self) -> usize {
+        self.inner & 0xFFFF
+    }
+
+    /// Extracts the cache ID for HARDWARE_CACHE events (13 bits, [15:3])
+    #[inline]
+    const fn cache_id(&self) -> usize {
+        (self.inner >> 3) & 0x1FFF
+    }
+
+    /// Extracts the cache operation ID (2 bits, [2:1])
+    #[inline]
+    const fn cache_op_id(&self) -> usize {
+        (self.inner >> 1) & 0x3
+    }
+
+    /// Extracts the cache result ID (1 bit, [0])
+    #[inline]
+    const fn cache_result_id(&self) -> usize {
+        self.inner & 0x1
+    }
+
+    #[inline]
+    const fn is_general_event(&self) -> bool {
+        self.event_type() == event_type::HARDWARE_GENERAL
+    }
+
+    #[inline]
+    const fn is_cache_event(&self) -> bool {
+        self.event_type() == event_type::HARDWARE_CACHE
+    }
+
+    #[inline]
+    const fn is_raw_event_v1(&self) -> bool {
+        self.event_type() == event_type::HARDWARE_RAW
+    }
+
+    #[inline]
+    const fn is_raw_event_v2(&self) -> bool {
+        self.event_type() == event_type::HARDWARE_RAW_V2
+    }
+
+    #[inline]
+    const fn is_raw_event(&self) -> bool {
+        self.is_raw_event_v1() || self.is_raw_event_v2()
+    }
+
+    #[inline]
+    const fn is_firmware_event(&self) -> bool {
+        self.event_type() == event_type::FIRMWARE
+    }
+
+    #[inline]
+    fn check_event_type(self) -> bool {
+        let event_type = self.event_type();
+        let event_code = self.event_code();
+
+        match event_type {
+            event_type::HARDWARE_GENERAL => event_code <= hardware_event::REF_CPU_CYCLES,
+            event_type::HARDWARE_CACHE => {
+                self.cache_id() <= cache_event::NODE
+                    && self.cache_op_id() <= cache_operation::PREFETCH
+                    && self.cache_result_id() <= cache_result::MISS
+            }
+            event_type::HARDWARE_RAW | event_type::HARDWARE_RAW_V2 => event_code == 0,
+            event_type::FIRMWARE => true,
+            _ => false,
+        }
+    }
+
+    #[inline]
+    fn firmware_event_valid(self) -> bool {
+        let event_type = self.event_type();
+        let event_code = self.event_code();
+        if event_type != event_type::FIRMWARE {
+            return false;
+        }
+        if (event_code > firmware_event::HFENCE_VVMA_ASID_RECEIVED
+            && event_code < firmware_event::PLATFORM)
+            || event_code >= firmware_event::PLATFORM
+        {
+            // TODO:Currently RustSBI Prototyper does not support PLATFORM practice
+            return false;
+        }
+        true
+    }
+}
+
+/// event to mhpmcounter map
+#[derive(Debug)]
+pub struct EventToCounterMap {
+    counters_mask: u32,   // Bitmask of supported counters
+    event_start_idx: u32, // Start of event code range
+    event_end_idx: u32,   // End of event code range
+}
+
+impl EventToCounterMap {
+    pub fn new(counters_mask: u32, event_start_idx: u32, event_end_idx: u32) -> Self {
+        Self {
+            counters_mask,
+            event_start_idx,
+            event_end_idx,
+        }
+    }
+
+    #[inline]
+    pub const fn have_event(&self, event_idx: u32) -> bool {
+        event_idx >= self.event_start_idx && event_idx <= self.event_end_idx
+    }
+
+    #[inline]
+    pub fn get_counter_mask(&self) -> u32 {
+        self.counters_mask
+    }
+
+    #[inline]
+    pub fn is_overlop(&self, other_map: &EventToCounterMap) -> bool {
+        if (self.event_end_idx < other_map.event_start_idx
+            && self.event_end_idx < other_map.event_end_idx)
+            || (self.event_start_idx > other_map.event_start_idx
+                && self.event_start_idx > other_map.event_end_idx)
+        {
+            return false;
+        }
+        true
+    }
+}
+
+#[derive(Debug)]
+pub struct RawEventToCounterMap {
+    counters_mask: u32,    // Bitmask of supported counters
+    raw_event_select: u64, // Value to program into mhpmeventX
+    select_mask: u64,      // Mask for selecting bits (optional use)
+}
+
+impl RawEventToCounterMap {
+    pub fn new(counters_mask: u32, raw_event_select: u64, select_mask: u64) -> Self {
+        Self {
+            counters_mask,
+            raw_event_select,
+            select_mask,
+        }
+    }
+
+    #[inline]
+    pub const fn have_event(&self, event_idx: u64) -> bool {
+        self.raw_event_select == (event_idx & self.select_mask)
+    }
+
+    #[inline]
+    pub const fn get_counter_mask(&self) -> u32 {
+        self.counters_mask
+    }
+
+    #[inline]
+    pub const fn is_overlop(&self, other_map: &RawEventToCounterMap) -> bool {
+        self.select_mask == other_map.select_mask
+            && self.raw_event_select == other_map.raw_event_select
+    }
+}
+
+struct CounterMask {
+    counter_idx_base: usize,
+    counter_idx_mask: usize,
+}
+
+impl CounterMask {
+    pub fn new(counter_idx_base: usize, counter_idx_mask: usize) -> Self {
+        Self {
+            counter_idx_base,
+            counter_idx_mask,
+        }
+    }
+}
+
+impl Iterator for CounterMask {
+    type Item = usize;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        if self.counter_idx_mask == 0 {
+            return None;
+        } else {
+            let low_bit = self.counter_idx_mask.trailing_zeros();
+            let hart_id = usize::try_from(low_bit).unwrap() + self.counter_idx_base;
+            self.counter_idx_mask &= !(1usize << low_bit);
+            Some(hart_id)
+        }
+    }
+}
+
+// TODO: If all firmware events are implemented,
+// `PMU_FIRMWARE_EVENT_SUPPORTED` should be removed.
+const PMU_FIRMWARE_EVENT_SUPPORTED: [bool; 22] = [
+    true,  // SBI_PMU_FW_MISALIGNED_LOAD
+    true,  // SBI_PMU_FW_MISALIGNED_STORE
+    false, // SBI_PMU_FW_ACCESS_LOAD
+    false, // SBI_PMU_FW_ACCESS_STORE
+    true,  // SBI_PMU_FW_ILLEGAL_INSN
+    true,  // SBI_PMU_FW_SET_TIMER
+    true,  // SBI_PMU_FW_IPI_SENT
+    true,  // SBI_PMU_FW_IPI_RECEIVED
+    true,  // SBI_PMU_FW_FENCE_I_SENT
+    true,  // SBI_PMU_FW_FENCE_I_RECEIVED
+    true,  // SBI_PMU_FW_SFENCE_VMA_SENT
+    true,  // SBI_PMU_FW_SFENCE_VMA_RECEIVED
+    true,  // SBI_PMU_FW_SFENCE_VMA_ASID_SENT
+    true,  // SBI_PMU_FW_SFENCE_VMA_ASID_RECEIVED
+    false, // SBI_PMU_FW_HFENCE_GVMA_SENT
+    false, // SBI_PMU_FW_HFENCE_GVMA_RECEIVED
+    false, // SBI_PMU_FW_HFENCE_GVMA_VMID_SENT
+    false, // SBI_PMU_FW_HFENCE_GVMA_VMID_RECEIVED
+    false, // SBI_PMU_FW_HFENCE_VVMA_SENT
+    false, // SBI_PMU_FW_HFENCE_VVMA_RECEIVED
+    false, // SBI_PMU_FW_HFENCE_VVMA_ASID_SENT
+    false, // SBI_PMU_FW_HFENCE_VVMA_ASID_RECEIVED
+];
+
+pub fn pmu_firmware_counter_increment(firmware_event: usize) {
+    let pmu_state = &mut hart_context_mut(current_hartid()).pmu_state;
+    let counter_idx_start = pmu_state.hw_counters_num;
+    for counter_idx in counter_idx_start..counter_idx_start + PMU_FIRMWARE_COUNTER_MAX {
+        let fw_idx = counter_idx - counter_idx_start;
+        if pmu_state.active_event[counter_idx]
+            == EventIdx::from_firmwarw_event(firmware_event).raw()
+            && pmu_state.is_firmware_event_start(counter_idx)
+        {
+            pmu_state.fw_counter[fw_idx] += 1;
+        }
+    }
+}

+ 9 - 0
prototyper/prototyper/src/sbi/rfence.rs

@@ -1,4 +1,5 @@
 use rustsbi::{HartMask, SbiRet};
+use sbi_spec::pmu::firmware_event;
 use spin::Mutex;
 
 use crate::cfg::{PAGE_SIZE, TLB_FLUSH_LIMIT};
@@ -10,6 +11,8 @@ use core::arch::asm;
 
 use core::sync::atomic::{AtomicU32, Ordering};
 
+use super::pmu::pmu_firmware_counter_increment;
+
 /// Cell for managing remote fence operations between harts.
 pub(crate) struct RFenceCell {
     // Queue of fence operations with source hart ID
@@ -198,6 +201,7 @@ fn remote_fence_process(rfence_ctx: RFenceContext, hart_mask: HartMask) -> SbiRe
 impl rustsbi::Fence for SbiRFence {
     /// Remote instruction fence for specified harts.
     fn remote_fence_i(&self, hart_mask: HartMask) -> SbiRet {
+        pmu_firmware_counter_increment(firmware_event::FENCE_I_SENT);
         remote_fence_process(
             RFenceContext {
                 start_addr: 0,
@@ -212,6 +216,7 @@ impl rustsbi::Fence for SbiRFence {
 
     /// Remote supervisor fence for virtual memory on specified harts.
     fn remote_sfence_vma(&self, hart_mask: HartMask, start_addr: usize, size: usize) -> SbiRet {
+        pmu_firmware_counter_increment(firmware_event::SFENCE_VMA_SENT);
         let flush_size = match validate_address_range(start_addr, size) {
             Ok(size) => size,
             Err(e) => return e,
@@ -237,6 +242,7 @@ impl rustsbi::Fence for SbiRFence {
         size: usize,
         asid: usize,
     ) -> SbiRet {
+        pmu_firmware_counter_increment(firmware_event::SFENCE_VMA_ASID_SENT);
         let flush_size = match validate_address_range(start_addr, size) {
             Ok(size) => size,
             Err(e) => return e,
@@ -263,11 +269,13 @@ pub fn rfence_single_handler() {
         match ctx.op {
             // Handle instruction fence
             RFenceType::FenceI => unsafe {
+                pmu_firmware_counter_increment(firmware_event::FENCE_I_RECEIVED);
                 asm!("fence.i");
                 remote_rfence(id).unwrap().sub();
             },
             // Handle virtual memory address fence
             RFenceType::SFenceVma => {
+                pmu_firmware_counter_increment(firmware_event::SFENCE_VMA_RECEIVED);
                 // If the flush size is greater than the maximum limit then simply flush all
                 if (ctx.start_addr == 0 && ctx.size == 0)
                     || (ctx.size == usize::MAX)
@@ -288,6 +296,7 @@ pub fn rfence_single_handler() {
             }
             // Handle virtual memory address fence with ASID
             RFenceType::SFenceVmaAsid => {
+                pmu_firmware_counter_increment(firmware_event::SFENCE_VMA_ASID_RECEIVED);
                 let asid = ctx.asid;
                 // If the flush size is greater than the maximum limit then simply flush all
                 if (ctx.start_addr == 0 && ctx.size == 0)

+ 151 - 26
prototyper/prototyper/src/sbi/trap/handler.rs

@@ -1,6 +1,8 @@
-use fast_trap::{FastContext, FastResult};
-use riscv::register::{mepc, mie, mstatus, satp, sstatus};
+use fast_trap::{EntireContext, EntireContextSeparated, EntireResult, FastContext, FastResult};
+use riscv::register::{mepc, mie, mstatus, mtval, satp, sstatus};
+use riscv_decode::{Instruction, decode};
 use rustsbi::RustSBI;
+use sbi_spec::pmu::firmware_event;
 
 use crate::platform::PLATFORM;
 use crate::riscv::csr::{CSR_TIME, CSR_TIMEH};
@@ -8,8 +10,11 @@ use crate::riscv::current_hartid;
 use crate::sbi::console;
 use crate::sbi::hsm::local_hsm;
 use crate::sbi::ipi;
+use crate::sbi::pmu::pmu_firmware_counter_increment;
 use crate::sbi::rfence;
 
+use super::helper::*;
+
 #[inline]
 pub fn switch(mut ctx: FastContext, start_addr: usize, opaque: usize) -> FastResult {
     unsafe {
@@ -31,6 +36,7 @@ pub fn msoft_ipi_handler() {
     let ipi_type = get_and_reset_ipi_type();
     // Handle supervisor software interrupt
     if (ipi_type & ipi::IPI_TYPE_SSOFT) != 0 {
+        pmu_firmware_counter_increment(firmware_event::IPI_RECEIVED);
         unsafe {
             riscv::register::mip::set_ssoft();
         }
@@ -64,7 +70,7 @@ pub fn msoft_handler(ctx: FastContext) -> FastResult {
             riscv::asm::wfi();
             ctx.restore()
         }
-        // Handle RFence
+        // Handle IPI and RFence
         _ => {
             msoft_ipi_handler();
             ctx.restore()
@@ -123,14 +129,15 @@ pub fn sbi_call_handler(
         }
     }
     ctx.regs().a = [ret.error, ret.value, a2, a3, a4, a5, a6, a7];
-    mepc::write(mepc::read() + 4);
+    let epc = mepc::read();
+    mepc::write(epc + get_inst(epc).1);
     ctx.restore()
 }
 
 /// Delegate trap handling to supervisor mode.
 #[inline]
-pub fn delegate(ctx: &mut FastContext) {
-    use riscv::register::{mcause, mepc, mtval, scause, sepc, sstatus, stval, stvec};
+pub fn delegate(ctx: &mut EntireContextSeparated) {
+    use riscv::register::{mcause, scause, sepc, sstatus, stval, stvec};
     unsafe {
         sepc::write(ctx.regs().pc);
         scause::write(mcause::read().bits());
@@ -148,35 +155,153 @@ pub fn delegate(ctx: &mut FastContext) {
 
 /// Handle illegal instructions, particularly CSR access.
 #[inline]
-pub fn illegal_instruction_handler(ctx: &mut FastContext) -> bool {
-    use riscv::register::{mepc, mtval};
-    use riscv_decode::{Instruction, decode};
+pub extern "C" fn illegal_instruction_handler(raw_ctx: EntireContext) -> EntireResult {
+    let mut ctx = raw_ctx.split().0;
 
     let inst = decode(mtval::read() as u32);
     match inst {
-        Ok(Instruction::Csrrs(csr)) => match csr.csr() {
+        Ok(Instruction::Csrrs(csr)) => match csr.csr() as u16 {
             CSR_TIME => {
-                assert!(
-                    10 <= csr.rd() && csr.rd() <= 17,
-                    "Unsupported CSR rd: {}",
-                    csr.rd()
+                save_reg_x(
+                    &mut ctx,
+                    csr.rd() as usize,
+                    unsafe { PLATFORM.sbi.ipi.as_ref() }.unwrap().get_time(),
                 );
-                ctx.regs().a[(csr.rd() - 10) as usize] =
-                    unsafe { PLATFORM.sbi.ipi.as_ref() }.unwrap().get_time();
             }
             CSR_TIMEH => {
-                assert!(
-                    10 <= csr.rd() && csr.rd() <= 17,
-                    "Unsupported CSR rd: {}",
-                    csr.rd()
+                save_reg_x(
+                    &mut ctx,
+                    csr.rd() as usize,
+                    unsafe { PLATFORM.sbi.ipi.as_ref() }.unwrap().get_timeh(),
                 );
-                ctx.regs().a[(csr.rd() - 10) as usize] =
-                    unsafe { PLATFORM.sbi.ipi.as_ref() }.unwrap().get_timeh();
             }
-            _ => return false,
+            _ => {
+                delegate(&mut ctx);
+                return ctx.restore();
+            }
+        },
+        _ => {
+            delegate(&mut ctx);
+            return ctx.restore();
+        }
+    }
+    let epc = mepc::read();
+    mepc::write(epc + get_inst(epc).1);
+    ctx.restore()
+}
+
+#[inline]
+pub extern "C" fn load_misaligned_handler(ctx: EntireContext) -> EntireResult {
+    let mut ctx = ctx.split().0;
+    let current_pc = mepc::read();
+    let current_addr = mtval::read();
+
+    let (current_inst, inst_len) = get_inst(current_pc);
+    debug!(
+        "Misaligned load: inst/{:x?}, load {:x?} in {:x?}",
+        current_inst, current_addr, current_pc
+    );
+    let decode_result = decode(current_inst as u32);
+
+    // TODO: INST FLD c.*sp
+    // TODO: maybe can we reduce the time to update csr for read virtual-address.
+    let inst_type = match decode_result {
+        Ok(Instruction::Lb(data)) => (data.rd(), VarType::Signed, 1),
+        Ok(Instruction::Lbu(data)) => (data.rd(), VarType::UnSigned, 1),
+        Ok(Instruction::Lh(data)) => (data.rd(), VarType::Signed, 2),
+        Ok(Instruction::Lhu(data)) => (data.rd(), VarType::UnSigned, 2),
+        Ok(Instruction::Lw(data)) => (data.rd(), VarType::Signed, 4),
+        Ok(Instruction::Lwu(data)) => (data.rd(), VarType::UnSigned, 4),
+        Ok(Instruction::Ld(data)) => (data.rd(), VarType::Signed, 8),
+        Ok(Instruction::Flw(data)) => (data.rd(), VarType::Float, 4),
+        _ => panic!("Unsupported inst"),
+    };
+    let (target_reg, var_type, len) = inst_type;
+    let raw_data = get_data(current_addr, len);
+    let read_data = match var_type {
+        VarType::Signed => match len {
+            1 => raw_data as i8 as usize,
+            2 => raw_data as i16 as usize,
+            4 => raw_data as i32 as usize,
+            8 => raw_data as i64 as usize,
+            _ => panic!("Invalid len"),
+        },
+        VarType::UnSigned => match len {
+            1 => raw_data as u8 as usize,
+            2 => raw_data as u16 as usize,
+            4 => raw_data as u32 as usize,
+            8 => raw_data as u64 as usize,
+            _ => panic!("Invalid len"),
+        },
+        VarType::Float => match len {
+            // 4 => raw_data as f32 as usize,
+            // 8 => raw_data as f64 as usize,
+            _ => panic!("Misaligned float is unsupported"),
+        },
+    };
+    debug!(
+        "read 0x{:x} from 0x{:x} to x{}, len 0x{:x}",
+        read_data, current_addr, target_reg, len
+    );
+    save_reg_x(&mut ctx, target_reg as usize, read_data);
+    mepc::write(current_pc + inst_len);
+    ctx.restore()
+}
+
+#[inline]
+pub extern "C" fn store_misaligned_handler(ctx: EntireContext) -> EntireResult {
+    let mut ctx = ctx.split().0;
+    let current_pc = mepc::read();
+    let current_addr = mtval::read();
+
+    let (current_inst, inst_len) = get_inst(current_pc);
+    debug!(
+        "Misaligned store: inst/{:x?}, store {:x?} in {:x?}",
+        current_inst, current_addr, current_pc
+    );
+
+    let decode_result = decode(current_inst as u32);
+
+    // TODO: INST FSD c.*sp
+    // TODO: maybe can we reduce the time to update csr for read virtual-address.
+    let inst_type = match decode_result {
+        Ok(Instruction::Sb(data)) => (data.rs2(), VarType::UnSigned, 1),
+        Ok(Instruction::Sh(data)) => (data.rs2(), VarType::UnSigned, 2),
+        Ok(Instruction::Sw(data)) => (data.rs2(), VarType::UnSigned, 4),
+        Ok(Instruction::Sd(data)) => (data.rs2(), VarType::UnSigned, 8),
+        Ok(Instruction::Fsw(data)) => (data.rs2(), VarType::Float, 4),
+        _ => panic!("Unsupported inst"),
+    };
+    let (target_reg, var_type, len) = inst_type;
+    let raw_data = get_reg_x(&mut ctx, target_reg as usize);
+
+    // TODO: Float support
+    let read_data = match var_type {
+        VarType::Signed => match len {
+            _ => panic!("Can not store signed data"),
+        },
+        VarType::UnSigned => match len {
+            1 => &(raw_data as u8).to_le_bytes()[..],
+            2 => &(raw_data as u16).to_le_bytes()[..],
+            4 => &(raw_data as u32).to_le_bytes()[..],
+            8 => &(raw_data as u64).to_le_bytes()[..],
+            _ => panic!("Invalid len"),
+        },
+        VarType::Float => match len {
+            // 4 => (raw_data as f32).to_le_bytes().to_vec(),
+            // 8 => (raw_data as f64).to_le_bytes().to_vec(),
+            _ => panic!("Misaligned float is unsupported"),
         },
-        _ => return false,
+    };
+
+    debug!(
+        "save 0x{:x} to 0x{:x}, len 0x{:x}",
+        raw_data, current_addr, len
+    );
+    for i in 0..read_data.len() {
+        save_byte(current_addr + i, read_data[i] as usize);
     }
-    mepc::write(mepc::read() + 4);
-    true
+
+    mepc::write(current_pc + inst_len);
+    ctx.restore()
 }

+ 161 - 0
prototyper/prototyper/src/sbi/trap/helper.rs

@@ -0,0 +1,161 @@
+use core::arch::asm;
+use riscv::register::{mtvec, sscratch};
+
+use fast_trap::EntireContextSeparated;
+
+const MPRV_BIT: usize = 1usize << 17;
+const MXR_BIT: usize = 1usize << 19;
+
+pub enum VarType {
+    Signed,
+    UnSigned,
+    Float,
+}
+
+#[inline(always)]
+pub fn read_gp() -> usize {
+    let mut data: usize;
+    unsafe { asm!("mv {}, gp", out(reg) data, options(nomem)) };
+    data
+}
+
+#[inline(always)]
+pub fn read_tp() -> usize {
+    let mut data: usize;
+    unsafe { asm!("mv {}, tp", out(reg) data, options(nomem)) };
+    data
+}
+
+#[inline(always)]
+pub fn write_gp(data: usize) {
+    unsafe { asm!("mv gp, {}", in(reg) data, options(nomem)) };
+}
+
+#[inline(always)]
+pub fn write_tp(data: usize) {
+    unsafe { asm!("mv tp, {}", in(reg) data, options(nomem)) };
+}
+
+// If inline this and next function will cause crash. It looks like magic.
+#[inline(never)]
+pub fn get_unsigned_byte(addr: usize) -> u8 {
+    let mut data: usize = 0;
+    let mut status: usize = 0;
+    unsafe {
+        let prev_mtvec = mtvec::read().bits();
+        mtvec::write(
+            crate::sbi::early_trap::expected_trap as _,
+            mtvec::TrapMode::Direct,
+        );
+        asm!(
+            "csrrs t3, mstatus, t3",
+            "lbu t0, 0(t1)",
+            "csrw mstatus, t3",
+            "csrw mtvec, t4",
+            in("t1") addr,
+            in("t3") MPRV_BIT | MXR_BIT,
+            in("a1") 0,
+            in("t4") prev_mtvec,
+            inout("t0") data,
+            inout("a0") status,
+        );
+        if status != 0 {
+            panic!("Error when load byte");
+        }
+    }
+    data as u8
+}
+
+#[inline(never)]
+pub fn save_byte(addr: usize, data: usize) {
+    unsafe {
+        let prev_mtvec = mtvec::read().bits();
+        let mut status: usize = 0;
+        mtvec::write(
+            crate::sbi::early_trap::expected_trap as _,
+            mtvec::TrapMode::Direct,
+        );
+        asm!(
+              "csrrs t3, mstatus, t3",
+              "sb t0, 0(t1)",
+              "csrw mstatus, t3",
+              "csrw mtvec, t4",
+            in("t0") data,
+            in("t1") addr,
+            in("t4") prev_mtvec,
+            in("a1") 0,
+            in("t3") MPRV_BIT | MXR_BIT,
+            inout("a0") status,
+        );
+        if status != 0 {
+            panic!("Error when save byte");
+        }
+    }
+}
+
+#[inline(always)]
+pub fn get_data(addr: usize, len: usize) -> usize {
+    let mut data: usize = 0;
+    for i in (addr..addr + len).rev() {
+        data <<= 8;
+        data |= get_unsigned_byte(i) as usize;
+    }
+    data
+}
+
+#[inline(always)]
+pub fn get_inst(addr: usize) -> (usize, usize) {
+    let low_data = get_data(addr, 2);
+    // We assume we only have 16bit and 32bit inst.
+    if riscv_decode::instruction_length(low_data as u16) == 2 {
+        return (low_data, 2);
+    } else {
+        return (low_data | (get_data(addr + 2, 2) << 16), 4);
+    }
+}
+
+#[inline(always)]
+pub fn save_reg_x(ctx: &mut EntireContextSeparated, reg_id: usize, data: usize) {
+    match reg_id {
+        00 => (),
+        01 => ctx.regs().ra = data,
+        02 => sscratch::write(data),
+        03 => write_gp(data),
+        04 => write_tp(data),
+        05 => ctx.regs().t[0] = data,
+        06 => ctx.regs().t[1] = data,
+        07 => ctx.regs().t[2] = data,
+        08 => ctx.regs().s[0] = data,
+        09 => ctx.regs().s[1] = data,
+        // x10 = a0 ..= x17 = a7
+        10..=17 => ctx.regs().a[reg_id - 10] = data,
+        // x18 = s2 ..= x27 = s11
+        18..=27 => ctx.regs().s[reg_id - 16] = data,
+        // x28 = t3 ..= x31 = t6
+        28..=31 => ctx.regs().t[reg_id - 25] = data,
+        _ => panic!(),
+    }
+}
+
+#[inline(always)]
+pub fn get_reg_x(ctx: &mut EntireContextSeparated, reg_id: usize) -> usize {
+    match reg_id {
+        00 => 0,
+        01 => ctx.regs().ra,
+        02 => sscratch::read(),
+        03 => read_gp(),
+        04 => read_tp(),
+        05 => ctx.regs().t[0],
+        06 => ctx.regs().t[1],
+        07 => ctx.regs().t[2],
+        08 => ctx.regs().s[0],
+        09 => ctx.regs().s[1],
+        // x10 = a0 ..= x17 = a7
+        10..=17 => ctx.regs().a[reg_id - 10],
+        // x18 = s2 ..= x27 = s11
+        18..=27 => ctx.regs().s[reg_id - 16],
+        // x28 = t3 ..= x31 = t6
+        28..=31 => ctx.regs().t[reg_id - 25],
+        _ => panic!(),
+    }
+}

+ 16 - 6
prototyper/prototyper/src/sbi/trap/mod.rs

@@ -1,6 +1,8 @@
 pub mod boot;
 pub mod handler;
 
+mod helper;
+use super::pmu::pmu_firmware_counter_increment;
 use crate::fail::unsupported_trap;
 
 use fast_trap::{FastContext, FastResult};
@@ -9,6 +11,7 @@ use riscv::register::{
     mcause::{self, Trap},
     mepc, mip, mstatus,
 };
+use sbi_spec::pmu::firmware_event;
 
 /// Fast trap handler for all trap.
 pub extern "C" fn fast_handler(
@@ -42,7 +45,7 @@ pub extern "C" fn fast_handler(
 
                     ipi::clear_mtime();
                     unsafe {
-                        mip::clear_stimer();
+                        mip::set_stimer();
                     }
                     save_regs(&mut ctx);
                     ctx.restore()
@@ -53,15 +56,22 @@ pub extern "C" fn fast_handler(
                 }
                 // Handle illegal instructions
                 Trap::Exception(Exception::IllegalInstruction) => {
+                    pmu_firmware_counter_increment(firmware_event::ILLEGAL_INSN);
                     if mstatus::read().mpp() == mstatus::MPP::Machine {
                         panic!("Cannot handle illegal instruction exception from M-MODE");
                     }
-
                     save_regs(&mut ctx);
-                    if !handler::illegal_instruction_handler(&mut ctx) {
-                        handler::delegate(&mut ctx);
-                    }
-                    ctx.restore()
+                    ctx.continue_with(handler::illegal_instruction_handler, ())
+                }
+                Trap::Exception(Exception::LoadMisaligned) => {
+                    pmu_firmware_counter_increment(firmware_event::MISALIGNED_LOAD);
+                    save_regs(&mut ctx);
+                    ctx.continue_with(handler::load_misaligned_handler, ())
+                }
+                Trap::Exception(Exception::StoreMisaligned) => {
+                    pmu_firmware_counter_increment(firmware_event::MISALIGNED_STORE);
+                    save_regs(&mut ctx);
+                    ctx.continue_with(handler::store_misaligned_handler, ())
                 }
                 // Handle other traps
                 trap => unsupported_trap(Some(trap)),

+ 16 - 2
prototyper/prototyper/src/sbi/trap_stack.rs

@@ -42,6 +42,14 @@ pub(crate) fn prepare_for_trap() {
     };
 }
 
+pub fn hart_context_mut(hart_id: usize) -> &'static mut HartContext {
+    unsafe { ROOT_STACK.get_mut(hart_id).unwrap().hart_context_mut() }
+}
+
+pub fn hart_context(hart_id: usize) -> &'static HartContext {
+    unsafe { ROOT_STACK.get(hart_id).unwrap().hart_context() }
+}
+
 /// Stack type for each hart.
 ///
 /// Memory layout:
@@ -58,15 +66,21 @@ impl Stack {
 
     /// Gets mutable reference to hart context at bottom of stack.
     #[inline]
-    pub fn hart_context(&mut self) -> &mut HartContext {
+    pub fn hart_context_mut(&mut self) -> &mut HartContext {
         unsafe { &mut *self.0.as_mut_ptr().cast() }
     }
 
+    /// Gets immutable reference to hart context at bottom of stack.
+    #[inline]
+    pub fn hart_context(&self) -> &HartContext {
+        unsafe { &*self.0.as_ptr().cast() }
+    }
+
     /// Initializes stack for trap handling.
     /// - Sets up hart context.
     /// - Creates and loads FreeTrapStack with the stack range.
     fn load_as_stack(&'static mut self) {
-        let hart = self.hart_context();
+        let hart = self.hart_context_mut();
         let context_ptr = hart.context_ptr();
         hart.init();
 

+ 3 - 0
prototyper/test-kernel/Cargo.toml

@@ -10,6 +10,9 @@ publish = false
 
 [dependencies]
 sbi-testing = { features = ["log"], path = "../../library/sbi-testing" }
+sbi-spec = { version = "0.0.8", features = [
+    "legacy",
+], path = "../../library/sbi-spec" }
 log = "0.4"
 riscv = "0.12.1"
 spin = "0.9"

+ 283 - 2
prototyper/test-kernel/src/main.rs

@@ -10,7 +10,13 @@ use core::{
     arch::{asm, naked_asm},
     ptr::null,
 };
-use sbi_testing::sbi;
+use riscv::register::cycle;
+use sbi_spec::{
+    binary::{CounterMask, HartMask, SbiRet},
+    pmu::firmware_event,
+};
+use sbi_testing::sbi::{self, ConfigFlags, StartFlags, StopFlags};
+// use sbi_spec::pmu::*;
 use uart16550::Uart16550;
 
 const RISCV_HEAD_FLAGS: u64 = 0;
@@ -108,7 +114,172 @@ extern "C" fn rust_main(hartid: usize, dtb_pa: usize) -> ! {
         hart_mask_base: 0,
         delay: frequency,
     };
-    if testing.test() {
+    let test_result = testing.test();
+
+    // PMU test, only available in qemu-system-riscv64 single core
+    let counters_num = sbi::pmu_num_counters();
+    println!("[pmu] counters number: {}", counters_num);
+    for idx in 0..counters_num {
+        let counter_info = sbi::pmu_counter_get_info(idx);
+        let counter_info = CounterInfo::new(counter_info.value);
+        if counter_info.is_firmware_counter() {
+            println!("[pmu] counter index:{:>2}, is a firmware counter", idx);
+        } else {
+            println!(
+                "[pmu] counter index:{:>2}, csr num: {:#03x}, width: {}",
+                idx,
+                counter_info.get_csr(),
+                counter_info.get_width()
+            );
+        }
+    }
+
+    /* PMU test for hardware event */
+    let counter_mask = CounterMask::from_mask_base(0x7ffff, 0);
+    let result = sbi::pmu_counter_config_matching(counter_mask, Flag::new(0b110), 0x2, 0);
+    assert!(result.is_ok());
+    let result = sbi::pmu_counter_config_matching(counter_mask, Flag::new(0b110), 0x10019, 0);
+    assert!(result.is_ok());
+    let result = sbi::pmu_counter_config_matching(counter_mask, Flag::new(0b110), 0x1001b, 0);
+    assert!(result.is_ok());
+    let result = sbi::pmu_counter_config_matching(counter_mask, Flag::new(0b110), 0x10021, 0);
+    assert!(result.is_ok());
+    let result = sbi::pmu_counter_config_matching(counter_mask, Flag::new(0b110), 0x3, 0);
+    assert_eq!(result, SbiRet::not_supported());
+
+    // `SBI_PMU_HW_CPU_CYCLES` event test
+    let result = sbi::pmu_counter_config_matching(counter_mask, Flag::new(0b010), 0x1, 0);
+    assert!(result.is_ok());
+    // the counter index should be 0(mcycle)
+    assert_eq!(result.value, 0);
+    let cycle_counter_idx = result.value;
+    let cycle_num = cycle::read64();
+    assert_eq!(cycle_num, 0);
+    // Start counting `SBI_PMU_HW_CPU_CYCLES` events
+    let start_result = sbi::pmu_counter_start(
+        CounterMask::from_mask_base(0x1, cycle_counter_idx),
+        Flag::new(0x1),
+        0xffff,
+    );
+    assert!(start_result.is_ok());
+    let cycle_num = cycle::read64();
+    assert!(cycle_num >= 0xffff);
+    // Stop counting `SBI_PMU_HW_CPU_CYCLES` events
+    let stop_result = sbi::pmu_counter_stop(
+        CounterMask::from_mask_base(0x1, cycle_counter_idx),
+        Flag::new(0x0),
+    );
+    assert!(stop_result.is_ok());
+    let old_cycle_num = cycle::read64();
+    let mut _j = 0;
+    for i in 0..1000 {
+        _j += i
+    }
+    let new_cycle_num = cycle::read64();
+    assert_eq!(old_cycle_num, new_cycle_num);
+    // Restart counting `SBI_PMU_HW_CPU_CYCLES` events
+    let start_result = sbi::pmu_counter_start(
+        CounterMask::from_mask_base(0x1, cycle_counter_idx),
+        Flag::new(0x0),
+        0,
+    );
+    assert!(start_result.is_ok());
+    let mut _j = 0;
+    for i in 0..1000 {
+        _j += i
+    }
+    let restart_cycle_num = cycle::read64();
+    assert!(restart_cycle_num > new_cycle_num);
+
+    /* PMU test for firmware  event */
+    let counter_mask = CounterMask::from_mask_base(0x7ffffffff, 0);
+
+    // Mapping a counter to the `SBI_PMU_FW_ACCESS_LOAD` event should result in unsupported
+    let result = sbi::pmu_counter_config_matching(
+        counter_mask,
+        Flag::new(0b010),
+        EventIdx::new_firmware_event(firmware_event::ACCESS_LOAD).raw(),
+        0,
+    );
+    assert_eq!(result, SbiRet::not_supported());
+
+    // Map a counter to the `SBI_PMU_FW_IPI_SENT` event.
+    // This counter should be a firmware counter and its value should be initialized to 0.
+    let result = sbi::pmu_counter_config_matching(
+        counter_mask,
+        Flag::new(0b010),
+        EventIdx::new_firmware_event(firmware_event::IPI_SENT).raw(),
+        0,
+    );
+    assert!(result.is_ok());
+    assert!(result.value >= 19);
+    let ipi_counter_idx = result.value;
+    let ipi_num = sbi::pmu_counter_fw_read(ipi_counter_idx);
+    assert!(ipi_num.is_ok());
+    assert_eq!(ipi_num.value, 0);
+
+    // Start counting `SBI_PMU_FW_IPI_SENT` events and assign an initial value of 25 to the event counter
+    let start_result = sbi::pmu_counter_start(
+        CounterMask::from_mask_base(0x1, ipi_counter_idx),
+        Flag::new(0x1),
+        25,
+    );
+    assert!(start_result.is_ok());
+    // Read the value of the `SBI_PMU_FW_IPI_SENT` event counter, which should be 25
+    let ipi_num = sbi::pmu_counter_fw_read(ipi_counter_idx);
+    assert!(ipi_num.is_ok());
+    assert_eq!(ipi_num.value, 25);
+
+    // Send IPI to other core, and the `SBI_PMU_FW_IPI_SENT` event counter value increases by one
+    let send_ipi_result = sbi::send_ipi(HartMask::from_mask_base(0b10, 0));
+    assert_eq!(send_ipi_result, SbiRet::invalid_param());
+
+    // Read the value of the `SBI_PMU_FW_IPI_SENT` event counter, which should be 26
+    let ipi_num = sbi::pmu_counter_fw_read(ipi_counter_idx);
+    assert!(ipi_num.is_ok());
+    assert_eq!(ipi_num.value, 26);
+
+    // Stop counting `SBI_PMU_FW_IPI_SENT` events
+    let stop_result = sbi::pmu_counter_stop(
+        CounterMask::from_mask_base(0x1, ipi_counter_idx),
+        Flag::new(0x0),
+    );
+    assert!(stop_result.is_ok());
+
+    // Restop counting `SBI_PMU_FW_IPI_SENT` events, the result should be already stop
+    let stop_result = sbi::pmu_counter_stop(
+        CounterMask::from_mask_base(0x1, ipi_counter_idx),
+        Flag::new(0x0),
+    );
+    assert_eq!(stop_result, SbiRet::already_stopped());
+
+    // Send IPI to other core, `SBI_PMU_FW_IPI_SENT` event counter should not change
+    let send_ipi_result = sbi::send_ipi(HartMask::from_mask_base(0b10, 0));
+    assert_eq!(send_ipi_result, SbiRet::invalid_param());
+
+    // Read the value of the `SBI_PMU_FW_IPI_SENT` event counter, which should be 26
+    let ipi_num = sbi::pmu_counter_fw_read(ipi_counter_idx);
+    assert!(ipi_num.is_ok());
+    assert_eq!(ipi_num.value, 26);
+
+    // Restart counting `SBI_PMU_FW_IPI_SENT` events
+    let start_result = sbi::pmu_counter_start(
+        CounterMask::from_mask_base(0x1, ipi_counter_idx),
+        Flag::new(0x0),
+        0,
+    );
+    assert!(start_result.is_ok());
+
+    // Send IPI to other core, and the `SBI_PMU_FW_IPI_SENT` event counter value increases by one
+    let send_ipi_result = sbi::send_ipi(HartMask::from_mask_base(0b10, 0));
+    assert_eq!(send_ipi_result, SbiRet::invalid_param());
+
+    // Read the value of the `SBI_PMU_FW_IPI_SENT` event counter, which should be 27
+    let ipi_num = sbi::pmu_counter_fw_read(ipi_counter_idx);
+    assert!(ipi_num.is_ok());
+    assert_eq!(ipi_num.value, 27);
+
+    if test_result {
         sbi::system_reset(sbi::Shutdown, sbi::NoReason);
     } else {
         sbi::system_reset(sbi::Shutdown, sbi::SystemFailure);
@@ -211,3 +382,113 @@ impl rcore_console::Console for Console {
         unsafe { UART.get().write(s.as_bytes()) };
     }
 }
+
+struct Flag {
+    inner: usize,
+}
+
+impl ConfigFlags for Flag {
+    fn raw(&self) -> usize {
+        self.inner
+    }
+}
+
+impl StartFlags for Flag {
+    fn raw(&self) -> usize {
+        self.inner
+    }
+}
+
+impl StopFlags for Flag {
+    fn raw(&self) -> usize {
+        self.inner
+    }
+}
+
+impl Flag {
+    pub fn new(flag: usize) -> Self {
+        Self { inner: flag }
+    }
+}
+
+/// Wrap for counter info
+struct CounterInfo {
+    /// Packed representation of counter information:
+    /// - Bits [11:0]: CSR number for hardware counters
+    /// - Bits [17:12]: Counter width (typically 63 for RV64)
+    /// - MSB: Set for firmware counters, clear for hardware counters
+    inner: usize,
+}
+
+#[allow(unused)]
+impl CounterInfo {
+    const CSR_MASK: usize = 0xFFF; // Bits [11:0]
+    const WIDTH_MASK: usize = 0x3F << 12; // Bits [17:12]
+    const FIRMWARE_FLAG: usize = 1 << (size_of::<usize>() * 8 - 1); // MSB
+
+    #[inline]
+    pub const fn new(counter_info: usize) -> Self {
+        Self {
+            inner: counter_info,
+        }
+    }
+
+    #[inline]
+    pub fn set_csr(&mut self, csr_num: u16) {
+        self.inner = (self.inner & !Self::CSR_MASK) | ((csr_num as usize) & Self::CSR_MASK);
+    }
+
+    #[inline]
+    pub fn get_csr(&self) -> usize {
+        self.inner & Self::CSR_MASK
+    }
+
+    #[inline]
+    pub fn set_width(&mut self, width: u8) {
+        self.inner = (self.inner & !Self::WIDTH_MASK) | (((width as usize) & 0x3F) << 12);
+    }
+
+    #[inline]
+    pub fn get_width(&self) -> usize {
+        (self.inner & Self::WIDTH_MASK) >> 12
+    }
+
+    #[inline]
+    pub fn is_firmware_counter(&self) -> bool {
+        self.inner & Self::FIRMWARE_FLAG != 0
+    }
+
+    #[inline]
+    pub const fn with_hardware_info(csr_num: u16, width: u8) -> Self {
+        Self {
+            inner: ((csr_num as usize) & Self::CSR_MASK) | (((width as usize) & 0x3F) << 12),
+        }
+    }
+
+    #[inline]
+    pub const fn with_firmware_info() -> Self {
+        Self {
+            inner: Self::FIRMWARE_FLAG,
+        }
+    }
+
+    #[inline]
+    pub const fn inner(self) -> usize {
+        self.inner
+    }
+}
+
+struct EventIdx {
+    inner: usize,
+}
+
+impl EventIdx {
+    fn raw(&self) -> usize {
+        self.inner
+    }
+
+    fn new_firmware_event(event_code: usize) -> Self {
+        let inner = 0xf << 16 | event_code;
+        Self { inner }
+    }
+}

+ 126 - 53
xtask/src/bench.rs

@@ -1,5 +1,6 @@
 use std::{
     env, fs,
+    path::{Path, PathBuf},
     process::{Command, ExitStatus},
 };
 
@@ -9,72 +10,144 @@ use crate::utils::cargo;
 
 #[derive(Debug, Args, Clone)]
 pub struct BenchArg {
-    /// Package Prototyper and Test-Kernel
-    #[clap(long)]
+    /// Package Prototyper and bench kernel into a single image
+    #[clap(
+        long,
+        help = "Create a combined image with Prototyper and bench kernel"
+    )]
     pub pack: bool,
 }
 
+const ARCH: &str = "riscv64imac-unknown-none-elf";
+const BENCH_KERNEL_NAME: &str = "rustsbi-bench-kernel";
+const PROTOTYPER_BIN: &str = "rustsbi-prototyper.bin";
+
 #[must_use]
 pub fn run(arg: &BenchArg) -> Option<ExitStatus> {
-    let arch = "riscv64imac-unknown-none-elf";
-    let current_dir = env::current_dir();
-    let target_dir = current_dir
-        .as_ref()
-        .unwrap()
-        .join("target")
-        .join(arch)
-        .join("release");
+    let current_dir = env::current_dir().ok()?;
+    let target_dir = get_target_dir(&current_dir);
 
+    // Build the bench kernel
     info!("Building bench kernel");
-    cargo::Cargo::new("build")
-        .package("rustsbi-bench-kernel")
-        .target(arch)
-        .release()
-        .status()
-        .ok()?;
-
-    info!("Copy to binary");
-    let exit_status = Command::new("rust-objcopy")
-        .args(["-O", "binary"])
-        .arg("--binary-architecture=riscv64")
-        .arg(target_dir.join("rustsbi-bench-kernel"))
-        .arg(target_dir.join("rustsbi-bench-kernel.bin"))
-        .status()
-        .ok()?;
+    let build_status = build_bench_kernel()?;
+    if !build_status.success() {
+        error!("Failed to build bench kernel");
+        return Some(build_status);
+    }
+
+    // Convert to binary format
+    info!("Converting to binary format");
+    let exit_status = convert_to_binary(&target_dir)?;
+    if !exit_status.success() {
+        error!("Failed to convert bench kernel to binary format");
+        return Some(exit_status);
+    }
 
+    // Pack into image if requested
     if arg.pack {
-        info!("Pack to image");
-        match fs::exists(target_dir.join("rustsbi-prototyper.bin")) {
-            Ok(true) => {}
-            Ok(false) => {
-                panic!(
-                    " Couldn't open \"rustsbi-prototyper.bin\": No such file or directory. Please compile Prototyper first"
+        info!("Packing into image");
+        match pack_image(&current_dir, &target_dir) {
+            Ok(status) => {
+                info!(
+                    "Output image created at: {}",
+                    target_dir
+                        .join(format!("{}.itb", BENCH_KERNEL_NAME))
+                        .display()
                 );
+                return Some(status);
             }
-            Err(_) => {
-                panic!(
-                    "Can't check existence of file rustsbi-prototyper.bin, please compile Prototyper first"
-                );
+            Err(err_msg) => {
+                error!("{}", err_msg);
+                return Some(<ExitStatus as std::os::unix::process::ExitStatusExt>::from_raw(1));
             }
         }
-        fs::copy(
-            current_dir
-                .as_ref()
-                .unwrap()
-                .join("prototyper")
-                .join("bench-kernel")
-                .join("scripts")
-                .join("rustsbi-bench-kernel.its"),
-            target_dir.join("rustsbi-bench-kernel.its"),
-        )
-        .ok()?;
-        env::set_current_dir(&target_dir).ok()?;
-        Command::new("mkimage")
-            .args(["-f", "rustsbi-bench-kernel.its"])
-            .arg("rustsbi-bench-kernel.itb")
-            .status()
-            .ok()?;
-        fs::remove_file(env::current_dir().unwrap().join("rustsbi-bench-kernel.its")).ok()?;
+    } else {
+        info!(
+            "Output binary created at: {}",
+            target_dir
+                .join(format!("{}.bin", BENCH_KERNEL_NAME))
+                .display()
+        );
     }
+
     Some(exit_status)
 }
+
+fn get_target_dir(current_dir: &Path) -> PathBuf {
+    current_dir.join("target").join(ARCH).join("release")
+}
+
+fn build_bench_kernel() -> Option<ExitStatus> {
+    cargo::Cargo::new("build")
+        .package(BENCH_KERNEL_NAME)
+        .target(ARCH)
+        .release()
+        .status()
+        .ok()
+}
+
+fn convert_to_binary(target_dir: &Path) -> Option<ExitStatus> {
+    let kernel_path = target_dir.join(BENCH_KERNEL_NAME);
+    let bin_path = target_dir.join(format!("{}.bin", BENCH_KERNEL_NAME));
+
+    Command::new("rust-objcopy")
+        .args([
+            "-O",
+            "binary",
+            "--binary-architecture=riscv64",
+            &kernel_path.to_string_lossy(),
+            &bin_path.to_string_lossy(),
+        ])
+        .status()
+        .ok()
+}
+
+fn pack_image(current_dir: &Path, target_dir: &Path) -> Result<ExitStatus, String> {
+    // Check if prototyper binary exists
+    let prototyper_bin_path = target_dir.join(PROTOTYPER_BIN);
+    if !prototyper_bin_path.exists() {
+        return Err(format!(
+            "Error: Prototyper binary not found at '{}'\n\
+             Please run 'cargo prototyper' first to build the Prototyper binary.",
+            prototyper_bin_path.display()
+        ));
+    }
+
+    // Copy ITS file
+    let its_source = current_dir
+        .join("prototyper")
+        .join("bench-kernel")
+        .join("scripts")
+        .join(format!("{}.its", BENCH_KERNEL_NAME));
+
+    let its_dest = target_dir.join(format!("{}.its", BENCH_KERNEL_NAME));
+
+    fs::copy(&its_source, &its_dest).map_err(|e| format!("Failed to copy ITS file: {}", e))?;
+
+    // Change to target directory
+    let original_dir =
+        env::current_dir().map_err(|e| format!("Failed to get current directory: {}", e))?;
+
+    env::set_current_dir(target_dir)
+        .map_err(|e| format!("Failed to change directory to target: {}", e))?;
+
+    // Create image
+    let status = Command::new("mkimage")
+        .args([
+            "-f",
+            &format!("{}.its", BENCH_KERNEL_NAME),
+            &format!("{}.itb", BENCH_KERNEL_NAME),
+        ])
+        .status()
+        .map_err(|e| format!("Failed to execute mkimage command: {}", e))?;
+
+    // Clean up
+    fs::remove_file(format!("{}.its", BENCH_KERNEL_NAME))
+        .map_err(|e| format!("Failed to clean up ITS file: {}", e))?;
+
+    // Restore original directory
+    env::set_current_dir(original_dir)
+        .map_err(|e| format!("Failed to restore original directory: {}", e))?;
+
+    Ok(status)
+}

+ 34 - 13
xtask/src/main.rs

@@ -31,26 +31,47 @@ struct Cli {
 
 #[derive(Subcommand)]
 enum Cmd {
+    /// Build and configure the RustSBI Prototyper bootloader.
     Prototyper(PrototyperArg),
+    /// Build test-kernel for the RustSBI Prototyper.
     Test(TestArg),
+    /// Build bench-kernel for the RustSBI Prototyper.
     Bench(BenchArg),
 }
 
 fn main() -> ExitCode {
     let cli_args = Cli::parse();
-    logger::Logger::init(&cli_args).expect("Unable to init logger");
-
-    if let Some(code) = match cli_args.cmd {
-        Cmd::Prototyper(ref arg) => prototyper::run(arg),
-        Cmd::Test(ref arg) => test::run(arg),
-        Cmd::Bench(ref arg) => bench::run(arg),
-    } {
-        if code.success() {
-            info!("Finished");
-            return ExitCode::SUCCESS;
-        }
+    if let Err(e) = logger::Logger::init(&cli_args) {
+        eprintln!("Logger initialization failed: {}", e);
+        return ExitCode::FAILURE;
     }
 
-    error!("Failed to run task!");
-    ExitCode::FAILURE
+    // Execute the selected command
+    let result = match &cli_args.cmd {
+        Cmd::Prototyper(arg) => prototyper::run(arg),
+        Cmd::Test(arg) => test::run(arg),
+        Cmd::Bench(arg) => bench::run(arg),
+    };
+
+    match result {
+        Some(exit_status) if exit_status.success() => {
+            info!("Task completed successfully");
+            ExitCode::SUCCESS
+        }
+        Some(exit_status) => {
+            let cmd_name = match &cli_args.cmd {
+                Cmd::Prototyper(_) => "prototyper",
+                Cmd::Test(_) => "test",
+                Cmd::Bench(_) => "bench",
+            };
+            error!("Task '{}' failed with exit code: {}", cmd_name, exit_status);
+            ExitCode::FAILURE
+        }
+        None => {
+            error!(
+                "Task execution failed: operation was interrupted or encountered an unrecoverable error"
+            );
+            ExitCode::FAILURE
+        }
+    }
 }

+ 126 - 85
xtask/src/prototyper.rs

@@ -4,11 +4,9 @@ use std::{
     process::{Command, ExitStatus},
 };
 
+use crate::utils::{CmdOptional, cargo};
 use clap::Args;
 
-use crate::utils::CmdOptional;
-use crate::utils::cargo;
-
 #[derive(Debug, Args, Clone)]
 pub struct PrototyperArg {
     #[clap(long, short = 'f')]
@@ -27,117 +25,160 @@ pub struct PrototyperArg {
     pub config_file: Option<PathBuf>,
 }
 
+const ARCH: &str = "riscv64imac-unknown-none-elf";
+const PACKAGE_NAME: &str = "rustsbi-prototyper";
+
 #[must_use]
-#[rustfmt::skip] // "export_env!("PROTOTYPER_FDT_PATH" ?= fdt.unwrap());" is a macro, rustfmt will not format it correctly
 pub fn run(arg: &PrototyperArg) -> Option<ExitStatus> {
-    let arch = "riscv64imac-unknown-none-elf";
-    let fdt = arg.fdt.clone();
-    let payload = arg.payload.clone();
-    let jump = arg.jump;
-
-    let current_dir = env::current_dir();
-    let raw_target_dir = current_dir
-        .as_ref()
-        .unwrap()
-        .join("target");
-    let target_dir = raw_target_dir
-        .join(arch)
-        .join("release");
+    let dirs = prepare_directories()?;
+    setup_config_file(&dirs.target_config_toml, arg)?;
+
+    let exit_status = build_prototyper(arg)?;
+    if !exit_status.success() {
+        error!(
+            "Failed to execute rust-objcopy. Please ensure that cargo-binutils is installed and available in your system's PATH."
+        );
+        return Some(exit_status);
+    }
+
+    copy_output_files(&dirs.target_dir, arg)?;
+
+    Some(exit_status)
+}
+
+struct Directories {
+    target_dir: PathBuf,
+    target_config_toml: PathBuf,
+}
+
+fn prepare_directories() -> Option<Directories> {
+    let current_dir = env::current_dir().ok()?;
+    let raw_target_dir = current_dir.join("target");
+    let target_dir = raw_target_dir.join(ARCH).join("release");
     let target_config_toml = raw_target_dir.join("config.toml");
 
+    Some(Directories {
+        target_dir,
+        target_config_toml,
+    })
+}
+
+fn setup_config_file(target_config_toml: &PathBuf, arg: &PrototyperArg) -> Option<()> {
+    // Delete old config if exists
+    if fs::exists(target_config_toml).ok()? {
+        info!("Delete old config");
+        fs::remove_file(target_config_toml).ok()?;
+    }
+
+    // Determine config file path
+    let current_dir = env::current_dir().ok()?;
     let default_config_file = current_dir
-        .as_ref()
-        .unwrap()
         .join("prototyper")
         .join("prototyper")
         .join("config")
         .join("default.toml");
     let config_file = arg.config_file.clone().unwrap_or(default_config_file);
 
-    if fs::exists(&target_config_toml).ok()? {
-        info!("Delete old config");
-        fs::remove_file(&target_config_toml).ok()?;
-    }
+    // Copy config
+    info!("Copy config from: {}", config_file.display());
+    fs::copy(&config_file, target_config_toml).ok()?;
 
-    info!("Copy config");
-    fs::copy(
-        &config_file,
-        target_config_toml
-    ).ok()?;
-
-    info!("Building Protoyper");
-    cargo::Cargo::new("build")
-        .package("rustsbi-prototyper")
-        .target(arch)
-        .unstable("build-std", ["core","alloc"])
+    Some(())
+}
+
+fn build_prototyper(arg: &PrototyperArg) -> Option<ExitStatus> {
+    info!("Building Prototyper");
+
+    // Build the prototyper
+    let status = cargo::Cargo::new("build")
+        .package(PACKAGE_NAME)
+        .target(ARCH)
+        .unstable("build-std", ["core", "alloc"])
         .env("RUSTFLAGS", "-C relocation-model=pie -C link-arg=-pie")
         .features(&arg.features)
         .optional(arg.fdt.is_some(), |cargo| {
-            cargo.env("PROTOTYPER_FDT_PATH", fdt.as_ref().unwrap());
+            cargo.env("PROTOTYPER_FDT_PATH", arg.fdt.as_ref().unwrap());
             cargo.features(["fdt".to_string()])
         })
-        .optional(payload.is_some(), |cargo| {
-            cargo.env("PROTOTYPER_PAYLOAD_PATH", payload.as_ref().unwrap());
+        .optional(arg.payload.is_some(), |cargo| {
+            cargo.env("PROTOTYPER_PAYLOAD_PATH", arg.payload.as_ref().unwrap());
             cargo.features(["payload".to_string()])
         })
-        .optional(jump, |cargo| {
-            cargo.features(["jump".to_string()])
-        })
+        .optional(arg.jump, |cargo| cargo.features(["jump".to_string()]))
         .release()
         .status()
         .ok()?;
 
-    info!("Copy to binary");
-    let exit_status = Command::new("rust-objcopy")
-        .args(["-O", "binary"])
-        .arg("--binary-architecture=riscv64")
-        .arg(target_dir.join("rustsbi-prototyper"))
-        .arg(target_dir.join("rustsbi-prototyper.bin"))
+    if !status.success() {
+        error!(
+            "Failed to build prototyper. Please check the cargo output above for detailed error information."
+        );
+        return Some(status);
+    }
+
+    // Get target directory once instead of recreating it
+    let target_dir = prepare_directories()?.target_dir;
+    let elf_path = target_dir.join(PACKAGE_NAME);
+    let bin_path = target_dir.join(format!("{}.bin", PACKAGE_NAME));
+
+    // Create binary from ELF
+    info!("Converting ELF to binary with rust-objcopy");
+    let result = Command::new("rust-objcopy")
+        .args([
+            "-O",
+            "binary",
+            "--binary-architecture=riscv64",
+            &elf_path.to_string_lossy(),
+            &bin_path.to_string_lossy(),
+        ])
         .status()
-        .ok()?;
-    if !exit_status.success() {
-        error!("Failed to exec rust-objcopy, please check if cargo-binutils has been installed?");
-        return Some(exit_status);
+        .ok();
+
+    if result.is_none() {
+        error!(
+            "Failed to execute rust-objcopy. Command not found or failed to start.\n\
+             Source: {}\n\
+             Destination: {}\n\
+             Please install cargo-binutils with cmd: cargo install cargo-binutils",
+            elf_path.display(),
+            bin_path.display()
+        );
     }
 
-    if arg.payload.is_some() {
+    result
+}
+
+fn copy_output_files(target_dir: &PathBuf, arg: &PrototyperArg) -> Option<()> {
+    let mode_suffix = if arg.payload.is_some() {
         info!("Copy for payload mode");
-        fs::copy(
-            target_dir.join("rustsbi-prototyper"),
-            target_dir.join("rustsbi-prototyper-payload.elf"),
-        )
-        .ok()?;
-        fs::copy(
-            target_dir.join("rustsbi-prototyper.bin"),
-            target_dir.join("rustsbi-prototyper-payload.bin"),
-        )
-        .ok()?;
+        "payload"
     } else if arg.jump {
         info!("Copy for jump mode");
-        fs::copy(
-            target_dir.join("rustsbi-prototyper"),
-            target_dir.join("rustsbi-prototyper-jump.elf"),
-        )
-        .ok()?;
-        fs::copy(
-            target_dir.join("rustsbi-prototyper.bin"),
-            target_dir.join("rustsbi-prototyper-jump.bin"),
-        )
-        .ok()?;
+        "jump"
     } else {
         info!("Copy for dynamic mode");
-        fs::copy(
-            target_dir.join("rustsbi-prototyper"),
-            target_dir.join("rustsbi-prototyper-dynamic.elf"),
-        )
-        .ok()?;
-        fs::copy(
-            target_dir.join("rustsbi-prototyper.bin"),
-            target_dir.join("rustsbi-prototyper-dynamic.bin"),
-        )
-        .ok()?;
-
-    }
-
-    Some(exit_status)
+        "dynamic"
+    };
+
+    // Copy ELF file
+    let elf_source = target_dir.join(PACKAGE_NAME);
+    let elf_dest = target_dir.join(format!("{}-{}.elf", PACKAGE_NAME, mode_suffix));
+    info!(
+        "Copying ELF file: {} -> {}",
+        elf_source.display(),
+        elf_dest.display()
+    );
+    fs::copy(&elf_source, &elf_dest).ok()?;
+
+    // Copy binary file
+    let bin_source = target_dir.join(format!("{}.bin", PACKAGE_NAME));
+    let bin_dest = target_dir.join(format!("{}-{}.bin", PACKAGE_NAME, mode_suffix));
+    info!(
+        "Copying binary file: {} -> {}",
+        bin_source.display(),
+        bin_dest.display()
+    );
+    fs::copy(&bin_source, &bin_dest).ok()?;
+
+    Some(())
 }

+ 123 - 53
xtask/src/test.rs

@@ -1,5 +1,6 @@
 use std::{
     env, fs,
+    path::{Path, PathBuf},
     process::{Command, ExitStatus},
 };
 
@@ -9,72 +10,141 @@ use crate::utils::cargo;
 
 #[derive(Debug, Args, Clone)]
 pub struct TestArg {
-    /// Package Prototyper and Test-Kernel
-    #[clap(long)]
+    /// Package Prototyper and Test-Kernel into a single image
+    #[clap(long, help = "Create a combined image with Prototyper and test kernel")]
     pub pack: bool,
 }
 
+const ARCH: &str = "riscv64imac-unknown-none-elf";
+const TEST_KERNEL_NAME: &str = "rustsbi-test-kernel";
+const PROTOTYPER_BIN: &str = "rustsbi-prototyper.bin";
+
 #[must_use]
 pub fn run(arg: &TestArg) -> Option<ExitStatus> {
-    let arch = "riscv64imac-unknown-none-elf";
-    let current_dir = env::current_dir();
-    let target_dir = current_dir
-        .as_ref()
-        .unwrap()
-        .join("target")
-        .join(arch)
-        .join("release");
+    let current_dir = env::current_dir().ok()?;
+    let target_dir = get_target_dir(&current_dir);
 
+    // Build the test kernel
     info!("Building test kernel");
-    cargo::Cargo::new("build")
-        .package("rustsbi-test-kernel")
-        .target(arch)
-        .release()
-        .status()
-        .ok()?;
-
-    info!("Copy to binary");
-    let exit_status = Command::new("rust-objcopy")
-        .args(["-O", "binary"])
-        .arg("--binary-architecture=riscv64")
-        .arg(target_dir.join("rustsbi-test-kernel"))
-        .arg(target_dir.join("rustsbi-test-kernel.bin"))
-        .status()
-        .ok()?;
+    let build_status = build_test_kernel()?;
+    if !build_status.success() {
+        error!("Failed to build test kernel");
+        return Some(build_status);
+    }
+
+    // Convert to binary format
+    info!("Converting to binary format");
+    let exit_status = convert_to_binary(&target_dir)?;
+    if !exit_status.success() {
+        error!("Failed to convert test kernel to binary format");
+        return Some(exit_status);
+    }
 
+    // Pack into image if requested
     if arg.pack {
-        info!("Pack to image");
-        match fs::exists(target_dir.join("rustsbi-prototyper.bin")) {
-            Ok(true) => {}
-            Ok(false) => {
-                panic!(
-                    " Couldn't open \"rustsbi-prototyper.bin\": No such file or directory. Please compile Prototyper first"
+        info!("Packing into image");
+        match pack_image(&current_dir, &target_dir) {
+            Ok(status) => {
+                info!(
+                    "Output image created at: {}",
+                    target_dir
+                        .join(format!("{}.itb", TEST_KERNEL_NAME))
+                        .display()
                 );
+                return Some(status);
             }
-            Err(_) => {
-                panic!(
-                    "Can't check existence of file rustsbi-prototyper.bin, please compile Prototyper first"
-                );
+            Err(err_msg) => {
+                error!("{}", err_msg);
+                return Some(<ExitStatus as std::os::unix::process::ExitStatusExt>::from_raw(1));
             }
         }
-        fs::copy(
-            current_dir
-                .as_ref()
-                .unwrap()
-                .join("prototyper")
-                .join("test-kernel")
-                .join("scripts")
-                .join("rustsbi-test-kernel.its"),
-            target_dir.join("rustsbi-test-kernel.its"),
-        )
-        .ok()?;
-        env::set_current_dir(&target_dir).ok()?;
-        Command::new("mkimage")
-            .args(["-f", "rustsbi-test-kernel.its"])
-            .arg("rustsbi-test-kernel.itb")
-            .status()
-            .ok()?;
-        fs::remove_file(env::current_dir().unwrap().join("rustsbi-test-kernel.its")).ok()?;
+    } else {
+        info!(
+            "Output binary created at: {}",
+            target_dir
+                .join(format!("{}.bin", TEST_KERNEL_NAME))
+                .display()
+        );
     }
+
     Some(exit_status)
 }
+
+fn get_target_dir(current_dir: &Path) -> PathBuf {
+    current_dir.join("target").join(ARCH).join("release")
+}
+
+fn build_test_kernel() -> Option<ExitStatus> {
+    cargo::Cargo::new("build")
+        .package(TEST_KERNEL_NAME)
+        .target(ARCH)
+        .release()
+        .status()
+        .ok()
+}
+
+fn convert_to_binary(target_dir: &Path) -> Option<ExitStatus> {
+    let kernel_path = target_dir.join(TEST_KERNEL_NAME);
+    let bin_path = target_dir.join(format!("{}.bin", TEST_KERNEL_NAME));
+
+    Command::new("rust-objcopy")
+        .args([
+            "-O",
+            "binary",
+            "--binary-architecture=riscv64",
+            &kernel_path.to_string_lossy(),
+            &bin_path.to_string_lossy(),
+        ])
+        .status()
+        .ok()
+}
+
+fn pack_image(current_dir: &Path, target_dir: &Path) -> Result<ExitStatus, String> {
+    // Check if prototyper binary exists
+    let prototyper_bin_path = target_dir.join(PROTOTYPER_BIN);
+    if !prototyper_bin_path.exists() {
+        return Err(format!(
+            "Error: Prototyper binary not found at '{}'\n\
+             Please run 'cargo prototyper' first to build the Prototyper binary.",
+            prototyper_bin_path.display()
+        ));
+    }
+
+    // Copy ITS file
+    let its_source = current_dir
+        .join("prototyper")
+        .join("test-kernel")
+        .join("scripts")
+        .join(format!("{}.its", TEST_KERNEL_NAME));
+
+    let its_dest = target_dir.join(format!("{}.its", TEST_KERNEL_NAME));
+
+    fs::copy(&its_source, &its_dest).map_err(|e| format!("Failed to copy ITS file: {}", e))?;
+
+    // Change to target directory
+    let original_dir =
+        env::current_dir().map_err(|e| format!("Failed to get current directory: {}", e))?;
+
+    env::set_current_dir(target_dir)
+        .map_err(|e| format!("Failed to change directory to target: {}", e))?;
+
+    // Create image
+    let status = Command::new("mkimage")
+        .args([
+            "-f",
+            &format!("{}.its", TEST_KERNEL_NAME),
+            &format!("{}.itb", TEST_KERNEL_NAME),
+        ])
+        .status()
+        .map_err(|e| format!("Failed to execute mkimage command: {}", e))?;
+
+    // Clean up
+    fs::remove_file(format!("{}.its", TEST_KERNEL_NAME))
+        .map_err(|e| format!("Failed to clean up ITS file: {}", e))?;
+
+    // Restore original directory
+    env::set_current_dir(original_dir)
+        .map_err(|e| format!("Failed to restore original directory: {}", e))?;
+
+    Ok(status)
+}