Browse Source

Merge pull request #59 from luojia65/feat/rustsbi-macro

Derive macro based RustSBI implementation
Luo Jia / Zhouqi Jiang 1 year ago
parent
commit
6bf5df0c12
25 changed files with 2150 additions and 1004 deletions
  1. 3 2
      CHANGELOG.md
  2. 6 1
      Cargo.toml
  3. 38 0
      examples/derive/commons.rs
  4. 63 0
      examples/derive/main.rs
  5. 18 0
      macros/Cargo.toml
  6. 242 0
      macros/src/lib.rs
  7. 1 1
      src/console.rs
  8. 2 2
      src/cppc.rs
  9. 1 1
      src/hart_mask.rs
  10. 2 2
      src/hsm.rs
  11. 0 844
      src/instance.rs
  12. 2 2
      src/ipi.rs
  13. 636 134
      src/lib.rs
  14. 3 3
      src/nacl.rs
  15. 2 2
      src/pmu.rs
  16. 2 2
      src/reset.rs
  17. 2 2
      src/rfence.rs
  18. 2 2
      src/sta.rs
  19. 2 2
      src/susp.rs
  20. 2 2
      src/timer.rs
  21. 395 0
      src/traits.rs
  22. 311 0
      tests/build-full.rs
  23. 106 0
      tests/build-generics.rs
  24. 154 0
      tests/build-rename.rs
  25. 155 0
      tests/build-skip.rs

+ 3 - 2
CHANGELOG.md

@@ -14,10 +14,11 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 - `new_uninit` and `uninit_with_machine_info` constructors for RustSBI instance
 - `handle_ecall` now only requires `&self` since RustSBI trait implementations are internally mutable
 - support NACL and STA extensions
+- macro based `#[derive(RustSBI)]` interface
 
 ### Modified
 
-- run on provided `MachineInfo` by default; bare metal M-mode environment should gate `machine`
+- run on provided `EnvInfo` by default; bare metal M-mode environment should gate `machine`
 - doc: grammar tweaks in hsm module
 - update dependency `sbi-spec` to v0.0.6, use `Physical` struct from `sbi-spec` crate.
 
@@ -25,7 +26,7 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
 - `sbi_2_0` feature; RustSBI now supports SBI 2.0-rc1 by default
 - support for legacy SBI extensions
-- singleton based RustSBI interface; use struct `RustSBI` instead
+- singleton based RustSBI interface; use derive macro `#[derive(RustSBI)]` instead
 
 ## [0.3.2] - 2023-02-26
 

+ 6 - 1
Cargo.toml

@@ -19,6 +19,8 @@ exclude = ["/.github"]
 [dependencies]
 sbi-spec = "0.0.7-alpha.2"
 riscv = { version = "0.10.1", optional = true }
+rustsbi-macros = { path = "macros" }
+static_assertions = "1.1.0"
 
 [features]
 default = []
@@ -26,7 +28,7 @@ default = []
 # This feature enables to use RISC-V primitives on current machine mode environment
 # If you are developing a cross-architecture virtual machine, consider disabling this feature
 # to customize environment variables for RISC-V architecture like mvendorid, mimpid, etc.
-machine = ["dep:riscv"]
+machine = ["rustsbi-macros/machine", "dep:riscv"]
 
 [package.metadata.docs.rs]
 default-target = "riscv64imac-unknown-none-elf"
@@ -34,4 +36,7 @@ targets = [
     "riscv32imac-unknown-none-elf", "riscv64imac-unknown-none-elf",
 ]
 
+[workspace]
+members = ["macros"]
+
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

+ 38 - 0
examples/derive/commons.rs

@@ -0,0 +1,38 @@
+// Mock implementaion module. Actual SBI implementaion should implement
+// those SBI extensions with machine environment specific hardware features.
+
+use rustsbi::{EnvInfo, HartMask};
+use sbi_spec::binary::SbiRet;
+
+pub struct MyFence;
+
+impl rustsbi::Fence for MyFence {
+    fn remote_fence_i(&self, _: HartMask) -> SbiRet {
+        println!("MyFence remote_fence_i function is called!");
+        SbiRet::success(0)
+    }
+
+    fn remote_sfence_vma(&self, _: HartMask, _: usize, _: usize) -> SbiRet {
+        todo!()
+    }
+
+    fn remote_sfence_vma_asid(&self, _: HartMask, _: usize, _: usize, _: usize) -> SbiRet {
+        todo!()
+    }
+}
+
+pub struct MyEnvInfo;
+
+impl EnvInfo for MyEnvInfo {
+    fn mvendorid(&self) -> usize {
+        0x100
+    }
+
+    fn marchid(&self) -> usize {
+        0x200
+    }
+
+    fn mimpid(&self) -> usize {
+        0x300
+    }
+}

+ 63 - 0
examples/derive/main.rs

@@ -0,0 +1,63 @@
+// RustSBI derive example. To derive RustSBI implementation, first we use `RustSBI`
+// derive macro using code `use rustsbi::RustSBI`.
+use rustsbi::RustSBI;
+mod commons;
+use commons::*;
+
+// Now we create a new structure and fill fields into it.
+#[derive(RustSBI)]
+struct MySBI {
+    // We include a SBI RFNC (rustsbi::Fence) extension implementation by including
+    // a struct field. The name `fence` is special; RustSBI derive macro will identify
+    // fence implementation using the variable name. Valid names are listed in RISC-V
+    // SBI specification.
+    // Here we include a mock MyFence implementation; this structure prints to output
+    // then the SBI function `remote_fence_i` is called. Actual code should use any
+    // machine-mode mechanism as a valid RISC-V SBI implementation.
+    fence: MyFence,
+    // Machine information is required by RISC-V SBI specification to provide supervisor
+    // with some method to read `mvendorid`, `marchid` and `mimpid` values from the SBI
+    // environment.
+    // By default RustSBI requires the implementation to declare machine info values
+    // for the environment explicitly, which is suitable for emulators and hypervisors.
+    // For bare metal developers, RustSBI also provides a way to read from machine-mode
+    // CSR accesses; developers should enable RustSBI feature `machine` in this case.
+    // The name `info` is also special, like the name `fence` we have mentioned;
+    // RustSBI identifies machine information from the field name `info`.
+    info: MyEnvInfo,
+}
+
+// We have a properly defined RustSBI implementation called `MySBI`. Now `MySBI`
+// implements Rust trait `rustsbi::RustSBI` with derived code dealing with RISC-V
+// SBI extensions, functions and forward it to all fields of `MySBI` with minimum
+// runtime cost. Let's try to use it!
+
+fn main() {
+    // In main program, create an SBI instance. It's normally located in global storages
+    // like global variables or stack of the main function. As a mock example we define it
+    // as a stack variable for now.
+    let sbi = MySBI {
+        fence: MyFence,
+        info: MyEnvInfo,
+    };
+
+    // In S-mode environment call handler, call the `handle_ecall` of the SBI instance.
+    // We mock this method by providing consts here; actual implementation should fill
+    // `extension`, `function` and `param` from trap context.
+    let ret = sbi.handle_ecall(sbi_spec::rfnc::EID_RFNC, 0, [0; 6]);
+
+    // Finally, fill SBI return value into exception environment and return.
+    // In bare metal: fill `a0` and `a1` register in trap context with `SbiRet` value;
+    // In hypervisor: fill guest supervisor `a0` and `a1` with `SbiRet` value.
+    let _ = ret; // It should be filled into context on real programs.
+
+    // Congratulations! You have learned how to use RustSBI to create your SBI implementaion.
+    // You may consider using the RustSBI Prototyping System, build a standalone
+    // binary package with runtime environment from scratch, or begin with your hypervisor
+    // development.
+
+    // Additionally, we present another mock function suggesting this instance is running
+    // RustSBI by showing that SBI implementation ID equals 4.
+    let ret = sbi.handle_ecall(0x10, 0x1, [0; 6]);
+    println!("SBI implementation ID: {:x?}", ret.value);
+}

+ 18 - 0
macros/Cargo.toml

@@ -0,0 +1,18 @@
+[package]
+name = "rustsbi-macros"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+proc-macro = true
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+quote = "1.0.33"
+syn = "2.0.39"
+
+[features]
+default = []
+# Run RustSBI macros on machine mode - See Cargo.toml in root project
+machine = []

+ 242 - 0
macros/src/lib.rs

