浏览代码

lib: move `sbi-spec` code from rustsbi/sbi-spec repo to rustsbi/rustsbi

Thanks to sbi-spec developers: @luojia65, @YdrMaster, @hwk2077,
@jakezhu9, @duskmoon314 .

Signed-off-by: DongQing <[email protected]>
DongQing 1 年之前
父节点
当前提交
21fd138882

+ 45 - 0
.github/workflows/rust.yml

@@ -118,3 +118,48 @@ jobs:
         with:
           command: test
           args: --features "machine" --verbose -p rustsbi
+
+  sbi-spec:
+    name: Compile and test sbi-spec
+    needs: fmt
+    runs-on: ubuntu-latest
+    strategy:
+      fail-fast: false
+      matrix:
+        target:
+          [
+            riscv32imac-unknown-none-elf,
+            riscv64imac-unknown-none-elf,
+            x86_64-unknown-none,
+          ]
+    steps:
+      - uses: actions/checkout@v3
+      # Cache REF:
+      #   - https://github.com/actions/cache/blob/main/examples.md#rust---cargo
+      #   - https://github.com/actions-rs/toolchain/issues/54
+      - name: Cache Rust
+        uses: actions/cache@v3
+        with:
+          key: rust-toolchain-${{ matrix.target }}
+          path: |
+            ~/.rustup/settings.toml
+            ~/.rustup/toolchains/stable-*
+            ~/.rustup/update-hashes/stable-*
+            ~/.cargo/bin/
+            ~/.cargo/registry/index/
+            ~/.cargo/registry/cache/
+            ~/.cargo/git/db/
+            target/
+      - name: Setup Rust
+        run: |
+          rustup toolchain install stable --profile minimal
+          rustup component add rustfmt clippy
+          rustup target add ${{ matrix.target }}
+      - name: Check format
+        run: cargo fmt --all --check
+      - name: Check clippy
+        run: cargo clippy -- -D warnings
+      - name: Check build
+        run: cargo build
+      - name: Check test
+        run: cargo test

+ 1 - 0
Cargo.toml

@@ -3,5 +3,6 @@ members = [
     "rustsbi",
     "rustsbi/macros",
     "sbi-rt",
+    "sbi-spec",
 ]
 resolver = "2"

+ 7 - 0
sbi-spec/.gitignore

