Ver Fonte

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

Thanks to sbi-rt developers: @luojia65, @YdrMaster, @jakezhu9, @hwk2077,
@OccupyMars2025, @lowa9.

Signed-off-by: DongQing <[email protected]>
DongQing há 1 ano atrás
pai
commit
330e995922

+ 1 - 0
Cargo.toml

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

+ 2 - 0
sbi-rt/.cargo/config.toml

@@ -0,0 +1,2 @@
+[build]
+target = "riscv64imac-unknown-none-elf"

+ 7 - 0
sbi-rt/.gitignore

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

+ 52 - 0
sbi-rt/CHANGELOG.md

@@ -0,0 +1,52 @@
+# 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.0.0/), and this project adheres
+to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [Unreleased]
+
+## [0.0.3]
+
+This version adds support to RISC-V SBI Specification version 2.0-rc7.
+
+### Added
+
+- Debug Console extension support
+- `pmu_counter_fw_read_hi` in PMU extension
+- Support for SBI CPPC extension
+- Support for NACL and STA extensions
+
+### Modified
+
+- SPI and RFNC extensions now use `HartMask` parameter
+
+### Fixed
+
+- Minor document fixes on `ConfigFlags` and `set_timer`
+- Document fixes on HSM extension from SBI 2.0-rc1
+
+## [0.0.2] - 2022-10-10
+
+In this version we changed API style to trait based type parameters, which would make it easier to
+check parameter types at runtime to reduce errors. If user choose to use `integer-impls` feature,
+it would fall back to older style functions using integer types.
+
+### Added
+
+- Trait based type parameter for all extensions
+- Feature `integer-impls` to allow fast prototyping with sbi-rt crate
+- Feature `legacy` to gate SBI legacy extension
+- Documents on various functions
+
+### Modified
+
+- Update `sbi-spec` to version 0.0.4, re-export `Version` structure
+- Function `probe_extension` now returns an `ExtensionInfo` value
+- Function `pmu_num_counters` returns a `usize` value
+
+[Unreleased]: https://github.com/rustsbi/sbi-rt/compare/v0.0.3...HEAD
+[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

+ 35 - 0
sbi-rt/Cargo.toml

@@ -0,0 +1,35 @@
+[package]
+name = "sbi-rt"
+version = "0.0.3-rc.5"
+edition = "2021"
+description = "Runtime library for supervisors to call RISC-V Supervisor Binary Interface (RISC-V SBI)"
+categories = ["os", "embedded", "hardware-support", "no-std"]
+keywords = ["riscv", "sbi", "rustsbi"]
+authors = [
+    "YdrMaster <[email protected]>",
+    "Luo Jia <[email protected]>",
+]
+repository = "https://github.com/rustsbi/sbi-rt"
+documentation = "https://docs.rs/sbi-rt"
+license = "MulanPSL-2.0 OR MIT"
+readme = "README.md"
+
+[package.metadata.docs.rs]
+default-target = "riscv64imac-unknown-none-elf"
+targets = [
+    "riscv32imac-unknown-none-elf", "riscv64imac-unknown-none-elf",
+]
+
+[dependencies]
+sbi-spec = "0.0.7-alpha.3"
+
+[features]
+default = []
+# Implement sbi-rt traits for integer types
+# By using this feature, parameter types of sbi-rt functions fall back to integers,
+# static type checks are disabled so this library won't detect parameters in incorrect orders.
+# Although some people may find it useful in prototyping sbi-rt implementations,
+# users of this crate are strongly encouraged not to enable this feature in production.
+integer-impls = []
+# Support legacy extension; this feature is not included by default.
+legacy = ["sbi-spec/legacy"]

+ 9 - 0
sbi-rt/LICENSE-MIT

@@ -0,0 +1,9 @@
+The MIT License (MIT)
+
+Copyright © 2022 YdrMaster
+
+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-rt/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

+ 26 - 0
sbi-rt/README.md

@@ -0,0 +1,26 @@
+# sbi-rt
+
+[![crates.io](https://img.shields.io/crates/v/sbi-rt.svg)](https://crates.io/crates/sbi-rt)
+[![Documentation](https://docs.rs/sbi-rt/badge.svg)](https://docs.rs/sbi-rt)
+![License](https://img.shields.io/crates/l/sbi-rt.svg)
+
+- [An English README](README_EN.md)
+
+为特权软件提供 RISC-V 特权二进制接口(Supervisor Binary Interface)的运行时库。
+
+2.0-rc7 标准各章节的实现情况:
+
+- [x] §3
+- [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

+ 21 - 0
sbi-rt/README_EN.md

@@ -0,0 +1,21 @@
+# sbi-rt
+
+- [中文自述文件](README.md)
+
+Runtime library for supervisors to call RISC-V Supervisor Binary Interface (RISC-V SBI).
+
+Charaters implementation in 2.0-rc7 specification:
+
+- [x] §3
+- [x] §4
+- [x] §5
+- [x] §6
+- [x] §7
+- [x] §8
+- [x] §9
+- [x] §10
+- [x] §11
+- [x] §13
+- [x] §14
+- [x] §15
+- [x] §16

+ 179 - 0
sbi-rt/src/base.rs

@@ -0,0 +1,179 @@
+//! Chapter 4. Base Extension (EID #0x10)
+
+use crate::binary::{sbi_call_0, sbi_call_1};
+
+pub use sbi_spec::base::Version;
+use sbi_spec::base::{
+    EID_BASE, GET_MARCHID, GET_MIMPID, GET_MVENDORID, GET_SBI_IMPL_ID, GET_SBI_IMPL_VERSION,
+    GET_SBI_SPEC_VERSION, PROBE_EXTENSION,
+};
+
+/// Return the current SBI specification version.
+///
+/// The minor number of the SBI specification is encoded in the low 24 bits,
+/// with the major number encoded in the next 7 bits. Bit 31 must be 0 and
+/// is reserved for future expansion.
+///
+/// This function is defined in RISC-V SBI Specification chapter 4.1.
+/// According to introduction of chapter 4, all base extension functions
+/// must success and return no error code.
+#[inline]
+pub fn get_spec_version() -> Version {
+    Version::from_raw(sbi_call_0(EID_BASE, GET_SBI_SPEC_VERSION).value)
+}
+
+/// Return the current SBI implementation ID.
+///
+/// Implementation ID is different for every SBI implementation.
+/// It is intended that this implementation ID allows software to probe
+/// for SBI implementation quirks.
+///
+/// This function is defined in RISC-V SBI Specification chapter 4.2.
+/// According to introduction of chapter 4, all base extension functions
+/// must success and return no error code.
+#[inline]
+pub fn get_sbi_impl_id() -> usize {
+    sbi_call_0(EID_BASE, GET_SBI_IMPL_ID).value
+}
+
+/// Return the current SBI implementation version.
+///
+/// The encoding of this version number is specific to the SBI implementation.
+///
+/// This function is defined in RISC-V SBI Specification chapter 4.3.
+/// According to introduction of chapter 4, all base extension functions
+/// must success and return no error code.
+#[inline]
+pub fn get_sbi_impl_version() -> usize {
+    sbi_call_0(EID_BASE, GET_SBI_IMPL_VERSION).value
+}
+
+/// Probe information about one SBI extension from current environment.
+///
+/// Returns 0 if given SBI `extension_id` is not available, or typically
+/// 1 if it's available. Implementation would define further non-zero
+/// return values for information about this extension if it is available.
+///
+/// This function is defined in RISC-V SBI Specification chapter 4.4.
+/// According to introduction of chapter 4, all base extension functions
+/// must success and return no error code.
+#[inline]
+pub fn probe_extension<E>(extension: E) -> ExtensionInfo
+where
+    E: Extension,
+{
+    let ans = sbi_call_1(EID_BASE, PROBE_EXTENSION, extension.extension_id());
+    ExtensionInfo { raw: ans.value }
+}
+
+/// Return value of `mvendorid` register in current environment.
+///
+/// This function returns a value that is legal for the `mvendorid` register,
+/// and 0 is always a legal value for this register.
+///
+/// This function is defined in RISC-V SBI Specification chapter 4.5.
+/// According to introduction of chapter 4, all base extension functions
+/// must success and return no error code.
+#[inline]
+pub fn get_mvendorid() -> usize {
+    sbi_call_0(EID_BASE, GET_MVENDORID).value
+}
+
+/// Return value of `marchid` register in current environment.
+///
+/// This function returns a value that is legal for the `marchid` register,
+/// and 0 is always a legal value for this register.
+///
+/// This function is defined in RISC-V SBI Specification chapter 4.6.
+/// According to introduction of chapter 4, all base extension functions
+/// must success and return no error code.
+#[inline]
+pub fn get_marchid() -> usize {
+    sbi_call_0(EID_BASE, GET_MARCHID).value
+}
+
+/// Return value of `mimpid` register in current environment.
+///
+/// This function returns a value that is legal for the `mimpid` register,
+/// and 0 is always a legal value for this register.
+///
+/// This function is defined in RISC-V SBI Specification chapter 4.7.
+/// According to introduction of chapter 4, all base extension functions
+/// must success and return no error code.
+#[inline]
+pub fn get_mimpid() -> usize {
+    sbi_call_0(EID_BASE, GET_MIMPID).value
+}
+
+/// An SBI extension.
+pub trait Extension {
+    /// Get a raw `extension_id` value to pass to SBI environment.
+    fn extension_id(&self) -> usize;
+}
+
+macro_rules! define_extension {
+    ($($struct:ident($value:expr) #[$doc:meta])*) => {
+        $(
+            #[derive(Clone, Copy, Debug)]
+            #[$doc]
+            pub struct $struct;
+            impl Extension for $struct {
+                #[inline]
+                fn extension_id(&self) -> usize {
+                    $value
+                }
+            }
+        )*
+    };
+}
+
+define_extension! {
+    Base(sbi_spec::base::EID_BASE) /// RISC-V SBI Base extension.
+    Timer(sbi_spec::time::EID_TIME) /// Timer programmer extension.
+    Ipi(sbi_spec::spi::EID_SPI) /// Inter-processor Interrupt extension.
+    Fence(sbi_spec::rfnc::EID_RFNC) /// Remote Fence extension.
+    Hsm(sbi_spec::hsm::EID_HSM) /// Hart State Monitor extension.
+    Reset(sbi_spec::srst::EID_SRST) /// System Reset extension.
+    Pmu(sbi_spec::pmu::EID_PMU) /// Performance Monitoring Unit extension.
+    Console(sbi_spec::dbcn::EID_DBCN) /// Debug Console extension.
+    Suspend(sbi_spec::susp::SUSPEND) /// System Suspend extension.
+    Cppc(sbi_spec::cppc::EID_CPPC) /// SBI CPPC extension.
+    Nacl(sbi_spec::cppc::EID_CPPC) /// Nested Acceleration extension.
+    Sta(sbi_spec::cppc::EID_CPPC) /// Steal-time Accounting extension.
+}
+
+#[cfg(feature = "integer-impls")]
+impl Extension for usize {
+    #[inline]
+    fn extension_id(&self) -> usize {
+        *self
+    }
+}
+
+#[cfg(feature = "integer-impls")]
+impl Extension for isize {
+    #[inline]
+    fn extension_id(&self) -> usize {
+        usize::from_ne_bytes(isize::to_ne_bytes(*self))
+    }
+}
+
+/// Information about an SBI extension.
+#[derive(Clone, Copy, Debug)]
+pub struct ExtensionInfo {
+    pub raw: usize,
+}
+
+impl ExtensionInfo {
+    /// Is this extension available?
+    #[inline]
+    pub const fn is_available(&self) -> bool {
+        self.raw != 0
+    }
+
+    /// Is this extension not available?
+    #[inline]
+    pub const fn is_unavailable(&self) -> bool {
+        self.raw == 0
+    }
+}

+ 143 - 0
sbi-rt/src/binary.rs

@@ -0,0 +1,143 @@
+//! Capture 3. Binary Encoding
+
+pub use sbi_spec::binary::SbiRet;
+
+#[inline(always)]
+pub(crate) fn sbi_call_0(eid: usize, fid: usize) -> SbiRet {
+    let (error, value);
+    unsafe {
+        core::arch::asm!(
+            "ecall",
+            in("a7") eid,
+            in("a6") fid,
+            lateout("a0") error,
+            lateout("a1") value,
+        );
+    }
+    SbiRet { error, value }
+}
+
+#[inline(always)]
+pub(crate) fn sbi_call_1(eid: usize, fid: usize, arg0: usize) -> SbiRet {
+    let (error, value);
+    unsafe {
+        core::arch::asm!(
+            "ecall",
+            in("a7") eid,
+            in("a6") fid,
+            inlateout("a0") arg0 => error,
+            lateout("a1") value,
+        );
+    }
+    SbiRet { error, value }
+}
+
+#[inline(always)]
+pub(crate) fn sbi_call_2(eid: usize, fid: usize, arg0: usize, arg1: usize) -> SbiRet {
+    let (error, value);
+    unsafe {
+        core::arch::asm!(
+            "ecall",
+            in("a7") eid,
+            in("a6") fid,
+            inlateout("a0") arg0 => error,
+            inlateout("a1") arg1 => value,
+        );
+    }
+    SbiRet { error, value }
+}
+
+#[inline(always)]
+pub(crate) fn sbi_call_3(eid: usize, fid: usize, arg0: usize, arg1: usize, arg2: usize) -> SbiRet {
+    let (error, value);
+    unsafe {
+        core::arch::asm!(
+            "ecall",
+            in("a7") eid,
+            in("a6") fid,
+            inlateout("a0") arg0 => error,
+            inlateout("a1") arg1 => value,
+            in("a2") arg2,
+        );
+    }
+    SbiRet { error, value }
+}
+
+#[inline(always)]
+pub(crate) fn sbi_call_4(
+    eid: usize,
+    fid: usize,
+    arg0: usize,
+    arg1: usize,
+    arg2: usize,
+    arg3: usize,
+) -> SbiRet {
+    let (error, value);
+    unsafe {
+        core::arch::asm!(
+            "ecall",
+            in("a7") eid,
+            in("a6") fid,
+            inlateout("a0") arg0 => error,
+            inlateout("a1") arg1 => value,
+            in("a2") arg2,
+            in("a3") arg3,
+        );
+    }
+    SbiRet { error, value }
+}
+
+#[inline(always)]
+pub(crate) fn sbi_call_5(
+    eid: usize,
+    fid: usize,
+    arg0: usize,
+    arg1: usize,
+    arg2: usize,
+    arg3: usize,
+    arg4: usize,
+) -> SbiRet {
+    let (error, value);
+    unsafe {
+        core::arch::asm!(
+            "ecall",
+            in("a7") eid,
+            in("a6") fid,
+            inlateout("a0") arg0 => error,
+            inlateout("a1") arg1 => value,
+            in("a2") arg2,
+            in("a3") arg3,
+            in("a4") arg4,
+        );
+    }
+    SbiRet { error, value }
+}
+
+#[cfg(target_pointer_width = "32")]
+#[inline(always)]
+pub(crate) fn sbi_call_6(
+    eid: usize,
+    fid: usize,
+    arg0: usize,
+    arg1: usize,
+    arg2: usize,
+    arg3: usize,
+    arg4: usize,
+    arg5: usize,
+) -> SbiRet {
+    let (error, value);
+    unsafe {
+        core::arch::asm!(
+            "ecall",
+            in("a7") eid,
+            in("a6") fid,
+            inlateout("a0") arg0 => error,
+            inlateout("a1") arg1 => value,
+            in("a2") arg2,
+            in("a3") arg3,
+            in("a4") arg4,
+            in("a5") arg5,
+        );
+    }
+    SbiRet { error, value }
+}

+ 124 - 0
sbi-rt/src/cppc.rs

@@ -0,0 +1,124 @@
+//! Chapter 14. CPPC Extension (EID #0x43505043 "CPPC")
+
+#[cfg(target_pointer_width = "64")]
+use crate::binary::sbi_call_2;
+#[cfg(target_pointer_width = "32")]
+use crate::binary::sbi_call_3;
+use crate::binary::{sbi_call_1, SbiRet};
+use sbi_spec::cppc::{EID_CPPC, PROBE, READ, READ_HI, WRITE};
+
+/// Probe whether the CPPC register is implemented or not by the platform.
+///
+/// # Parameters
+///
+/// The `cppc_reg_id` parameter specifies the CPPC register ID.
+///
+/// # Return value
+///
+/// If the register is implemented, `SbiRet.value` will contain the register width.
+/// If the register is not implemented, `SbiRet.value` will be set to 0.
+///
+/// The possible error codes returned in `SbiRet.error` are shown in the table below:
+///
+/// | Return code               | Description
+/// |:--------------------------|:----------------------------------------------
+/// | `SbiRet::success()`       | Probe completed successfully.
+/// | `SbiRet::invalid_param()` | `cppc_reg_id` is reserved.
+/// | `SbiRet::failed()`        | The probe request failed for unspecified or unknown other reasons.
+///
+/// This function is defined in RISC-V SBI Specification chapter 14.1.
+#[inline]
+pub fn cppc_probe(cppc_reg_id: u32) -> SbiRet {
+    sbi_call_1(EID_CPPC, PROBE, cppc_reg_id as _)
+}
+
+/// Read the CPPC register identified by given `cppc_reg_id`.
+///
+/// # Parameters
+///
+/// The `cppc_reg_id` parameter specifies the CPPC register ID.
+///
+/// # Return value
+///
+/// `SbiRet.value` will contain the register value. When supervisor mode XLEN is 32, the `SbiRet.value`
+/// will only contain the lower 32 bits of the CPPC register value.
+///
+/// The possible error codes returned in `SbiRet.error` are shown in the table below:
+///
+/// | Return code               | Description
+/// |:--------------------------|:----------------------------------------------
+/// | `SbiRet::success()`       | Read completed successfully.
+/// | `SbiRet::invalid_param()` | `cppc_reg_id` is reserved.
+/// | `SbiRet::not_supported()` | `cppc_reg_id` is not implemented by the platform.
+/// | `SbiRet::denied()`        | `cppc_reg_id` is a write-only register.
+/// | `SbiRet::failed()`        | The read request failed for unspecified or unknown other reasons.
+///
+/// This function is defined in RISC-V SBI Specification chapter 14.2.
+#[inline]
+pub fn cppc_read(cppc_reg_id: u32) -> SbiRet {
+    sbi_call_1(EID_CPPC, READ, cppc_reg_id as _)
+}
+
+/// Read the upper 32-bit value of the CPPC register identified by `cppc_reg_id`.
+///
+/// # Parameters
+///
+/// The `cppc_reg_id` parameter specifies the CPPC register ID.
+///
+/// # Return value
+///
+/// `SbiRet.value` will contain the upper 32 bits of the register value. This function always
+/// returns zero in `SbiRet.value` when supervisor mode XLEN is 64 or higher.
+///
+/// The possible error codes returned in `SbiRet.error` are shown in the table below:
+///
+/// | Return code               | Description
+/// |:--------------------------|:----------------------------------------------
+/// | `SbiRet::success()`       | Read completed successfully.
+/// | `SbiRet::invalid_param()` | `cppc_reg_id` is reserved.
+/// | `SbiRet::not_supported()` | `cppc_reg_id` is not implemented by the platform.
+/// | `SbiRet::denied()`        | `cppc_reg_id` is a write-only register.
+/// | `SbiRet::failed()`        | The read request failed for unspecified or unknown other reasons.
+///
+/// This function is defined in RISC-V SBI Specification chapter 14.3.
+#[inline]
+pub fn cppc_read_hi(cppc_reg_id: u32) -> SbiRet {
+    sbi_call_1(EID_CPPC, READ_HI, cppc_reg_id as _)
+}
+
+/// Write 64-bit value to the CPPC register identified by given `cppc_reg_id`.
+///
+/// # Parameters
+///
+/// The `cppc_reg_id` parameter specifies the CPPC register ID.
+///
+/// The `value` parameter specifies the value to be written to the register.
+///
+/// # Return value
+///
+/// The possible error codes returned in `SbiRet.error` are shown in the table below:
+///
+/// | Return code               | Description
+/// |:--------------------------|:----------------------------------------------
+/// | `SbiRet::success()`       | Write completed successfully.
+/// | `SbiRet::invalid_param()` | `cppc_reg_id` is reserved.
+/// | `SbiRet::not_supported()` | `cppc_reg_id` is not implemented by the platform.
+/// | `SbiRet::denied()`        | `cppc_reg_id` is a read-only register.
+/// | `SbiRet::failed()`        | The write request failed for unspecified or unknown other reasons.
+///
+/// This function is defined in RISC-V SBI Specification chapter 14.4.
+#[inline]
+pub fn cppc_write(cppc_reg_id: u32, value: u64) -> SbiRet {
+    match () {
+        #[cfg(target_pointer_width = "32")]
+        () => sbi_call_3(
+            EID_CPPC,
+            WRITE,
+            cppc_reg_id as _,
+            value as _,
+            (value >> 32) as _,
+        ),
+        #[cfg(target_pointer_width = "64")]
+        () => sbi_call_2(EID_CPPC, WRITE, cppc_reg_id as _, value as _),
+    }
+}

+ 102 - 0
sbi-rt/src/dbcn.rs

@@ -0,0 +1,102 @@
+//! Chapter 12. Debug Console Extension (EID #0x4442434E "DBCN")
+use crate::binary::{sbi_call_1, sbi_call_3, SbiRet};
+use sbi_spec::{
+    binary::Physical,
+    dbcn::{CONSOLE_READ, CONSOLE_WRITE, CONSOLE_WRITE_BYTE, EID_DBCN},
+};
+
+/// Write bytes to the debug console from input memory.
+///
+/// # Parameters
+///
+/// The `bytes` parameter specifies the input memory, including its length
+/// and memory physical base address (both lower and upper bits).
+///
+/// # Non-blocking function
+///
+/// This is a non-blocking SBI call and it may do partial/no writes if
+/// the debug console is not able to accept more bytes.
+///
+/// # Return value
+///
+/// The number of bytes written is returned in `SbiRet.value` and the
+/// possible return error codes returned in `SbiRet.error` are shown in
+/// the table below:
+///
+/// | Return code               | Description
+/// |:--------------------------|:----------------------------------------------
+/// | `SbiRet::success()`       | Bytes written successfully.
+/// | `SbiRet::invalid_param()` | The memory pointed to by `bytes` does not satisfy the requirements described in shared memory physical address range.
+/// | `SbiRet::failed()`        | Failed to write due to I/O errors.
+///
+/// This function is defined in RISC-V SBI Specification chapter 12.1.
+#[inline]
+pub fn console_write(bytes: Physical<&[u8]>) -> SbiRet {
+    sbi_call_3(
+        EID_DBCN,
+        CONSOLE_WRITE,
+        bytes.num_bytes(),
+        bytes.phys_addr_lo(),
+        bytes.phys_addr_hi(),
+    )
+}
+
+/// Read bytes from the debug console into an output memory.
+///
+/// # Parameters
+///
+/// The `bytes` parameter specifies the output memory, including the maximum
+/// bytes which can be written, and its memory physical base address
+/// (both lower and upper bits).
+///
+/// # Non-blocking function
+///
+/// This is a non-blocking SBI call and it will not write anything
+/// into the output memory if there are no bytes to be read in the
+/// debug console.
+///
+/// # Return value
+///
+/// The number of bytes read is returned in `SbiRet.value` and the
+/// possible return error codes returned in `SbiRet.error` are shown in
+/// the table below:
+///
+/// | Return code               | Description
+/// |:--------------------------|:----------------------------------------------
+/// | `SbiRet::success()`       | Bytes read successfully.
+/// | `SbiRet::invalid_param()` | The memory pointed to by `bytes` does not satisfy the requirements described in shared memory physical address range.
+/// | `SbiRet::failed()`        | Failed to read due to I/O errors.
+///
+/// This function is defined in RISC-V SBI Specification chapter 12.2.
+pub fn console_read(bytes: Physical<&mut [u8]>) -> SbiRet {
+    sbi_call_3(
+        EID_DBCN,
+        CONSOLE_READ,
+        bytes.num_bytes(),
+        bytes.phys_addr_lo(),
+        bytes.phys_addr_hi(),
+    )
+}
+
+/// Write a single byte to the debug console.
+///
+/// # Blocking function
+///
+/// This is a blocking SBI call and it will only return after writing
+/// the specified byte to the debug console. It will also return, with
+/// `SbiRet::failed()`, if there are I/O errors.
+/// # Return value
+///
+/// The `SbiRet.value` is set to zero and the possible return error
+/// codes returned in `SbiRet.error` are shown in the table below:
+///
+/// | Return code               | Description
+/// |:--------------------------|:----------------------------------------------
+/// | `SbiRet::success()`       | Byte written successfully.
+/// | `SbiRet::failed()`        | Failed to write the byte due to I/O errors.
+///
+/// This function is defined in RISC-V SBI Specification chapter 12.3.
+#[inline]
+pub fn console_write_byte(byte: u8) -> SbiRet {
+    sbi_call_1(EID_DBCN, CONSOLE_WRITE_BYTE, byte as usize)
+}

+ 227 - 0
sbi-rt/src/hsm.rs

@@ -0,0 +1,227 @@
+//! Chapter 9. Hart State Management Extension (EID #0x48534D "HSM")
+
+use crate::binary::{sbi_call_0, sbi_call_1, sbi_call_3, SbiRet};
+
+use sbi_spec::hsm::{EID_HSM, HART_GET_STATUS, HART_START, HART_STOP, HART_SUSPEND};
+
+/// Start executing the given hart at specified address in supervisor-mode.
+///
+/// This call is asynchronous - more specifically, the `hart_start()` may return before target hart
+/// starts executing as long as the SBI implemenation is capable of ensuring the return code is accurate.
+///
+/// It is recommended that if the SBI implementation is a platform runtime firmware executing in machine-mode (M-mode)
+/// then it MUST configure PMP and other the M-mode state before executing in supervisor-mode.
+///
+/// # Parameters
+///
+/// - The `hartid` parameter specifies the target hart which is to be started.
+/// - The `start_addr` parameter points to a runtime-specified physical address, where the hart can start executing in supervisor-mode.
+/// - The `opaque` parameter is a `usize` value which will be set in the `a1` register when the hart starts executing at `start_addr`.
+///
+/// *NOTE:* A single `usize` parameter is sufficient as `start_addr`,
+/// because the hart will start execution in the supervisor-mode with MMU off,
+/// hence the `start_addr` must be less than XLEN bits wide.
+///
+/// # Behavior
+///
+/// The target hart jumps to supervisor mode at address specified by `start_addr` with following values in specific registers.
+///
+/// | Register Name | Register Value
+/// |:--------------|:--------------
+/// | `satp`        | 0
+/// | `sstatus.SIE` | 0
+/// | a0            | hartid
+/// | a1            | `opaque` parameter
+///
+/// # Return value
+///
+/// The possible return error codes returned in `SbiRet.error` are shown in the table below:
+///
+/// | Return code                   | Description
+/// |:------------------------------|:----------------------------------------------
+/// | `SbiRet::success()`           | Hart was previously in stopped state. It will start executing from `start_addr`.
+/// | `SbiRet::invalid_address()`   | `start_addr` is not valid, possibly due to the following reasons: it is not a valid physical address, or the address is prohibited by PMP or H-extension G-stage to run in supervisor-mode.
+/// | `SbiRet::invalid_param()`     | `hartid` is not a valid hartid as corresponding hart cannot started in supervisor mode.
+/// | `SbiRet::already_available()` | The given hartid is already started.
+/// | `SbiRet::failed()`            | The start request failed for unknown reasons.
+///
+/// This function is defined in RISC-V SBI Specification chapter 9.1.
+#[inline]
+pub fn hart_start(hartid: usize, start_addr: usize, opaque: usize) -> SbiRet {
+    sbi_call_3(EID_HSM, HART_START, hartid, start_addr, opaque)
+}
+
+/// Stop executing the calling hart in supervisor-mode.
+///
+/// This function requests the SBI implementation to stop executing the calling hart in
+/// supervisor-mode and return its ownership to the SBI implementation.
+///
+/// This call is not expected to return under normal conditions.
+/// The `sbi_hart_stop()` must be called with the supervisor-mode interrupts disabled.
+///
+/// # Return value
+///
+/// The possible return error codes returned in `SbiRet.error` are shown in the table below:
+///
+/// | Error code  | Description
+/// |:------------|:------------
+/// | `SbiRet::failed()` | Failed to stop execution of the current hart
+///
+/// This function is defined in RISC-V SBI Specification chapter 9.2.
+#[inline]
+pub fn hart_stop() -> SbiRet {
+    sbi_call_0(EID_HSM, HART_STOP)
+}
+
+/// Get the current status (or HSM state id) of the given hart.
+///
+/// The harts may transition HSM states at any time due to any concurrent `hart_start()`
+/// or `hart_stop()` calls, the return value from this function may not represent the actual state
+/// of the hart at the time of return value verification.
+///
+/// # Parameters
+///
+/// The `hartid` parameter specifies the target hart which status is required.
+///
+/// # Return value
+///
+/// The possible status values returned in `SbiRet.value` are shown in the table below:
+///
+/// | Name          | Value | Description
+/// |:--------------|:------|:-------------------------
+/// | STARTED       |   0   | Hart Started
+/// | STOPPED       |   1   | Hart Stopped
+/// | START_PENDING |   2   | Hart start request pending
+/// | STOP_PENDING  |   3   | Hart stop request pending
+///
+/// The possible return error codes returned in `SbiRet.error` are shown in the table below:
+///
+/// | Error code                | Description
+/// |:--------------------------|:------------
+/// | `SbiRet::invalid_param()` | The given `hartid` is not valid
+///
+/// This function is defined in RISC-V SBI Specification chapter 9.3.
+#[inline]
+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
+/// (or low power) state specified by the `suspend_type` parameter.
+///
+/// The hart will automatically come out of suspended state and resume normal execution
+/// when it recieves an interrupt or platform specific hardware event.
+///
+/// # Suspend behavior
+///
+/// The platform specific suspend states for a hart can be either retentive or non-rententive in nature.
+///
+/// A retentive suspend state will preserve hart register and CSR values for all privilege modes,
+/// whereas a non-retentive suspend state will not preserve hart register and CSR values.
+///
+/// # Resuming
+///
+/// Resuming from a retentive suspend state is straight forward and the supervisor-mode software
+/// will see SBI suspend call return without any failures.
+///
+/// Resuming from a non-retentive suspend state is relatively more involved and requires software
+/// to restore various hart registers and CSRs for all privilege modes.
+/// Upon resuming from non-retentive suspend state, the hart will jump to supervisor-mode at address
+/// specified by `resume_addr` with specific registers values described in the table below:
+///
+/// | Register Name | Register Value
+/// |:--------------|:--------------
+/// | `satp`        | 0
+/// | `sstatus.SIE` | 0
+/// | a0            | hartid
+/// | a1            | `opaque` parameter
+///
+/// # Parameters
+///
+/// The `suspend_type` parameter is 32 bits wide and the possible values are shown in the table below:
+///
+/// | Value                   | Description
+/// |:------------------------|:--------------
+/// | 0x00000000              | Default retentive suspend
+/// | 0x00000001 - 0x0FFFFFFF | _Reserved for future use_
+/// | 0x10000000 - 0x7FFFFFFF | Platform specific retentive suspend
+/// | 0x80000000              | Default non-retentive suspend
+/// | 0x80000001 - 0x8FFFFFFF | _Reserved for future use_
+/// | 0x90000000 - 0xFFFFFFFF | Platform specific non-retentive suspend
+/// | > 0xFFFFFFFF            | _Reserved_
+///
+/// The `resume_addr` parameter points to a runtime-specified physical address,
+/// where the hart can resume execution in supervisor-mode after a non-retentive
+/// suspend.
+///
+/// *NOTE:* A single `usize` parameter is sufficient as `resume_addr`,
+/// because the hart will resume execution in the supervisor-mode with MMU off,
+/// hence the `resume_addr` must be less than XLEN bits wide.
+///
+/// The `opaque` parameter is an XLEN-bit value which will be set in the `a1`
+/// register when the hart resumes exectution at `resume_addr` after a
+/// non-retentive suspend.
+///
+/// # Return value
+///
+/// The possible return error codes returned in `SbiRet.error` are shown in the table below:
+///
+/// | Error code                  | Description
+/// |:----------------------------|:------------
+/// | `SbiRet::success()`         | Hart has suspended and resumed back successfully from a retentive suspend state.
+/// | `SbiRet::invalid_param()`   | `suspend_type` is not valid.
+/// | `SbiRet::not_supported()`   | `suspend_type` is valid but not implemented.
+/// | `SbiRet::invalid_address()` | `resume_addr` is not valid, possibly due to the following reasons: it is not a valid physical address, or the address is prohibited by PMP or H-extension G-stage to run in supervisor-mode.
+/// | `SbiRet::failed()`          | The suspend request failed for unknown reasons.
+///
+/// This function is defined in RISC-V SBI Specification chapter 9.4.
+#[inline]
+pub fn hart_suspend<T>(suspend_type: T, resume_addr: usize, opaque: usize) -> SbiRet
+where
+    T: SuspendType,
+{
+    sbi_call_3(
+        EID_HSM,
+        HART_SUSPEND,
+        suspend_type.raw() as _,
+        resume_addr,
+        opaque,
+    )
+}
+
+/// A valid suspend type for hart state monitor.
+pub trait SuspendType {
+    /// Get a raw value to pass to SBI environment.
+    fn raw(&self) -> u32;
+}
+
+#[cfg(feature = "integer-impls")]
+impl SuspendType for u32 {
+    #[inline]
+    fn raw(&self) -> u32 {
+        *self
+    }
+}
+
+macro_rules! define_suspend_type {
+    ($($struct:ident($value:expr) #[$doc:meta])*) => {
+        $(
+            #[derive(Clone, Copy, Debug)]
+            #[$doc]
+            pub struct $struct;
+            impl SuspendType for $struct {
+                #[inline]
+                fn raw(&self) -> u32 {
+                    $value
+                }
+            }
+        )*
+    };
+}
+
+define_suspend_type! {
+    Retentive(sbi_spec::hsm::suspend_type::RETENTIVE) /// Default retentive hart suspension.
+    NonRetentive(sbi_spec::hsm::suspend_type::NON_RETENTIVE) /// Default non-retentive hart suspension.
+}

+ 160 - 0
sbi-rt/src/legacy.rs

@@ -0,0 +1,160 @@
+//! Chapter 5. Legacy Extensions (EIDs #0x00 - #0x0F)
+
+pub use sbi_spec::legacy::*;
+
+/// §5.1
+#[deprecated = "replaced by `set_timer` from Timer extension"]
+#[inline]
+pub fn set_timer(stime_value: u64) -> usize {
+    match () {
+        #[cfg(target_pointer_width = "32")]
+        () => sbi_call_legacy_2(LEGACY_SET_TIMER, stime_value as _, (stime_value >> 32) as _),
+        #[cfg(target_pointer_width = "64")]
+        () => sbi_call_legacy_1(LEGACY_SET_TIMER, stime_value as _),
+    }
+}
+
+/// §5.2
+///
+/// No replacement.
+#[deprecated = "no replacement"]
+#[inline]
+pub fn console_putchar(c: usize) -> usize {
+    sbi_call_legacy_1(LEGACY_CONSOLE_PUTCHAR, c)
+}
+
+/// §5.3
+///
+/// No replacement.
+#[deprecated = "no replacement"]
+#[inline]
+pub fn console_getchar() -> usize {
+    sbi_call_legacy_0(LEGACY_CONSOLE_GETCHAR)
+}
+
+/// §5.4
+///
+/// No replacement. Just clear `sip.SSIP` directly.
+#[deprecated = "you can clear `sip.SSIP` CSR bit directly"]
+#[inline]
+pub fn clear_ipi() -> usize {
+    sbi_call_legacy_0(LEGACY_CLEAR_IPI)
+}
+
+/// §5.5
+///
+/// Replaced by [`send_ipi`](super::send_ipi) from [`sPI`](super::EID_SPI) extension.
+#[deprecated = "replaced by `send_ipi` from `sPI` extension"]
+#[inline]
+pub fn send_ipi(hart_mask: usize) -> usize {
+    sbi_call_legacy_1(LEGACY_SEND_IPI, hart_mask)
+}
+
+/// §5.6
+///
+/// Replaced by [`remote_fence_i`](super::remote_fence_i) from [`RFNC`](super::EID_RFNC) extension.
+#[deprecated = "replaced by `remote_fence_i` from `RFNC` extension"]
+#[inline]
+pub fn remote_fence_i(hart_mask: usize) -> usize {
+    sbi_call_legacy_1(LEGACY_REMOTE_FENCE_I, hart_mask)
+}
+
+/// §5.7
+///
+/// Replaced by [`remote_sfence_vma`](super::remote_sfence_vma) from [`RFNC`](super::EID_RFNC) extension.
+#[deprecated = "replaced by `remote_sfence_vma` from `RFNC` extension"]
+#[inline]
+pub fn remote_fence_vma(hart_mask: usize, start: usize, size: usize) -> usize {
+    sbi_call_legacy_3(LEGACY_REMOTE_SFENCE_VMA, hart_mask, start, size)
+}
+
+/// §5.8
+///
+/// Replaced by [`remote_sfence_vma_asid`](super::remote_sfence_vma_asid) from [`RFNC`](super::EID_RFNC) extension.
+#[deprecated = "replaced by `remote_sfence_vma_asid` from `RFNC` extension"]
+#[inline]
+pub fn remote_fence_vma_asid(hart_mask: usize, start: usize, size: usize, asid: usize) -> usize {
+    sbi_call_legacy_4(LEGACY_REMOTE_SFENCE_VMA_ASID, hart_mask, start, size, asid)
+}
+
+/// §5.9
+///
+/// Replaced by [`system_reset`](super::system_reset) from [`SRST`](super::EID_SRST) extension.
+#[deprecated = "replaced by `system_reset` from System `SRST` extension"]
+#[inline]
+pub fn shutdown() -> ! {
+    sbi_call_legacy_0(LEGACY_SHUTDOWN);
+    unreachable!()
+}
+
+#[inline(always)]
+fn sbi_call_legacy_0(eid: usize) -> usize {
+    let error;
+    unsafe {
+        core::arch::asm!(
+            "ecall",
+            in("a7") eid,
+            lateout("a0") error,
+        );
+    }
+    error
+}
+
+#[inline(always)]
+fn sbi_call_legacy_1(eid: usize, arg0: usize) -> usize {
+    let error;
+    unsafe {
+        core::arch::asm!(
+            "ecall",
+            in("a7") eid,
+            inlateout("a0") arg0 => error,
+        );
+    }
+    error
+}
+
+#[cfg(target_pointer_width = "32")]
+#[inline(always)]
+fn sbi_call_legacy_2(eid: usize, arg0: usize, arg1: usize) -> usize {
+    let error;
+    unsafe {
+        core::arch::asm!(
+            "ecall",
+            in("a7") eid,
+            inlateout("a0") arg0 => error,
+            in("a1") arg1,
+        );
+    }
+    error
+}
+
+#[inline(always)]
+fn sbi_call_legacy_3(eid: usize, arg0: usize, arg1: usize, arg2: usize) -> usize {
+    let error;
+    unsafe {
+        core::arch::asm!(
+            "ecall",
+            in("a7") eid,
+            inlateout("a0") arg0 => error,
+            in("a1") arg1,
+            in("a2") arg2,
+        );
+    }
+    error
+}
+
+#[inline(always)]
+fn sbi_call_legacy_4(eid: usize, arg0: usize, arg1: usize, arg2: usize, arg3: usize) -> usize {
+    let error;
+    unsafe {
+        core::arch::asm!(
+            "ecall",
+            in("a7") eid,
+            inlateout("a0") arg0 => error,
+            in("a1") arg1,
+            in("a2") arg2,
+            in("a3") arg3,
+        );
+    }
+    error
+}

+ 46 - 0
sbi-rt/src/lib.rs

@@ -0,0 +1,46 @@
+//! Simple RISC-V SBI runtime primitives.
+#![no_std]
+#[cfg_attr(not(feature = "legacy"), deny(missing_docs))]
+// §3
+mod binary;
+// §4
+mod base;
+// §5
+#[cfg(feature = "legacy")]
+pub mod legacy;
+// §6
+mod time;
+// §7
+mod spi;
+// §8
+mod rfnc;
+// §9
+mod hsm;
+// §10
+mod srst;
+// §11
+mod pmu;
+// §12
+mod dbcn;
+// §13
+mod susp;
+// §14
+mod cppc;
+// §15
+mod nacl;
+// §16
+mod sta;
+
+pub use base::*;
+pub use binary::*;
+pub use cppc::*;
+pub use dbcn::*;
+pub use hsm::*;
+pub use nacl::*;
+pub use pmu::*;
+pub use rfnc::*;
+pub use spi::*;
+pub use srst::*;
+pub use sta::*;
+pub use susp::*;
+pub use time::*;

+ 146 - 0
sbi-rt/src/nacl.rs

@@ -0,0 +1,146 @@
+//! Chapter 15. Nested Acceleration Extension (EID #0x4E41434C "NACL")
+
+use crate::binary::{sbi_call_0, sbi_call_1, sbi_call_3, SbiRet};
+
+use sbi_spec::{
+    binary::SharedPtr,
+    nacl::{shmem_size, EID_NACL, PROBE_FEATURE, SET_SHMEM, SYNC_CSR, SYNC_HFENCE, SYNC_SRET},
+};
+
+/// Probe a nested acceleration feature.
+///
+/// This is a mandatory function of the SBI nested acceleration extension.
+///
+/// # Parameters
+///
+/// The `feature_id` parameter specifies the nested acceleration feature to probe.
+/// Possible feature IDs are defined in the table below:
+///
+/// # Return value
+///
+/// This function always returns `SbiRet::success()` in `SbiRet.error`.
+/// It returns 0 in `SbiRet.value` if the given `feature_id` is not available,
+/// or 1 in `SbiRet.value` if it is available.
+///
+/// This function is defined in RISC-V SBI Specification chapter 15.5.
+#[inline]
+pub fn nacl_probe_feature(feature_id: u32) -> SbiRet {
+    sbi_call_1(EID_NACL, PROBE_FEATURE, feature_id as _)
+}
+
+/// Set and enable the shared memory for nested acceleration on the calling hart.
+///
+/// This is a mandatory function of the SBI nested acceleration extension.
+///
+/// # Parameters
+///
+/// If `shmem` parameter is not all-ones bitwise then `shmem` specifies the shared
+/// memory physical base address. `shmem` MUST be 4096 bytes (i.e. page) aligned and
+/// the size of the shared memory must be `4096 + (XLEN * 128)` bytes.
+///
+/// If `shmem` parameter is all-ones bitwise then the nested acceleration features
+/// are disabled.
+///
+/// The `flags` parameter is reserved for future use and must be zero.
+///
+/// The possible error codes returned in `SbiRet.error` are shown in the table below:
+///
+/// | Error code                  | Description
+/// |:----------------------------|:---------------------------------
+/// | `SbiRet::success()`         | Shared memory was set or cleared successfully.
+/// | `SbiRet::invalid_param()`   | The `flags` parameter is not zero or or the `shmem` parameter is not 4096 bytes aligned.
+/// | `SbiRet::invalid_address()` | The shared memory pointed to by the `shmem` parameters does not satisfy the requirements.
+///
+/// This function is defined in RISC-V SBI Specification chapter 15.6.
+#[inline]
+pub fn nacl_set_shmem(shmem: SharedPtr<[u8; shmem_size::NATIVE]>, flags: usize) -> SbiRet {
+    sbi_call_3(
+        EID_NACL,
+        SET_SHMEM,
+        shmem.phys_addr_lo(),
+        shmem.phys_addr_hi(),
+        flags,
+    )
+}
+
+/// Synchronize CSRs in the nested acceleration shared memory.
+///
+/// This is an optional function which is only available if the SBI_NACL_FEAT_SYNC_CSR feature is available.
+///
+/// # Parameters
+///
+/// The parameter `csr_num` specifies the set of RISC-V H-extension CSRs to be synchronized.
+///
+/// If `csr_num` is all-ones bitwise then all RISC-V H-extension CSRs implemented by the SBI implementation (or L0 hypervisor) are synchronized.
+///
+/// If `(csr_num & 0x300) == 0x200` and `csr_num < 0x1000` then only a single
+/// RISC-V H-extension CSR specified by the csr_num parameter is synchronized.
+///
+/// # Return value
+///
+/// The possible error codes returned in `SbiRet.error` are shown in the table below:
+///
+/// | Error code                | Description
+/// |:--------------------------|:---------------------------------
+/// | `SbiRet::success()`       | CSRs synchronized successfully.
+/// | `SbiRet::not_supported()` | SBI_NACL_FEAT_SYNC_CSR feature is not available.
+/// | `SbiRet::invalid_param()` | `csr_num` is not all-ones bitwise and either: <br> * `(csr_num & 0x300) != 0x200` or <br> * `csr_num >= 0x1000` or <br> * `csr_num` is not implemented by the SBI implementation
+/// | `SbiRet::no_shmem()`      | Nested acceleration shared memory not available.
+///
+/// This function is defined in RISC-V SBI Specification chapter 15.7.
+#[inline]
+pub fn nacl_sync_csr(csr_num: usize) -> SbiRet {
+    sbi_call_1(EID_NACL, SYNC_CSR, csr_num)
+}
+
+/// Synchronize HFENCEs in the nested acceleration shared memory.
+///
+/// This is an optional function which is only available if the SBI_NACL_FEAT_SYNC_HFENCE feature is available.
+///
+/// # Parameters
+///
+/// The parameter `entry_index` specifies the set of nested HFENCE entries to be synchronized.
+///
+/// If `entry_index` is all-ones bitwise then all nested HFENCE entries are synchronized.
+///
+/// If `entry_index < (3840 / XLEN)` then only a single nested HFENCE entry specified by the `entry_index` parameter is synchronized
+///
+/// # Return value
+///
+/// The possible error codes returned in `SbiRet.error` are shown in the table below:
+///
+/// | Error code                | Description
+/// |:--------------------------|:---------------------------------
+/// | `SbiRet::success()`       | HFENCEs synchronized successfully.
+/// | `SbiRet::not_supported()` | SBI_NACL_FEAT_SYNC_HFENCE feature is not available.
+/// | `SbiRet::invalid_param()` | `entry_index` is not all-ones bitwise and `entry_index >= (3840 / XLEN)`.
+/// | `SbiRet::no_shmem()`      | Nested acceleration shared memory not available.
+///
+/// This function is defined in RISC-V SBI Specification chapter 15.8.
+#[inline]
+pub fn nacl_sync_hfence(entry_index: usize) -> SbiRet {
+    sbi_call_1(EID_NACL, SYNC_HFENCE, entry_index)
+}
+
+/// Synchronize CSRs and HFENCEs in the NACL shared memory and emulate the SRET instruction.
+///
+/// This is an optional function which is only available if the SBI_NACL_FEAT_SYNC_SRET feature is available.
+///
+/// This function is used by supervisor software (or L1 hypervisor) to do a synchronize SRET request
+/// and the SBI implementation (or L0 hypervisor) MUST handle it.
+///
+/// # Return value
+///
+/// This function does not return upon success and the possible error codes
+/// returned in `SbiRet.error` upon failure are shown in table below:
+///
+/// | Error code                | Description
+/// |:--------------------------|:------------
+/// | `SbiRet::no_shmem()`      | Nested acceleration shared memory not available.
+/// | `SbiRet::not_supported()` | SBI_NACL_FEAT_SYNC_SRET feature is not available.
+///
+/// This function is defined in RISC-V SBI Specification chapter 15.9.
+#[inline]
+pub fn nacl_sync_sret() -> SbiRet {
+    sbi_call_0(EID_NACL, SYNC_SRET)
+}

+ 326 - 0
sbi-rt/src/pmu.rs

@@ -0,0 +1,326 @@
+//! Chapter 11. Performance Monitoring Unit Extension (EID #0x504D55 "PMU")
+
+use crate::binary::{sbi_call_0, sbi_call_1, sbi_call_3, SbiRet};
+
+use sbi_spec::pmu::{
+    COUNTER_CONFIG_MATCHING, COUNTER_FW_READ, COUNTER_FW_READ_HI, COUNTER_GET_INFO, COUNTER_START,
+    COUNTER_STOP, EID_PMU, NUM_COUNTERS,
+};
+
+/// Returns the number of counters, both hardware and firmware.
+///
+/// This call would always succeed without returning any error.
+///
+/// This function is defined in RISC-V SBI Specification chapter 11.5.
+#[inline]
+pub fn pmu_num_counters() -> usize {
+    sbi_call_0(EID_PMU, NUM_COUNTERS).value
+}
+
+/// Get details about the specified counter.
+///
+/// The value returned includes details such as underlying CSR number, width of the counter,
+/// type of counter hardware/firmware, etc.
+///
+/// The `counter_info` returned by this SBI call is encoded as follows:
+///
+/// ```text
+///     counter_info[11:0] = CSR; // (12bit CSR number)
+///     counter_info[17:12] = Width; // (One less than number of bits in CSR)
+///     counter_info[XLEN-2:18] = Reserved; // Reserved for future use
+///     counter_info[XLEN-1] = Type; // (0 = hardware and 1 = firmware)
+/// ```
+/// If `counter_info.type` == `1` then `counter_info.csr` and `counter_info.width` should be ignored.
+///
+/// This function is defined in RISC-V SBI Specification chapter 11.6.
+///
+/// # Return value
+///
+/// Returns the `counter_info` described above in `SbiRet.value`.
+///
+/// The possible return error codes returned in `SbiRet.error` are shown in the table below:    
+///
+/// | Return code               | Description
+/// |:--------------------------|:----------------------------------------------
+/// | `SbiRet::success()`       | `counter_info` read successfully.
+/// | `SbiRet::invalid_param()` | `counter_idx` points to an invalid counter.
+///
+/// This function is defined in RISC-V SBI Specification chapter 11.6.
+#[inline]
+pub fn pmu_counter_get_info(counter_idx: usize) -> SbiRet {
+    sbi_call_1(EID_PMU, COUNTER_GET_INFO, counter_idx)
+}
+
+/// Find and configure a counter from a set of counters.
+///
+/// The counters to be found and configured should not be started (or enabled)
+/// and should be able to monitor the specified event.
+///
+/// # Parameters
+///
+/// The `counter_idx_base` and `counter_idx_mask` parameters represent the set of counters,
+/// whereas the `event_idx` represent the event to be monitored
+/// and `event_data` represents any additional event configuration.
+///
+/// The `config_flags` parameter represent additional counter configuration and filter flags.
+/// The bit definitions of the `config_flags` parameter are shown in the table below:
+///
+/// | Flag Name                    | Bits       | Description
+/// |:-----------------------------|:-----------|:------------
+/// | SBI_PMU_CFG_FLAG_SKIP_MATCH  | 0:0        | Skip the counter matching
+/// | SBI_PMU_CFG_FLAG_CLEAR_VALUE | 1:1        | Clear (or zero) the counter value in counter configuration
+/// | SBI_PMU_CFG_FLAG_AUTO_START  | 2:2        | Start the counter after configuring a matching counter
+/// | SBI_PMU_CFG_FLAG_SET_VUINH   | 3:3        | Event counting inhibited in VU-mode
+/// | SBI_PMU_CFG_FLAG_SET_VSINH   | 4:4        | Event counting inhibited in VS-mode
+/// | SBI_PMU_CFG_FLAG_SET_UINH    | 5:5        | Event counting inhibited in U-mode
+/// | SBI_PMU_CFG_FLAG_SET_SINH    | 6:6        | Event counting inhibited in S-mode
+/// | SBI_PMU_CFG_FLAG_SET_MINH    | 7:7        | Event counting inhibited in M-mode
+/// | _RESERVED_                   | 8:(XLEN-1) | _All non-zero values are reserved for future use._
+///
+/// *NOTE:* When *SBI_PMU_CFG_FLAG_SKIP_MATCH* is set in `config_flags`, the
+/// SBI implementation will unconditionally select the first counter from the
+/// set of counters specified by the `counter_idx_base` and `counter_idx_mask`.
+///
+/// *NOTE:* The *SBI_PMU_CFG_FLAG_AUTO_START* flag in `config_flags` has no
+/// impact on the counter value.    
+///
+/// *NOTE:* The `config_flags[3:7]` bits are event filtering hints so these
+/// can be ignored or overridden by the SBI implementation for security concerns
+/// or due to lack of event filtering support in the underlying RISC-V platform.
+///
+/// # Return value
+///
+/// Returns the `counter_idx` in `sbiret.value` upon success.
+///
+/// In case of failure, the possible error codes returned in `sbiret.error` are shown in the table below:    
+///
+/// | Return code               | Description
+/// |:--------------------------|:----------------------------------------------
+/// | `SbiRet::success()`       | counter found and configured successfully.
+/// | `SbiRet::invalid_param()` | set of counters has an invalid counter.
+/// | `SbiRet::not_supported()` | none of the counters can monitor specified event.
+///
+/// This function is defined in RISC-V SBI Specification chapter 11.7.
+#[inline]
+pub fn pmu_counter_config_matching<T>(
+    counter_idx_base: usize,
+    counter_idx_mask: usize,
+    config_flags: T,
+    event_idx: usize,
+    event_data: u64,
+) -> SbiRet
+where
+    T: ConfigFlags,
+{
+    match () {
+        #[cfg(target_pointer_width = "32")]
+        () => crate::binary::sbi_call_6(
+            EID_PMU,
+            COUNTER_CONFIG_MATCHING,
+            counter_idx_base,
+            counter_idx_mask,
+            config_flags.raw(),
+            event_idx,
+            event_data as _,
+            (event_data >> 32) as _,
+        ),
+        #[cfg(target_pointer_width = "64")]
+        () => crate::binary::sbi_call_5(
+            EID_PMU,
+            COUNTER_CONFIG_MATCHING,
+            counter_idx_base,
+            counter_idx_mask,
+            config_flags.raw(),
+            event_idx,
+            event_data as _,
+        ),
+    }
+}
+
+/// Start or enable a set of counters on the calling hart with the specified initial value.
+///
+/// # Parameters
+///
+/// The `counter_idx_base` and `counter_idx_mask` parameters represent the set of counters.
+/// whereas the `initial_value` parameter specifies the initial value of the counter.
+///
+/// The bit definitions of the `start_flags` parameter are shown in the table below:
+///
+/// | Flag Name                    | Bits       | Description
+/// |:-----------------------------|:-----------|:------------
+/// | SBI_PMU_START_SET_INIT_VALUE | 0:0        | Set the value of counters based on the `initial_value` parameter.
+/// | _RESERVED_                   | 1:(XLEN-1) | _All non-zero values are reserved for future use._
+///
+/// *NOTE*: When `SBI_PMU_START_SET_INIT_VALUE` is not set in `start_flags`, the counter value will
+/// not be modified and event counting will start from current counter value.
+///
+/// # Return value
+///
+/// The possible return error codes returned in `SbiRet.error` are shown in the table below:    
+///
+/// | Return code                 | Description
+/// |:----------------------------|:----------------------------------------------
+/// | `SbiRet::success()`         | counter started successfully.
+/// | `SbiRet::invalid_param()`   | some of the counters specified in parameters are invalid.
+/// | `SbiRet::already_started()` | some of the counters specified in parameters are already started.
+///
+/// This function is defined in RISC-V SBI Specification chapter 11.8.
+#[inline]
+pub fn pmu_counter_start<T>(
+    counter_idx_base: usize,
+    counter_idx_mask: usize,
+    start_flags: T,
+    initial_value: u64,
+) -> SbiRet
+where
+    T: StartFlags,
+{
+    match () {
+        #[cfg(target_pointer_width = "32")]
+        () => crate::binary::sbi_call_5(
+            EID_PMU,
+            COUNTER_START,
+            counter_idx_base,
+            counter_idx_mask,
+            start_flags.raw(),
+            initial_value as _,
+            (initial_value >> 32) as _,
+        ),
+        #[cfg(target_pointer_width = "64")]
+        () => crate::binary::sbi_call_4(
+            EID_PMU,
+            COUNTER_START,
+            counter_idx_base,
+            counter_idx_mask,
+            start_flags.raw(),
+            initial_value as _,
+        ),
+    }
+}
+
+/// Stop or disable a set of counters on the calling hart.
+///
+/// # Parameters
+///
+/// The `counter_idx_base` and `counter_idx_mask` parameters represent the set of counters.
+/// The bit definitions of the `stop_flags` parameter are shown in the table below:
+///
+/// | Flag Name               | Bits       | Description
+/// |:------------------------|:-----------|:------------
+/// | SBI_PMU_STOP_FLAG_RESET | 0:0        | Reset the counter to event mapping.
+/// | _RESERVED_              | 1:(XLEN-1) | *All non-zero values are reserved for future use.*
+///
+/// # Return value
+///
+/// The possible return error codes returned in `SbiRet.error` are shown in the table below:    
+///
+/// | Return code                 | Description
+/// |:----------------------------|:----------------------------------------------
+/// | `SbiRet::success()`         | counter stopped successfully.
+/// | `SbiRet::invalid_param()`   | some of the counters specified in parameters are invalid.
+/// | `SbiRet::already_stopped()` | some of the counters specified in parameters are already stopped.
+///
+/// This function is defined in RISC-V SBI Specification chapter 11.9.
+#[inline]
+pub fn pmu_counter_stop<T>(
+    counter_idx_base: usize,
+    counter_idx_mask: usize,
+    stop_flags: T,
+) -> SbiRet
+where
+    T: StopFlags,
+{
+    sbi_call_3(
+        EID_PMU,
+        COUNTER_STOP,
+        counter_idx_base,
+        counter_idx_mask,
+        stop_flags.raw(),
+    )
+}
+
+/// Provide the current value of a firmware counter.
+///
+/// On RV32 systems, the `SbiRet.value` will only contain the lower 32 bits of the current
+/// firmware counter value.
+///
+/// # Parameters
+///
+/// This function should be only used to read a firmware counter. It will return an error
+/// when user provides a hardware counter in `counter_idx` parameter.
+///
+/// # Return value
+///
+/// The possible return error codes returned in `SbiRet.error` are shown in the table below:    
+///
+/// | Return code               | Description
+/// |:--------------------------|:----------------------------------------------
+/// | `SbiRet::success()`       | firmware counter read successfully.
+/// | `SbiRet::invalid_param()` | `counter_idx` points to a hardware counter or an invalid counter.
+///
+/// This function is defined in RISC-V SBI Specification chapter 11.10.
+#[inline]
+pub fn pmu_counter_fw_read(counter_idx: usize) -> SbiRet {
+    sbi_call_1(EID_PMU, COUNTER_FW_READ, counter_idx)
+}
+
+/// Provide the upper 32 bits of the current firmware counter value.
+///
+/// This function always returns zero in `SbiRet.value` for RV64 (or higher) systems.
+///
+/// # Return value
+///
+/// The possible return error codes returned in `SbiRet.error` are shown in the table below:
+///
+/// | Return code               | Description
+/// |:--------------------------|:----------------------------------------------
+/// | `SbiRet::success()`       | firmware counter read successfully.
+/// | `SbiRet::invalid_param()` | `counter_idx` points to a hardware counter or an invalid counter.
+///
+/// This function is defined in RISC-V SBI Specification chapter 11.11.
+#[inline]
+pub fn pmu_counter_fw_read_hi(counter_idx: usize) -> SbiRet {
+    sbi_call_1(EID_PMU, COUNTER_FW_READ_HI, counter_idx)
+}
+
+/// Flags to configure performance counter.
+pub trait ConfigFlags {
+    /// Get a raw value to pass to SBI environment.
+    fn raw(&self) -> usize;
+}
+
+#[cfg(feature = "integer-impls")]
+impl ConfigFlags for usize {
+    #[inline]
+    fn raw(&self) -> usize {
+        *self
+    }
+}
+
+/// Flags to start performance counter.
+pub trait StartFlags {
+    /// Get a raw value to pass to SBI environment.
+    fn raw(&self) -> usize;
+}
+
+#[cfg(feature = "integer-impls")]
+impl StartFlags for usize {
+    #[inline]
+    fn raw(&self) -> usize {
+        *self
+    }
+}
+
+/// Flags to stop performance counter.
+pub trait StopFlags {
+    /// Get a raw value to pass to SBI environment.
+    fn raw(&self) -> usize;
+}
+
+#[cfg(feature = "integer-impls")]
+impl StopFlags for usize {
+    #[inline]
+    fn raw(&self) -> usize {
+        *self
+    }
+}

+ 228 - 0
sbi-rt/src/rfnc.rs

@@ -0,0 +1,228 @@
+//! Chapter 8. RFENCE Extension (EID #0x52464E43 "RFNC")
+
+use crate::binary::{sbi_call_2, sbi_call_4, sbi_call_5, SbiRet};
+
+use sbi_spec::{
+    binary::HartMask,
+    rfnc::{
+        EID_RFNC, REMOTE_FENCE_I, REMOTE_HFENCE_GVMA, REMOTE_HFENCE_GVMA_VMID, REMOTE_HFENCE_VVMA,
+        REMOTE_HFENCE_VVMA_ASID, REMOTE_SFENCE_VMA, REMOTE_SFENCE_VMA_ASID,
+    },
+};
+
+/// Execute `FENCE.I` instruction on remote harts.
+///
+/// # Return value
+///
+/// Returns `SbiRet::success()` when remote fence was sent to all the targeted harts successfully.
+///
+/// This function is defined in RISC-V SBI Specification chapter 8.1.
+#[inline]
+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)
+}
+
+/// Execute `SFENCE.VMA` instructions for all address spaces on remote harts.
+///
+/// This function instructs the remote harts to execute one or more `SFENCE.VMA` instructions,
+/// covering the range of virtual addresses between `start_addr` and `size`.
+///
+/// # Return value
+///
+/// The possible return error codes returned in `SbiRet.error` are shown in the table below:
+///
+/// | Return code                 | Description
+/// |:----------------------------|:----------------------------------------------
+/// | `SbiRet::success()`         | Remote fence was sent to all the targeted harts successfully.
+/// | `SbiRet::invalid_address()` | `start_addr` or `size` is not valid.
+///
+/// This function is defined in RISC-V SBI Specification chapter 8.2.
+#[inline]
+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(
+        EID_RFNC,
+        REMOTE_SFENCE_VMA,
+        hart_mask,
+        hart_mask_base,
+        start_addr,
+        size,
+    )
+}
+
+/// Execute address space based `SFENCE.VMA` instructions on remote harts.
+///
+/// This function instructs the remote harts to execute one or more `SFENCE.VMA` instructions,
+/// covering the range of virtual addresses between `start_addr` and `size`.
+/// This covers only the given address space by `asid`.
+///
+/// # Return value
+///
+/// The possible return error codes returned in `SbiRet.error` are shown in the table below:
+///
+/// | Return code                 | Description
+/// |:----------------------------|:----------------------------------------------
+/// | `SbiRet::success()`         | Remote fence was sent to all the targeted harts successfully.
+/// | `SbiRet::invalid_address()` | `start_addr` or `size` is not valid.
+///
+/// This function is defined in RISC-V SBI Specification chapter 8.3.
+#[inline]
+pub fn remote_sfence_vma_asid(
+    hart_mask: HartMask,
+    start_addr: usize,
+    size: usize,
+    asid: usize,
+) -> SbiRet {
+    let (hart_mask, hart_mask_base) = hart_mask.into_inner();
+    sbi_call_5(
+        EID_RFNC,
+        REMOTE_SFENCE_VMA_ASID,
+        hart_mask,
+        hart_mask_base,
+        start_addr,
+        size,
+        asid,
+    )
+}
+
+/// Execute virtual machine id based `HFENCE.GVMA` instructions on remote harts.
+///
+/// This function instructs the remote harts to execute one or more `HFENCE.GVMA`
+/// instructions, covering the range of guest physical addresses between `start_addr`
+/// and `size` only for the given virtual machine by `vmid`.
+///
+/// This function call is only valid for harts implementing hypervisor extension.
+///
+/// # Return value
+///
+/// The possible return error codes returned in `SbiRet.error` are shown in the table below:
+///
+/// | Return code                 | Description
+/// |:----------------------------|:----------------------------------------------
+/// | `SbiRet::success()`         | 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.
+///
+/// This function is defined in RISC-V SBI Specification chapter 8.4.
+#[inline]
+pub fn remote_hfence_gvma_vmid(
+    hart_mask: HartMask,
+    start_addr: usize,
+    size: usize,
+    vmid: usize,
+) -> SbiRet {
+    let (hart_mask, hart_mask_base) = hart_mask.into_inner();
+    sbi_call_5(
+        EID_RFNC,
+        REMOTE_HFENCE_GVMA_VMID,
+        hart_mask,
+        hart_mask_base,
+        start_addr,
+        size,
+        vmid,
+    )
+}
+
+/// Execute `HFENCE.GVMA` instructions for all virtual machines on remote harts.
+///
+/// This function instructs the remote harts to execute one or more `HFENCE.GVMA` instructions,
+/// covering the range of guest physical addresses between `start_addr` and `size`
+/// for all the guests.
+///
+/// This function call is only valid for harts implementing hypervisor extension.
+///
+/// # Return value
+///
+/// The possible return error codes returned in `SbiRet.error` are shown in the table below:
+///
+/// | Return code                 | Description
+/// |:----------------------------|:----------------------------------------------
+/// | `SbiRet::success()`         | 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.
+///
+/// This function is defined in RISC-V SBI Specification chapter 8.5.
+#[inline]
+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(
+        EID_RFNC,
+        REMOTE_HFENCE_GVMA,
+        hart_mask,
+        hart_mask_base,
+        start_addr,
+        size,
+    )
+}
+
+/// Execute address space based `HFENCE.VVMA` for current virtual machine on remote harts.
+///
+/// This function instructs the remote harts to execute one or more `HFENCE.VVMA` instructions,
+/// covering the range of guest virtual addresses between `start_addr` and `size` for the given
+/// address space by `asid` and current virtual machine (by `vmid` in `hgatp` CSR)
+/// of calling hart.
+///
+/// This function call is only valid for harts implementing hypervisor extension.
+///
+/// # Return value
+///
+/// The possible return error codes returned in `SbiRet.error` are shown in the table below:
+///
+/// | Return code                 | Description
+/// |:----------------------------|:----------------------------------------------
+/// | `SbiRet::success()`         | 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.
+///
+/// This function is defined in RISC-V SBI Specification chapter 8.6.
+#[inline]
+pub fn remote_hfence_vvma_asid(
+    hart_mask: HartMask,
+    start_addr: usize,
+    size: usize,
+    asid: usize,
+) -> SbiRet {
+    let (hart_mask, hart_mask_base) = hart_mask.into_inner();
+    sbi_call_5(
+        EID_RFNC,
+        REMOTE_HFENCE_VVMA_ASID,
+        hart_mask,
+        hart_mask_base,
+        start_addr,
+        size,
+        asid,
+    )
+}
+
+/// Execute `HFENCE.VVMA` for all address spaces in current virtual machine on remote harts.
+///
+/// This function instructs the remote harts to execute one or more `HFENCE.VVMA` instructions,
+/// covering the range of guest virtual addresses between `start_addr` and `size`
+/// for current virtual machine (by `vmid` in `hgatp` CSR) of calling hart.
+///
+/// This function call is only valid for harts implementing hypervisor extension.
+///
+/// # Return value
+///
+/// The possible return error codes returned in `SbiRet.error` are shown in the table below:
+///
+/// | Return code                 | Description
+/// |:----------------------------|:----------------------------------------------
+/// | `SbiRet::success()`         | 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.
+///
+/// This function is defined in RISC-V SBI Specification chapter 8.7.
+#[inline]
+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(
+        EID_RFNC,
+        REMOTE_HFENCE_VVMA,
+        hart_mask,
+        hart_mask_base,
+        start_addr,
+        size,
+    )
+}

+ 23 - 0
sbi-rt/src/spi.rs

@@ -0,0 +1,23 @@
+//! Chapter 7. IPI Extension (EID #0x735049 "sPI: s-mode IPI")
+
+use crate::binary::{sbi_call_2, SbiRet};
+
+use sbi_spec::{
+    binary::HartMask,
+    spi::{EID_SPI, SEND_IPI},
+};
+
+/// Send an inter-processor interrupt to all harts defined in hart mask.
+///
+/// Inter-processor interrupts manifest at the receiving harts as the supervisor software interrupts.
+///
+/// # Return value
+///
+/// Should return `SbiRet::success()` if IPI was sent to all the targeted harts successfully.
+///
+/// This function is defined in RISC-V SBI Specification chapter 7.1.
+#[inline]
+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)
+}

+ 109 - 0
sbi-rt/src/srst.rs

@@ -0,0 +1,109 @@
+//! Chapter 10. System Reset Extension (EID #0x53525354 "SRST")
+
+use crate::binary::{sbi_call_2, SbiRet};
+
+use sbi_spec::srst::{
+    EID_SRST, RESET_REASON_NO_REASON, RESET_REASON_SYSTEM_FAILURE, RESET_TYPE_COLD_REBOOT,
+    RESET_TYPE_SHUTDOWN, RESET_TYPE_WARM_REBOOT, SYSTEM_RESET,
+};
+
+/// Reset the system based on provided `reset_type` and `reset_reason`.
+///
+/// This is a synchronous call and does not return if it succeeds.
+///
+/// # Warm reboot and cold reboot
+///
+/// When supervisor software is running natively, the SBI implementation is machine mode firmware.
+/// In this case, shutdown is equivalent to physical power down of the entire system and
+/// cold reboot is equivalent to physical power cycle of the entire system. Further, warm reboot
+/// is equivalent to a power cycle of main processor and parts of the system but not the entire system.
+///
+/// For example, on a server class system with a BMC (board management controller),
+/// a warm reboot will not power cycle the BMC whereas a cold reboot will definitely power cycle the BMC.
+///
+/// When supervisor software is running inside a virtual machine, the SBI implementation is a hypervisor.
+/// The shutdown, cold reboot and warm reboot will behave functionally the same as the native case but might
+/// not result in any physical power changes.
+///
+/// This function is defined in RISC-V SBI Specification chapter 10.1.
+#[inline]
+pub fn system_reset<T, R>(reset_type: T, reset_reason: R) -> SbiRet
+where
+    T: ResetType,
+    R: ResetReason,
+{
+    sbi_call_2(
+        EID_SRST,
+        SYSTEM_RESET,
+        reset_type.raw() as _,
+        reset_reason.raw() as _,
+    )
+}
+
+/// A valid type for system reset.
+pub trait ResetType {
+    /// Get a raw value to pass to SBI environment.
+    fn raw(&self) -> u32;
+}
+
+#[cfg(feature = "integer-impls")]
+impl ResetType for u32 {
+    #[inline]
+    fn raw(&self) -> u32 {
+        *self
+    }
+}
+
+#[cfg(feature = "integer-impls")]
+impl ResetType for i32 {
+    #[inline]
+    fn raw(&self) -> u32 {
+        u32::from_ne_bytes(i32::to_ne_bytes(*self))
+    }
+}
+
+/// A valid reason for system reset.
+pub trait ResetReason {
+    /// Get a raw value to pass to SBI environment.
+    fn raw(&self) -> u32;
+}
+
+#[cfg(feature = "integer-impls")]
+impl ResetReason for u32 {
+    #[inline]
+    fn raw(&self) -> u32 {
+        *self
+    }
+}
+
+#[cfg(feature = "integer-impls")]
+impl ResetReason for i32 {
+    #[inline]
+    fn raw(&self) -> u32 {
+        u32::from_ne_bytes(i32::to_ne_bytes(*self))
+    }
+}
+
+macro_rules! define_reset_param {
+    ($($struct:ident($value:expr): $trait:ident #[$doc:meta])*) => {
+        $(
+            #[derive(Clone, Copy, Debug)]
+            #[$doc]
+            pub struct $struct;
+            impl $trait for $struct {
+                #[inline]
+                fn raw(&self) -> u32 {
+                    $value
+                }
+            }
+        )*
+    };
+}
+
+define_reset_param! {
+    Shutdown(RESET_TYPE_SHUTDOWN): ResetType /// Shutdown as reset type.
+    ColdReboot(RESET_TYPE_COLD_REBOOT): ResetType /// Cold reboot as reset type.
+    WarmReboot(RESET_TYPE_WARM_REBOOT): ResetType /// Warm reboot as reset type.
+    NoReason(RESET_REASON_NO_REASON): ResetReason /// No reason as reset reason.
+    SystemFailure(RESET_REASON_SYSTEM_FAILURE): ResetReason /// System failure as reset reason.
+}

+ 67 - 0
sbi-rt/src/sta.rs

@@ -0,0 +1,67 @@
+//! Chapter 16. Steal-time Accounting Extension (EID #0x535441 "STA")
+
+use crate::binary::{sbi_call_3, SbiRet};
+
+use sbi_spec::{
+    binary::SharedPtr,
+    sta::{EID_STA, SET_SHMEM},
+};
+
+/// Prepare shared memory for steal-time accounting feature.
+///
+/// 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.
+///
+/// It is not expected for the shared memory to be written by the supervisor-mode software
+/// while it is in use for steal-time accounting. However, the SBI implementation MUST not misbehave
+/// if a write from supervisor-mode software occurs, however, in that case,
+/// it MAY leave the shared memory filled with inconsistent data.
+///
+/// *NOTE:* Not writing to the shared memory when the supervisor-mode software is not runnable
+/// avoids unnecessary work and supports repeatable capture of a system image
+/// while the supervisor-mode software is suspended.
+///
+/// # STA Shared Memory Structure
+///
+/// | Name      | Offset | Size | Description
+/// |:----------|:-------|:-----|:------------
+/// | sequence  | 0      | 4    | The SBI implementation MUST increment this field to an odd value before writing the `steal` field, and increment it again to an even value after writing `steal` (i.e. an odd sequence number indicates an in-progress update). The SBI implementation SHOULD ensure that the sequence field remains odd for only very short periods of time. <br><br> The supervisor-mode software MUST check this field before and after reading the `steal` field, and repeat the read if it is different or odd. <br><br> This sequence field enables the value of the steal field to be read by supervisor-mode software executing in a 32-bit environment.
+/// | flags     | 4      | 4    | Always zero. <br><br> Future extensions of the SBI call might allow the supervisor-mode software to write to some of the fields of the shared memory. Such extensions will not be enabled as long as a zero value is used for the flags argument to the SBI call.
+/// | steal     | 8      | 8    | The amount of time in which this virtual hart was not idle and scheduled out, in nanoseconds. The time during which the virtual hart is idle will not be reported as steal-time.
+/// | preempted | 16     | 1    | An advisory flag indicating whether the virtual hart which registered this structure is running or not. A non-zero value MAY be written by the SBI implementation if the virtual hart has been preempted (i.e. while the `steal` field is increasing), while a zero value MUST be written before the virtual hart starts to run again. <br><br> This preempted field can, for example, be used by the supervisor-mode software to check if a lock holder has been preempted, and, in that case, disable optimistic spinning.
+/// | pad       | 17     | 47   | Pad with zeros to a 64 byte boundary.
+///
+/// # Parameters
+///
+/// If `shmem` address is not all-ones bitwise, then `shmem` specifies the shared memory
+/// physical base address. `shmem` MUST be 64-byte aligned. The size of the shared memory
+/// must be 64 bytes. All bytes MUST be set to zero by the SBI implementation before returning
+/// from the SBI call.
+///
+/// If `shmem` address is all-ones bitwise, the SBI implementation will stop reporting
+/// steal-time information for the virtual hart.
+///
+/// The `flags` parameter is reserved for future use and MUST be zero.
+///
+/// # Return value
+///
+/// `SbiRet.value` is set to zero and the possible error codes returned in `SbiRet.error` are shown in the table below:
+///
+/// | Error code                  | Description
+/// |:----------------------------|:---------------------------------
+/// | `SbiRet::success()`         | The steal-time shared memory physical base address was set or cleared successfully.
+/// | `SbiRet::invalid_param()`   | The `flags` parameter is not zero or the shmem_phys_lo is not 64-byte aligned.
+/// | `SbiRet::invalid_address()` | The shared memory pointed to by the `shmem_phys_lo` and `shmem_phys_hi` parameters is not writable or does not satisfy other requirements of STA Shared Memory Structure.
+/// | `SbiRet::failed()`          | The request failed for unspecified or unknown other reasons.
+///
+/// This function is defined in RISC-V SBI Specification chapter 16.1.
+#[inline]
+pub fn sta_set_shmem(shmem: SharedPtr<[u8; 64]>, flags: usize) -> SbiRet {
+    sbi_call_3(
+        EID_STA,
+        SET_SHMEM,
+        shmem.phys_addr_lo(),
+        shmem.phys_addr_hi(),
+        flags,
+    )
+}

+ 85 - 0
sbi-rt/src/susp.rs

@@ -0,0 +1,85 @@
+//! Chapter 13. System Suspend Extension (EID #0x53555350 "SUSP")
+
+use crate::binary::{sbi_call_3, SbiRet};
+use sbi_spec::susp::{EID_SUSP, SUSPEND};
+
+/// Suspend the system based on provided `sleep_type`.
+///
+/// # Parameters
+///
+/// The `sleep_type` parameter specifies the sleep type.
+///
+/// | Type                    | Name           | Description
+/// |:------------------------|:---------------|:----------------------------------------------
+/// | 0                       | SUSPEND_TO_RAM | This is a "suspend to RAM" sleep type, similar to ACPI's S2 or S3. Entry requires all but the calling hart be in the HSM `STOPPED` state and all hart registers and CSRs saved to RAM.
+/// | 0x00000001 - 0x7fffffff |                | Reserved for future use
+/// | 0x80000000 - 0xffffffff |                | Platform-specific system sleep types
+///
+/// The `resume_addr` parameter points to a runtime-specified physical address,
+/// where the hart can resume execution in supervisor-mode after a system suspend.
+///
+/// The `opaque` parameter is an XLEN-bit value which will be set in the `a1`
+/// register when the hart resumes execution at `resume_addr` after a system
+/// suspend.
+///
+/// # Return value
+///
+/// The possible return error codes returned in `SbiRet.error` are shown in
+/// the table below:
+///
+/// | Return code               | Description
+/// |:--------------------------|:----------------------------------------------
+/// | `SbiRet::success()`       | The suspend request is accepted and the system is suspended. The system will resume execution at `resume_addr` after the sleep period.
+/// | `SbiRet::invalid_param()` | `sleep_type` is reserved or is platform-specific and unimplemented.
+/// | `SbiRet::not_supported()` | `sleep_type` is not reserved and is implemented, but the platform does not support it due to one or more missing dependencies.
+/// | `SbiRet::invalid_address()` | `resume_addr` is not valid, possibly due to the following reasons: + * It is not a valid physical address. + * Executable access to the address is prohibited by a physical memory protection mechanism or H-extension G-stage for supervisor mode.
+/// | `SbiRet::denied()`        | The suspend request failed due to unsatisfied entry criteria.
+/// | `SbiRet::failed()`        | The suspend request failed for unspecified or unknown other reasons.
+///
+/// This function is defined in RISC-V SBI Specification chapter 13.1.
+#[inline]
+pub fn system_suspend<T>(sleep_type: T, resume_addr: usize, opaque: usize) -> SbiRet
+where
+    T: SleepType,
+{
+    sbi_call_3(
+        EID_SUSP,
+        SUSPEND,
+        sleep_type.raw() as _,
+        resume_addr,
+        opaque,
+    )
+}
+
+/// A valid sleep type for system suspend.
+pub trait SleepType {
+    /// Get a raw value to pass to SBI environment.
+    fn raw(&self) -> u32;
+}
+
+#[cfg(feature = "integer-impls")]
+impl SleepType for u32 {
+    #[inline]
+    fn raw(&self) -> u32 {
+        *self
+    }
+}
+
+#[cfg(feature = "integer-impls")]
+impl SleepType for i32 {
+    #[inline]
+    fn raw(&self) -> u32 {
+        u32::from_ne_bytes(i32::to_ne_bytes(*self))
+    }
+}
+
+/// Suspend to RAM as sleep type.
+#[derive(Clone, Copy, Debug)]
+pub struct SuspendToRam;
+
+impl SleepType for SuspendToRam {
+    #[inline]
+    fn raw(&self) -> u32 {
+        0
+    }
+}

+ 29 - 0
sbi-rt/src/time.rs

@@ -0,0 +1,29 @@
+//! Chapter 6. Timer Extension (EID #0x54494D45 "TIME")
+
+use crate::SbiRet;
+
+use sbi_spec::time::{EID_TIME, SET_TIMER};
+
+/// Programs the clock for next event after an absolute time.
+///
+/// Parameter `stime_value` is in absolute time. This function must clear the pending timer interrupt bit as well.
+///
+/// If the supervisor wishes to clear the timer interrupt without scheduling the next timer event,
+/// it can either request a timer interrupt infinitely far into the future (i.e., `u64::MAX`),
+/// or it can instead mask the timer interrupt by clearing `sie.STIE` CSR bit.
+///
+/// This function is defined in RISC-V SBI Specification chapter 6.1.
+#[inline]
+pub fn set_timer(stime_value: u64) -> SbiRet {
+    match () {
+        #[cfg(target_pointer_width = "32")]
+        () => crate::binary::sbi_call_2(
+            EID_TIME,
+            SET_TIMER,
+            stime_value as _,
+            (stime_value >> 32) as _,
+        ),
+        #[cfg(target_pointer_width = "64")]
+        () => crate::binary::sbi_call_1(EID_TIME, SET_TIMER, stime_value as _),
+    }
+}