@@ -0,0 +1,242 @@
+use proc_macro::TokenStream;
+use quote::{quote, ToTokens};
+use syn::{parse_macro_input, Data, DeriveInput, Generics, Ident, Member};
+
+#[derive(Clone, Default)]
+struct RustSBIImp {
+    fence: Option<Member>,
+    hsm: Option<Member>,
+    ipi: Option<Member>,
+    reset: Option<Member>,
+    timer: Option<Member>,
+    pmu: Option<Member>,
+    console: Option<Member>,
+    susp: Option<Member>,
+    cppc: Option<Member>,
+    nacl: Option<Member>,
+    sta: Option<Member>,
+    env_info: Option<Member>,
+}
+
+/// This macro should be used in `rustsbi` crate as `rustsbi::RustSBI`.
+#[proc_macro_derive(RustSBI, attributes(rustsbi))]
+pub fn derive_rustsbi(input: TokenStream) -> TokenStream {
+    let input = parse_macro_input!(input as DeriveInput);
+
+    let Data::Struct(strukt) = input.data else {
+        panic!("#[derive(RustSBI)] must be used on structs");
+    };
+
+    let mut imp = RustSBIImp::default();
+
+    let mut replace_sbi_extension_ident =
+        |extension_name: &str, member: Member| match extension_name {
+            "rfnc" | "fence" => (true, imp.fence.replace(member)),
+            "hsm" => (true, imp.hsm.replace(member)),
+            "spi" | "ipi" => (true, imp.ipi.replace(member)),
+            "srst" | "reset" => (true, imp.reset.replace(member)),
+            "time" | "timer" => (true, imp.timer.replace(member)),
+            "pmu" => (true, imp.pmu.replace(member)),
+            "dbcn" | "console" => (true, imp.console.replace(member)),
+            "susp" => (true, imp.susp.replace(member)),
+            "cppc" => (true, imp.cppc.replace(member)),
+            "nacl" => (true, imp.nacl.replace(member)),
+            "sta" => (true, imp.sta.replace(member)),
+            "info" | "env_info" => (true, imp.env_info.replace(member)),
+            _ => (false, None),
+        };
+    let mut ans = TokenStream::new();
+    let check_already_exists = |field: &syn::Field,
+                                extension_name: &str,
+                                origin: Option<Member>,
+                                ans: &mut TokenStream| {
+        if let Some(_origin) = origin {
+            // TODO: provide more detailed proc macro error hinting that previous
+            // definition of this extension resides in `origin` once RFC 1566
+            // (Procedural Macro Diagnostics) is stablized.
+            // Link: https://github.com/rust-lang/rust/issues/54140
+            let error = syn::Error::new_spanned(
+                &field,
+                format!(
+                    "more than one field defined SBI extension '{}'. \
+                At most one fields should define the same SBI extension; consider using \
+                #[rustsbi(skip)] to ignore fields that shouldn't be treated as an extension.",
+                    extension_name
+                ),
+            );
+            ans.extend(TokenStream::from(error.to_compile_error()));
+        }
+    };
+
+    for (i, field) in strukt.fields.iter().enumerate() {
+        let member = match &field.ident {
+            Some(ident) => Member::Named(ident.clone()),
+            None => Member::Unnamed(i.into()),
+        };
+        let mut field_already_parsed = false;
+        for attr in &field.attrs {
+            if !attr.path().is_ident("rustsbi") {
+                continue;
+            }
+            let parsed = attr.parse_nested_meta(|meta| {
+                let mut current_meta_accepted = false;
+                if meta.path.is_ident("skip") {
+                    // accept meta but do nothing, effectively skip this field in RustSBI
+                    current_meta_accepted = true;
+                } else if let Some(meta_path_ident) = meta.path.get_ident() {
+                    let extension_name = &meta_path_ident.to_string();
+                    let (replaced, origin) =
+                        replace_sbi_extension_ident(extension_name, member.clone());
+                    if replaced {
+                        check_already_exists(&field, extension_name, origin, &mut ans);
+                        current_meta_accepted = true;
+                    }
+                }
+                if current_meta_accepted {
+                    field_already_parsed = true;
+                    Ok(())
+                } else {
+                    let path = meta.path.to_token_stream().to_string().replace(' ', "");
+                    Err(meta.error(format_args!("unknown RustSBI variant attribute `{}`", path)))
+                }
+            });
+            if let Err(err) = parsed {
+                ans.extend(TokenStream::from(err.to_compile_error()));
+            }
+        }
+        // Already parsed by inner attribute.
+        // Could be either skipped using #[rustsbi(skip)], or renamed using #[rustsbi(some_extension)]
+        if field_already_parsed {
+            continue;
+        }
+        if let Some(field_ident) = &field.ident {
+            let (_replaced, origin) =
+                replace_sbi_extension_ident(field_ident.to_string().as_str(), member);
+            check_already_exists(&field, &field_ident.to_string(), origin, &mut ans);
+        }
+    }
+
+    ans.extend(impl_derive_rustsbi(&input.ident, imp, &input.generics));
+    ans
+}
+
+fn impl_derive_rustsbi(name: &Ident, imp: RustSBIImp, generics: &Generics) -> TokenStream {
+    let base_probe: usize = 1;
+    let fence_probe: usize = if imp.fence.is_some() { 1 } else { 0 };
+    let hsm_probe: usize = if imp.hsm.is_some() { 1 } else { 0 };
+    let ipi_probe: usize = if imp.ipi.is_some() { 1 } else { 0 };
+    let reset_probe: usize = if imp.reset.is_some() { 1 } else { 0 };
+    let timer_probe: usize = if imp.timer.is_some() { 1 } else { 0 };
+    let pmu_probe: usize = if imp.pmu.is_some() { 1 } else { 0 };
+    let console_probe: usize = if imp.console.is_some() { 1 } else { 0 };
+    let susp_probe: usize = if imp.susp.is_some() { 1 } else { 0 };
+    let cppc_probe: usize = if imp.cppc.is_some() { 1 } else { 0 };
+    let nacl_probe: usize = if imp.nacl.is_some() { 1 } else { 0 };
+    let sta_probe: usize = if imp.sta.is_some() { 1 } else { 0 };
+    let probe = quote! {
+        ::rustsbi::_StandardExtensionProbe {
+            base: #base_probe,
+            fence: #fence_probe,
+            hsm: #hsm_probe,
+            ipi: #ipi_probe,
+            reset: #reset_probe,
+            timer: #timer_probe,
+            pmu: #pmu_probe,
+            console: #console_probe,
+            susp: #susp_probe,
+            cppc: #cppc_probe,
+            nacl: #nacl_probe,
+            sta: #sta_probe,
+        }
+    };
+    let mut match_arms = quote! {};
+    let base_procedure = if let Some(env_info) = imp.env_info {
+        quote! {
+            ::rustsbi::spec::base::EID_BASE => ::rustsbi::_rustsbi_base_env_info(param, function, &self.#env_info, #probe),
+        }
+    } else {
+        match () {
+            #[cfg(not(feature = "machine"))]
+            () => quote! {
+                ::rustsbi::spec::base::EID_BASE => compile_error!(
+                    "can't derive RustSBI: #[cfg(feature = \"machine\")] is needed to derive RustSBI with no extra `EnvInfo` provided; \
+            consider adding an `info` parameter to provide machine information implementing `rustsbi::EnvInfo`\
+            if RustSBI is not run on machine mode."
+                ),
+            },
+            #[cfg(feature = "machine")]
+            () => quote! {
+                ::rustsbi::spec::base::EID_BASE => ::rustsbi::_rustsbi_base_bare(param, function, #probe),
+            },
+        }
+    };
+    match_arms.extend(base_procedure);
+    if let Some(fence) = &imp.fence {
+        match_arms.extend(quote! {
+            ::rustsbi::spec::rfnc::EID_RFNC => ::rustsbi::_rustsbi_fence(&self.#fence, param, function),
+        })
+    };
+    if let Some(timer) = &imp.timer {
+        match_arms.extend(quote! {
+            ::rustsbi::spec::time::EID_TIME => ::rustsbi::_rustsbi_timer(&self.#timer, param, function),
+        })
+    };
+    if let Some(ipi) = &imp.ipi {
+        match_arms.extend(quote! {
+            ::rustsbi::spec::spi::EID_SPI => ::rustsbi::_rustsbi_ipi(&self.#ipi, param, function),
+        })
+    }
+    if let Some(hsm) = &imp.hsm {
+        match_arms.extend(quote! {
+            ::rustsbi::spec::hsm::EID_HSM => ::rustsbi::_rustsbi_hsm(&self.#hsm, param, function),
+        })
+    }
+    if let Some(reset) = &imp.reset {
+        match_arms.extend(quote! {
+            ::rustsbi::spec::srst::EID_SRST => ::rustsbi::_rustsbi_reset(&self.#reset, param, function),
+        })
+    }
+    if let Some(pmu) = &imp.pmu {
+        match_arms.extend(quote! {
+            ::rustsbi::spec::pmu::EID_PMU => ::rustsbi::_rustsbi_pmu(&self.#pmu, param, function),
+        })
+    }
+    if let Some(console) = &imp.console {
+        match_arms.extend(quote! {
+            ::rustsbi::spec::dbcn::EID_DBCN => ::rustsbi::_rustsbi_console(&self.#console, param, function),
+        })
+    }
+    if let Some(susp) = &imp.susp {
+        match_arms.extend(quote! {
+            ::rustsbi::spec::susp::EID_SUSP => ::rustsbi::_rustsbi_susp(&self.#susp, param, function),
+        })
+    }
+    if let Some(cppc) = &imp.cppc {
+        match_arms.extend(quote! {
+            ::rustsbi::spec::cppc::EID_CPPC => ::rustsbi::_rustsbi_cppc(&self.#cppc, param, function),
+        })
+    }
+    if let Some(nacl) = &imp.nacl {
+        match_arms.extend(quote! {
+            ::rustsbi::spec::nacl::EID_NACL => ::rustsbi::_rustsbi_nacl(&self.#nacl, param, function),
+        })
+    }
+    if let Some(sta) = &imp.sta {
+        match_arms.extend(quote! {
+            ::rustsbi::spec::sta::EID_STA => ::rustsbi::_rustsbi_sta(&self.#sta, param, function),
+        })
+    }
+    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
+    let gen = quote! {
+    impl #impl_generics ::rustsbi::RustSBI for #name #ty_generics #where_clause {
+        #[inline]
+        fn handle_ecall(&self, extension: usize, function: usize, param: [usize; 6]) -> ::rustsbi::spec::binary::SbiRet {
+            match extension {
+                #match_arms
+                _ => ::rustsbi::spec::binary::SbiRet::not_supported(),
+            }
+        }
+    }
+        };
+    gen.into()
+}

+ 1 - 1
src/console.rs

@@ -1,6 +1,6 @@
 use spec::binary::{Physical, SbiRet};
 
-/// Debug Console Extension
+/// Debug Console extension.
 ///
 /// The debug console extension defines a generic mechanism for debugging
 /// and boot-time early prints from supervisor-mode software.

+ 2 - 2
src/cppc.rs

@@ -1,6 +1,6 @@
 use sbi_spec::binary::SbiRet;
 
-/// CPPC Extension
+/// SBI CPPC support extension.
 ///
 /// ACPI defines the Collaborative Processor Performance Control (CPPC) mechanism,
 /// which is an abstract and flexible mechanism for the supervisor-mode
@@ -46,7 +46,7 @@ use sbi_spec::binary::SbiRet;
 /// | 0x80000000              | TransitionLatency                     | 32        | Read-only    | Provides the maximum (worst-case) performance state transition latency in nanoseconds.
 /// | 0x80000001 - 0xFFFFFFFF |                                       |           |              | Reserved for future use.   
 ///
-pub trait Cppc: Send + Sync {
+pub trait Cppc {
     /// Probe whether the CPPC register as specified by the `reg_id` parameter
     /// is implemented or not by the platform.
     ///

+ 1 - 1
src/hart_mask.rs

@@ -1,4 +1,4 @@
-/// Hart mask structure reference
+/// Hart mask structure in SBI function calls.
 #[derive(Debug, Clone)]
 pub struct HartMask {
     inner: BitVector,

+ 2 - 2
src/hsm.rs

@@ -1,6 +1,6 @@
 use sbi_spec::binary::SbiRet;
 
-/// Hart State Management Extension
+/// Hart State Management extension.
 ///
 /// The Hart State Management (HSM) Extension introduces a set hart states and a set of functions
 /// which allow the supervisor-mode software to request a hart state change.
@@ -42,7 +42,7 @@ use sbi_spec::binary::SbiRet;
 /// - Always prefer most recent suspend state requested for higher topology group
 ///
 /// Ref: [Section 8, RISC-V Supervisor Binary Interface Specification](https://github.com/riscv-non-isa/riscv-sbi-doc/blob/master/riscv-sbi.adoc#8-hart-state-management-extension-eid-0x48534d-hsm)
-pub trait Hsm: Send + Sync {
+pub trait Hsm {
     /// Request the SBI implementation to 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

+ 0 - 844
src/instance.rs

@@ -1,844 +0,0 @@
-use crate::{
-    spec::binary::SbiRet, Console, Cppc, Fence, HartMask, Hsm, Ipi, Pmu, Reset, Susp, Timer,
-    IMPL_ID_RUSTSBI, RUSTSBI_VERSION, SBI_SPEC_MAJOR, SBI_SPEC_MINOR,
-};
-use core::convert::Infallible;
-#[cfg(feature = "machine")]
-use riscv::register::{marchid, mimpid, mvendorid};
-use spec::binary::Physical;
-
-/// RustSBI instance including standard extensions
-///
-/// By now RustSBI supports to run instance based interface on systems has environment pointer width
-/// that is the same as supervisor pointer width.
-#[derive(Clone, Debug)]
-pub struct RustSBI<T, I, R, H, S, P, C, SU, CP> {
-    timer: Option<T>,
-    ipi: Option<I>,
-    rfnc: Option<R>,
-    hsm: Option<H>,
-    srst: Option<S>,
-    pmu: Option<P>,
-    dbcn: Option<C>,
-    susp: Option<SU>,
-    cppc: Option<CP>,
-    #[cfg(not(feature = "machine"))]
-    info: MachineInfo,
-}
-
-/// Machine information for SBI environment
-///
-/// This structure is useful to build an SBI environment when RustSBI is not run directly on RISC-V machine mode.
-#[cfg(not(feature = "machine"))]
-#[derive(Clone, Copy, Debug)]
-pub struct MachineInfo {
-    /// Register `mvendorid` for supervisor environment
-    pub mvendorid: usize,
-    /// Register `marchid` for supervisor environment
-    pub marchid: usize,
-    /// Register `mimpid` for supervisor environment
-    pub mimpid: usize,
-}
-
-impl<T: Timer, I: Ipi, R: Fence, H: Hsm, S: Reset, P: Pmu, C: Console, SU: Susp, CP: Cppc>
-    RustSBI<T, I, R, H, S, P, C, SU, CP>
-{
-    /// Create RustSBI instance on current machine environment for all the SBI extensions
-    #[cfg(feature = "machine")]
-    #[inline]
-    pub const fn new_machine(
-        timer: T,
-        ipi: I,
-        rfnc: R,
-        hsm: H,
-        srst: S,
-        pmu: P,
-        dbcn: C,
-        susp: SU,
-        cppc: CP,
-    ) -> Self {
-        Self {
-            timer: Some(timer),
-            ipi: Some(ipi),
-            rfnc: Some(rfnc),
-            hsm: Some(hsm),
-            srst: Some(srst),
-            pmu: Some(pmu),
-            dbcn: Some(dbcn),
-            susp: Some(susp),
-            cppc: Some(cppc),
-        }
-    }
-
-    /// Create an uninitialized RustSBI instance on current machine environment.
-    #[cfg(feature = "machine")]
-    #[inline]
-    pub const fn new_uninit_machine() -> Self {
-        Self {
-            timer: None,
-            ipi: None,
-            rfnc: None,
-            hsm: None,
-            srst: None,
-            pmu: None,
-            dbcn: None,
-            susp: None,
-            cppc: None,
-        }
-    }
-
-    /// Create RustSBI instance on given machine information for all the SBI extensions
-    #[cfg(not(feature = "machine"))]
-    #[allow(clippy::too_many_arguments)] // fixme: is it possible to have a better design here?
-    #[inline]
-    pub const fn with_machine_info(
-        timer: T,
-        ipi: I,
-        rfnc: R,
-        hsm: H,
-        srst: S,
-        pmu: P,
-        dbcn: C,
-        susp: SU,
-        cppc: CP,
-        info: MachineInfo,
-    ) -> Self {
-        Self {
-            timer: Some(timer),
-            ipi: Some(ipi),
-            rfnc: Some(rfnc),
-            hsm: Some(hsm),
-            srst: Some(srst),
-            pmu: Some(pmu),
-            dbcn: Some(dbcn),
-            susp: Some(susp),
-            cppc: Some(cppc),
-            info,
-        }
-    }
-
-    /// Create an uninitialized RustSBI instance on current machine environment.
-    #[cfg(not(feature = "machine"))]
-    #[inline]
-    pub const fn uninit_with_machine_info(info: MachineInfo) -> Self {
-        Self {
-            timer: None,
-            ipi: None,
-            rfnc: None,
-            hsm: None,
-            srst: None,
-            pmu: None,
-            dbcn: None,
-            susp: None,
-            cppc: None,
-            info,
-        }
-    }
-
-    /// Handle supervisor environment call with given parameters and return the `SbiRet` result.
-    #[inline]
-    pub fn handle_ecall(&self, extension: usize, function: usize, param: [usize; 6]) -> SbiRet {
-        match extension {
-            spec::rfnc::EID_RFNC => {
-                let Some(rfnc) = &self.rfnc else {
-                    return SbiRet::not_supported();
-                };
-                let [param0, param1, param2, param3, param4] =
-                    [param[0], param[1], param[2], param[3], param[4]];
-                let hart_mask = crate::HartMask::from_mask_base(param0, param1);
-                match function {
-                    spec::rfnc::REMOTE_FENCE_I => rfnc.remote_fence_i(hart_mask),
-                    spec::rfnc::REMOTE_SFENCE_VMA => {
-                        rfnc.remote_sfence_vma(hart_mask, param2, param3)
-                    }
-                    spec::rfnc::REMOTE_SFENCE_VMA_ASID => {
-                        rfnc.remote_sfence_vma_asid(hart_mask, param2, param3, param4)
-                    }
-                    spec::rfnc::REMOTE_HFENCE_GVMA_VMID => {
-                        rfnc.remote_hfence_gvma_vmid(hart_mask, param2, param3, param4)
-                    }
-                    spec::rfnc::REMOTE_HFENCE_GVMA => {
-                        rfnc.remote_hfence_gvma(hart_mask, param2, param3)
-                    }
-                    spec::rfnc::REMOTE_HFENCE_VVMA_ASID => {
-                        rfnc.remote_hfence_vvma_asid(hart_mask, param2, param3, param4)
-                    }
-                    spec::rfnc::REMOTE_HFENCE_VVMA => {
-                        rfnc.remote_hfence_vvma(hart_mask, param2, param3)
-                    }
-                    _ => SbiRet::not_supported(),
-                }
-            }
-            spec::time::EID_TIME => match () {
-                #[cfg(target_pointer_width = "64")]
-                () => {
-                    let Some(timer) = &self.timer else {
-                        return SbiRet::not_supported();
-                    };
-                    let [param0] = [param[0]];
-                    match function {
-                        spec::time::SET_TIMER => {
-                            timer.set_timer(param0 as _);
-                            SbiRet::success(0)
-                        }
-                        _ => SbiRet::not_supported(),
-                    }
-                }
-                #[cfg(target_pointer_width = "32")]
-                () => {
-                    let Some(timer) = &self.timer else {
-                        return SbiRet::not_supported();
-                    };
-                    let [param0, param1] = [param[0], param[1]];
-                    match function {
-                        spec::time::SET_TIMER => {
-                            timer.set_timer(concat_u32(param1, param0));
-                            SbiRet::success(0)
-                        }
-                        _ => SbiRet::not_supported(),
-                    }
-                }
-            },
-            spec::spi::EID_SPI => {
-                let Some(ipi) = &self.ipi else {
-                    return SbiRet::not_supported();
-                };
-                let [param0, param1] = [param[0], param[1]];
-                match function {
-                    spec::spi::SEND_IPI => ipi.send_ipi(HartMask::from_mask_base(param0, param1)),
-                    _ => SbiRet::not_supported(),
-                }
-            }
-            spec::base::EID_BASE => {
-                let [param0] = [param[0]];
-                let value = match function {
-                    spec::base::GET_SBI_SPEC_VERSION => (SBI_SPEC_MAJOR << 24) | (SBI_SPEC_MINOR),
-                    spec::base::GET_SBI_IMPL_ID => IMPL_ID_RUSTSBI,
-                    spec::base::GET_SBI_IMPL_VERSION => RUSTSBI_VERSION,
-                    spec::base::PROBE_EXTENSION => {
-                        // only provides probes to standard extensions. If you have customized extensions to be probed,
-                        // run it even before this `handle_ecall` function.
-                        self.probe_extension(param0)
-                    }
-                    spec::base::GET_MVENDORID => match () {
-                        #[cfg(feature = "machine")]
-                        () => mvendorid::read().map(|r| r.bits()).unwrap_or(0),
-                        #[cfg(not(feature = "machine"))]
-                        () => self.info.mvendorid,
-                    },
-                    spec::base::GET_MARCHID => match () {
-                        #[cfg(feature = "machine")]
-                        () => marchid::read().map(|r| r.bits()).unwrap_or(0),
-                        #[cfg(not(feature = "machine"))]
-                        () => self.info.marchid,
-                    },
-                    spec::base::GET_MIMPID => match () {
-                        #[cfg(feature = "machine")]
-                        () => mimpid::read().map(|r| r.bits()).unwrap_or(0),
-                        #[cfg(not(feature = "machine"))]
-                        () => self.info.mimpid,
-                    },
-                    _ => return SbiRet::not_supported(),
-                };
-                SbiRet::success(value)
-            }
-            spec::hsm::EID_HSM => {
-                let Some(hsm) = &self.hsm else {
-                    return SbiRet::not_supported();
-                };
-                let [param0, param1, param2] = [param[0], param[1], param[2]];
-                match function {
-                    spec::hsm::HART_START => hsm.hart_start(param0, param1, param2),
-                    spec::hsm::HART_STOP => hsm.hart_stop(),
-                    spec::hsm::HART_GET_STATUS => hsm.hart_get_status(param0),
-                    spec::hsm::HART_SUSPEND => {
-                        if let Ok(suspend_type) = u32::try_from(param0) {
-                            hsm.hart_suspend(suspend_type, param1, param2)
-                        } else {
-                            SbiRet::invalid_param()
-                        }
-                    }
-                    _ => SbiRet::not_supported(),
-                }
-            }
-            spec::srst::EID_SRST => {
-                let Some(srst) = &self.srst else {
-                    return SbiRet::not_supported();
-                };
-                let [param0, param1] = [param[0], param[1]];
-                match function {
-                    spec::srst::SYSTEM_RESET => {
-                        match (u32::try_from(param0), u32::try_from(param1)) {
-                            (Ok(reset_type), Ok(reset_reason)) => {
-                                srst.system_reset(reset_type, reset_reason)
-                            }
-                            (_, _) => SbiRet::invalid_param(),
-                        }
-                    }
-                    _ => SbiRet::not_supported(),
-                }
-            }
-            spec::pmu::EID_PMU => match () {
-                #[cfg(target_pointer_width = "64")]
-                () => {
-                    let Some(pmu) = &self.pmu else {
-                        return SbiRet::not_supported();
-                    };
-                    let [param0, param1, param2, param3, param4] =
-                        [param[0], param[1], param[2], param[3], param[4]];
-                    match function {
-                        spec::pmu::NUM_COUNTERS => SbiRet::success(pmu.num_counters()),
-                        spec::pmu::COUNTER_GET_INFO => pmu.counter_get_info(param0),
-                        spec::pmu::COUNTER_CONFIG_MATCHING => {
-                            pmu.counter_config_matching(param0, param1, param2, param3, param4 as _)
-                        }
-                        spec::pmu::COUNTER_START => {
-                            pmu.counter_start(param0, param1, param2, param3 as _)
-                        }
-                        spec::pmu::COUNTER_STOP => pmu.counter_stop(param0, param1, param2),
-                        spec::pmu::COUNTER_FW_READ => pmu.counter_fw_read(param0),
-                        spec::pmu::COUNTER_FW_READ_HI => pmu.counter_fw_read_hi(param0),
-                        _ => SbiRet::not_supported(),
-                    }
-                }
-                #[cfg(target_pointer_width = "32")]
-                () => {
-                    let Some(pmu) = &self.pmu else {
-                        return SbiRet::not_supported();
-                    };
-                    let [param0, param1, param2, param3, param4, param5] =
-                        [param[0], param[1], param[2], param[3], param[4], param[5]];
-                    match function {
-                        spec::pmu::NUM_COUNTERS => SbiRet::success(pmu.num_counters()),
-                        spec::pmu::COUNTER_GET_INFO => pmu.counter_get_info(param0),
-                        spec::pmu::COUNTER_CONFIG_MATCHING => pmu.counter_config_matching(
-                            param0,
-                            param1,
-                            param2,
-                            param3,
-                            concat_u32(param5, param4),
-                        ),
-                        spec::pmu::COUNTER_START => {
-                            pmu.counter_start(param0, param1, param2, concat_u32(param4, param3))
-                        }
-                        spec::pmu::COUNTER_STOP => pmu.counter_stop(param0, param1, param2),
-                        spec::pmu::COUNTER_FW_READ => pmu.counter_fw_read(param0),
-                        spec::pmu::COUNTER_FW_READ_HI => pmu.counter_fw_read_hi(param0),
-                        _ => SbiRet::not_supported(),
-                    }
-                }
-            },
-            spec::dbcn::EID_DBCN => {
-                let Some(dbcn) = &self.dbcn else {
-                    return SbiRet::not_supported();
-                };
-                let [param0, param1, param2] = [param[0], param[1], param[2]];
-                match function {
-                    spec::dbcn::CONSOLE_WRITE => {
-                        let bytes = Physical::new(param0, param1, param2);
-                        dbcn.write(bytes)
-                    }
-                    spec::dbcn::CONSOLE_READ => {
-                        let bytes = Physical::new(param0, param1, param2);
-                        dbcn.read(bytes)
-                    }
-                    spec::dbcn::CONSOLE_WRITE_BYTE => dbcn.write_byte((param0 & 0xFF) as u8),
-                    _ => SbiRet::not_supported(),
-                }
-            }
-            spec::susp::EID_SUSP => {
-                let Some(susp) = &self.susp else {
-                    return SbiRet::not_supported();
-                };
-                let [param0, param1, param2] = [param[0], param[1], param[2]];
-                match function {
-                    spec::susp::SUSPEND => match u32::try_from(param0) {
-                        Ok(sleep_type) => susp.system_suspend(sleep_type, param1, param2),
-                        _ => SbiRet::invalid_param(),
-                    },
-                    _ => SbiRet::not_supported(),
-                }
-            }
-            spec::cppc::EID_CPPC => match () {
-                #[cfg(target_pointer_width = "64")]
-                () => {
-                    let Some(cppc) = &self.cppc else {
-                        return SbiRet::not_supported();
-                    };
-                    let [param0, param1] = [param[0], param[1]];
-                    match function {
-                        spec::cppc::PROBE => match u32::try_from(param0) {
-                            Ok(reg_id) => cppc.probe(reg_id),
-                            _ => SbiRet::invalid_param(),
-                        },
-                        spec::cppc::READ => match u32::try_from(param0) {
-                            Ok(reg_id) => cppc.read(reg_id),
-                            _ => SbiRet::invalid_param(),
-                        },
-                        spec::cppc::READ_HI => match u32::try_from(param0) {
-                            Ok(reg_id) => cppc.read_hi(reg_id),
-                            _ => SbiRet::invalid_param(),
-                        },
-                        spec::cppc::WRITE => match u32::try_from(param0) {
-                            Ok(reg_id) => cppc.write(reg_id, param1 as _),
-                            _ => SbiRet::invalid_param(),
-                        },
-                        _ => SbiRet::not_supported(),
-                    }
-                }
-                #[cfg(target_pointer_width = "32")]
-                () => {
-                    let Some(cppc) = &self.cppc else {
-                        return SbiRet::not_supported();
-                    };
-                    let [param0, param1, param2] = [param[0], param[1], param[2]];
-                    match function {
-                        spec::cppc::PROBE => cppc.probe(param0 as _),
-                        spec::cppc::READ => cppc.read(param0 as _),
-                        spec::cppc::READ_HI => cppc.read_hi(param0 as _),
-                        spec::cppc::WRITE => cppc.write(param0 as _, concat_u32(param2, param1)),
-                        _ => SbiRet::not_supported(),
-                    }
-                }
-            },
-            _ => SbiRet::not_supported(),
-        }
-    }
-
-    #[inline]
-    fn probe_extension(&self, extension: usize) -> usize {
-        let ans = match extension {
-            spec::base::EID_BASE => true,
-            spec::time::EID_TIME => self.timer.is_some(),
-            spec::spi::EID_SPI => self.ipi.is_some(),
-            spec::rfnc::EID_RFNC => self.rfnc.is_some(),
-            spec::srst::EID_SRST => self.srst.is_some(),
-            spec::hsm::EID_HSM => self.hsm.is_some(),
-            spec::pmu::EID_PMU => self.pmu.is_some(),
-            spec::dbcn::EID_DBCN => self.dbcn.is_some(),
-            spec::susp::EID_SUSP => self.susp.is_some(),
-            spec::cppc::EID_CPPC => self.cppc.is_some(),
-            _ => false,
-        };
-        if ans {
-            spec::base::UNAVAILABLE_EXTENSION.wrapping_add(1)
-        } else {
-            spec::base::UNAVAILABLE_EXTENSION
-        }
-    }
-}
-
-#[cfg(target_pointer_width = "32")]
-#[inline]
-const fn concat_u32(h: usize, l: usize) -> u64 {
-    ((h as u64) << 32) | (l as u64)
-}
-
-/// Structure to build a RustSBI instance
-pub struct Builder<T, I, R, H, S, P, C, SU, CP> {
-    inner: RustSBI<T, I, R, H, S, P, C, SU, CP>,
-}
-
-impl
-    Builder<
-        Infallible,
-        Infallible,
-        Infallible,
-        Infallible,
-        Infallible,
-        Infallible,
-        Infallible,
-        Infallible,
-        Infallible,
-    >
-{
-    /// Create a new `Builder` from current machine environment
-    #[inline]
-    #[cfg(feature = "machine")]
-    pub const fn new_machine() -> Builder<
-        Infallible,
-        Infallible,
-        Infallible,
-        Infallible,
-        Infallible,
-        Infallible,
-        Infallible,
-        Infallible,
-        Infallible,
-    > {
-        Builder {
-            inner: RustSBI {
-                timer: None,
-                ipi: None,
-                rfnc: None,
-                hsm: None,
-                srst: None,
-                pmu: None,
-                dbcn: None,
-                susp: None,
-                cppc: None,
-            },
-        }
-    }
-
-    /// Create a new `Builder` from machine information
-    #[inline]
-    #[cfg(not(feature = "machine"))]
-    pub const fn with_machine_info(
-        info: MachineInfo,
-    ) -> Builder<
-        Infallible,
-        Infallible,
-        Infallible,
-        Infallible,
-        Infallible,
-        Infallible,
-        Infallible,
-        Infallible,
-        Infallible,
-    > {
-        Builder {
-            inner: RustSBI {
-                timer: None,
-                ipi: None,
-                rfnc: None,
-                hsm: None,
-                srst: None,
-                pmu: None,
-                dbcn: None,
-                susp: None,
-                cppc: None,
-                info,
-            },
-        }
-    }
-}
-
-// fixme: in future releases we may use type-changing struct update syntax like:
-// Builder { inner: RustSBI { timer: None, ..self.inner } }
-// https://github.com/rust-lang/rust/issues/86555
-
-// fixme: struct `Infallible` should be replaced to never type once it's stablized
-
-impl<T, I, R, H, S, P, C, SU, CP> Builder<T, I, R, H, S, P, C, SU, CP> {
-    /// Add Timer programmer extension to RustSBI
-    #[inline]
-    pub fn with_timer<T2: Timer>(self, timer: T2) -> Builder<T2, I, R, H, S, P, C, SU, CP> {
-        Builder {
-            inner: RustSBI {
-                timer: Some(timer),
-                ipi: self.inner.ipi,
-                rfnc: self.inner.rfnc,
-                hsm: self.inner.hsm,
-                srst: self.inner.srst,
-                pmu: self.inner.pmu,
-                dbcn: self.inner.dbcn,
-                susp: self.inner.susp,
-                cppc: self.inner.cppc,
-                #[cfg(not(feature = "machine"))]
-                info: self.inner.info,
-            },
-        }
-    }
-
-    /// Add Inter-processor Interrupt extension to RustSBI
-    #[inline]
-    pub fn with_ipi<I2: Ipi>(self, ipi: I2) -> Builder<T, I2, R, H, S, P, C, SU, CP> {
-        Builder {
-            inner: RustSBI {
-                timer: self.inner.timer,
-                ipi: Some(ipi),
-                rfnc: self.inner.rfnc,
-                hsm: self.inner.hsm,
-                srst: self.inner.srst,
-                pmu: self.inner.pmu,
-                dbcn: self.inner.dbcn,
-                susp: self.inner.susp,
-                cppc: self.inner.cppc,
-                #[cfg(not(feature = "machine"))]
-                info: self.inner.info,
-            },
-        }
-    }
-
-    /// Add Remote Fence extension to RustSBI
-    #[inline]
-    pub fn with_fence<R2: Fence>(self, fence: R2) -> Builder<T, I, R2, H, S, P, C, SU, CP> {
-        Builder {
-            inner: RustSBI {
-                timer: self.inner.timer,
-                ipi: self.inner.ipi,
-                rfnc: Some(fence),
-                hsm: self.inner.hsm,
-                srst: self.inner.srst,
-                pmu: self.inner.pmu,
-                dbcn: self.inner.dbcn,
-                susp: self.inner.susp,
-                cppc: self.inner.cppc,
-                #[cfg(not(feature = "machine"))]
-                info: self.inner.info,
-            },
-        }
-    }
-
-    /// Add Hart State Monitor extension to RustSBI
-    #[inline]
-    pub fn with_hsm<H2: Hsm>(self, hsm: H2) -> Builder<T, I, R, H2, S, P, C, SU, CP> {
-        Builder {
-            inner: RustSBI {
-                timer: self.inner.timer,
-                ipi: self.inner.ipi,
-                rfnc: self.inner.rfnc,
-                hsm: Some(hsm),
-                srst: self.inner.srst,
-                pmu: self.inner.pmu,
-                dbcn: self.inner.dbcn,
-                susp: self.inner.susp,
-                cppc: self.inner.cppc,
-                #[cfg(not(feature = "machine"))]
-                info: self.inner.info,
-            },
-        }
-    }
-
-    /// Add System Reset extension to RustSBI
-    #[inline]
-    pub fn with_reset<S2: Reset>(self, reset: S2) -> Builder<T, I, R, H, S2, P, C, SU, CP> {
-        Builder {
-            inner: RustSBI {
-                timer: self.inner.timer,
-                ipi: self.inner.ipi,
-                rfnc: self.inner.rfnc,
-                hsm: self.inner.hsm,
-                srst: Some(reset),
-                pmu: self.inner.pmu,
-                dbcn: self.inner.dbcn,
-                susp: self.inner.susp,
-                cppc: self.inner.cppc,
-                #[cfg(not(feature = "machine"))]
-                info: self.inner.info,
-            },
-        }
-    }
-
-    /// Add Performance Monitor Unit extension to RustSBI
-    #[inline]
-    pub fn with_pmu<P2: Pmu>(self, pmu: P2) -> Builder<T, I, R, H, S, P2, C, SU, CP> {
-        Builder {
-            inner: RustSBI {
-                timer: self.inner.timer,
-                ipi: self.inner.ipi,
-                rfnc: self.inner.rfnc,
-                hsm: self.inner.hsm,
-                srst: self.inner.srst,
-                pmu: Some(pmu),
-                dbcn: self.inner.dbcn,
-                susp: self.inner.susp,
-                cppc: self.inner.cppc,
-                #[cfg(not(feature = "machine"))]
-                info: self.inner.info,
-            },
-        }
-    }
-    /// Add Debug Console extension to RustSBI
-    #[inline]
-    pub fn with_console<C2: Console>(self, console: C2) -> Builder<T, I, R, H, S, P, C2, SU, CP> {
-        Builder {
-            inner: RustSBI {
-                timer: self.inner.timer,
-                ipi: self.inner.ipi,
-                rfnc: self.inner.rfnc,
-                hsm: self.inner.hsm,
-                srst: self.inner.srst,
-                pmu: self.inner.pmu,
-                dbcn: Some(console),
-                susp: self.inner.susp,
-                cppc: self.inner.cppc,
-                #[cfg(not(feature = "machine"))]
-                info: self.inner.info,
-            },
-        }
-    }
-    /// Add System Suspend extension to RustSBI
-    #[inline]
-    pub fn with_susp<SU2: Susp>(self, susp: SU2) -> Builder<T, I, R, H, S, P, C, SU2, CP> {
-        Builder {
-            inner: RustSBI {
-                timer: self.inner.timer,
-                ipi: self.inner.ipi,
-                rfnc: self.inner.rfnc,
-                hsm: self.inner.hsm,
-                srst: self.inner.srst,
-                pmu: self.inner.pmu,
-                dbcn: self.inner.dbcn,
-                susp: Some(susp),
-                cppc: self.inner.cppc,
-                #[cfg(not(feature = "machine"))]
-                info: self.inner.info,
-            },
-        }
-    }
-    /// Add CPPC extension to RustSBI
-    #[inline]
-    pub fn with_cppc<CP2: Cppc>(self, cppc: CP2) -> Builder<T, I, R, H, S, P, C, SU, CP2> {
-        Builder {
-            inner: RustSBI {
-                timer: self.inner.timer,
-                ipi: self.inner.ipi,
-                rfnc: self.inner.rfnc,
-                hsm: self.inner.hsm,
-                srst: self.inner.srst,
-                pmu: self.inner.pmu,
-                dbcn: self.inner.dbcn,
-                susp: self.inner.susp,
-                cppc: Some(cppc),
-                #[cfg(not(feature = "machine"))]
-                info: self.inner.info,
-            },
-        }
-    }
-
-    /// Build the target RustSBI instance
-    #[inline]
-    pub fn build(self) -> RustSBI<T, I, R, H, S, P, C, SU, CP> {
-        self.inner
-    }
-}
-
-// Placeholder for a structure that implements all RustSBI traits but is never accessed
-
-// fixme: Should be replaced to never type `!` once it's stablized
-// https://github.com/rust-lang/rust/issues/35121
-
-// fixme: should be replaced to impl SomeTrait for ! once never type is stablized
-
-impl Timer for Infallible {
-    fn set_timer(&self, _: u64) {
-        unreachable!()
-    }
-}
-
-impl Ipi for Infallible {
-    fn send_ipi(&self, _: HartMask) -> SbiRet {
-        unreachable!()
-    }
-}
-
-impl Fence for Infallible {
-    fn remote_fence_i(&self, _: HartMask) -> SbiRet {
-        unreachable!()
-    }
-
-    fn remote_sfence_vma(&self, _: HartMask, _: usize, _: usize) -> SbiRet {
-        unreachable!()
-    }
-
-    fn remote_sfence_vma_asid(&self, _: HartMask, _: usize, _: usize, _: usize) -> SbiRet {
-        unreachable!()
-    }
-
-    fn remote_hfence_gvma_vmid(&self, _: HartMask, _: usize, _: usize, _: usize) -> SbiRet {
-        unreachable!()
-    }
-
-    fn remote_hfence_gvma(&self, _: HartMask, _: usize, _: usize) -> SbiRet {
-        unreachable!()
-    }
-
-    fn remote_hfence_vvma_asid(&self, _: HartMask, _: usize, _: usize, _: usize) -> SbiRet {
-        unreachable!()
-    }
-
-    fn remote_hfence_vvma(&self, _: HartMask, _: usize, _: usize) -> SbiRet {
-        unreachable!()
-    }
-}
-
-impl Hsm for Infallible {
-    fn hart_start(&self, _: usize, _: usize, _: usize) -> SbiRet {
-        unreachable!()
-    }
-
-    fn hart_stop(&self) -> SbiRet {
-        unreachable!()
-    }
-
-    fn hart_get_status(&self, _: usize) -> SbiRet {
-        unreachable!()
-    }
-
-    fn hart_suspend(&self, _: u32, _: usize, _: usize) -> SbiRet {
-        unreachable!()
-    }
-}
-
-impl Reset for Infallible {
-    fn system_reset(&self, _: u32, _: u32) -> SbiRet {
-        unreachable!()
-    }
-}
-
-impl Pmu for Infallible {
-    fn num_counters(&self) -> usize {
-        unreachable!()
-    }
-
-    fn counter_get_info(&self, _: usize) -> SbiRet {
-        unreachable!()
-    }
-
-    fn counter_config_matching(&self, _: usize, _: usize, _: usize, _: usize, _: u64) -> SbiRet {
-        unreachable!()
-    }
-
-    fn counter_start(&self, _: usize, _: usize, _: usize, _: u64) -> SbiRet {
-        unreachable!()
-    }
-
-    fn counter_stop(&self, _: usize, _: usize, _: usize) -> SbiRet {
-        unreachable!()
-    }
-
-    fn counter_fw_read(&self, _: usize) -> SbiRet {
-        unreachable!()
-    }
-
-    fn counter_fw_read_hi(&self, _: usize) -> SbiRet {
-        unreachable!()
-    }
-}
-
-impl Console for Infallible {
-    fn write(&self, _: Physical<&[u8]>) -> SbiRet {
-        unreachable!()
-    }
-
-    fn read(&self, _: Physical<&mut [u8]>) -> SbiRet {
-        unreachable!()
-    }
-
-    fn write_byte(&self, _: u8) -> SbiRet {
-        unreachable!()
-    }
-}
-
-impl Susp for Infallible {
-    fn system_suspend(&self, _: u32, _: usize, _: usize) -> SbiRet {
-        unreachable!()
-    }
-}
-
-impl Cppc for Infallible {
-    fn probe(&self, _: u32) -> SbiRet {
-        unreachable!()
-    }
-    fn read(&self, _: u32) -> SbiRet {
-        unreachable!()
-    }
-    fn read_hi(&self, _: u32) -> SbiRet {
-        unreachable!()
-    }
-    fn write(&self, _: u32, _: u64) -> SbiRet {
-        unreachable!()
-    }
-}

+ 2 - 2
src/ipi.rs

@@ -1,8 +1,8 @@
 use crate::hart_mask::HartMask;
 use sbi_spec::binary::SbiRet;
 
-/// Inter-processor interrupt support
-pub trait Ipi: Send + Sync {
+/// Inter-processor interrupt support.
+pub trait Ipi {
     /// Send an inter-processor interrupt to all the harts defined in `hart_mask`.
     ///
     /// Inter-processor interrupts manifest at the receiving harts as the supervisor software interrupts.

+ 636 - 134
src/lib.rs

@@ -4,65 +4,78 @@
 //! using the [RustSBI Prototyping System](https://github.com/rustsbi/standalone)
 //! which will provide binaries for each platforms.
 //! If you are a vendor or contributor who wants to adapt RustSBI to your new product or board,
-//! you may consider adapting the Prototyping System first to get your board adapted in an afternoon;
-//! you are only advised to build a discrete crate if your team have a lot of time working on this board.*
+//! you may consider adapting the Prototyping System first to get your board adapted in a short period of time;
+//! or build a discrete crate if your team have a plenty of time working on this board.*
 //!
 //! *For more details on binary downloads the the RustSBI Prototyping System,
 //! see section [Prototyping System vs discrete packages](#download-binary-file-the-prototyping-system-vs-discrete-packages).*
 //!
-//! The crate `rustsbi` acts as core trait and instance abstraction of the RustSBI ecosystem.
+//! The crate `rustsbi` acts as core trait, extension abstraction and implementation generator
+//! of the RustSBI ecosystem.
 //!
 //! # What is RISC-V SBI?
 //!
 //! RISC-V SBI is short for RISC-V Supervisor Binary Interface. SBI acts as an interface to environment
-//! for your operating system kernel.
-//! An SBI implementation will allow furtherly bootstrap your kernel, and provide an environment while the kernel is running.
+//! for the operating system kernel.
+//! An SBI implementation will allow furtherly bootstrap the kernel, and provide a supportive environment
+//! while the kernel is running.
 //!
 //! More generally, The SBI allows supervisor-mode (S-mode or VS-mode) software to be portable across
 //! all RISC-V implementations by defining an abstraction for platform (or hypervisor) specific functionality.
 //!
-//! # Use RustSBI services in your supervisor software
+//! # Use RustSBI services in the supervisor software
 //!
-//! SBI environment features include boot sequence and a kernel environment. To bootstrap your kernel,
-//! place kernel into RustSBI implementation defined address, then RustSBI will prepare an
-//! environment and call the entry function on this address.
+//! SBI environment features include boot sequence and an S-mode environment. To bootstrap the
+//! S-mode software, the kernel (or other supervisor-lenel software) would be loaded
+//! into an implementation defined address, then RustSBI will prepare an environment and
+//! enter the S-mode software on the S-mode visible harts. If the firmware environment provides
+//! other bootloading standards upon SBI, following bootstrap process will provide further
+//! information to the supervisor software.
 //!
 //! ## Make SBI environment calls
 //!
-//! To use the kernel environment, you either use SBI calls or emulated instructions.
-//! SBI calls are similar to operating systems' `syscall`s. RISC-V SBI defined many SBI extensions,
-//! and in each extension there are different functions, you should pick a function before calling.
-//! Then, you should prepare some parameters, whose definition are not the same among functions.
+//! To use the underlying environment, the supervisor either use SBI calls or run software
+//! implemented instructions.
+//! SBI calls are similar to the system calls for operating systems. The SBI extensions, whether
+//! defined by the RISC-V SBI Specification or by custom vendors, would either consume parameters
+//! only, or defined a list of functions identified by function IDs, where the S-mode software
+//! would pick and call. Definition of parameters varies between extensions and functions.
 //!
-//! Now you have an extension number, a function number, and a few SBI call parameters.
-//! You invoke a special `ecall` instruction on supervisor level, and it will trap into machine level
-//! SBI implementation. It will handle your `ecall`, similar to your kernel handling system calls
-//! from user level.
+//! At this point, we have picked up an extension ID, a function ID, and a few SBI call parameters.
+//! Now instead of a conventinoal jump instruction, the software would invoke a special `ecall`
+//! instruction on supervisor level to transfer the control flow, resulting into a trap to the SBI
+//! environment. The SBI environment will process the `ecall` and fill in SBI call results,
+//! similar to what an operating system would handle system calls from user level.
 //!
-//! SBI functions return two values other than one. First value will be an error number,
-//! it will tell if SBI call have succeeded, or which error have occurred.
-//! Second value is the real return value, its meaning is different according to which function you calls.
+//! All SBI calls would return two integers: the error number and the return value.
+//! The error number will tell if the SBI call have been successfully proceeded, or which error
+//! have occurred. The return value indicates the result of a successful SBI call, whose
+//! meaning being different among different SBI extensions.
 //!
-//! ## Call SBI in different programming languages
+//! ## Call SBI in Rust or other programming languages
 //!
-//! Making SBI calls are similar to making system calls.
+//! Making SBI calls are similar to making system calls; RISC-V SBI calls pass extension ID,
+//! function ID (if applicable) and parameters in integer registers.
 //!
-//! Extension number is required to put on register `a7`, function number on `a6` if applicable.
+//! The extension ID is required to put on register `a7`, function ID on `a6` if applicable.
 //! Parameters should be placed from `a0` to `a5`, first into `a0`, second into `a1`, etc.
 //! Unused parameters can be set to any value or leave untouched.
 //!
-//! After registers are ready, invoke an instruction called `ecall`.
-//! Then, the return value is placed into `a0` and `a1` registers.
-//! The error value could be read from `a0`, and return value is placed into `a1`.
+//! After registers are ready, the S-mode software would invoke an `ecall` instruction.
+//! The SBI call will return two values placed in `a0` and `a1` registers;
+//! the error value could be read from `a0`, and return value is placed into `a1`.
 //!
-//! In Rust, here is an example to call SBI functions using inline assembly:
+//! In Rust, we would usually use crates like [`sbi-rt`](https://crates.io/crates/sbi-rt)
+//! to hide implementation details and focus on supervisor software development.
+//! However, if in some cases we have to write them in inline assembly, here is an example
+//! to do this:
 //!
 //! ```no_run
 //! # #[repr(C)] struct SbiRet { error: usize, value: usize }
 //! # const EXTENSION_BASE: usize = 0x10;
 //! # const FUNCTION_BASE_GET_SPEC_VERSION: usize = 0x0;
 //! #[inline(always)]
-//! fn sbi_call(extension: usize, function: usize, arg0: usize, arg1: usize) -> SbiRet {
+//! fn sbi_call_2(extension: usize, function: usize, arg0: usize, arg1: usize) -> SbiRet {
 //!     let (error, value);
 //!     match () {
 //!         #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
@@ -83,37 +96,16 @@
 //!
 //! #[inline]
 //! pub fn get_spec_version() -> SbiRet {
-//!     sbi_call(EXTENSION_BASE, FUNCTION_BASE_GET_SPEC_VERSION, 0, 0)
+//!     sbi_call_2(EXTENSION_BASE, FUNCTION_BASE_GET_SPEC_VERSION, 0, 0)
 //! }
 //! ```
 //!
-//! SBI functions would return a result thus some of these may fail.
-//! In this example we only take the value, but in complete designs we should handle the `error`
-//! returned by SbiRet.
-//!
-//! You may use other languages to call SBI environment. In C programming language, we can call like this:
-//!
-//! ```text
-//! #define SBI_CALL(ext, funct, arg0, arg1, arg2, arg3) ({ \
-//!     register uintptr_t a0 asm ("a0") = (uintptr_t)(arg0); \
-//!     register uintptr_t a1 asm ("a1") = (uintptr_t)(arg1); \
-//!     register uintptr_t a2 asm ("a2") = (uintptr_t)(arg2); \
-//!     register uintptr_t a3 asm ("a3") = (uintptr_t)(arg3); \
-//!     register uintptr_t a6 asm ("a6") = (uintptr_t)(funct); \
-//!     register uintptr_t a7 asm ("a7") = (uintptr_t)(ext); \
-//!     asm volatile ("ecall" \
-//!         : "+r" (a0), "+r" (a1) \
-//!         : "r" (a1), "r" (a2), "r" (a3), "r" (a6), "r" (a7) \
-//!         : "memory") \
-//!     {a0, a1}; \
-//! })
-//!
-//! #define SBI_CALL_0(ext, funct) SBI_CALL(ext, funct, 0, 0, 0, 0)
-//!
-//! static inline sbiret get_spec_version() {
-//!     SBI_CALL_0(EXTENSION_BASE, FUNCTION_BASE_GET_SPEC_VERSION)
-//! }
-//! ```
+//! SBI calls may fail, returning the corresponding type of error in an error code.
+//! In this example we only take the value, but in complete designs we should handle
+//! the `error` code returned by SbiRet thoroughly.
+//!
+//! In other programming languages, similiar methods may be achieved by inline assembly
+//! or other features; its documentation may suggest which is the best way to achieve this.
 //!
 //! # Implement RustSBI on machine environment
 //!
@@ -125,26 +117,27 @@
 //!
 //! Hypervisor and supervisor environment emulator developers may refer to
 //! [Hypervisor and emulator development with RustSBI](#hypervisor-and-emulator-development-with-rustsbi)
-//! for such purposes as RustSBI provide different set of features dedicated for emulated or virtual
+//! for such purposes, as RustSBI provide different set of features dedicated for emulated or virtual
 //! environments.
 //!
 //! ## Use the Prototyping System
 //!
-//! The RustSBI Prototyping System aims to get your platform working with SBI in an afternoon.
+//! The RustSBI Prototyping System aims to get your platform working with SBI in a short period of time.
 //! It supports most RISC-V platforms available by providing scalable set of drivers and features.
-//! It provides custom features such as Penglai TEE, DramForever's emulated hypervisor extension, and Raven
-//! the firmware debugger framework.
+//! It provides useful custom features such as Penglai TEE, DramForever's emulated hypervisor extension,
+//! and Raven the firmware debugger framework.
 //!
 //! You may find further documents on [RustSBI Prototyping System repository](https://github.com/rustsbi/standalone).
 //!
 //! ## Discrete RustSBI package on bare metal RISC-V hardware
 //!
 //! Discrete packages provide developers with most scalability and complete control of underlying
-//! hardware. It is ideal if advanced low power features, management cores and other features should
-//! be used in this implementation.
+//! hardware. It is ideal if detailed SoC low power features, management cores and other features
+//! would be used in the SBI implementation.
 //!
-//! RustSBI supports discrete package by default. Create a new `#![no_std]` bare-metal package
-//! to get started. Add following lines to `Cargo.toml`:
+//! RustSBI supports discrete package development out-of-box. If we are running on bare-metal, we
+//! can create a new `#![no_std]` bare-metal package, add runtime code or use runtime libraries
+//! to get started. Then, we add following lines to `Cargo.toml`:
 //!
 //! ```toml
 //! [dependencies]
@@ -152,23 +145,15 @@
 //! ```
 //!
 //! The feature `machine` indicates that RustSBI library is run directly on machine mode RISC-V
-//! environment; it will use `riscv` crate to fetch machine mode environment, which fits our demand
-//! of using it on bare metal RISC-V.
+//! environment; it will use the `riscv` crate to fetch machine mode environment information by CSR
+//! instructions, which fits our demand of using it on bare metal RISC-V.
 //!
-//! After hardware initialization process, the part of firmware with RustSBI linked should run on memory
-//! blocks with fast accesses, as it would be called frequently by operating system.
-//! If the supervisor is called by trap generator semantics, insert `rustsbi::RustSBI` structure
-//! in your hart executor structure.
+//! After hardware initialization process, the part of firmware with RustSBI linked should run on
+//! memory blocks with fast accesses, as it would be called frequently by operating system.
+//! If the implementation treats the supervisor as a generator of traps, we insert `rustsbi::RustSBI`
+//! implementation in a hart executor structure.
 //!
 //! ```rust
-//! # struct Clint;
-//! # struct MyPlatRfnc;
-//! # struct MyPlatHsm;
-//! # struct MyBoardPower;
-//! # struct MyPlatPmu;
-//! # struct MyPlatDbcn;
-//! # struct MyPlatSusp;
-//! # struct MyPlatCppc;
 //! use rustsbi::RustSBI;
 //!
 //! # struct SupervisorContext;
@@ -176,8 +161,15 @@
 //! struct Executor {
 //!     ctx: SupervisorContext,
 //!     /* other environment variables ... */
-//!     sbi: RustSBI<Clint, Clint, MyPlatRfnc, MyPlatHsm, MyBoardPower, MyPlatPmu, MyPlatDbcn, MyPlatSusp, MyPlatCppc>,
-//!     /* custom_1: CustomSBI<...> */
+//!     sbi: MySBI,
+//!     /* custom_1: CustomSBI, ... */
+//! }
+//!
+//! #[derive(RustSBI)]
+//! struct MySBI {
+//!     console: MyConsole,
+//!     // todo: other extensions ...
+//!     info: MyEnvInfo,
 //! }
 //!
 //! # struct Trap;
@@ -192,25 +184,46 @@
 //!         todo!("fill in generic or platform specific trampoline procedure")
 //!     }
 //! }
+//! # use sbi_spec::binary::{SbiRet, Physical};
+//! # struct MyConsole;
+//! # impl rustsbi::Console for MyConsole {
+//! #     fn write(&self, _: Physical<&[u8]>) -> SbiRet { unimplemented!() }
+//! #     fn read(&self, _: Physical<&mut [u8]>) -> SbiRet { unimplemented!() }
+//! #     fn write_byte(&self, _: u8) -> SbiRet { unimplemented!() }
+//! # }
+//! # struct MyEnvInfo;
+//! # impl rustsbi::EnvInfo for MyEnvInfo {
+//! #     fn mvendorid(&self) -> usize { 1 }
+//! #     fn marchid(&self) -> usize { 2 }
+//! #     fn mimpid(&self) -> usize { 3 }
+//! # }
 //! ```
 //!
-//! After each `run()`, process the trap returned with the RustSBI instance in executor.
-//! Call `RustSBI::handle_ecall` and fill in developer provided `SupervisorContext` if necessary.
+//! After each `run()`, the SBI implmenetaion would process the trap returned with the RustSBI
+//! instance in executor. Call the function `handle_ecall` (generated by derive macro `RustSBI`)
+//! and fill in a `SupervisorContext` if necessary.
 //!
 //! ```no_run
+//! # use rustsbi::RustSBI;
 //! # use sbi_spec::binary::SbiRet;
-//! # struct RustSBI {} // Mock, prevent doc test error when feature singleton is enabled
-//! # impl RustSBI { fn handle_ecall(&self, e: (), f: (), p: ()) -> SbiRet { SbiRet::success(0) } }
-//! # struct Executor { sbi: RustSBI }
+//! # struct MyEnvInfo;
+//! # impl rustsbi::EnvInfo for MyEnvInfo {
+//! #     fn mvendorid(&self) -> usize { 1 }
+//! #     fn marchid(&self) -> usize { 2 }
+//! #     fn mimpid(&self) -> usize { 3 }
+//! # }
+//! # #[derive(RustSBI)]
+//! # struct MySBI { info: MyEnvInfo } // extensions ...
+//! # struct Executor { sbi: MySBI }
 //! # #[derive(Copy, Clone)] enum Trap { Exception(Exception) }
 //! # impl Trap { fn cause(&self) -> Self { *self } }
 //! # #[derive(Copy, Clone)] enum Exception { SupervisorEcall }
 //! # impl Executor {
-//! #     fn new(board_params: BoardParams) -> Executor { let _ = board_params; Executor { sbi: RustSBI {} } }
+//! #     fn new(board_params: BoardParams) -> Executor { let _ = board_params; Executor { sbi: MySBI { info: MyEnvInfo } } }
 //! #     fn run(&mut self) -> Trap { Trap::Exception(Exception::SupervisorEcall) }
-//! #     fn sbi_extension(&self) -> () { }
-//! #     fn sbi_function(&self) -> () { }
-//! #     fn sbi_params(&self) -> () { }
+//! #     fn sbi_extension(&self) -> usize { unimplemented!() }
+//! #     fn sbi_function(&self) -> usize { unimplemented!() }
+//! #     fn sbi_params(&self) -> [usize; 6] { unimplemented!() }
 //! #     fn fill_sbi_return(&mut self, ans: SbiRet) { let _ = ans; }
 //! # }
 //! # struct BoardParams;
@@ -246,7 +259,8 @@
 //! ```
 //!
 //! Now, call supervisor execution function in your bare metal package to finish the discrete
-//! package project.
+//! package project. Here is an example of a bare-metal entry; actual projects would either
+//! use a library for runtime, or write assemble code only if necessary.
 //!
 //! ```no_run
 //! # #[cfg(nightly)] // disable checks
@@ -298,7 +312,7 @@
 //! # fn board_init_once() {}
 //! # fn print_information_once() {}
 //! # fn execute_supervisor(_bp: &()) -> Operation { Operation::Shutdown }
-//! /// Power operation after main function
+//! /// Power operation after main function.
 //! enum Operation {
 //!     Reboot,
 //!     Shutdown,
@@ -317,7 +331,7 @@
 //! }
 //!
 //! # fn wfi() {}
-//! /// Perform board specific power operations
+//! /// Perform board specific power operations.
 //! ///
 //! /// The function here provides a stub to example power operations.
 //! /// Actual board developers should provide with more practical communications
@@ -338,7 +352,7 @@
 //! }
 //! ```
 //!
-//! Now RustSBI would run on machine environment, you may start a kernel or use an SBI test suite
+//! Now RustSBI would run on machine environment, a kernel may be started or use an SBI test suite
 //! to check if it is properly implemented.
 //!
 //! Some platforms would provide system memory under different grades in speed and size to reduce product cost.
@@ -346,7 +360,7 @@
 //! but instantly available after chip start, while the second one is larger in size but typically requires
 //! memory training. The former one would include built-in SRAM memory, and the later would include
 //! external SRAM or DDR memory. On those platforms, a first stage bootloader is typically needed to
-//! train memory for later stages. In such situation, RustSBI implementation should be linked or concatenated
+//! train memory for later stages. In such situation, RustSBI implementation should be treated as or concatenated
 //! to the second stage bootloader, and the first stage could be a standalone binary package bundled with it.
 //!
 //! # Hypervisor and emulator development with RustSBI
@@ -362,14 +376,17 @@
 //! or provide another set of information to override the current environment. Notably,
 //! RISC-V hypervisors do not have direct access to machine mode (M-mode) registers.
 //!
-//! RustSBI supports both by providing a `MachineInfo` structure in instance based interface.
-//! If RISC-V hypervisors choose to use existing information on current machine, it may require
-//! to call underlying M-mode environment using SBI calls and fill in information into `MachineInfo`.
+//! RustSBI supports both by accepting an implementation of the `EnvInfo` trait.
+//! If RISC-V hypervisors choose to use existing information on current machine,
+//! it may require to call underlying M-mode environment using SBI calls and fill in information
+//! into the variable implementing trait `EnvInfo`.
 //! If hypervisors use customized information other than taking the same one from the
-//! environment they reside in, they may fill in custom one into `MachineInfo` structures.
-//! When creating RustSBI instance, `MachineInfo` structure is required as an input of constructor.
+//! environment they reside in, they may build custom structures implementing `EnvInfo` to provide
+//! customized machine information.
+//! Deriving a RustSBI instance without bare-metal support would require an `EnvInfo` implementation
+//! as a input of the derive macro.
 //!
-//! To begin with, include RustSBI library in file `Cargo.toml`:
+//! To begin with, include the RustSBI library in file `Cargo.toml`:
 //!
 //! ```toml
 //! [dependencies]
@@ -377,21 +394,37 @@
 //! ```
 //!
 //! This will disable default feature `machine` which will assume that RustSBI runs on M-mode directly,
-//! which is not appropriate in our purpose. After that, a `RustSBI` instance may be placed
-//! in the virtual machine structure to prepare for SBI environment:
+//! which is not appropriate in our purpose. After that, define an SBI structure and derive its `RustSBI`
+//! implementation using `#[derive(RustSBI)]`. The defined SBI structure can be placed in
+//! a virtual machine structure representing a control flow executor to prepare for SBI environment:
 //!
 //! ```rust
-//! # struct RustSBI<>();
+//! use rustsbi::RustSBI;
+//!
+//! #[derive(RustSBI)]
+//! struct MySBI {
+//!     // add other fields later ...
+//!     // An environment information must be provided on
+//!     // non bare-metal RustSBI development.
+//!     info: MyEnvInfo,
+//! }
+//!
 //! struct VmHart {
 //!     // other fields ...
-//!     env: RustSBI</* Types, .. */>,
+//!     sbi: MySBI,
 //! }
+//! # struct MyEnvInfo;
+//! # impl rustsbi::EnvInfo for MyEnvInfo {
+//! #     fn mvendorid(&self) -> usize { 1 }
+//! #     fn marchid(&self) -> usize { 2 }
+//! #     fn mimpid(&self) -> usize { 3 }
+//! # }
 //! ```
 //!
 //! When the virtual machine hart traps into hypervisor, its code should decide whether
-//! this trap is an SBI environment call. If that is true, pass in parameters by `env.handle_ecall`
-//! function. RustSBI will handle with SBI standard constants, call corresponding extension module
-//! and provide parameters according to the extension and function IDs.
+//! this trap is an SBI environment call. If that is true, pass in parameters by `sbi.handle_ecall`
+//! function. RustSBI will handle with SBI standard constants, call corresponding extension field
+//! and provide parameters according to the extension and function IDs (if applicable).
 //!
 //! Crate `rustsbi` adapts to standard RISC-V SBI calls.
 //! If the hypervisor has custom SBI extensions that RustSBI does not recognize, those extension
@@ -399,16 +432,16 @@
 //!
 //! ```no_run
 //! # use sbi_spec::binary::SbiRet;
-//! # struct MyExtensionEnv {}
-//! # impl MyExtensionEnv { fn handle_ecall(&self, params: ()) -> SbiRet { SbiRet::success(0) } }
-//! # struct RustSBI {} // Mock, prevent doc test error when feature singleton is enabled
-//! # impl RustSBI { fn handle_ecall(&self, params: ()) -> SbiRet { SbiRet::success(0) } }
-//! # struct VmHart { my_extension_env: MyExtensionEnv, env: RustSBI }
+//! # struct MyExtensionSBI {}
+//! # impl MyExtensionSBI { fn handle_ecall(&self, params: ()) -> SbiRet { SbiRet::success(0) } }
+//! # struct MySBI {} // Mock, prevent doc test error when feature singleton is enabled
+//! # impl MySBI { fn handle_ecall(&self, params: ()) -> SbiRet { SbiRet::success(0) } }
+//! # struct VmHart { my_extension_sbi: MyExtensionSBI, sbi: MySBI }
 //! # #[derive(Copy, Clone)] enum Trap { Exception(Exception) }
 //! # impl Trap { fn cause(&self) -> Self { *self } }
 //! # #[derive(Copy, Clone)] enum Exception { SupervisorEcall }
 //! # impl VmHart {
-//! #     fn new() -> VmHart { VmHart { my_extension_env: MyExtensionEnv {}, env: RustSBI {} } }
+//! #     fn new() -> VmHart { VmHart { my_extension_sbi: MyExtensionSBI {}, sbi: MySBI {} } }
 //! #     fn run(&mut self) -> Trap { Trap::Exception(Exception::SupervisorEcall) }
 //! #     fn trap_params(&self) -> () { }
 //! #     fn fill_in(&mut self, ans: SbiRet) { let _ = ans; }
@@ -418,7 +451,7 @@
 //!     let trap = hart.run();
 //!     if let Trap::Exception(Exception::SupervisorEcall) = trap.cause() {
 //!         // Firstly, handle custom extensions
-//!         let my_extension_sbiret = hart.my_extension_env.handle_ecall(hart.trap_params());
+//!         let my_extension_sbiret = hart.my_extension_sbi.handle_ecall(hart.trap_params());
 //!         // If custom extension handles correctly, fill in its result and continue to hart.
 //!         // The custom handler may handle `probe_extension` in `base` extension as well
 //!         // to allow detections to whether custom extension exists.
@@ -427,7 +460,7 @@
 //!             continue;
 //!         }
 //!         // Then, if it's not a custom extension, handle it using standard SBI handler.
-//!         let standard_sbiret = hart.env.handle_ecall(hart.trap_params());
+//!         let standard_sbiret = hart.sbi.handle_ecall(hart.trap_params());
 //!         hart.fill_in(standard_sbiret);
 //!     }
 //! }
@@ -478,14 +511,16 @@
 //!
 //! ## RustSBI is a library for interfaces
 //!
-//! This library adapts to individual Rust traits to provide basic SBI features.
-//! When building for your own platform, implement traits in this library and pass them to the functions
-//! begin with `init`. After that, you may call `rustsbi::ecall`, `RustSBI::handle_ecall` or
-//! similiar functions in your own exception handler.
-//! It would dispatch parameters from supervisor to the traits to execute SBI functions.
+//! This library adapts to individual Rust traits and a derive macro to provide basic SBI features.
+//! When building for a specific platform, implement traits in this library and pass the types into
+//! a structure to derive RustSBI macro onto. After that, `handle_ecall`  would be called in the
+//! platform specific exception handler.
+//! The derive macro `RustSBI` would dispatch parameters from supervisor to the trait implementations
+//! to handle the SBI calls.
 //!
-//! The library also implements useful functions which may help with platform specific binaries.
-//! The `LOGO` can be printed if necessary when the binary is initializing.
+//! The library also implements useful constants which may help with platform specific binaries.
+//! The `LOGO` and information on `VERSION` can be printed if necessary on SBI initialization
+//! processes.
 //!
 //! Note that this crate is a library which contains common building blocks in SBI implementation.
 //! The RustSBI ecosystem would provide different level of support for each board, those support
@@ -493,14 +528,15 @@
 //!
 //! ## Hardware discovery and feature detection
 //!
-//! According to the RISC-V SBI specification, SBI itself does not specify any method for hardware discovery.
-//! The supervisor software must rely on the other industry standard hardware
-//! discovery methods (i.e. Device Tree or ACPI) for that purpose.
+//! According to the RISC-V SBI specification, the SBI itself does not specify any method for
+//! hardware discovery. The supervisor software must rely on the other industry standard hardware
+//! discovery methods (i.e. Device Tree, ACPI, vendor specific ones or upcoming `configptr` CSRs)
+//! for that purpose.
 //!
 //! To detect any feature under bare metal or under supervisor level, developers may depend on
 //! any hardware discovery methods, or use try-execute-trap method to detect any instructions or
 //! CSRs. If SBI is implemented in user level emulators, it may require to depend on operating
-//! system calls or use the signal trap method to detect any RISC-V core features.
+//! system calls or use a signal-trap procedure to detect any RISC-V core features.
 
 #![no_std]
 
@@ -508,7 +544,7 @@ mod console;
 mod cppc;
 mod hart_mask;
 mod hsm;
-mod instance;
+// mod instance;
 mod ipi;
 mod nacl;
 mod pmu;
@@ -517,8 +553,9 @@ mod rfence;
 mod sta;
 mod susp;
 mod timer;
+mod traits;
 
-/// The RustSBI logo without blank lines on the beginning
+/// The RustSBI logo without blank lines on the beginning.
 pub const LOGO: &str = r".______       __    __      _______.___________.  _______..______   __
 |   _  \     |  |  |  |    /       |           | /       ||   _  \ |  |
 |  |_)  |    |  |  |  |   |   (----`---|  |----`|   (----`|  |_)  ||  |
@@ -526,11 +563,11 @@ pub const LOGO: &str = r".______       __    __      _______.___________.  _____
 |  |\  \----.|  `--'  |.----)   |      |  |  .----)   |   |  |_)  ||  |
 | _| `._____| \______/ |_______/       |__|  |_______/    |______/ |__|";
 
-// RustSBI supports RISC-V SBI specification 2.0-rc1
+// RustSBI supports RISC-V SBI specification 2.0-rc8.
 const SBI_SPEC_MAJOR: usize = 2;
 const SBI_SPEC_MINOR: usize = 0;
 
-/// RustSBI implementation ID: 4
+/// RustSBI implementation ID: 4.
 ///
 /// Ref: https://github.com/riscv-non-isa/riscv-sbi-doc/pull/61
 const IMPL_ID_RUSTSBI: usize = 4;
@@ -541,15 +578,470 @@ const RUSTSBI_VERSION_PATCH: usize = (env!("CARGO_PKG_VERSION_PATCH").as_bytes()
 const RUSTSBI_VERSION: usize =
     (RUSTSBI_VERSION_MAJOR << 16) + (RUSTSBI_VERSION_MINOR << 8) + RUSTSBI_VERSION_PATCH;
 
-/// RustSBI version as a string
+/// RustSBI version as a string.
 pub const VERSION: &str = env!("CARGO_PKG_VERSION");
 
 pub extern crate sbi_spec as spec;
+/// Generate `RustSBI` implementation for structure of each extensions.
+///
+/// # Usage
+///
+/// The `#[derive(RustSBI)]` macro provides a convenient way of building `RustSBI` trait implementations.
+/// To use this macro, say that we have a struct `MyFence` with RISC-V SBI Remote Fence extension
+/// implemented using `rustsbi::Fence` trait. Then, we build a struct around it, representing a
+/// whole SBI implementation including one `Fence` extension only; we can name it `MySBI`:
+///
+/// ```rust
+/// struct MySBI {
+///     fence: MyFence,
+/// }
+///
+/// struct MyFence { /* fields */ }
+///
+/// # use rustsbi::{RustSBI, HartMask};
+/// # use sbi_spec::binary::SbiRet;
+/// impl rustsbi::Fence for MyFence {
+///     /* implementation details */
+/// #   fn remote_fence_i(&self, _: HartMask) -> SbiRet { unimplemented!() }
+/// #   fn remote_sfence_vma(&self, _: HartMask, _: usize, _: usize) -> SbiRet { unimplemented!() }
+/// #   fn remote_sfence_vma_asid(&self, _: HartMask, _: usize, _: usize, _: usize) -> SbiRet { unimplemented!() }
+/// }
+/// ```
+///
+/// Here, we declared the field named `fence` with type `MyFence`. The variable name `fence` is special,
+/// it tells RustSBI derive macro that this field implements SBI Remote Fence instead of other SBI extensions.
+/// We can continue to adding more fields into `MySBI`. For example, if we have RISC-V SBI Time extension
+/// implementation with type `MyTimer`, we can add it to `MySBI`:
+///
+/// ```rust
+/// struct MySBI {
+///     fence: MyFence,
+///     timer: MyTimer,
+/// }
+/// # struct MyFence;
+/// # struct MyTimer;
+/// ```
+///
+/// Don't forget that the name `timer` is also a special field name. There is a detailed list after this
+/// chapter describing what special field name would the `RustSBI` macro identify.
+///
+/// It looks like we are ready to derive `RustSBI` macro on `MySBI`! Let's try it now ...
+///
+/// ```compile_fail
+/// #[derive(RustSBI)]
+/// struct MySBI {
+///     fence: MyFence,
+///     timer: MyTimer,
+/// #   #[cfg(feature = "machine")] info: () // compile would success on #[cfg(feature = "machine")], cause it always to fail
+/// }
+/// # use rustsbi::{RustSBI, HartMask};
+/// # use sbi_spec::binary::SbiRet;
+/// # struct MyFence;
+/// # impl rustsbi::Fence for MyFence {
+/// #     fn remote_fence_i(&self, _: HartMask) -> SbiRet { unimplemented!() }
+/// #     fn remote_sfence_vma(&self, _: HartMask, _: usize, _: usize) -> SbiRet { unimplemented!() }
+/// #     fn remote_sfence_vma_asid(&self, _: HartMask, _: usize, _: usize, _: usize) -> SbiRet { unimplemented!() }
+/// # }
+/// # struct MyTimer;
+/// # impl rustsbi::Timer for MyTimer {
+/// #     fn set_timer(&self, stime_value: u64) { unimplemented!() }
+/// # }
+/// ```
+///
+/// Oops! Compile failed. We'd check what happened here:
+///
+/// ```text
+/// error: can't derive RustSBI: #[cfg(feature = "machine")] is needed to derive RustSBI with no extra `EnvInfo` provided; consider adding an `info` parameter to provide machine information implementing `rustsbi::EnvInfo` if RustSBI is not run on machine mode.
+///  --> example.rs:LL:10
+///    |
+/// LL | #[derive(RustSBI)]
+///    |          ^^^^^^^
+///    |
+///  = note: this error originates in the derive macro `RustSBI` (in Nightly builds, run with -Z macro-backtrace for more info)
+///
+/// error: aborting due to previous error
+/// ```
+///
+/// The error message hints that we didn't provide any SBI environment information implementing trait
+/// `EnvInfo`. By default, RustSBI is targeted to provide RISC-V supervisor environment on any hardware,
+/// targeting hypervisor, emulator and binary translation applications. In this case, the virtualized
+/// environment should provide the supervisor with machine environment information like `mvendorid`,
+/// `marchid` and `mimpid` values. RustSBI could also be used on bare-metal RISC-V machines where such
+/// values would be directly accessible through CSR read operations.
+///
+/// If we are targeting bare-metal, we can use the RustSBI library with `#[cfg(feature = "machine")]`
+/// enabled by changing `dependencies` section in `Cargo.toml` file (if we are using Cargo):
+///
+/// ```toml
+/// [dependencies]
+/// rustsbi = { version = "0.4.0", features = ["machine"] }
+/// ```
+///
+/// If that's not the case and we are writing a virtualization targeted application, we should add a
+/// `EnvInfo` implementation into the structure like `MySBI` mentioned above, with a special field
+/// name `info`. We can do it like:
+///
+/// ```rust
+/// #[derive(RustSBI)]
+/// struct MySBI {
+///     fence: MyFence,
+///     timer: MyTimer,
+/// #   #[cfg(not(feature = "machine"))]
+///     info: MyEnvInfo,
+/// }
+///
+/// struct MyEnvInfo;
+///
+/// impl rustsbi::EnvInfo for MyEnvInfo {
+///     #[inline]
+///     fn mvendorid(&self) -> usize { todo!("add real value here") }
+///     #[inline]
+///     fn marchid(&self) -> usize { todo!("add real value here") }
+///     #[inline]
+///     fn mimpid(&self) -> usize { todo!("add real value here") }
+/// }
+/// # use rustsbi::{RustSBI, HartMask};
+/// # use sbi_spec::binary::SbiRet;
+/// # struct MyFence;
+/// # impl rustsbi::Fence for MyFence {
+/// #     fn remote_fence_i(&self, _: HartMask) -> SbiRet { unimplemented!() }
+/// #     fn remote_sfence_vma(&self, _: HartMask, _: usize, _: usize) -> SbiRet { unimplemented!() }
+/// #     fn remote_sfence_vma_asid(&self, _: HartMask, _: usize, _: usize, _: usize) -> SbiRet { unimplemented!() }
+/// # }
+/// # struct MyTimer;
+/// # impl rustsbi::Timer for MyTimer {
+/// #     fn set_timer(&self, stime_value: u64) { unimplemented!() }
+/// # }
+/// ```
+///
+/// Then, when we compile our code with `MySBI`, we'll found that the code now compiles successfully.
+///
+/// To use the derived `RustSBI` implementation, we note out that this structure now implements the trait
+/// `RustSBI` with function `handle_ecall`. We can pass SBI extension, function and parameters into
+/// `handle_ecall`, and reads the SBI call result from its return value with the type `SbiRet`.
+/// To illustrate this feature, we make an SBI call to read the SBI implementation ID, like:
+///
+/// ```rust
+/// # use rustsbi::RustSBI;
+/// #[derive(RustSBI)]
+/// struct MySBI {
+///     /* we omit the extension fields by now */
+/// #   info: MyEnvInfo,
+/// }
+///
+/// fn main() {
+///     // Create a MySBI instance.
+///     let sbi = MySBI {
+///         /* include initial values for fields */
+/// #       info: MyEnvInfo
+///     };
+///     // Make the call. Read SBI implementation ID resides in extension Base (0x10),
+///     // with function id 1, and it doesn't have any parameters.
+///     let ret = sbi.handle_ecall(0x10, 0x1, [0; 6]);
+///     // Let's check the result...
+///     println!("SBI implementation ID for MySBI: {}", ret.value);
+///     assert_eq!(ret.value, 4);
+/// }
+/// # struct MyEnvInfo;
+/// # impl rustsbi::EnvInfo for MyEnvInfo {
+/// #     fn mvendorid(&self) -> usize { unimplemented!() }
+/// #     fn marchid(&self) -> usize { unimplemented!() }
+/// #     fn mimpid(&self) -> usize { unimplemented!() }
+/// # }
+/// ```
+///
+/// Run the code and we'll find following output in the console:
+///
+/// ```text
+/// SBI implementation ID for MySBI: 4
+/// ```
+///
+/// The SBI call returns the number 4 as SBI call result. By looking up
+/// [the RISC-V SBI Specification](https://github.com/riscv-non-isa/riscv-sbi-doc/blob/cf86bda6f57afb8e0e7011b61504d4e8664b9b1d/src/ext-base.adoc#sbi-implementation-ids),
+/// we can know that RustSBI have the implementation ID of 4. You have successfully made your first
+/// SBI call from a derived `RustSBI` implementation!
+///
+/// If we learn further from the RISC-V privileged software architecture, we may know more about how
+/// RISC-V SBI works on an environment to support supervisor software. RISC-V SBI implementations
+/// accept SBI calls by supervisor-level environment call caused by `ecall` instruction under supervisor
+/// mode. Each `ecall` raises a RISC-V exception which the environment must process with. The SBI
+/// environment, either bare-metal or virtually, would save context, read extension, function and parameters
+/// and call the `handle_ecall` function provided by `RustSBI` trait. Then, the function returns
+/// with an `SbiRet`; we reads back `value` and `error` to store them into the saved context.
+/// Finally, when the context restores, the supervisor mode software (kernels, etc.) could get the
+/// SBI call result from register values.
+///
+/// Now we have learned basical usages of the derive macro `RustSBI`. We can dive deeper and use RustSBI
+/// in real cases with ease. Congratulations!
+///
+/// # Supported extensions
+///
+/// The derive macro `RustSBI` supports all the standard RISC-V SBI extensions this library supports.
+/// When we add extensions into SBI structure fields, special field names are identified by RustSBI
+/// derive macro. Here is a list on them:
+///
+/// | Field names | RustSBI trait | Extension |
+/// |:------------|:----------|:--------------|
+/// | `time` or `timer` | [`Timer`](trait.Timer.html) | Timer programmer extension |
+/// | `ipi` or `spi` | [`Ipi`](trait.Ipi.html) | S-mode Inter Processor Interrupt |
+/// | `fence` or `rfnc` | [`Fence`](trait.Fence.html) | Remote Fence extension |
+/// | `hsm` | [`Hsm`](trait.Hsm.html) | Hart State Monitor extension |
+/// | `reset` or `srst` | [`Reset`](trait.Reset.html) | System Reset extension |
+/// | `pmu` | [`Pmu`](trait.Pmu.html) | Performance Monitor Unit extension |
+/// | `console` or `dbcn` | [`Console`](trait.Console.html) | Debug Console extension |
+/// | `susp` | [`Susp`](trait.Susp.html) | System Suspend extension |
+/// | `cppc` | [`Cppc`](trait.Cppc.html) | SBI CPPC extension |
+/// | `nacl` | [`Nacl`](trait.Nacl.html) | Nested Acceleration extension |
+/// | `sta` | [`Sta`](trait.Sta.html) | Steal Time Accounting extension |
+///
+/// The `EnvInfo` parameter is used by RISC-V SBI Base extension which is always supported on all
+/// RISC-V SBI implementations. RustSBI provides Base extension with additional `EnvInfo` by default.
+///
+/// | Field names | RustSBI trait | Description |
+/// |:------------|:----------|:--------------|
+/// | `info` or `env_info` | [`EnvInfo`](trait.EnvInfo.html) | Machine environment information used by Base extension |
+///
+/// Or, if `#[cfg(feature = "machine")]` is enabled, RustSBI derive macro does not require additional
+/// machine environment information but reads them by RISC-V CSR operation when we don't have any `EnvInfo`s
+/// in the structure. This feature would only work if RustSBI runs directly on machine mode hardware.
+/// If we are targeting other environments (virtualization etc.) we should provide `EnvInfo` instead
+/// of using the machine feature.
+///
+/// # Examples
+///
+/// This macro should be used over a struct of RISC-V SBI extension implementaions.
+/// For example:
+///
+/// ```rust
+/// #[derive(RustSBI)]
+/// struct MySBI {
+///     fence: MyFence,
+///     info: MyEnvInfo,
+/// }
+///
+/// // Here, we assume that `MyFence` implements `rustsbi::Fence`
+/// // and `MyEnvInfo` implements `rustsbi::EnvInfo`.
+/// # use rustsbi::{RustSBI, HartMask};
+/// # use sbi_spec::binary::SbiRet;
+/// # struct MyFence;
+/// # impl rustsbi::Fence for MyFence {
+/// #     fn remote_fence_i(&self, _: HartMask) -> SbiRet { unimplemented!() }
+/// #     fn remote_sfence_vma(&self, _: HartMask, _: usize, _: usize) -> SbiRet { unimplemented!() }
+/// #     fn remote_sfence_vma_asid(&self, _: HartMask, _: usize, _: usize, _: usize) -> SbiRet { unimplemented!() }
+/// # }
+/// # struct MyEnvInfo;
+/// # impl rustsbi::EnvInfo for MyEnvInfo {
+/// #     fn mvendorid(&self) -> usize { 1 }
+/// #     fn marchid(&self) -> usize { 2 }
+/// #     fn mimpid(&self) -> usize { 3 }
+/// # }
+/// ```
+///
+/// Fields indicating the same extension (SBI extension or `EnvInfo`) shouldn't be included
+/// more than once.
+///
+/// ```compile_fail
+/// #[derive(RustSBI)]
+/// struct MySBI {
+///     fence: MyFence,
+///     rfnc: MyFence, // <-- Second field providing `rustsbi::Fence` implementation
+///     info: MyEnvInfo,
+/// }
+/// # use rustsbi::{RustSBI, HartMask};
+/// # use sbi_spec::binary::SbiRet;
+/// # struct MyFence;
+/// # impl rustsbi::Fence for MyFence {
+/// #     fn remote_fence_i(&self, _: HartMask) -> SbiRet { unimplemented!() }
+/// #     fn remote_sfence_vma(&self, _: HartMask, _: usize, _: usize) -> SbiRet { unimplemented!() }
+/// #     fn remote_sfence_vma_asid(&self, _: HartMask, _: usize, _: usize, _: usize) -> SbiRet { unimplemented!() }
+/// # }
+/// # struct MyEnvInfo;
+/// # impl rustsbi::EnvInfo for MyEnvInfo {
+/// #     fn mvendorid(&self) -> usize { 1 }
+/// #     fn marchid(&self) -> usize { 2 }
+/// #     fn mimpid(&self) -> usize { 3 }
+/// # }
+/// ```
+///
+/// The struct as derive input may include generics, specifically type generics, lifetimes,
+/// constant generics and where clauses.
+///
+/// ```rust
+/// #[derive(RustSBI)]
+/// struct MySBI<'a, T: rustsbi::Fence, U, const N: usize>
+/// where
+///     U: rustsbi::Timer,
+/// {
+///     fence: T,
+///     timer: U,
+///     info: &'a MyEnvInfo,
+///     _dummy: [u8; N],
+/// }
+///
+/// # use rustsbi::{RustSBI, HartMask};
+/// # use sbi_spec::binary::SbiRet;
+/// # struct MyFence;
+/// # impl rustsbi::Fence for MyFence {
+/// #     fn remote_fence_i(&self, _: HartMask) -> SbiRet { unimplemented!() }
+/// #     fn remote_sfence_vma(&self, _: HartMask, _: usize, _: usize) -> SbiRet { unimplemented!() }
+/// #     fn remote_sfence_vma_asid(&self, _: HartMask, _: usize, _: usize, _: usize) -> SbiRet { unimplemented!() }
+/// # }
+/// # struct MyEnvInfo;
+/// # impl rustsbi::EnvInfo for MyEnvInfo {
+/// #     fn mvendorid(&self) -> usize { 1 }
+/// #     fn marchid(&self) -> usize { 2 }
+/// #     fn mimpid(&self) -> usize { 3 }
+/// # }
+/// ```
+///
+/// Inner attribute `#[rustsbi(skip)]` informs the macro to skip a certain field when
+/// generating a RustSBI implementation.
+///
+/// ```rust
+/// #[derive(RustSBI)]
+/// struct MySBI {
+///     console: MyConsole,
+///     #[rustsbi(skip)]
+///     fence: MyFence,
+///     info: MyEnvInfo,
+/// }
+///
+/// // The derived `MySBI` implementation ignores the `fence: MyFence`. It can now
+/// // be used as a conventional struct field.
+/// // Notably, a `#[warn(unused)]` would be raised if `fence` is not furtherly used
+/// // by following code; `console` and `info` fields are not warned because they are
+/// // internally used by the trait implementation derived in the RustSBI macro.
+/// # use rustsbi::{HartMask, RustSBI};
+/// # use sbi_spec::binary::{SbiRet, Physical};
+/// # struct MyConsole;
+/// # impl rustsbi::Console for MyConsole {
+/// #     fn write(&self, _: Physical<&[u8]>) -> SbiRet { unimplemented!() }
+/// #     fn read(&self, _: Physical<&mut [u8]>) -> SbiRet { unimplemented!() }
+/// #     fn write_byte(&self, _: u8) -> SbiRet { unimplemented!() }
+/// # }
+/// # struct MyFence;
+/// # impl rustsbi::Fence for MyFence {
+/// #     fn remote_fence_i(&self, _: HartMask) -> SbiRet { unimplemented!() }
+/// #     fn remote_sfence_vma(&self, _: HartMask, _: usize, _: usize) -> SbiRet { unimplemented!() }
+/// #     fn remote_sfence_vma_asid(&self, _: HartMask, _: usize, _: usize, _: usize) -> SbiRet { unimplemented!() }
+/// # }
+/// # struct MyEnvInfo;
+/// # impl rustsbi::EnvInfo for MyEnvInfo {
+/// #     fn mvendorid(&self) -> usize { 1 }
+/// #     fn marchid(&self) -> usize { 2 }
+/// #     fn mimpid(&self) -> usize { 3 }
+/// # }
+/// ```
+///
+/// In some cases we may manually assign fields to certain SBI extension other than defaulting
+/// to special names defined above, and sometimes we need to provide multiple SBI extensions
+/// with one field only. By listing the extension names separated by comma in the helper attribute,
+/// we can assign one or multiple SBI extensions to a field to solve the issues above.
+///
+/// ```rust
+/// #[derive(RustSBI)]
+/// struct MySBI {
+///     console: MyConsole,
+///     #[rustsbi(fence)]
+///     some_other_name: MyFence,
+///     info: MyEnvInfo,
+/// }
+///
+/// // RustSBI will now use the `some_other_name` field implementing `rustsbi::Fence`
+/// // as the implementation of SBI Remote Fence extension.
+/// # use rustsbi::{HartMask, RustSBI};
+/// # use sbi_spec::binary::{SbiRet, Physical};
+/// # struct MyConsole;
+/// # impl rustsbi::Console for MyConsole {
+/// #     fn write(&self, _: Physical<&[u8]>) -> SbiRet { unimplemented!() }
+/// #     fn read(&self, _: Physical<&mut [u8]>) -> SbiRet { unimplemented!() }
+/// #     fn write_byte(&self, _: u8) -> SbiRet { unimplemented!() }
+/// # }
+/// # struct MyFence;
+/// # impl rustsbi::Fence for MyFence {
+/// #     fn remote_fence_i(&self, _: HartMask) -> SbiRet { unimplemented!() }
+/// #     fn remote_sfence_vma(&self, _: HartMask, _: usize, _: usize) -> SbiRet { unimplemented!() }
+/// #     fn remote_sfence_vma_asid(&self, _: HartMask, _: usize, _: usize, _: usize) -> SbiRet { unimplemented!() }
+/// # }
+/// # struct MyEnvInfo;
+/// # impl rustsbi::EnvInfo for MyEnvInfo {
+/// #     fn mvendorid(&self) -> usize { 1 }
+/// #     fn marchid(&self) -> usize { 2 }
+/// #     fn mimpid(&self) -> usize { 3 }
+/// # }
+/// ```
+/// ```rust
+/// #[derive(RustSBI)]
+/// struct MySBI {
+///     #[rustsbi(ipi, timer)]
+///     clint: Clint, // <-- RISC-V CLINT will provide both Ipi and Timer extensions.
+///     info: MyEnvInfo,
+/// }
+///
+/// // Both Ipi and Timer extension implementations are now provided by the
+/// // `clint` field implementing both `rustsbi::Ipi` and `rustsbi::Timer`.
+/// # use rustsbi::{HartMask, RustSBI};
+/// # use sbi_spec::binary::{SbiRet, Physical};
+/// # struct Clint;
+/// # impl rustsbi::Timer for Clint {
+/// #     fn set_timer(&self, stime_value: u64) { unimplemented!() }
+/// # }
+/// # impl rustsbi::Ipi for Clint {
+/// #     fn send_ipi(&self, _: HartMask) -> SbiRet { unimplemented!() }
+/// # }
+/// # struct MyEnvInfo;
+/// # impl rustsbi::EnvInfo for MyEnvInfo {
+/// #     fn mvendorid(&self) -> usize { 1 }
+/// #     fn marchid(&self) -> usize { 2 }
+/// #     fn mimpid(&self) -> usize { 3 }
+/// # }
+/// ```
+///
+/// RustSBI implementations usually provide regular structs to the derive macro.
+/// Alternatively, the RustSBI derive macro also accepts tuple structs or unit structs.
+///
+/// ```rust
+/// // Tuple structs.
+/// // No field names are provided; the structure must provide helper attributes
+/// // to identify the extensions for the RustSBI derive macro.
+/// #[derive(RustSBI)]
+/// struct MySBI(#[rustsbi(fence)] MyFence, #[rustsbi(info)] MyEnvInfo);
+///
+/// # use rustsbi::{RustSBI, HartMask};
+/// # use sbi_spec::binary::SbiRet;
+/// # struct MyFence;
+/// # impl rustsbi::Fence for MyFence {
+/// #     fn remote_fence_i(&self, _: HartMask) -> SbiRet { unimplemented!() }
+/// #     fn remote_sfence_vma(&self, _: HartMask, _: usize, _: usize) -> SbiRet { unimplemented!() }
+/// #     fn remote_sfence_vma_asid(&self, _: HartMask, _: usize, _: usize, _: usize) -> SbiRet { unimplemented!() }
+/// # }
+/// # struct MyEnvInfo;
+/// # impl rustsbi::EnvInfo for MyEnvInfo {
+/// #     fn mvendorid(&self) -> usize { 1 }
+/// #     fn marchid(&self) -> usize { 2 }
+/// #     fn mimpid(&self) -> usize { 3 }
+/// # }
+/// ```
+/// ```rust
+/// // Unit structs.
+/// // Note that `info` is required on non-machine enironment; thus this crate
+/// // only allow unit structs with `machine` feature. No extensions except Base
+/// // extension are provided on unit struct implementations.
+/// # use rustsbi::RustSBI;
+/// # #[cfg(feature = "machine")]
+/// #[derive(RustSBI)]
+/// struct MySBI;
+/// ```
+///
+/// # Notes
+// note: following documents are inherted from `RustSBI` in the `rustsbi_macros` package.
+#[doc(inline)]
+pub use rustsbi_macros::RustSBI;
+
 pub use console::Console;
 pub use cppc::Cppc;
 pub use hart_mask::HartMask;
 pub use hsm::Hsm;
-pub use instance::{Builder, RustSBI};
 pub use ipi::Ipi;
 pub use nacl::Nacl;
 pub use pmu::Pmu;
@@ -558,6 +1050,16 @@ pub use rfence::Rfence as Fence;
 pub use sta::Sta;
 pub use susp::Susp;
 pub use timer::Timer;
+pub use traits::{EnvInfo, RustSBI};
+
+// Macro internal functions and structures
 
-#[cfg(not(feature = "machine"))]
-pub use instance::MachineInfo;
+#[cfg(feature = "machine")]
+#[doc(hidden)]
+pub use traits::_rustsbi_base_bare;
+#[doc(hidden)]
+pub use traits::{
+    _StandardExtensionProbe, _rustsbi_base_env_info, _rustsbi_console, _rustsbi_cppc,
+    _rustsbi_fence, _rustsbi_hsm, _rustsbi_ipi, _rustsbi_nacl, _rustsbi_pmu, _rustsbi_reset,
+    _rustsbi_sta, _rustsbi_susp, _rustsbi_timer,
+};

+ 3 - 3
src/nacl.rs

@@ -3,7 +3,7 @@ use spec::{
     nacl::shmem_size::NATIVE,
 };
 
-/// Nested Acceleration Extension
+/// Nested Acceleration extension.
 ///
 /// Nested virtualization is the ability of a hypervisor to run another hypervisor
 /// as a guest. RISC-V nested virtualization requires an L0 hypervisor (running
@@ -23,7 +23,7 @@ use spec::{
 /// extension if the underlying platform has the RISC-V H-extension implemented
 /// in hardware.
 
-pub trait Nacl: Send + Sync {
+pub trait Nacl {
     /// Probe nested acceleration feature.
     ///
     /// Probe a nested acceleration feature. This is a mandatory function of the
@@ -143,7 +143,7 @@ pub trait Nacl: Send + Sync {
     fn sync_sret(&self) -> SbiRet;
 }
 
-impl<T: Nacl> Nacl for T {
+impl<T: Nacl> Nacl for &T {
     #[inline]
     fn probe_feature(&self, feature_id: u32) -> SbiRet {
         T::probe_feature(self, feature_id)

+ 2 - 2
src/pmu.rs

@@ -1,6 +1,6 @@
 use sbi_spec::binary::SbiRet;
 
-/// Performance Monitoring Unit Extension
+/// Performance Monitoring Unit extension.
 ///
 /// The RISC-V hardware performance counters such as `mcycle`, `minstret`, and `mhpmcounterX` CSRs
 /// are accessible as read-only from supervisor-mode using `cycle`, `instret`, and `hpmcounterX` CSRs.
@@ -35,7 +35,7 @@ use sbi_spec::binary::SbiRet;
 /// event_idx[19:16] = type;
 /// event_idx[15:0] = code;
 /// ```
-pub trait Pmu: Send + Sync {
+pub trait Pmu {
     /// Returns the number of counters (both hardware and firmware).
     ///
     /// The value is returned in `SbiRet.value`; this call always returns `SbiRet::success()`.

+ 2 - 2
src/reset.rs

@@ -1,6 +1,6 @@
 use sbi_spec::binary::SbiRet;
 
-/// System Reset Extension
+/// System Reset extension.
 ///
 /// Provides a function that allow the supervisor software to request system-level reboot or shutdown.
 ///
@@ -8,7 +8,7 @@ use sbi_spec::binary::SbiRet;
 /// could be machine mode firmware or hypervisor.
 ///
 /// Ref: [Section 9, RISC-V Supervisor Binary Interface Specification](https://github.com/riscv-non-isa/riscv-sbi-doc/blob/master/riscv-sbi.adoc#9-system-reset-extension-eid-0x53525354-srst)
-pub trait Reset: Send + Sync {
+pub trait Reset {
     /// Reset the system based on provided `reset_type` and `reset_reason`.
     ///
     /// This is a synchronous call and does not return if it succeeds.

+ 2 - 2
src/rfence.rs

@@ -1,12 +1,12 @@
 use crate::hart_mask::HartMask;
 use sbi_spec::binary::SbiRet;
 
-/// Remote fence support
+/// Remote Fence support extension.
 ///
 /// The remote fence function acts as a full TLB flush if
 /// - `start_addr` and `size` are both 0, or
 /// - `size` is equal to `usize::MAX`.
-pub trait Rfence: Send + Sync {
+pub trait Rfence {
     /// Instructs remote harts to execute `FENCE.I` instruction.
     ///
     /// # Return value

+ 2 - 2
src/sta.rs

@@ -1,6 +1,6 @@
 use sbi_spec::binary::{SbiRet, SharedPtr};
 
-/// Steal-time Accounting Extension
+/// Steal-time Accounting extension.
 ///
 /// SBI implementations may encounter situations where virtual harts are ready to
 /// run, but must be withheld from running. These situations may be, for example,
@@ -14,7 +14,7 @@ use sbi_spec::binary::{SbiRet, SharedPtr};
 /// mechanism in which an SBI implementation provides steal-time and preemption
 /// information, for each virtual hart, to supervisor-mode software.
 
-pub trait Sta: Send + Sync {
+pub trait Sta {
     /// Set Steal-time Shared Memory Address.
     ///
     /// Set the shared memory physical base address for steal-time accounting of the

+ 2 - 2
src/susp.rs

@@ -1,6 +1,6 @@
 use sbi_spec::binary::SbiRet;
 
-/// System Suspend Extension
+/// System Suspend extension.
 ///
 /// The system suspend extension defines a set of system-level sleep states and a
 /// function which allows the supervisor-mode software to request that the system
@@ -24,7 +24,7 @@ use sbi_spec::binary::SbiRet;
 /// types and per-type wake up devices in their hardware descriptions. The
 /// `SUSPEND_TO_RAM` sleep type is the one exception, and its presence is implied
 /// by that of the extension.
-pub trait Susp: Send + Sync {
+pub trait Susp {
     /// Request the SBI implementation to put the system transitions to a sleep state.
     ///
     /// A return from a `system_suspend()` call implies an error and an error code

+ 2 - 2
src/timer.rs

@@ -1,5 +1,5 @@
-/// Timer programmer support
-pub trait Timer: Send + Sync {
+/// Timer programmer support extension.
+pub trait Timer {
     /// Programs the clock for next event after `stime_value` time.
     ///
     /// `stime_value` is in absolute time. This function must clear the pending timer interrupt bit as well.

+ 395 - 0
src/traits.rs

@@ -0,0 +1,395 @@
+use crate::HartMask;
+#[cfg(feature = "machine")]
+use riscv::register::{marchid, mimpid, mvendorid};
+use spec::binary::{Physical, SbiRet, SharedPtr};
+
+/// RustSBI environment call handler.
+pub trait RustSBI {
+    /// Handle supervisor environment call with given parameters and return the `SbiRet` result.
+    fn handle_ecall(&self, extension: usize, function: usize, param: [usize; 6]) -> SbiRet;
+}
+
+impl<T: RustSBI> RustSBI for &T {
+    #[inline(always)]
+    fn handle_ecall(&self, extension: usize, function: usize, param: [usize; 6]) -> SbiRet {
+        <T as RustSBI>::handle_ecall(self, extension, function, param)
+    }
+}
+
+/// Machine environment information.
+///
+/// This trait is useful to build an SBI environment when RustSBI is not run directly on RISC-V machine mode.
+pub trait EnvInfo {
+    /// Vendor ID for the supervisor environment.
+    ///
+    /// Provides JEDEC manufacturer ID of the provider of the core.
+    fn mvendorid(&self) -> usize;
+    /// Architecture ID for the supervisor environment.
+    ///
+    /// Encodes the base micro-architecture of the hart.
+    fn marchid(&self) -> usize;
+    /// Implementation ID for the supervisor environment.
+    ///
+    /// Provides a unique encoding of the version of the processor implementation.
+    fn mimpid(&self) -> usize;
+}
+
+impl<T: EnvInfo> EnvInfo for &T {
+    #[inline(always)]
+    fn mvendorid(&self) -> usize {
+        <T as EnvInfo>::mvendorid(self)
+    }
+    #[inline(always)]
+    fn marchid(&self) -> usize {
+        <T as EnvInfo>::marchid(self)
+    }
+    #[inline(always)]
+    fn mimpid(&self) -> usize {
+        <T as EnvInfo>::mimpid(self)
+    }
+}
+
+// Macro internal structures and functions.
+// DO NOT USE code here directly; use derive macro #[derive(RustSBI)] instead.
+
+#[cfg(feature = "machine")]
+#[doc(hidden)]
+#[inline(always)]
+pub fn _rustsbi_base_bare<U: _ExtensionProbe>(
+    param: [usize; 6],
+    function: usize,
+    probe: U,
+) -> SbiRet {
+    let [param0] = [param[0]];
+    let value = match function {
+        spec::base::GET_SBI_SPEC_VERSION => (crate::SBI_SPEC_MAJOR << 24) | (crate::SBI_SPEC_MINOR),
+        spec::base::GET_SBI_IMPL_ID => crate::IMPL_ID_RUSTSBI,
+        spec::base::GET_SBI_IMPL_VERSION => crate::RUSTSBI_VERSION,
+        spec::base::PROBE_EXTENSION => probe.probe_extension(param0),
+        spec::base::GET_MVENDORID => mvendorid::read().map(|r| r.bits()).unwrap_or(0),
+        spec::base::GET_MARCHID => marchid::read().map(|r| r.bits()).unwrap_or(0),
+        spec::base::GET_MIMPID => mimpid::read().map(|r| r.bits()).unwrap_or(0),
+        _ => return SbiRet::not_supported(),
+    };
+    SbiRet::success(value)
+}
+
+#[doc(hidden)]
+#[inline(always)]
+pub fn _rustsbi_base_env_info<T: EnvInfo, U: _ExtensionProbe>(
+    param: [usize; 6],
+    function: usize,
+    machine_info: &T,
+    probe: U,
+) -> SbiRet {
+    let [param0] = [param[0]];
+    let value = match function {
+        spec::base::GET_SBI_SPEC_VERSION => (crate::SBI_SPEC_MAJOR << 24) | (crate::SBI_SPEC_MINOR),
+        spec::base::GET_SBI_IMPL_ID => crate::IMPL_ID_RUSTSBI,
+        spec::base::GET_SBI_IMPL_VERSION => crate::RUSTSBI_VERSION,
+        spec::base::PROBE_EXTENSION => probe.probe_extension(param0),
+        spec::base::GET_MVENDORID => machine_info.mvendorid(),
+        spec::base::GET_MARCHID => machine_info.marchid(),
+        spec::base::GET_MIMPID => machine_info.mimpid(),
+        _ => return SbiRet::not_supported(),
+    };
+    SbiRet::success(value)
+}
+
+// Probe not only standard SBI extensions, but also (reserving for) custom extensions.
+// For standard SBI extensions only, the macro would use `_StandardExtensionProbe`;
+// for implementation with custom SBI extension, a custom structure implementing this trait
+// would be used by macro.
+pub trait _ExtensionProbe {
+    // Implementors are encouraged to add #[inline] hints on this function.
+    fn probe_extension(&self, extension: usize) -> usize;
+}
+
+#[doc(hidden)]
+pub struct _StandardExtensionProbe {
+    pub base: usize,
+    pub fence: usize,
+    pub timer: usize,
+    pub ipi: usize,
+    pub hsm: usize,
+    pub reset: usize,
+    pub pmu: usize,
+    pub console: usize,
+    pub susp: usize,
+    pub cppc: usize,
+    pub nacl: usize,
+    pub sta: usize,
+    // NOTE: don't forget to add to `fn probe_extension` in `impl _ExtensionProbe` as well
+}
+
+impl _ExtensionProbe for _StandardExtensionProbe {
+    #[inline(always)]
+    fn probe_extension(&self, extension: usize) -> usize {
+        match extension {
+            spec::base::EID_BASE => self.base,
+            spec::time::EID_TIME => self.timer,
+            spec::spi::EID_SPI => self.ipi,
+            spec::rfnc::EID_RFNC => self.fence,
+            spec::srst::EID_SRST => self.reset,
+            spec::hsm::EID_HSM => self.hsm,
+            spec::pmu::EID_PMU => self.pmu,
+            spec::dbcn::EID_DBCN => self.console,
+            spec::susp::EID_SUSP => self.susp,
+            spec::cppc::EID_CPPC => self.cppc,
+            spec::nacl::EID_NACL => self.nacl,
+            spec::sta::EID_STA => self.sta,
+            _ => spec::base::UNAVAILABLE_EXTENSION,
+        }
+    }
+}
+
+#[doc(hidden)]
+#[inline(always)]
+pub fn _rustsbi_fence<T: crate::Fence>(fence: &T, param: [usize; 6], function: usize) -> SbiRet {
+    let [param0, param1, param2, param3, param4] =
+        [param[0], param[1], param[2], param[3], param[4]];
+    let hart_mask = HartMask::from_mask_base(param0, param1);
+    match function {
+        spec::rfnc::REMOTE_FENCE_I => fence.remote_fence_i(hart_mask),
+        spec::rfnc::REMOTE_SFENCE_VMA => fence.remote_sfence_vma(hart_mask, param2, param3),
+        spec::rfnc::REMOTE_SFENCE_VMA_ASID => {
+            fence.remote_sfence_vma_asid(hart_mask, param2, param3, param4)
+        }
+        spec::rfnc::REMOTE_HFENCE_GVMA_VMID => {
+            fence.remote_hfence_gvma_vmid(hart_mask, param2, param3, param4)
+        }
+        spec::rfnc::REMOTE_HFENCE_GVMA => fence.remote_hfence_gvma(hart_mask, param2, param3),
+        spec::rfnc::REMOTE_HFENCE_VVMA_ASID => {
+            fence.remote_hfence_vvma_asid(hart_mask, param2, param3, param4)
+        }
+        spec::rfnc::REMOTE_HFENCE_VVMA => fence.remote_hfence_vvma(hart_mask, param2, param3),
+        _ => SbiRet::not_supported(),
+    }
+}
+
+#[doc(hidden)]
+#[inline(always)]
+pub fn _rustsbi_timer<T: crate::Timer>(timer: &T, param: [usize; 6], function: usize) -> SbiRet {
+    match () {
+        #[cfg(target_pointer_width = "64")]
+        () => {
+            let [param0] = [param[0]];
+            match function {
+                spec::time::SET_TIMER => {
+                    timer.set_timer(param0 as _);
+                    SbiRet::success(0)
+                }
+                _ => SbiRet::not_supported(),
+            }
+        }
+        #[cfg(target_pointer_width = "32")]
+        () => {
+            let [param0, param1] = [param[0], param[1]];
+            match function {
+                spec::time::SET_TIMER => {
+                    timer.set_timer(concat_u32(param1, param0));
+                    SbiRet::success(0)
+                }
+                _ => SbiRet::not_supported(),
+            }
+        }
+    }
+}
+
+#[doc(hidden)]
+#[inline(always)]
+pub fn _rustsbi_ipi<T: crate::Ipi>(ipi: &T, param: [usize; 6], function: usize) -> SbiRet {
+    let [param0, param1] = [param[0], param[1]];
+    match function {
+        spec::spi::SEND_IPI => ipi.send_ipi(HartMask::from_mask_base(param0, param1)),
+        _ => SbiRet::not_supported(),
+    }
+}
+
+#[doc(hidden)]
+#[inline(always)]
+pub fn _rustsbi_hsm<T: crate::Hsm>(hsm: &T, param: [usize; 6], function: usize) -> SbiRet {
+    let [param0, param1, param2] = [param[0], param[1], param[2]];
+    match function {
+        spec::hsm::HART_START => hsm.hart_start(param0, param1, param2),
+        spec::hsm::HART_STOP => hsm.hart_stop(),
+        spec::hsm::HART_GET_STATUS => hsm.hart_get_status(param0),
+        spec::hsm::HART_SUSPEND => {
+            if let Ok(suspend_type) = u32::try_from(param0) {
+                hsm.hart_suspend(suspend_type, param1, param2)
+            } else {
+                SbiRet::invalid_param()
+            }
+        }
+        _ => SbiRet::not_supported(),
+    }
+}
+
+#[doc(hidden)]
+#[inline(always)]
+pub fn _rustsbi_reset<T: crate::Reset>(reset: &T, param: [usize; 6], function: usize) -> SbiRet {
+    let [param0, param1] = [param[0], param[1]];
+    match function {
+        spec::srst::SYSTEM_RESET => match (u32::try_from(param0), u32::try_from(param1)) {
+            (Ok(reset_type), Ok(reset_reason)) => reset.system_reset(reset_type, reset_reason),
+            (_, _) => SbiRet::invalid_param(),
+        },
+        _ => SbiRet::not_supported(),
+    }
+}
+
+#[doc(hidden)]
+#[inline(always)]
+pub fn _rustsbi_pmu<T: crate::Pmu>(pmu: &T, param: [usize; 6], function: usize) -> SbiRet {
+    match () {
+        #[cfg(target_pointer_width = "64")]
+        () => {
+            let [param0, param1, param2, param3, param4] =
+                [param[0], param[1], param[2], param[3], param[4]];
+            match function {
+                spec::pmu::NUM_COUNTERS => SbiRet::success(pmu.num_counters()),
+                spec::pmu::COUNTER_GET_INFO => pmu.counter_get_info(param0),
+                spec::pmu::COUNTER_CONFIG_MATCHING => {
+                    pmu.counter_config_matching(param0, param1, param2, param3, param4 as _)
+                }
+                spec::pmu::COUNTER_START => pmu.counter_start(param0, param1, param2, param3 as _),
+                spec::pmu::COUNTER_STOP => pmu.counter_stop(param0, param1, param2),
+                spec::pmu::COUNTER_FW_READ => pmu.counter_fw_read(param0),
+                spec::pmu::COUNTER_FW_READ_HI => pmu.counter_fw_read_hi(param0),
+                _ => SbiRet::not_supported(),
+            }
+        }
+        #[cfg(target_pointer_width = "32")]
+        () => {
+            let [param0, param1, param2, param3, param4, param5] =
+                [param[0], param[1], param[2], param[3], param[4], param[5]];
+            match function {
+                spec::pmu::NUM_COUNTERS => SbiRet::success(pmu.num_counters()),
+                spec::pmu::COUNTER_GET_INFO => pmu.counter_get_info(param0),
+                spec::pmu::COUNTER_CONFIG_MATCHING => pmu.counter_config_matching(
+                    param0,
+                    param1,
+                    param2,
+                    param3,
+                    concat_u32(param5, param4),
+                ),
+                spec::pmu::COUNTER_START => {
+                    pmu.counter_start(param0, param1, param2, concat_u32(param4, param3))
+                }
+                spec::pmu::COUNTER_STOP => pmu.counter_stop(param0, param1, param2),
+                spec::pmu::COUNTER_FW_READ => pmu.counter_fw_read(param0),
+                spec::pmu::COUNTER_FW_READ_HI => pmu.counter_fw_read_hi(param0),
+                _ => SbiRet::not_supported(),
+            }
+        }
+    }
+}
+
+#[doc(hidden)]
+#[inline(always)]
+pub fn _rustsbi_console<T: crate::Console>(
+    console: &T,
+    param: [usize; 6],
+    function: usize,
+) -> SbiRet {
+    let [param0, param1, param2] = [param[0], param[1], param[2]];
+    match function {
+        spec::dbcn::CONSOLE_WRITE => {
+            let bytes = Physical::new(param0, param1, param2);
+            console.write(bytes)
+        }
+        spec::dbcn::CONSOLE_READ => {
+            let bytes = Physical::new(param0, param1, param2);
+            console.read(bytes)
+        }
+        spec::dbcn::CONSOLE_WRITE_BYTE => console.write_byte((param0 & 0xFF) as u8),
+        _ => SbiRet::not_supported(),
+    }
+}
+
+#[doc(hidden)]
+#[inline(always)]
+pub fn _rustsbi_susp<T: crate::Susp>(susp: &T, param: [usize; 6], function: usize) -> SbiRet {
+    let [param0, param1, param2] = [param[0], param[1], param[2]];
+    match function {
+        spec::susp::SUSPEND => match u32::try_from(param0) {
+            Ok(sleep_type) => susp.system_suspend(sleep_type, param1, param2),
+            _ => SbiRet::invalid_param(),
+        },
+        _ => SbiRet::not_supported(),
+    }
+}
+
+#[doc(hidden)]
+#[inline(always)]
+pub fn _rustsbi_cppc<T: crate::Cppc>(cppc: &T, param: [usize; 6], function: usize) -> SbiRet {
+    match () {
+        #[cfg(target_pointer_width = "64")]
+        () => {
+            let [param0, param1] = [param[0], param[1]];
+            match function {
+                spec::cppc::PROBE => match u32::try_from(param0) {
+                    Ok(reg_id) => cppc.probe(reg_id),
+                    _ => SbiRet::invalid_param(),
+                },
+                spec::cppc::READ => match u32::try_from(param0) {
+                    Ok(reg_id) => cppc.read(reg_id),
+                    _ => SbiRet::invalid_param(),
+                },
+                spec::cppc::READ_HI => match u32::try_from(param0) {
+                    Ok(reg_id) => cppc.read_hi(reg_id),
+                    _ => SbiRet::invalid_param(),
+                },
+                spec::cppc::WRITE => match u32::try_from(param0) {
+                    Ok(reg_id) => cppc.write(reg_id, param1 as _),
+                    _ => SbiRet::invalid_param(),
+                },
+                _ => SbiRet::not_supported(),
+            }
+        }
+        #[cfg(target_pointer_width = "32")]
+        () => {
+            let [param0, param1, param2] = [param[0], param[1], param[2]];
+            match function {
+                spec::cppc::PROBE => cppc.probe(param0 as _),
+                spec::cppc::READ => cppc.read(param0 as _),
+                spec::cppc::READ_HI => cppc.read_hi(param0 as _),
+                spec::cppc::WRITE => cppc.write(param0 as _, concat_u32(param2, param1)),
+                _ => SbiRet::not_supported(),
+            }
+        }
+    }
+}
+
+#[doc(hidden)]
+#[inline(always)]
+pub fn _rustsbi_nacl<T: crate::Nacl>(nacl: &T, param: [usize; 6], function: usize) -> SbiRet {
+    let [param0, param1, param2] = [param[0], param[1], param[2]];
+    match function {
+        spec::nacl::PROBE_FEATURE => match u32::try_from(param0) {
+            Ok(feature_id) => nacl.probe_feature(feature_id),
+            _ => SbiRet::invalid_param(),
+        },
+        spec::nacl::SET_SHMEM => nacl.set_shmem(SharedPtr::new(param0, param1), param2),
+        spec::nacl::SYNC_CSR => nacl.sync_csr(param0),
+        spec::nacl::SYNC_HFENCE => nacl.sync_hfence(param0),
+        spec::nacl::SYNC_SRET => nacl.sync_sret(),
+        _ => SbiRet::not_supported(),
+    }
+}
+
+#[doc(hidden)]
+#[inline(always)]
+pub fn _rustsbi_sta<T: crate::Sta>(sta: &T, param: [usize; 6], function: usize) -> SbiRet {
+    let [param0, param1, param2] = [param[0], param[1], param[2]];
+    match function {
+        spec::sta::SET_SHMEM => sta.set_shmem(SharedPtr::new(param0, param1), param2),
+        _ => SbiRet::not_supported(),
+    }
+}
+
+#[cfg(target_pointer_width = "32")]
+#[inline]
+const fn concat_u32(h: usize, l: usize) -> u64 {
+    ((h as u64) << 32) | (l as u64)
+}

+ 311 - 0
tests/build-full.rs

@@ -0,0 +1,311 @@
+use rustsbi::RustSBI;
+use sbi_spec::{
+    binary::{Physical, SbiRet, SharedPtr},
+    nacl::shmem_size::NATIVE,
+};
+
+#[derive(RustSBI)]
+struct FullyImplemented {
+    console: DummyConsole,
+    cppc: DummyCppc,
+    hsm: DummyHsm,
+    ipi: DummyIpi,
+    nacl: DummyNacl,
+    pmu: DummyPmu,
+    reset: DummyReset,
+    fence: DummyFence,
+    sta: DummySta,
+    susp: DummySusp,
+    timer: DummyTimer,
+    info: DummyEnvInfo,
+}
+
+#[derive(RustSBI)]
+struct AlternateName {
+    dbcn: DummyConsole,
+    cppc: DummyCppc,
+    hsm: DummyHsm,
+    ipi: DummyIpi,
+    nacl: DummyNacl,
+    pmu: DummyPmu,
+    srst: DummyReset,
+    rfnc: DummyFence,
+    sta: DummySta,
+    susp: DummySusp,
+    time: DummyTimer,
+    info: DummyEnvInfo,
+}
+
+#[derive(RustSBI)]
+struct TupleStruct(
+    #[rustsbi(dbcn)] DummyConsole,
+    #[rustsbi(cppc)] DummyCppc,
+    #[rustsbi(hsm)] DummyHsm,
+    #[rustsbi(ipi)] DummyIpi,
+    #[rustsbi(nacl)] DummyNacl,
+    #[rustsbi(pmu)] DummyPmu,
+    #[rustsbi(srst)] DummyReset,
+    #[rustsbi(rfnc)] DummyFence,
+    #[rustsbi(sta)] DummySta,
+    #[rustsbi(susp)] DummySusp,
+    #[rustsbi(time)] DummyTimer,
+    #[rustsbi(info)] DummyEnvInfo,
+);
+
+#[cfg(feature = "machine")]
+#[derive(RustSBI)]
+struct UnitStruct;
+
+#[test]
+fn rustsbi_impl_id() {
+    let sbi = FullyImplemented {
+        console: DummyConsole,
+        cppc: DummyCppc,
+        hsm: DummyHsm,
+        ipi: DummyIpi,
+        nacl: DummyNacl,
+        pmu: DummyPmu,
+        reset: DummyReset,
+        fence: DummyFence,
+        sta: DummySta,
+        susp: DummySusp,
+        timer: DummyTimer,
+        info: DummyEnvInfo,
+    };
+    assert_eq!(sbi.handle_ecall(0x10, 0x1, [0; 6]).value, 4);
+    let sbi = AlternateName {
+        dbcn: DummyConsole,
+        cppc: DummyCppc,
+        hsm: DummyHsm,
+        ipi: DummyIpi,
+        nacl: DummyNacl,
+        pmu: DummyPmu,
+        srst: DummyReset,
+        rfnc: DummyFence,
+        sta: DummySta,
+        susp: DummySusp,
+        time: DummyTimer,
+        info: DummyEnvInfo,
+    };
+    assert_eq!(sbi.handle_ecall(0x10, 0x1, [0; 6]).value, 4);
+    let sbi = TupleStruct(
+        DummyConsole,
+        DummyCppc,
+        DummyHsm,
+        DummyIpi,
+        DummyNacl,
+        DummyPmu,
+        DummyReset,
+        DummyFence,
+        DummySta,
+        DummySusp,
+        DummyTimer,
+        DummyEnvInfo,
+    );
+    assert_eq!(sbi.handle_ecall(0x10, 0x1, [0; 6]).value, 4);
+}
+
+#[cfg(feature = "machine")]
+#[test]
+fn unit_struct() {
+    let sbi = UnitStruct;
+    assert_eq!(sbi.handle_ecall(0x10, 0x1, [0; 6]).value, 4);
+}
+
+#[test]
+fn extension_impl() {
+    let sbi = FullyImplemented {
+        console: DummyConsole,
+        cppc: DummyCppc,
+        hsm: DummyHsm,
+        ipi: DummyIpi,
+        nacl: DummyNacl,
+        pmu: DummyPmu,
+        reset: DummyReset,
+        fence: DummyFence,
+        sta: DummySta,
+        susp: DummySusp,
+        timer: DummyTimer,
+        info: DummyEnvInfo,
+    };
+    assert_eq!(
+        sbi.handle_ecall(0x4442434E, 0x0, [0; 6]).error,
+        -1isize as _
+    );
+}
+
+struct DummyConsole;
+
+impl rustsbi::Console for DummyConsole {
+    fn write(&self, _: Physical<&[u8]>) -> SbiRet {
+        // special return value for test cases
+        SbiRet::failed()
+    }
+
+    fn read(&self, _: Physical<&mut [u8]>) -> SbiRet {
+        unimplemented!()
+    }
+
+    fn write_byte(&self, _: u8) -> SbiRet {
+        unimplemented!()
+    }
+}
+
+struct DummyCppc;
+
+impl rustsbi::Cppc for DummyCppc {
+    fn probe(&self, _: u32) -> SbiRet {
+        unimplemented!()
+    }
+
+    fn read(&self, _: u32) -> SbiRet {
+        unimplemented!()
+    }
+
+    fn read_hi(&self, _: u32) -> SbiRet {
+        unimplemented!()
+    }
+
+    fn write(&self, _: u32, _: u64) -> SbiRet {
+        unimplemented!()
+    }
+}
+
+struct DummyHsm;
+
+impl rustsbi::Hsm for DummyHsm {
+    fn hart_start(&self, _: usize, _: usize, _: usize) -> SbiRet {
+        unimplemented!()
+    }
+
+    fn hart_stop(&self) -> SbiRet {
+        unimplemented!()
+    }
+
+    fn hart_get_status(&self, _: usize) -> SbiRet {
+        unimplemented!()
+    }
+}
+
+struct DummyIpi;
+
+impl rustsbi::Ipi for DummyIpi {
+    fn send_ipi(&self, _: rustsbi::HartMask) -> SbiRet {
+        unimplemented!()
+    }
+}
+
+struct DummyNacl;
+
+impl rustsbi::Nacl for DummyNacl {
+    fn probe_feature(&self, _: u32) -> SbiRet {
+        unimplemented!()
+    }
+    fn set_shmem(&self, _: SharedPtr<[u8; NATIVE]>, _: usize) -> SbiRet {
+        unimplemented!()
+    }
+
+    fn sync_csr(&self, _: usize) -> SbiRet {
+        unimplemented!()
+    }
+
+    fn sync_hfence(&self, _: usize) -> SbiRet {
+        unimplemented!()
+    }
+
+    fn sync_sret(&self) -> SbiRet {
+        unimplemented!()
+    }
+}
+
+struct DummyPmu;
+
+impl rustsbi::Pmu for DummyPmu {
+    fn num_counters(&self) -> usize {
+        unimplemented!()
+    }
+
+    fn counter_get_info(&self, _: usize) -> SbiRet {
+        unimplemented!()
+    }
+
+    fn counter_config_matching(&self, _: usize, _: usize, _: usize, _: usize, _: u64) -> SbiRet {
+        unimplemented!()
+    }
+
+    fn counter_start(&self, _: usize, _: usize, _: usize, _: u64) -> SbiRet {
+        unimplemented!()
+    }
+
+    fn counter_stop(&self, _: usize, _: usize, _: usize) -> SbiRet {
+        unimplemented!()
+    }
+
+    fn counter_fw_read(&self, _: usize) -> SbiRet {
+        unimplemented!()
+    }
+}
+
+struct DummyReset;
+
+impl rustsbi::Reset for DummyReset {
+    fn system_reset(&self, _: u32, _: u32) -> SbiRet {
+        unimplemented!()
+    }
+}
+
+struct DummyFence;
+
+impl rustsbi::Fence for DummyFence {
+    fn remote_fence_i(&self, _: rustsbi::HartMask) -> SbiRet {
+        unimplemented!()
+    }
+
+    fn remote_sfence_vma(&self, _: rustsbi::HartMask, _: usize, _: usize) -> SbiRet {
+        unimplemented!()
+    }
+
+    fn remote_sfence_vma_asid(&self, _: rustsbi::HartMask, _: usize, _: usize, _: usize) -> SbiRet {
+        unimplemented!()
+    }
+}
+
+struct DummySta;
+
+impl rustsbi::Sta for DummySta {
+    fn set_shmem(&self, _: SharedPtr<[u8; 64]>, _: usize) -> SbiRet {
+        unimplemented!()
+    }
+}
+
+struct DummySusp;
+
+impl rustsbi::Susp for DummySusp {
+    fn system_suspend(&self, _: u32, _: usize, _: usize) -> SbiRet {
+        unimplemented!()
+    }
+}
+
+struct DummyTimer;
+
+impl rustsbi::Timer for DummyTimer {
+    fn set_timer(&self, _: u64) {
+        unimplemented!()
+    }
+}
+
+struct DummyEnvInfo;
+
+impl rustsbi::EnvInfo for DummyEnvInfo {
+    fn mvendorid(&self) -> usize {
+        unimplemented!()
+    }
+
+    fn marchid(&self) -> usize {
+        unimplemented!()
+    }
+
+    fn mimpid(&self) -> usize {
+        unimplemented!()
+    }
+}

+ 106 - 0
tests/build-generics.rs

@@ -0,0 +1,106 @@
+use rustsbi::RustSBI;
+use sbi_spec::binary::SbiRet;
+
+// These structs should pass Rust build.
+
+#[derive(RustSBI)]
+struct WithGenerics<T: rustsbi::Timer> {
+    reset: DummyReset,
+    timer: T,
+    info: DummyEnvInfo,
+}
+
+#[derive(RustSBI)]
+struct WithWhereClause<T>
+where
+    T: rustsbi::Timer,
+{
+    reset: DummyReset,
+    timer: T,
+    info: DummyEnvInfo,
+}
+
+#[derive(RustSBI)]
+struct WithConstantGenerics<const N: usize> {
+    info: DummyEnvInfo,
+    _dummy: [u8; N],
+}
+
+#[derive(RustSBI)]
+struct WithLifetime<'a> {
+    info: &'a DummyEnvInfo,
+}
+
+#[derive(RustSBI)]
+struct WithEverythingCombined<'a, T: rustsbi::Timer, U, const N: usize>
+where
+    U: rustsbi::Reset,
+{
+    timer: T,
+    reset: U,
+    info: &'a DummyEnvInfo,
+    _dummy: [u8; N],
+}
+
+#[test]
+fn test_impl_id() {
+    let sbi = WithGenerics {
+        reset: DummyReset,
+        timer: DummyTimer,
+        info: DummyEnvInfo,
+    };
+    assert_eq!(sbi.handle_ecall(0x10, 0x1, [0; 6]).value, 4);
+    let sbi = WithWhereClause {
+        reset: DummyReset,
+        timer: DummyTimer,
+        info: DummyEnvInfo,
+    };
+    assert_eq!(sbi.handle_ecall(0x10, 0x1, [0; 6]).value, 4);
+    let sbi = WithConstantGenerics {
+        info: DummyEnvInfo,
+        _dummy: [0; 100],
+    };
+    assert_eq!(sbi.handle_ecall(0x10, 0x1, [0; 6]).value, 4);
+    let dummy_info = DummyEnvInfo;
+    let sbi = WithLifetime { info: &dummy_info };
+    assert_eq!(sbi.handle_ecall(0x10, 0x1, [0; 6]).value, 4);
+    let sbi = WithEverythingCombined {
+        timer: DummyTimer,
+        reset: DummyReset,
+        info: &dummy_info,
+        _dummy: [0; 10],
+    };
+    assert_eq!(sbi.handle_ecall(0x10, 0x1, [0; 6]).value, 4);
+}
+
+struct DummyReset;
+
+impl rustsbi::Reset for DummyReset {
+    fn system_reset(&self, _: u32, _: u32) -> SbiRet {
+        unimplemented!()
+    }
+}
+
+struct DummyTimer;
+
+impl rustsbi::Timer for DummyTimer {
+    fn set_timer(&self, _: u64) {
+        unimplemented!()
+    }
+}
+
+struct DummyEnvInfo;
+
+impl rustsbi::EnvInfo for DummyEnvInfo {
+    fn mvendorid(&self) -> usize {
+        unimplemented!()
+    }
+
+    fn marchid(&self) -> usize {
+        unimplemented!()
+    }
+
+    fn mimpid(&self) -> usize {
+        unimplemented!()
+    }
+}

+ 154 - 0
tests/build-rename.rs

@@ -0,0 +1,154 @@
+use std::cell::RefCell;
+
+use rustsbi::{HartMask, RustSBI};
+use sbi_spec::{
+    binary::{Physical, SbiRet},
+    dbcn::EID_DBCN,
+    rfnc::EID_RFNC,
+    spi::EID_SPI,
+    time::EID_TIME,
+};
+
+#[derive(RustSBI)]
+struct AssignOne {
+    console: DummyConsole,
+    #[rustsbi(fence)]
+    some_other_name: DummyFence,
+    info: DummyEnvInfo,
+}
+
+#[derive(RustSBI)]
+struct AssignMultiple {
+    #[rustsbi(ipi, timer)]
+    clint: MockClint,
+    info: DummyEnvInfo,
+}
+
+#[test]
+fn rustsbi_assign_one() {
+    let sbi = AssignOne {
+        console: DummyConsole,
+        some_other_name: DummyFence,
+        info: DummyEnvInfo,
+    };
+    // Both extension return correct values.
+    assert_eq!(sbi.handle_ecall(EID_DBCN, 0x0, [0; 6]), SbiRet::success(1));
+    assert_eq!(sbi.handle_ecall(EID_DBCN, 0x1, [0; 6]), SbiRet::success(2));
+    assert_eq!(sbi.handle_ecall(EID_DBCN, 0x2, [0; 6]), SbiRet::success(3));
+    assert_eq!(sbi.handle_ecall(EID_RFNC, 0x0, [0; 6]), SbiRet::success(4));
+    assert_eq!(sbi.handle_ecall(EID_RFNC, 0x1, [0; 6]), SbiRet::success(5));
+    assert_eq!(sbi.handle_ecall(EID_RFNC, 0x2, [0; 6]), SbiRet::success(6));
+    // Both extension exists.
+    assert_ne!(
+        sbi.handle_ecall(0x10, 0x3, [EID_DBCN, 0, 0, 0, 0, 0]),
+        SbiRet::success(0)
+    );
+    assert_ne!(
+        sbi.handle_ecall(0x10, 0x3, [EID_RFNC, 0, 0, 0, 0, 0]),
+        SbiRet::success(0)
+    );
+}
+
+#[test]
+fn rustsbi_assign_multiple() {
+    let sbi = AssignMultiple {
+        clint: MockClint {
+            time: RefCell::new(0),
+        },
+        info: DummyEnvInfo,
+    };
+    assert_eq!(sbi.handle_ecall(EID_SPI, 0x0, [0; 6]), SbiRet::success(10));
+    #[cfg(target_pointer_width = "64")]
+    sbi.handle_ecall(EID_TIME, 0x0, [0x1122334455667788, 0, 0, 0, 0, 0]);
+    #[cfg(target_pointer_width = "32")]
+    sbi.handle_ecall(EID_TIME, 0x0, [0x11223344, 0x55667788, 0, 0, 0, 0]);
+    assert_eq!(sbi.clint.time.take(), 0x1122334455667788);
+    assert_ne!(
+        sbi.handle_ecall(0x10, 0x3, [EID_SPI, 0, 0, 0, 0, 0]),
+        SbiRet::success(0)
+    );
+    assert_ne!(
+        sbi.handle_ecall(0x10, 0x3, [EID_TIME, 0, 0, 0, 0, 0]),
+        SbiRet::success(0)
+    );
+}
+
+struct DummyConsole;
+
+impl rustsbi::Console for DummyConsole {
+    fn write(&self, _: Physical<&[u8]>) -> SbiRet {
+        SbiRet::success(1)
+    }
+
+    fn read(&self, _: Physical<&mut [u8]>) -> SbiRet {
+        SbiRet::success(2)
+    }
+
+    fn write_byte(&self, _: u8) -> SbiRet {
+        SbiRet::success(3)
+    }
+}
+
+struct DummyFence;
+
+impl rustsbi::Fence for DummyFence {
+    fn remote_fence_i(&self, _: rustsbi::HartMask) -> SbiRet {
+        SbiRet::success(4)
+    }
+
+    fn remote_sfence_vma(&self, _: rustsbi::HartMask, _: usize, _: usize) -> SbiRet {
+        SbiRet::success(5)
+    }
+
+    fn remote_sfence_vma_asid(&self, _: rustsbi::HartMask, _: usize, _: usize, _: usize) -> SbiRet {
+        SbiRet::success(6)
+    }
+}
+
+struct DummyEnvInfo;
+
+impl rustsbi::EnvInfo for DummyEnvInfo {
+    fn mvendorid(&self) -> usize {
+        unimplemented!()
+    }
+
+    fn marchid(&self) -> usize {
+        unimplemented!()
+    }
+
+    fn mimpid(&self) -> usize {
+        unimplemented!()
+    }
+}
+
+struct RealEnvInfo;
+
+impl rustsbi::EnvInfo for RealEnvInfo {
+    fn mvendorid(&self) -> usize {
+        7
+    }
+
+    fn marchid(&self) -> usize {
+        8
+    }
+
+    fn mimpid(&self) -> usize {
+        9
+    }
+}
+
+struct MockClint {
+    time: RefCell<u64>,
+}
+
+impl rustsbi::Timer for MockClint {
+    fn set_timer(&self, stime_value: u64) {
+        self.time.replace(stime_value);
+    }
+}
+
+impl rustsbi::Ipi for MockClint {
+    fn send_ipi(&self, _: HartMask) -> SbiRet {
+        SbiRet::success(10)
+    }
+}

+ 155 - 0
tests/build-skip.rs

@@ -0,0 +1,155 @@
+use rustsbi::RustSBI;
+use sbi_spec::{
+    binary::{Physical, SbiRet},
+    dbcn::EID_DBCN,
+    rfnc::EID_RFNC,
+};
+
+#[derive(RustSBI)]
+struct SkipExtension {
+    console: DummyConsole,
+    #[rustsbi(skip)]
+    fence: DummyFence,
+    info: DummyEnvInfo,
+}
+
+#[derive(RustSBI)]
+struct SkipEnvInfo {
+    console: DummyConsole,
+    fence: DummyFence,
+    #[rustsbi(skip)]
+    info: DummyEnvInfo,
+    env_info: RealEnvInfo,
+}
+
+#[test]
+fn rustsbi_skip_extension() {
+    let sbi = SkipExtension {
+        console: DummyConsole,
+        fence: DummyFence,
+        info: DummyEnvInfo,
+    };
+    // 1. Skipped fields are neither used during RustSBI macro generation, ...
+    assert_eq!(sbi.handle_ecall(EID_DBCN, 0x0, [0; 6]), SbiRet::success(1));
+    assert_eq!(sbi.handle_ecall(EID_DBCN, 0x1, [0; 6]), SbiRet::success(2));
+    assert_eq!(sbi.handle_ecall(EID_DBCN, 0x2, [0; 6]), SbiRet::success(3));
+    assert_eq!(
+        sbi.handle_ecall(EID_RFNC, 0x0, [0; 6]),
+        SbiRet::not_supported()
+    );
+    assert_eq!(
+        sbi.handle_ecall(EID_RFNC, 0x1, [0; 6]),
+        SbiRet::not_supported()
+    );
+    assert_eq!(
+        sbi.handle_ecall(EID_RFNC, 0x2, [0; 6]),
+        SbiRet::not_supported()
+    );
+    // 2. ... nor do they appear in extension detection in Base extension.
+    // note that it's `assert_ne` - the handle_ecall should not return a success detection with
+    // value 0 indicating this feature is not supported, ...
+    assert_ne!(
+        sbi.handle_ecall(0x10, 0x3, [EID_DBCN, 0, 0, 0, 0, 0]),
+        SbiRet::success(0)
+    );
+    // ... and the `assert_eq` here means extension detection detected successfully that this
+    // extension is not supported in SBI implementation `SkipExtension`.
+    assert_eq!(
+        sbi.handle_ecall(0x10, 0x3, [EID_RFNC, 0, 0, 0, 0, 0]),
+        SbiRet::success(0)
+    );
+    // Additionally, we illustrate here that the skipped fields may be used elsewhere.
+    let _ = sbi.fence;
+}
+
+#[test]
+fn rustsbi_skip_env_info() {
+    let sbi = SkipEnvInfo {
+        console: DummyConsole,
+        fence: DummyFence,
+        info: DummyEnvInfo,
+        env_info: RealEnvInfo,
+    };
+    // The `env_info` instead of `info` field would be used by RustSBI macro; struct
+    // `RealEnvInfo` would return 7, 8 and 9 for mvendorid, marchid and mimpid.
+    assert_eq!(
+        sbi.handle_ecall(0x10, 0x4, [EID_DBCN, 0, 0, 0, 0, 0]),
+        SbiRet::success(7)
+    );
+    assert_eq!(
+        sbi.handle_ecall(0x10, 0x5, [EID_DBCN, 0, 0, 0, 0, 0]),
+        SbiRet::success(8)
+    );
+    assert_eq!(
+        sbi.handle_ecall(0x10, 0x6, [EID_DBCN, 0, 0, 0, 0, 0]),
+        SbiRet::success(9)
+    );
+    let _ = sbi.info;
+}
+
+// Return values of following trait impls are special values,
+// they are used by software to detect if one implementation is used by RustSBI.
+
+struct DummyConsole;
+
+impl rustsbi::Console for DummyConsole {
+    fn write(&self, _: Physical<&[u8]>) -> SbiRet {
+        SbiRet::success(1)
+    }
+
+    fn read(&self, _: Physical<&mut [u8]>) -> SbiRet {
+        SbiRet::success(2)
+    }
+
+    fn write_byte(&self, _: u8) -> SbiRet {
+        SbiRet::success(3)
+    }
+}
+
+struct DummyFence;
+
+impl rustsbi::Fence for DummyFence {
+    fn remote_fence_i(&self, _: rustsbi::HartMask) -> SbiRet {
+        SbiRet::success(4)
+    }
+
+    fn remote_sfence_vma(&self, _: rustsbi::HartMask, _: usize, _: usize) -> SbiRet {
+        SbiRet::success(5)
+    }
+
+    fn remote_sfence_vma_asid(&self, _: rustsbi::HartMask, _: usize, _: usize, _: usize) -> SbiRet {
+        SbiRet::success(6)
+    }
+}
+
+struct DummyEnvInfo;
+
+impl rustsbi::EnvInfo for DummyEnvInfo {
+    fn mvendorid(&self) -> usize {
+        unimplemented!()
+    }
+
+    fn marchid(&self) -> usize {
+        unimplemented!()
+    }
+
+    fn mimpid(&self) -> usize {
+        unimplemented!()
+    }
+}
+
+struct RealEnvInfo;
+
+impl rustsbi::EnvInfo for RealEnvInfo {
+    fn mvendorid(&self) -> usize {
+        7
+    }
+
+    fn marchid(&self) -> usize {
+        8
+    }
+
+    fn mimpid(&self) -> usize {
+        9
+    }
+}