@@ -0,0 +1,7 @@
+**/.*/*
+!/.github/*
+!/.cargo/*
+!/.vscode/settings.json
+
+/target
+Cargo.lock

+ 116 - 0
sbi-spec/CHANGELOG.md

@@ -0,0 +1,116 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [Unreleased]
+
+### Added
+
+### Modified
+
+### Fixed
+
+## [0.0.7] - 2023-12-08
+
+`sbi-spec` crate now supports RISC-V SBI version 2.0-rc7.
+
+### Added
+
+- Support to PMU events in Chapter 11
+- Support `NACL` extension in Chapter 15
+- Support `STA` extension in Chapter 16
+- Add new SBI error `NoShmem`
+- binary: add `SharedPtr` struct to represent shared memory range feature.
+- nacl: add `shmem_size` module
+- Move `HartMask` structure to `sbi-spec` crate from `rustsbi` crate.
+
+### Modified
+
+- Rearrange `HSM` constants into modules.
+
+### Fixed
+
+- Remove redundant prefixes in `PMU`
+- Add new function id `SNAPSHOT_SET_SHMEM` in `PMU`
+
+## [0.0.6] - 2023-04-04
+
+### Added
+
+- `Physical` shared memory physical address range with type annotation in Chapter 3
+- Support to RISC-V SBI System Suspend extension
+- Support to CPPC extension
+
+## [0.0.5] - 2023-02-16
+
+### Added
+
+- Adapt to RISC-V SBI specification version 2.0-rc1
+- `PMU_COUNTER_FW_READ_HI` function in `pmu` module for RV32 systems
+- SBI DBCN extension support
+- `Result`-like documents to `SbiRet`
+
+### Modified
+
+- style: add period to docs
+
+## [0.0.4] - 2022-10-10
+
+### Added
+
+- Various convenient functions to `SbiRet` structure
+- Add documents on whole `sbi-rt` crate to coply with `deny(missing_docs)`
+- Feature `legacy` to gate legacy SBI extension
+
+### Modified
+
+- Rename `SbiRet::ok` to `SbiRet::success`
+- Rename `SbiSpecVersion` to struct `Version` 
+
+## [0.0.3] - 2022-10-06
+
+### Added
+
+- deps: static_assertions
+  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
+
+### Modified
+
+- rename `GET_SPEC_VERSION` to `GET_SBI_SPEC_VERSION`
+- rename `impl_id::IMPL_XXX` to `impl_id::XXX`
+
+### Removed
+
+- default target to RISC-V
+
+## [0.0.2] - 2022-07-21
+
+### Added
+
+- A changelog to this project
+
+### Modified
+
+- Lift build target limit; now this crate would build on targets other than RISC-V
+
+## [0.0.1] - 2022-07-11
+
+This is the first release of sbi-spec crate. This crate includes definition of RISC-V Supervisor Binary Interface (SBI) including structures and constants.
+
+### Added
+
+- Adapt to SBI specification version 1.0.0 ratified
+
+[Unreleased]: https://github.com/rustsbi/sbi-spec/compare/v0.0.7...HEAD
+[0.0.7]: https://github.com/rustsbi/sbi-spec/compare/v0.0.6...v0.0.7
+[0.0.6]: https://github.com/rustsbi/sbi-spec/compare/v0.0.5...v0.0.6
+[0.0.5]: https://github.com/rustsbi/sbi-spec/compare/v0.0.4...v0.0.5
+[0.0.4]: https://github.com/rustsbi/sbi-spec/compare/v0.0.3...v0.0.4
+[0.0.3]: https://github.com/rustsbi/sbi-spec/compare/v0.0.2...v0.0.3
+[0.0.2]: https://github.com/rustsbi/sbi-spec/compare/v0.0.1...v0.0.2
+[0.0.1]: https://github.com/rustsbi/sbi-spec/releases/tag/v0.0.1

+ 20 - 0
sbi-spec/Cargo.toml

@@ -0,0 +1,20 @@
+[package]
+name = "sbi-spec"
+description = "Definitions and constants in RISC-V Supervisor Binary Interface (RISC-V SBI)"
+version = "0.0.7-alpha.3"
+authors = ["YdrMaster <[email protected]>", "Luo Jia <[email protected]>"]
+repository = "https://github.com/rustsbi/sbi-spec"
+documentation = "https://docs.rs/sbi-spec"
+license = "MulanPSL-2.0 OR MIT"
+readme = "README.md"
+keywords = ["riscv", "sbi", "rustsbi"]
+categories = ["os", "embedded", "hardware-support", "no-std"]
+edition = "2021"
+
+[dev-dependencies]
+static_assertions = "1.1.0"
+
+[features]
+default = []
+# Support legacy extension; this feature is not included by default.
+legacy = []

+ 8 - 0
sbi-spec/LICENSE-MIT

@@ -0,0 +1,8 @@
+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.
+

+ 101 - 0
sbi-spec/LICENSE-MULAN

@@ -0,0 +1,101 @@
+木兰宽松许可证, 第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

+ 41 - 0
sbi-spec/README.md

@@ -0,0 +1,41 @@
+# SBI 标准的 Rust 实现
+
+[![CI](https://github.com/rustsbi/sbi-spec/actions/workflows/build.yml/badge.svg?branch=main)](https://github.com/rustsbi/sbi-spec/actions)
+[![Latest version](https://img.shields.io/crates/v/sbi-spec.svg)](https://crates.io/crates/sbi-spec)
+[![issue](https://img.shields.io/github/issues/rustsbi/sbi-spec)](https://github.com/rustsbi/sbi-spec/issues)
+[![Documentation](https://docs.rs/sbi-spec/badge.svg)](https://docs.rs/sbi-spec)
+![license](https://img.shields.io/github/license/rustsbi/sbi-spec)
+
+这个库用 Rust 实现了 [SBI 标准](https://github.com/riscv-non-isa/riscv-sbi-doc) 定义的常量和结构。
+
+2.0-rc7 标准各章节的实现情况:
+
+- §3
+  - [x] 常量
+  - [x] 结构
+- §4
+  - [x] 常量
+- §5
+  - [x] 常量
+- §6
+  - [x] 常量
+- §7
+  - [x] 常量
+- §8
+  - [x] 常量
+- §9
+  - [x] 常量
+- §10
+  - [x] 常量
+- §11
+  - [x] 常量
+- §12
+  - [x] 常量
+- §13
+  - [x] 常量
+- §14
+  - [x] 常量
+- §15
+  - [x] 常量
+- §16
+  - [x] 常量

+ 98 - 0
sbi-spec/src/base.rs

@@ -0,0 +1,98 @@
+//! Chapter 4. Base Extension (EID #0x10).
+
+/// Extension ID for RISC-V SBI Base extension.
+pub const EID_BASE: usize = 0x10;
+pub use fid::*;
+
+/// Default probe value for the target SBI extension is unavailable.
+pub const UNAVAILABLE_EXTENSION: usize = 0;
+
+/// SBI specification version.
+///
+/// Not to be confused with 'implementation version'.
+///
+/// Declared in §4.1.
+#[derive(Clone, Copy, Debug)]
+#[repr(transparent)]
+pub struct Version {
+    raw: usize,
+}
+
+impl Version {
+    /// Converts raw extension value into Version structure.
+    #[inline]
+    pub const fn from_raw(raw: usize) -> Self {
+        Self { raw }
+    }
+
+    /// Reads major version of specification.
+    #[inline]
+    pub const fn major(self) -> usize {
+        (self.raw >> 24) & ((1 << 7) - 1)
+    }
+
+    /// Reads minor version of specification.
+    #[inline]
+    pub const fn minor(self) -> usize {
+        self.raw & ((1 << 24) - 1)
+    }
+}
+
+impl core::fmt::Display for Version {
+    #[inline]
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        write!(f, "{}.{}", self.major(), self.minor())
+    }
+}
+
+/// Declared in §4.8
+mod fid {
+    /// Function ID to get the current SBI specification version.
+    ///
+    /// Declared in §4.1.
+    pub const GET_SBI_SPEC_VERSION: usize = 0x0;
+    /// Function ID to get the current SBI implementation ID.
+    ///
+    /// Declared in §4.2.
+    pub const GET_SBI_IMPL_ID: usize = 0x1;
+    /// Function ID to get the current SBI implementation version.
+    ///
+    /// Declared in §4.3.
+    pub const GET_SBI_IMPL_VERSION: usize = 0x2;
+    /// Function ID to probe information about one SBI extension from current environment.
+    ///
+    /// Declared in §4.4.
+    pub const PROBE_EXTENSION: usize = 0x3;
+    /// Function ID to get value of `mvendorid` register in current environment.
+    ///
+    /// Declared in §4.5.
+    pub const GET_MVENDORID: usize = 0x4;
+    /// Function ID to get value of `marchid` register in current environment.
+    ///
+    /// Declared in §4.6.
+    pub const GET_MARCHID: usize = 0x5;
+    /// Function ID to get value of `mimpid` register in current environment.
+    ///
+    /// Declared in §4.7.
+    pub const GET_MIMPID: usize = 0x6;
+}
+
+/// SBI Implementation IDs.
+///
+/// Declared in §4.9.
+pub mod impl_id {
+    /// Berkley Bootloader
+    pub const BBL: usize = 0;
+    /// OpenSBI
+    pub const OPEN_SBI: usize = 1;
+    /// Xvisor
+    pub const XVISOR: usize = 2;
+    /// KVM
+    pub const KVM: usize = 3;
+    /// RustSBI
+    pub const RUST_SBI: usize = 4;
+    /// Diosix
+    pub const DIOSIX: usize = 5;
+    /// Coffer
+    pub const COFFER: usize = 6;
+}

+ 907 - 0
sbi-spec/src/binary.rs

@@ -0,0 +1,907 @@
+//! 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 to pin 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 {
+    /// Error number.
+    pub error: usize,
+    /// Result value.
+    pub value: usize,
+}
+
+/// SBI success state return value.
+pub const RET_SUCCESS: usize = 0;
+/// Error for SBI call failed for unknown reasons.
+pub const RET_ERR_FAILED: usize = -1isize as _;
+/// Error for target operation not supported.
+pub const RET_ERR_NOT_SUPPORTED: usize = -2isize as _;
+/// Error for invalid parameter.
+pub const RET_ERR_INVALID_PARAM: usize = -3isize as _;
+/// Error for denied (unused in standard extensions).
+pub const RET_ERR_DENIED: usize = -4isize as _;
+/// Error for invalid address.
+pub const RET_ERR_INVALID_ADDRESS: usize = -5isize as _;
+/// Error for resource already available.
+pub const RET_ERR_ALREADY_AVAILABLE: usize = -6isize as _;
+/// Error for resource already started.
+pub const RET_ERR_ALREADY_STARTED: usize = -7isize as _;
+/// Error for resource already stopped.
+pub const RET_ERR_ALREADY_STOPPED: usize = -8isize as _;
+/// Error for shared memory not available.
+pub const RET_ERR_NO_SHMEM: usize = -9isize as _;
+
+impl core::fmt::Debug for SbiRet {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        match self.error {
+            RET_SUCCESS => self.value.fmt(f),
+            RET_ERR_FAILED => write!(f, "<SBI call failed>"),
+            RET_ERR_NOT_SUPPORTED => write!(f, "<SBI feature not supported>"),
+            RET_ERR_INVALID_PARAM => write!(f, "<SBI invalid parameter>"),
+            RET_ERR_DENIED => write!(f, "<SBI denied>"),
+            RET_ERR_INVALID_ADDRESS => write!(f, "<SBI invalid address>"),
+            RET_ERR_ALREADY_AVAILABLE => write!(f, "<SBI already available>"),
+            RET_ERR_ALREADY_STARTED => write!(f, "<SBI already started>"),
+            RET_ERR_ALREADY_STOPPED => write!(f, "<SBI already stopped>"),
+            RET_ERR_NO_SHMEM => write!(f, "<SBI shared memory not available>"),
+            unknown => write!(f, "[SBI Unknown error: {unknown:#x}]"),
+        }
+    }
+}
+
+/// RISC-V SBI error in enumeration.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum Error {
+    /// Error for SBI call failed for unknown reasons.
+    Failed,
+    /// Error for target operation not supported.
+    NotSupported,
+    /// Error for invalid parameter.
+    InvalidParam,
+    /// Error for denied (unused in standard extensions).
+    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,
+    /// Custom error code.
+    Custom(isize),
+}
+
+impl SbiRet {
+    /// Returns success SBI state with given `value`.
+    #[inline]
+    pub const fn success(value: usize) -> Self {
+        Self {
+            error: RET_SUCCESS,
+            value,
+        }
+    }
+
+    /// The SBI call request failed for unknown reasons.
+    #[inline]
+    pub const fn failed() -> Self {
+        Self {
+            error: RET_ERR_FAILED,
+            value: 0,
+        }
+    }
+
+    /// 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: RET_ERR_NOT_SUPPORTED,
+            value: 0,
+        }
+    }
+
+    /// 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: RET_ERR_INVALID_PARAM,
+            value: 0,
+        }
+    }
+    /// SBI call failed due to denied.
+    ///
+    /// As the time this document was written,
+    /// there is currently no function in SBI standard that returns this error.
+    /// However, custom extensions or future standard functions may return this
+    /// error if appropriate.
+    #[inline]
+    pub const fn denied() -> Self {
+        Self {
+            error: RET_ERR_DENIED,
+            value: 0,
+        }
+    }
+
+    /// 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: RET_ERR_INVALID_ADDRESS,
+            value: 0,
+        }
+    }
+
+    /// SBI call failed for the target resource is already available,
+    /// e.g. the target hart is already started when caller still request it to start.
+    #[inline]
+    pub const fn already_available() -> Self {
+        Self {
+            error: RET_ERR_ALREADY_AVAILABLE,
+            value: 0,
+        }
+    }
+
+    /// 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: RET_ERR_ALREADY_STARTED,
+            value: 0,
+        }
+    }
+
+    /// 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: RET_ERR_ALREADY_STOPPED,
+            value: 0,
+        }
+    }
+
+    /// 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: RET_ERR_NO_SHMEM,
+            value: 0,
+        }
+    }
+}
+
+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),
+            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 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()
+    }
+
+    /// 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 {
+    ///         format!("error: already started!")
+    ///     } else {
+    ///         format!("error: other error!")
+    ///     }
+    /// }
+    ///
+    /// 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)
+    }
+
+    /// 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()
+    }
+
+    /// 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()
+    }
+
+    /// 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(self, res: Result<usize, Error>) -> Result<usize, 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)
+    }
+}
+
+/// Hart mask structure in SBI function calls.
+#[derive(Debug, Copy, Clone)]
+pub struct HartMask {
+    inner: BitVector,
+}
+
+impl HartMask {
+    /// Construct a hart mask from mask value and base hart id.
+    #[inline]
+    pub const fn from_mask_base(hart_mask: usize, hart_mask_base: usize) -> HartMask {
+        HartMask {
+            inner: BitVector {
+                hart_mask,
+                hart_mask_base,
+            },
+        }
+    }
+
+    /// Returns `hart_mask` and `hart_mask_base` parameters from the hart mask structure.
+    #[inline]
+    pub const fn into_inner(self) -> (usize, usize) {
+        (self.inner.hart_mask, self.inner.hart_mask_base)
+    }
+
+    /// Check if the `hart_id` is included in this hart mask structure.
+    #[inline]
+    pub const fn has_bit(&self, hart_id: usize) -> bool {
+        let BitVector {
+            hart_mask,
+            hart_mask_base,
+        } = self.inner;
+        if hart_mask_base == usize::MAX {
+            // If `hart_mask_base` equals `usize::MAX`, that means `hart_mask` is ignored
+            // and all available harts must be considered.
+            return true;
+        }
+        let Some(idx) = hart_id.checked_sub(hart_mask_base) else {
+            // hart_id < hart_mask_base, not in current mask range
+            return false;
+        };
+        if idx >= usize::BITS as usize {
+            // hart_idx >= hart_mask_base + XLEN, not in current mask range
+            return false;
+        }
+        hart_mask & (1 << idx) != 0
+    }
+}
+
+#[derive(Debug, Copy, Clone)]
+struct BitVector {
+    hart_mask: usize,
+    hart_mask_base: usize,
+}
+
+/// 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 value of type `T` without
+/// 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 from raw
+/// pointer structure.
+///
+/// `SharedPtr` can be used as a parameter to pass shared memory physical pointer
+/// with 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 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 than 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 stablized.
+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 shared physical memory pointer.
+    #[inline]
+    pub const fn phys_addr_lo(self) -> usize {
+        self.phys_addr_lo
+    }
+
+    /// Returns high part physical address of 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::HartMask;
+
+    #[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));
+    }
+}

+ 25 - 0
sbi-spec/src/cppc.rs

@@ -0,0 +1,25 @@
+//! Chapter 14. CPPC Extension (EID #0x43505043 "CPPC").
+
+/// Extension ID for CPPC Extension.
+pub const EID_CPPC: usize = crate::eid_from_str("CPPC") as _;
+pub use fid::*;
+
+/// Declared in §14.
+mod fid {
+    /// Function ID to probe a CPPC register.
+    ///
+    /// Declared in §14.1.
+    pub const PROBE: usize = 0;
+    /// Function ID to read CPPC register bits.
+    ///
+    /// Declared in §14.2.
+    pub const READ: usize = 1;
+    /// Function ID to read high bits of a CPPC register.
+    ///
+    /// Declared in §14.3.
+    pub const READ_HI: usize = 2;
+    /// Function ID to write to a CPPC register.
+    ///
+    /// Declared in §14.4.
+    pub const WRITE: usize = 3;
+}

+ 21 - 0
sbi-spec/src/dbcn.rs

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

+ 70 - 0
sbi-spec/src/hsm.rs

@@ -0,0 +1,70 @@
+//! Chapter 9. Hart State Management Extension (EID #0x48534D "HSM").
+
+/// Extension ID for Hart State Management extension.
+pub const EID_HSM: usize = crate::eid_from_str("HSM") as _;
+pub use fid::*;
+
+/// Hart states.
+///
+/// Declared in Table 1 at §9.
+pub mod hart_state {
+    /// The hart is physically powered-up and executing normally.
+    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.
+    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.
+    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.
+    pub const STOP_PENDING: usize = 3;
+    /// The hart is in a platform specific suspend (or low power) state.
+    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.
+    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.
+    pub const RESUME_PENDING: usize = 6;
+}
+
+/// Hart suspend types.
+pub mod suspend_type {
+    /// Default retentive hart suspend type.
+    pub const RETENTIVE: u32 = 0;
+    /// Default non-retentive hart suspend type.
+    pub const NON_RETENTIVE: u32 = 0x8000_0000;
+}
+
+/// Declared in §9.5.
+mod fid {
+    /// Function ID to start executing the given hart at specified address in supervisor-mode.
+    ///
+    /// Declared in §9.1.
+    pub const HART_START: usize = 0;
+    /// Function ID to stop executing the calling hart in supervisor-mode.
+    ///
+    /// Declared in §9.2.
+    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.
+    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.
+    pub const HART_SUSPEND: usize = 3;
+}

+ 25 - 0
sbi-spec/src/legacy.rs

@@ -0,0 +1,25 @@
+//! Chapter 5. Legacy Extensions (EIDs #0x00 - #0x0F).
+
+pub use id::*;
+
+/// §5.10
+mod id {
+    /// §5.1
+    pub const LEGACY_SET_TIMER: usize = 0;
+    /// §5.2
+    pub const LEGACY_CONSOLE_PUTCHAR: usize = 1;
+    /// §5.3
+    pub const LEGACY_CONSOLE_GETCHAR: usize = 2;
+    /// §5.4
+    pub const LEGACY_CLEAR_IPI: usize = 3;
+    /// §5.5
+    pub const LEGACY_SEND_IPI: usize = 4;
+    /// §5.6
+    pub const LEGACY_REMOTE_FENCE_I: usize = 5;
+    /// §5.7
+    pub const LEGACY_REMOTE_SFENCE_VMA: usize = 6;
+    /// §5.8
+    pub const LEGACY_REMOTE_SFENCE_VMA_ASID: usize = 7;
+    /// §5.9
+    pub const LEGACY_SHUTDOWN: usize = 8;
+}

+ 318 - 0
sbi-spec/src/lib.rs

@@ -0,0 +1,318 @@
+//! RISC-V SBI Specification structure and constant definitions.
+//!
+//! This crate adapts to RISC-V SBI Specification verion 2.0-rc7.
+//! It provides structures in Rust semantics and best practices to simplify
+//! designs of RISC-V SBI ecosystem, both implementation and applications.
+//!
+//! You may find it convenient to use this library in a vast range of packages,
+//! from operating system kernels, hypervisors, to SBI bare metal implementations.
+//! This crate is `no_std` compatible and does not need dymanic memory allocation,
+//! which make it suitable for embedded development.
+//!
+//! Although this library is dedicated to RISC-V architecture, it does not limit
+//! which build target the dependents should compile into. For example, you are
+//! developing a RISC-V emulator on platforms other than RISC-V, the emulator
+//! designed on other platforms can still make use of `sbi-spec` structures to
+//! provide necessary features the emulated RISC-V environment would make use of.
+#![no_std]
+#![deny(missing_docs, unsafe_code, unstable_features)]
+
+// §3
+pub mod binary;
+// §4
+pub mod base;
+// §5
+#[cfg(feature = "legacy")]
+pub mod legacy;
+// §6
+pub mod time;
+// §7
+pub mod spi;
+// §8
+pub mod rfnc;
+// §9
+pub mod hsm;
+// §10
+pub mod srst;
+// §11
+pub mod pmu;
+// §12
+pub mod dbcn;
+// §13
+pub mod susp;
+// §14
+pub mod cppc;
+// §15
+pub mod nacl;
+// §16
+pub mod sta;
+
+/// Converts SBI EID from str.
+const fn eid_from_str(name: &str) -> i32 {
+    match *name.as_bytes() {
+        [a] => i32::from_be_bytes([0, 0, 0, a]),
+        [a, b] => i32::from_be_bytes([0, 0, a, b]),
+        [a, b, c] => i32::from_be_bytes([0, a, b, c]),
+        [a, b, c, d] => i32::from_be_bytes([a, b, c, d]),
+        _ => unreachable!(),
+    }
+}
+
+/// Checks during compilation, and provides an item list for developers.
+#[cfg(test)]
+mod tests {
+    use static_assertions::{
+        assert_eq_align, assert_eq_size, assert_fields, assert_impl_all, const_assert_eq,
+    };
+    // §3
+    #[test]
+    fn test_binary() {
+        use crate::binary::*;
+        assert_eq_align!(SbiRet, usize);
+        assert_eq_size!(SbiRet, [usize; 2]);
+        assert_fields!(SbiRet: error);
+        assert_fields!(SbiRet: value);
+        assert_impl_all!(SbiRet: Copy, Clone, PartialEq, Eq, core::fmt::Debug);
+
+        const_assert_eq!(0, RET_SUCCESS as isize);
+        const_assert_eq!(-1, RET_ERR_FAILED as isize);
+        const_assert_eq!(-2, RET_ERR_NOT_SUPPORTED as isize);
+        const_assert_eq!(-3, RET_ERR_INVALID_PARAM as isize);
+        const_assert_eq!(-4, RET_ERR_DENIED as isize);
+        const_assert_eq!(-5, RET_ERR_INVALID_ADDRESS as isize);
+        const_assert_eq!(-6, RET_ERR_ALREADY_AVAILABLE as isize);
+        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);
+    }
+    // §4
+    #[test]
+    fn test_base() {
+        use crate::base::*;
+        const_assert_eq!(0x10, EID_BASE);
+        const_assert_eq!(0, GET_SBI_SPEC_VERSION);
+        const_assert_eq!(1, GET_SBI_IMPL_ID);
+        const_assert_eq!(2, GET_SBI_IMPL_VERSION);
+        const_assert_eq!(3, PROBE_EXTENSION);
+        const_assert_eq!(4, GET_MVENDORID);
+        const_assert_eq!(5, GET_MARCHID);
+        const_assert_eq!(6, GET_MIMPID);
+        const_assert_eq!(0, impl_id::BBL);
+        const_assert_eq!(1, impl_id::OPEN_SBI);
+        const_assert_eq!(2, impl_id::XVISOR);
+        const_assert_eq!(3, impl_id::KVM);
+        const_assert_eq!(4, impl_id::RUST_SBI);
+        const_assert_eq!(5, impl_id::DIOSIX);
+        const_assert_eq!(6, impl_id::COFFER);
+    }
+    // §5
+    #[cfg(feature = "legacy")]
+    #[test]
+    fn test_legacy() {
+        use crate::legacy::*;
+        const_assert_eq!(0, LEGACY_SET_TIMER);
+        const_assert_eq!(1, LEGACY_CONSOLE_PUTCHAR);
+        const_assert_eq!(2, LEGACY_CONSOLE_GETCHAR);
+        const_assert_eq!(3, LEGACY_CLEAR_IPI);
+        const_assert_eq!(4, LEGACY_SEND_IPI);
+        const_assert_eq!(5, LEGACY_REMOTE_FENCE_I);
+        const_assert_eq!(6, LEGACY_REMOTE_SFENCE_VMA);
+        const_assert_eq!(7, LEGACY_REMOTE_SFENCE_VMA_ASID);
+        const_assert_eq!(8, LEGACY_SHUTDOWN);
+    }
+    // §6
+    #[test]
+    fn test_time() {
+        use crate::time::*;
+        const_assert_eq!(0x54494D45, EID_TIME);
+        const_assert_eq!(0, SET_TIMER);
+    }
+    // §7
+    #[test]
+    fn test_spi() {
+        use crate::spi::*;
+        const_assert_eq!(0x735049, EID_SPI);
+        const_assert_eq!(0, SEND_IPI);
+    }
+    // §8
+    #[test]
+    fn test_rfnc() {
+        use crate::rfnc::*;
+        const_assert_eq!(0x52464E43, EID_RFNC);
+        const_assert_eq!(0, REMOTE_FENCE_I);
+        const_assert_eq!(1, REMOTE_SFENCE_VMA);
+        const_assert_eq!(2, REMOTE_SFENCE_VMA_ASID);
+        const_assert_eq!(3, REMOTE_HFENCE_GVMA_VMID);
+        const_assert_eq!(4, REMOTE_HFENCE_GVMA);
+        const_assert_eq!(5, REMOTE_HFENCE_VVMA_ASID);
+        const_assert_eq!(6, REMOTE_HFENCE_VVMA);
+    }
+    // §9
+    #[test]
+    fn test_hsm() {
+        use crate::hsm::*;
+        const_assert_eq!(0x48534D, EID_HSM);
+        const_assert_eq!(0, hart_state::STARTED);
+        const_assert_eq!(1, hart_state::STOPPED);
+        const_assert_eq!(2, hart_state::START_PENDING);
+        const_assert_eq!(3, hart_state::STOP_PENDING);
+        const_assert_eq!(4, hart_state::SUSPENDED);
+        const_assert_eq!(5, hart_state::SUSPEND_PENDING);
+        const_assert_eq!(6, hart_state::RESUME_PENDING);
+        const_assert_eq!(0x0000_0000, suspend_type::RETENTIVE);
+        const_assert_eq!(0x8000_0000, suspend_type::NON_RETENTIVE);
+        const_assert_eq!(0, HART_START);
+        const_assert_eq!(1, HART_STOP);
+        const_assert_eq!(2, HART_GET_STATUS);
+        const_assert_eq!(3, HART_SUSPEND);
+    }
+    // §10
+    #[test]
+    fn test_srst() {
+        use crate::srst::*;
+        const_assert_eq!(0x53525354, EID_SRST);
+        const_assert_eq!(0, RESET_TYPE_SHUTDOWN);
+        const_assert_eq!(1, RESET_TYPE_COLD_REBOOT);
+        const_assert_eq!(2, RESET_TYPE_WARM_REBOOT);
+        const_assert_eq!(0, RESET_REASON_NO_REASON);
+        const_assert_eq!(1, RESET_REASON_SYSTEM_FAILURE);
+        const_assert_eq!(0, SYSTEM_RESET);
+    }
+    // §11
+    #[test]
+    fn test_pmu() {
+        use crate::pmu::*;
+        const_assert_eq!(0x504D55, EID_PMU);
+        const_assert_eq!(0, NUM_COUNTERS);
+        const_assert_eq!(1, COUNTER_GET_INFO);
+        const_assert_eq!(2, COUNTER_CONFIG_MATCHING);
+        const_assert_eq!(3, COUNTER_START);
+        const_assert_eq!(4, COUNTER_STOP);
+        const_assert_eq!(5, COUNTER_FW_READ);
+        const_assert_eq!(6, COUNTER_FW_READ_HI);
+        const_assert_eq!(7, SNAPSHOT_SET_SHMEM);
+
+        const_assert_eq!(0, event_type::HARDWARE_GENERAL);
+        const_assert_eq!(1, event_type::HARDWARE_CACHE);
+        const_assert_eq!(2, event_type::HARDWARE_RAW);
+        const_assert_eq!(15, event_type::FIRMWARE);
+
+        const_assert_eq!(0, hardware_event::NO_EVENT);
+        const_assert_eq!(1, hardware_event::CPU_CYCLES);
+        const_assert_eq!(2, hardware_event::INSTRUCTIONS);
+        const_assert_eq!(3, hardware_event::CACHE_REFERENCES);
+        const_assert_eq!(4, hardware_event::CACHE_MISSES);
+        const_assert_eq!(5, hardware_event::BRANCH_INSTRUCTIONS);
+        const_assert_eq!(6, hardware_event::BRANCH_MISSES);
+        const_assert_eq!(7, hardware_event::BUS_CYCLES);
+        const_assert_eq!(8, hardware_event::STALLED_CYCLES_FRONTEND);
+        const_assert_eq!(9, hardware_event::STALLED_CYCLES_BACKEND);
+        const_assert_eq!(10, hardware_event::REF_CPU_CYCLES);
+
+        const_assert_eq!(0, cache_event::L1D);
+        const_assert_eq!(1, cache_event::L1I);
+        const_assert_eq!(2, cache_event::LL);
+        const_assert_eq!(3, cache_event::DTLB);
+        const_assert_eq!(4, cache_event::ITLB);
+        const_assert_eq!(5, cache_event::BPU);
+        const_assert_eq!(6, cache_event::NODE);
+
+        const_assert_eq!(0, cache_operation::READ);
+        const_assert_eq!(1, cache_operation::WRITE);
+        const_assert_eq!(2, cache_operation::PREFETCH);
+
+        const_assert_eq!(0, cache_result::ACCESS);
+        const_assert_eq!(1, cache_result::MISS);
+
+        const_assert_eq!(0, firmware_event::MISALIGNED_LOAD);
+        const_assert_eq!(1, firmware_event::MISALIGNED_STORE);
+        const_assert_eq!(2, firmware_event::ACCESS_LOAD);
+        const_assert_eq!(3, firmware_event::ACCESS_STORE);
+        const_assert_eq!(4, firmware_event::ILLEGAL_INSN);
+        const_assert_eq!(5, firmware_event::SET_TIMER);
+        const_assert_eq!(6, firmware_event::IPI_SENT);
+        const_assert_eq!(7, firmware_event::IPI_RECEIVED);
+        const_assert_eq!(8, firmware_event::FENCE_I_SENT);
+        const_assert_eq!(9, firmware_event::FENCE_I_RECEIVED);
+        const_assert_eq!(10, firmware_event::SFENCE_VMA_SENT);
+        const_assert_eq!(11, firmware_event::SFENCE_VMA_RECEIVED);
+        const_assert_eq!(12, firmware_event::SFENCE_VMA_ASID_SENT);
+        const_assert_eq!(13, firmware_event::SFENCE_VMA_ASID_RECEIVED);
+        const_assert_eq!(14, firmware_event::HFENCE_GVMA_SENT);
+        const_assert_eq!(15, firmware_event::HFENCE_GVMA_RECEIVED);
+        const_assert_eq!(16, firmware_event::HFENCE_GVMA_VMID_SENT);
+        const_assert_eq!(17, firmware_event::HFENCE_GVMA_VMID_RECEIVED);
+        const_assert_eq!(18, firmware_event::HFENCE_VVMA_SENT);
+        const_assert_eq!(19, firmware_event::HFENCE_VVMA_RECEIVED);
+        const_assert_eq!(20, firmware_event::HFENCE_VVMA_ASID_SENT);
+        const_assert_eq!(21, firmware_event::HFENCE_VVMA_ASID_RECEIVED);
+        const_assert_eq!(65535, firmware_event::PLATFORM);
+    }
+    // §12
+    #[test]
+    fn test_dbcn() {
+        use crate::dbcn::*;
+        const_assert_eq!(0x4442434E, EID_DBCN);
+        const_assert_eq!(0, CONSOLE_WRITE);
+        const_assert_eq!(1, CONSOLE_READ);
+        const_assert_eq!(2, CONSOLE_WRITE_BYTE);
+    }
+    // §13
+    #[test]
+    fn test_susp() {
+        use crate::susp::*;
+        const_assert_eq!(0x53555350, EID_SUSP);
+        const_assert_eq!(0, SUSPEND);
+    }
+    // §14
+    #[test]
+    fn test_cppc() {
+        use crate::cppc::*;
+        const_assert_eq!(0x43505043, EID_CPPC);
+        const_assert_eq!(0, PROBE);
+        const_assert_eq!(1, READ);
+        const_assert_eq!(2, READ_HI);
+        const_assert_eq!(3, WRITE);
+    }
+    // §15
+    #[test]
+    fn test_nacl() {
+        use crate::nacl::*;
+        const_assert_eq!(0x4E41434C, EID_NACL);
+        const_assert_eq!(0, PROBE_FEATURE);
+        const_assert_eq!(1, SET_SHMEM);
+        const_assert_eq!(2, SYNC_CSR);
+        const_assert_eq!(3, SYNC_HFENCE);
+        const_assert_eq!(4, SYNC_SRET);
+
+        const_assert_eq!(0, feature_id::SYNC_CSR);
+        const_assert_eq!(1, feature_id::SYNC_HFENCE);
+        const_assert_eq!(2, feature_id::SYNC_SRET);
+        const_assert_eq!(3, feature_id::AUTOSWAP_CSR);
+
+        const_assert_eq!(8192, shmem_size::RV32);
+        const_assert_eq!(12288, shmem_size::RV64);
+        const_assert_eq!(20480, shmem_size::RV128);
+        match () {
+            #[cfg(target_pointer_width = "32")]
+            () => {
+                const_assert_eq!(shmem_size::NATIVE, shmem_size::RV32);
+            }
+            #[cfg(target_pointer_width = "64")]
+            () => {
+                const_assert_eq!(shmem_size::NATIVE, shmem_size::RV64);
+            }
+            #[cfg(target_pointer_width = "128")]
+            () => {
+                const_assert_eq!(shmem_size::NATIVE, shmem_size::RV128);
+            }
+        }
+    }
+    // §16
+    #[test]
+    fn test_sta() {
+        use crate::sta::*;
+        const_assert_eq!(0x535441, EID_STA);
+        const_assert_eq!(0, SET_SHMEM);
+    }
+}

+ 75 - 0
sbi-spec/src/nacl.rs

@@ -0,0 +1,75 @@
+//! Chapter 15. Nested Acceleration Extension (EID #0x4E41434C "NACL").
+
+/// Extension ID for Nested Acceleration Extension.
+pub const EID_NACL: usize = crate::eid_from_str("NACL") as _;
+
+pub use fid::*;
+
+/// Declared in § 15.15.
+mod fid {
+    /// Function ID to probe a nested acceleration feature.
+    ///
+    /// Declared in §15.5.
+    pub const PROBE_FEATURE: usize = 0;
+    /// Function ID to set and enable the shared memory for nested acceleration on the calling hart.
+    ///
+    /// Declared in §15.6.
+    pub const SET_SHMEM: usize = 1;
+    /// Function ID to synchronize CSRs in the nested acceleration shared memory.
+    ///
+    /// Declared in §15.7.
+    pub const SYNC_CSR: usize = 2;
+    /// Function ID to synchronize HFENCEs in the nested acceleration shared memory.
+    ///
+    /// Declared in §15.8.
+    pub const SYNC_HFENCE: usize = 3;
+    /// Function ID to synchronize CSRs and HFENCEs in the nested acceleration shared memory and emulate the SRET instruction.
+    ///
+    /// Declared in §15.9.
+    pub const SYNC_SRET: usize = 4;
+}
+
+/// Nested Acceleration Feature ID.
+///
+/// Declared in §15.
+pub mod feature_id {
+    /// Feature ID for the synchronize CSR feature.
+    ///
+    /// Declared in §15.1.
+    pub const SYNC_CSR: usize = 0;
+    /// Feature ID for the synchronize HFENCE feature.
+    ///
+    /// Declared in §15.2.
+    pub const SYNC_HFENCE: usize = 1;
+    /// Feature ID for the synchronize SRET feature.
+    ///
+    /// Declared in §15.3.
+    pub const SYNC_SRET: usize = 2;
+    /// Feature ID for the autoswap CSR feature.
+    ///
+    /// Declared in §15.4.
+    pub const AUTOSWAP_CSR: usize = 3;
+}
+
+/// Size of shared memory set by supervisor software for current hart.
+///
+/// NACL shared memory includes scratch space and CSR space. Due to the difference
+/// of CSR width, this size varies between different `XLEN` values. `NATIVE`
+/// constant here only matches the integer width for the target this crate is compiled.
+/// If you are writing an SEE with different `XLEN` from host platform, you should
+/// choose other correct constant value from `RV32`, `RV64` or `RV128` in module `shmem_size`
+/// instead.
+pub mod shmem_size {
+    use core::mem::size_of;
+    /// Size of NACL shared memory on platforms with `XLEN` of same width as the current platform.
+    pub const NATIVE: usize = 4096 + 1024 * size_of::<usize>();
+
+    /// Size of NACL shared memory on RV32 platforms.
+    pub const RV32: usize = 4096 + 1024 * size_of::<u32>();
+
+    /// Size of NACL shared memory on RV64 platforms.
+    pub const RV64: usize = 4096 + 1024 * size_of::<u64>();
+
+    /// Size of NACL shared memory on RV128 platforms.
+    pub const RV128: usize = 4096 + 1024 * size_of::<u128>();
+}

+ 187 - 0
sbi-spec/src/pmu.rs

@@ -0,0 +1,187 @@
+//! Chapter 11. Performance Monitoring Unit Extension (EID #0x504D55 "PMU").
+
+/// Extension ID for Performance Monitoring Unit extension.
+pub const EID_PMU: usize = crate::eid_from_str("PMU") as _;
+pub use fid::*;
+
+/// Declared in §11.11.
+mod fid {
+    /// Function ID to get the number of counters, both hardware and firmware.
+    ///
+    /// Declared in §11.5.
+    pub const NUM_COUNTERS: usize = 0;
+    /// Function ID to get details about the specified counter.
+    ///
+    /// Declared in §11.6.
+    pub const COUNTER_GET_INFO: usize = 1;
+    /// Function ID to find and configure a counter from a set of counters.
+    ///
+    /// Declared in §11.7.
+    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.8.
+    pub const COUNTER_START: usize = 3;
+    /// Function ID to stop or disable a set of counters on the calling hart.
+    ///
+    /// Declared in §11.9.
+    pub const COUNTER_STOP: usize = 4;
+    /// Function ID to provide the current value of a firmware counter.
+    ///
+    /// Declared in §11.10.
+    pub const COUNTER_FW_READ: usize = 5;
+    /// Function ID to provide the upper 32 bits of the current firmware counter value.
+    ///
+    /// Declared in §11.11.
+    pub const COUNTER_FW_READ_HI: usize = 6;
+    /// Function ID to set and enable the PMU snapshot shared memory.
+    ///
+    /// Declared in §11.12.
+    pub const SNAPSHOT_SET_SHMEM: usize = 7;
+}
+
+/// PMU Event Types.
+///
+/// Declared in §11.
+pub mod event_type {
+    /// Type for all hardware general events.
+    ///
+    /// Declared in §11.1.
+    pub const HARDWARE_GENERAL: usize = 0;
+    /// Type for all hardware cache events.
+    ///
+    /// Declared in §11.2.
+    pub const HARDWARE_CACHE: usize = 1;
+    /// Type for all hardware raw events.
+    ///
+    /// Declared in §11.3.
+    pub const HARDWARE_RAW: usize = 2;
+    /// Type for for all firmware events.
+    ///
+    /// Declared in §11.4.
+    pub const FIRMWARE: usize = 15;
+}
+
+/// Hardware General Event Codes.
+///
+/// Declared in §11.1.
+pub mod hardware_event {
+    /// Unused event because event_idx cannot be zero
+    pub const NO_EVENT: usize = 0;
+    /// Event for each CPU cycle
+    pub const CPU_CYCLES: usize = 1;
+    /// Event for each completed instruction
+    pub const INSTRUCTIONS: usize = 2;
+    /// Event for cache hit
+    pub const CACHE_REFERENCES: usize = 3;
+    /// Event for cache miss
+    pub const CACHE_MISSES: usize = 4;
+    /// Event for a branch instruction
+    pub const BRANCH_INSTRUCTIONS: usize = 5;
+    /// Event for a branch misprediction
+    pub const BRANCH_MISSES: usize = 6;
+    /// Event for each BUS cycle
+    pub const BUS_CYCLES: usize = 7;
+    /// Event for a stalled cycle in microarchitecture frontend
+    pub const STALLED_CYCLES_FRONTEND: usize = 8;
+    /// Event for a stalled cycle in microarchitecture backend
+    pub const STALLED_CYCLES_BACKEND: usize = 9;
+    /// Event for each reference CPU cycle
+    pub const REF_CPU_CYCLES: usize = 10;
+}
+
+/// Hardware Cache Event ID.
+///
+/// Declared in §11.2.
+pub mod cache_event {
+    /// Level 1 data cache event.
+    pub const L1D: usize = 0;
+    /// Level 1 instruction cache event.
+    pub const L1I: usize = 1;
+    /// Last level cache event.
+    pub const LL: usize = 2;
+    /// Data TLB event.
+    pub const DTLB: usize = 3;
+    /// Instruction TLB event.
+    pub const ITLB: usize = 4;
+    /// Branch predictor unit event.
+    pub const BPU: usize = 5;
+    /// NUMA node cache event.
+    pub const NODE: usize = 6;
+}
+
+/// Hardware Cache Operation ID.
+///
+/// Declared in §11.2.
+pub mod cache_operation {
+    /// Read cache line.
+    pub const READ: usize = 0;
+    /// Write cache line.
+    pub const WRITE: usize = 1;
+    /// Prefetch cache line.
+    pub const PREFETCH: usize = 2;
+}
+
+/// Hardware Cache Operation Result ID.
+///
+/// Declared in §11.2.
+pub mod cache_result {
+    /// Cache access.
+    pub const ACCESS: usize = 0;
+    /// Cache miss.
+    pub const MISS: usize = 1;
+}
+
+/// Firmware Event Codes.
+///
+/// Declared in §11.4.
+pub mod firmware_event {
+    /// Misaligned load trap event.
+    pub const MISALIGNED_LOAD: usize = 0;
+    /// Misaligned store trap event.
+    pub const MISALIGNED_STORE: usize = 1;
+    /// Load access trap event.
+    pub const ACCESS_LOAD: usize = 2;
+    /// Store access trap event.
+    pub const ACCESS_STORE: usize = 3;
+    /// Illegal instruction trap event.
+    pub const ILLEGAL_INSN: usize = 4;
+    /// Set timer event.
+    pub const SET_TIMER: usize = 5;
+    /// Sent IPI to other HART event.
+    pub const IPI_SENT: usize = 6;
+    /// Received IPI from other HART event.
+    pub const IPI_RECEIVED: usize = 7;
+    /// Sent FENCE.I request to other HART event.
+    pub const FENCE_I_SENT: usize = 8;
+    /// Received FENCE.I request from other HART event.
+    pub const FENCE_I_RECEIVED: usize = 9;
+    /// Sent SFENCE.VMA request to other HART event.
+    pub const SFENCE_VMA_SENT: usize = 10;
+    /// Received SFENCE.VMA request from other HART event.
+    pub const SFENCE_VMA_RECEIVED: usize = 11;
+    /// Sent SFENCE.VMA with ASID request to other HART event.
+    pub const SFENCE_VMA_ASID_SENT: usize = 12;
+    /// Received SFENCE.VMA with ASID request from other HART event.
+    pub const SFENCE_VMA_ASID_RECEIVED: usize = 13;
+    /// Sent HFENCE.GVMA request to other HART event.
+    pub const HFENCE_GVMA_SENT: usize = 14;
+    /// Received HFENCE.GVMA request from other HART event.
+    pub const HFENCE_GVMA_RECEIVED: usize = 15;
+    /// Sent HFENCE.GVMA with VMID request to other HART event.
+    pub const HFENCE_GVMA_VMID_SENT: usize = 16;
+    /// Received HFENCE.GVMA with VMID request from other HART event.
+    pub const HFENCE_GVMA_VMID_RECEIVED: usize = 17;
+    /// Sent HFENCE.VVMA request to other HART event.
+    pub const HFENCE_VVMA_SENT: usize = 18;
+    /// Received HFENCE.VVMA request from other HART event.
+    pub const HFENCE_VVMA_RECEIVED: usize = 19;
+    /// Sent HFENCE.VVMA with ASID request to other HART event.
+    pub const HFENCE_VVMA_ASID_SENT: usize = 20;
+    /// Received HFENCE.VVMA with ASID request from other HART event.
+    pub const HFENCE_VVMA_ASID_RECEIVED: usize = 21;
+    /// RISC-V platform specific firmware events.
+    ///
+    /// The `event_data` configuration (or parameter) contains the event encoding.
+    pub const PLATFORM: usize = 65535;
+}

+ 37 - 0
sbi-spec/src/rfnc.rs

@@ -0,0 +1,37 @@
+//! Chapter 8. RFENCE Extension (EID #0x52464E43 "RFNC").
+
+/// Extension ID for Remote Fence extension.
+pub const EID_RFNC: usize = crate::eid_from_str("RFNC") as _;
+pub use fid::*;
+
+/// Declared in §8.8.
+mod fid {
+    /// Function ID to `FENCE.I` instruction on remote harts.
+    ///
+    /// Declared in §8.1.
+    pub const REMOTE_FENCE_I: usize = 0;
+    /// Function ID to `SFENCE.VMA` for all address spaces on remote harts.
+    ///
+    /// Declared in §8.2.
+    pub const REMOTE_SFENCE_VMA: usize = 1;
+    /// Function ID to address space based `SFENCE.VMA` on remote harts.
+    ///
+    /// Declared in §8.3.
+    pub const REMOTE_SFENCE_VMA_ASID: usize = 2;
+    /// Function ID to virtual machine id based `HFENCE.GVMA` on remote harts.
+    ///
+    /// Declared in §8.4.
+    pub const REMOTE_HFENCE_GVMA_VMID: usize = 3;
+    /// Function ID to `HFENCE.GVMA` for all virtual machines on remote harts.
+    ///
+    /// Declared in §8.5.
+    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.
+    pub const REMOTE_HFENCE_VVMA_ASID: usize = 5;
+    /// Function ID to `HFENCE.VVMA` for all address spaces in current virtual machine on remote harts.
+    ///
+    /// Declared in §8.7.
+    pub const REMOTE_HFENCE_VVMA: usize = 6;
+}

+ 13 - 0
sbi-spec/src/spi.rs

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

+ 25 - 0
sbi-spec/src/srst.rs

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

+ 13 - 0
sbi-spec/src/sta.rs

@@ -0,0 +1,13 @@
+//! Chapter 16. Steal-time Accounting Extension (EID #0x535441 "STA").
+
+/// Extension ID for Steal-time Accounting Extension.
+pub const EID_STA: usize = crate::eid_from_str("STA") as _;
+pub use fid::*;
+
+/// Declared in §16.2.
+mod fid {
+    /// Function ID to set the shared memory physical base address for steal-time accounting of the calling virtual hart and enable the SBI implementation’s steal-time information reporting.
+    ///
+    /// Declared in §16.1.
+    pub const SET_SHMEM: usize = 0;
+}

+ 13 - 0
sbi-spec/src/susp.rs

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

+ 13 - 0
sbi-spec/src/time.rs

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