Browse Source

lib: instance based, machine independent RustSBI for hypervisor development

machine independent RustSBI library may build on stable rust
luojia65 2 years ago
parent
commit
7d98cb9456
11 changed files with 470 additions and 14 deletions
  1. 1 0
      CHANGELOG.md
  2. 12 2
      Cargo.toml
  3. 1 0
      src/base.rs
  4. 8 0
      src/hsm.rs
  5. 384 0
      src/instance.rs
  6. 9 2
      src/ipi.rs
  7. 23 9
      src/lib.rs
  8. 10 0
      src/pmu.rs
  9. 6 1
      src/reset.rs
  10. 11 0
      src/rfence.rs
  11. 5 0
      src/timer.rs

+ 1 - 0
CHANGELOG.md

@@ -9,6 +9,7 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
 ### Added
 
+- Instance based and/or machine independent RustSBI to support hypervisor development
 - Feature `legacy` to gate SBI legacy extension
 - Expose `init_*` functions on instance based RustSBI implementation
 - LEGACY_CLEAR_IPI implemented

+ 12 - 2
Cargo.toml

@@ -16,11 +16,21 @@ categories = ["os", "embedded", "hardware-support", "no-std"]
 edition = "2021"
 
 [dependencies]
-riscv = "0.9.0"
 sbi-spec = "0.0.4"
+riscv = { version = "0.9.0", optional = true }
 
 [features]
-default = []
+default = ["machine"]
+# Run RustSBI on machine mode
+# 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"]
+# Build RustSBI singleton
+# This would enable `init_*` functions. It will take extra place on bss or data region
+# to take care of singleton reference locks.
+# Disable this feature to use instance based RustSBI environment.
+singleton = ["dep:riscv", "machine"]
 # Support legacy extension; this feature is not included by default.
 legacy = ["sbi-spec/legacy"]
 

+ 1 - 0
src/base.rs

@@ -1,3 +1,4 @@
+#[cfg(feature = "singleton")]
 #[inline]
 pub fn probe_extension(extension: usize) -> bool {
     use sbi_spec::*;

+ 8 - 0
src/hsm.rs

@@ -190,10 +190,13 @@ pub trait Hsm: Send + Sync {
     }
 }
 
+#[cfg(feature = "singleton")]
 use crate::util::AmoOnceRef;
 
+#[cfg(feature = "singleton")]
 static HSM: AmoOnceRef<dyn Hsm> = AmoOnceRef::new();
 
+#[cfg(feature = "singleton")]
 /// Init HSM module
 pub fn init_hsm(hsm: &'static dyn Hsm) {
     if !HSM.try_call_once(hsm) {
@@ -201,11 +204,13 @@ pub fn init_hsm(hsm: &'static dyn Hsm) {
     }
 }
 
+#[cfg(feature = "singleton")]
 #[inline]
 pub(crate) fn probe_hsm() -> bool {
     HSM.get().is_some()
 }
 
+#[cfg(feature = "singleton")]
 #[inline]
 pub(crate) fn hart_start(hartid: usize, start_addr: usize, opaque: usize) -> SbiRet {
     if let Some(obj) = HSM.get() {
@@ -214,6 +219,7 @@ pub(crate) fn hart_start(hartid: usize, start_addr: usize, opaque: usize) -> Sbi
     SbiRet::not_supported()
 }
 
+#[cfg(feature = "singleton")]
 #[inline]
 pub(crate) fn hart_stop() -> SbiRet {
     if let Some(obj) = HSM.get() {
@@ -222,6 +228,7 @@ pub(crate) fn hart_stop() -> SbiRet {
     SbiRet::not_supported()
 }
 
+#[cfg(feature = "singleton")]
 #[inline]
 pub(crate) fn hart_get_status(hartid: usize) -> SbiRet {
     if let Some(obj) = HSM.get() {
@@ -230,6 +237,7 @@ pub(crate) fn hart_get_status(hartid: usize) -> SbiRet {
     SbiRet::not_supported()
 }
 
+#[cfg(feature = "singleton")]
 #[inline]
 pub(crate) fn hart_suspend(suspend_type: u32, resume_addr: usize, opaque: usize) -> SbiRet {
     if let Some(obj) = HSM.get() {

+ 384 - 0
src/instance.rs

@@ -0,0 +1,384 @@
+use crate::{
+    spec::binary::SbiRet, Fence, HartMask, Hsm, Ipi, Pmu, Reset, Timer, IMPL_ID_RUSTSBI,
+    RUSTSBI_VERSION, SBI_SPEC_MAJOR, SBI_SPEC_MINOR,
+};
+#[cfg(feature = "machine")]
+use riscv::register::{marchid, mimpid, mvendorid};
+
+/// 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> {
+    timer: Option<T>,
+    ipi: Option<I>,
+    rfnc: Option<R>,
+    hsm: Option<H>,
+    srst: Option<S>,
+    pmu: Option<P>,
+    #[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> RustSBI<T, I, R, H, S, P> {
+    /// 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) -> Self {
+        Self {
+            timer: Some(timer),
+            ipi: Some(ipi),
+            rfnc: Some(rfnc),
+            hsm: Some(hsm),
+            srst: Some(srst),
+            pmu: Some(pmu),
+        }
+    }
+
+    /// Handle supervisor environment call with given parameters and return the `SbiRet` result.
+    #[inline]
+    pub fn handle_ecall(&mut self, extension: usize, function: usize, param: [usize; 6]) -> SbiRet {
+        match extension {
+            spec::rfnc::EID_RFNC => {
+                let rfnc = if let Some(rfnc) = &mut self.rfnc {
+                    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 timer = if let Some(timer) = &mut self.timer {
+                        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 timer = if let Some(timer) = &mut self.timer {
+                        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 ipi = if let Some(ipi) = &mut self.ipi {
+                    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 hsm = if let Some(hsm) = &mut self.hsm {
+                    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 srst = if let Some(srst) = &mut self.srst {
+                    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 pmu = if let Some(pmu) = &mut self.pmu {
+                        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::PMU_NUM_COUNTERS => SbiRet::success(pmu.num_counters()),
+                        spec::pmu::PMU_COUNTER_GET_INFO => pmu.counter_get_info(param0),
+                        spec::pmu::PMU_COUNTER_CONFIG_MATCHING => {
+                            pmu.counter_config_matching(param0, param1, param2, param3, param4 as _)
+                        }
+                        spec::pmu::PMU_COUNTER_START => {
+                            pmu.counter_start(param0, param1, param2, param3 as _)
+                        }
+                        spec::pmu::PMU_COUNTER_STOP => pmu.counter_stop(param0, param1, param2),
+                        spec::pmu::PMU_COUNTER_FW_READ => pmu.counter_fw_read(param0),
+                        _ => SbiRet::not_supported(),
+                    }
+                }
+                #[cfg(target_pointer_width = "32")]
+                () => {
+                    let pmu = if let Some(pmu) = &mut self.pmu {
+                        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::PMU_NUM_COUNTERS => SbiRet::success(pmu.num_counters()),
+                        spec::pmu::PMU_COUNTER_GET_INFO => pmu.counter_get_info(param0),
+                        spec::pmu::PMU_COUNTER_CONFIG_MATCHING => pmu.counter_config_matching(
+                            param0,
+                            param1,
+                            param2,
+                            param3,
+                            concat_u32(param5, param4),
+                        ),
+                        spec::pmu::PMU_COUNTER_START => {
+                            pmu.counter_start(param0, param1, param2, concat_u32(param4, param3))
+                        }
+                        spec::pmu::PMU_COUNTER_STOP => pmu.counter_stop(param0, param1, param2),
+                        spec::pmu::PMU_COUNTER_FW_READ => pmu.counter_fw_read(param0),
+                        _ => 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(),
+            _ => false,
+        };
+        if ans {
+            spec::base::UNAVAILABLE_EXTENSION
+        } else {
+            spec::base::UNAVAILABLE_EXTENSION.wrapping_add(1)
+        }
+    }
+}
+
+#[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> {
+    inner: RustSBI<T, I, R, H, S, P>,
+}
+
+impl<T, I, R, H, S, P> Builder<T, I, R, H, S, P>
+where
+    T: Timer,
+    I: Ipi,
+    R: Fence,
+    H: Hsm,
+    S: Reset,
+    P: Pmu,
+{
+    /// Create a new `Builder` from current machine environment
+    #[inline]
+    #[cfg(feature = "machine")]
+    pub const fn new_machine() -> Self {
+        Self {
+            inner: RustSBI {
+                timer: None,
+                ipi: None,
+                rfnc: None,
+                hsm: None,
+                srst: None,
+                pmu: None,
+            },
+        }
+    }
+
+    /// Create a new `Builder` from machine information
+    #[inline]
+    #[cfg(not(feature = "machine"))]
+    pub const fn with_machine_info(info: MachineInfo) -> Self {
+        Self {
+            inner: RustSBI {
+                timer: None,
+                ipi: None,
+                rfnc: None,
+                hsm: None,
+                srst: None,
+                pmu: None,
+                info,
+            },
+        }
+    }
+
+    /// Add Timer programmer extension to RustSBI
+    #[inline]
+    pub fn with_timer(mut self, timer: T) -> Self {
+        self.inner.timer = Some(timer);
+        self
+    }
+
+    /// Add Inter-processor Interrupt extension to RustSBI
+    #[inline]
+    pub fn with_ipi(mut self, ipi: I) -> Self {
+        self.inner.ipi = Some(ipi);
+        self
+    }
+
+    /// Add Hart State Monitor extension to RustSBI
+    #[inline]
+    pub fn with_hsm(mut self, hsm: H) -> Self {
+        self.inner.hsm = Some(hsm);
+        self
+    }
+
+    /// Add Remote Fence extension to RustSBI
+    #[inline]
+    pub fn with_fence(mut self, fence: R) -> Self {
+        self.inner.rfnc = Some(fence);
+        self
+    }
+
+    /// Add System Reset extension to RustSBI
+    #[inline]
+    pub fn with_reset(mut self, reset: S) -> Self {
+        self.inner.srst = Some(reset);
+        self
+    }
+
+    /// Add Performance Monitor Unit extension to RustSBI
+    #[inline]
+    pub fn with_pmu(mut self, pmu: P) -> Self {
+        self.inner.pmu = Some(pmu);
+        self
+    }
+
+    /// Build the target RustSBI instance
+    #[inline]
+    pub fn build(self) -> RustSBI<T, I, R, H, S, P> {
+        self.inner
+    }
+}

+ 9 - 2
src/ipi.rs

@@ -1,6 +1,9 @@
-use crate::{hart_mask::HartMask, util::AmoOnceRef};
+use crate::hart_mask::HartMask;
 use sbi_spec::binary::SbiRet;
 
+#[cfg(feature = "singleton")]
+use crate::util::AmoOnceRef;
+
 /// Inter-processor interrupt support
 pub trait Ipi: Send + Sync {
     /// Send an inter-processor interrupt to all the harts defined in `hart_mask`.
@@ -13,20 +16,24 @@ pub trait Ipi: Send + Sync {
     fn send_ipi(&self, hart_mask: HartMask) -> SbiRet;
 }
 
+#[cfg(feature = "singleton")]
 static IPI: AmoOnceRef<dyn Ipi> = AmoOnceRef::new();
 
-/// Init IPI module
+/// Init singleton IPI module
+#[cfg(feature = "singleton")]
 pub fn init_ipi(ipi: &'static dyn Ipi) {
     if !IPI.try_call_once(ipi) {
         panic!("load sbi module when already loaded")
     }
 }
 
+#[cfg(feature = "singleton")]
 #[inline]
 pub(crate) fn probe_ipi() -> bool {
     IPI.get().is_some()
 }
 
+#[cfg(feature = "singleton")]
 #[inline]
 pub(crate) fn send_ipi(hart_mask: HartMask) -> SbiRet {
     if let Some(ipi) = IPI.get() {

+ 23 - 9
src/lib.rs

@@ -182,25 +182,27 @@
 //! ```
 
 #![no_std]
-#![feature(ptr_metadata)]
+#![cfg_attr(feature = "singleton", feature(ptr_metadata))]
 
 #[cfg(feature = "legacy")]
 #[doc(hidden)]
 #[macro_use]
 pub mod legacy_stdio;
 mod base;
+#[cfg(feature = "singleton")]
 mod ecall;
 mod hart_mask;
 mod hsm;
+#[cfg(not(feature = "legacy"))]
+mod instance;
 mod ipi;
 mod pmu;
 mod reset;
 mod rfence;
 mod timer;
+#[cfg(feature = "singleton")]
 mod util;
 
-// pub mod instance; // TODO: SBI instances, useful for developing hypervisors
-
 /// The const rustsbi logo with blank line at the beginning.
 const LOGO: &str = r"
 .______       __    __      _______.___________.  _______..______   __
@@ -235,14 +237,26 @@ const RUSTSBI_VERSION: usize =
 pub const VERSION: &str = env!("CARGO_PKG_VERSION");
 
 pub extern crate sbi_spec as spec;
+#[cfg(feature = "singleton")]
 pub use ecall::handle_ecall as ecall;
 pub use hart_mask::HartMask;
-pub use hsm::{init_hsm, Hsm};
-pub use ipi::{init_ipi, Ipi};
+pub use hsm::Hsm;
+#[cfg(not(feature = "legacy"))]
+pub use instance::{Builder, RustSBI};
+pub use ipi::Ipi;
 #[cfg(feature = "legacy")]
 #[doc(hidden)]
 pub use legacy_stdio::{legacy_stdio_getchar, legacy_stdio_putchar};
-pub use pmu::{init_pmu, Pmu};
-pub use reset::{init_reset, Reset};
-pub use rfence::{init_rfence as init_remote_fence, Rfence as Fence};
-pub use timer::{init_timer, Timer};
+pub use pmu::Pmu;
+pub use reset::Reset;
+pub use rfence::Rfence as Fence;
+pub use timer::Timer;
+
+#[cfg(not(feature = "machine"))]
+pub use instance::MachineInfo;
+
+#[cfg(feature = "singleton")]
+pub use {
+    hsm::init_hsm, ipi::init_ipi, pmu::init_pmu, reset::init_reset,
+    rfence::init_rfence as init_remote_fence, timer::init_timer,
+};

+ 10 - 0
src/pmu.rs

@@ -196,10 +196,13 @@ pub trait Pmu: Send + Sync {
     fn counter_fw_read(&self, counter_idx: usize) -> SbiRet;
 }
 
+#[cfg(feature = "singleton")]
 use crate::util::AmoOnceRef;
 
+#[cfg(feature = "singleton")]
 static PMU: AmoOnceRef<dyn Pmu> = AmoOnceRef::new();
 
+#[cfg(feature = "singleton")]
 /// Init PMU module
 pub fn init_pmu(pmu: &'static dyn Pmu) {
     if !PMU.try_call_once(pmu) {
@@ -207,11 +210,13 @@ pub fn init_pmu(pmu: &'static dyn Pmu) {
     }
 }
 
+#[cfg(feature = "singleton")]
 #[inline]
 pub(crate) fn probe_pmu() -> bool {
     PMU.get().is_some()
 }
 
+#[cfg(feature = "singleton")]
 #[inline]
 pub(crate) fn num_counters() -> SbiRet {
     if let Some(obj) = PMU.get() {
@@ -222,6 +227,7 @@ pub(crate) fn num_counters() -> SbiRet {
     SbiRet::not_supported()
 }
 
+#[cfg(feature = "singleton")]
 #[inline]
 pub(crate) fn counter_get_info(counter_idx: usize) -> SbiRet {
     if let Some(obj) = PMU.get() {
@@ -230,6 +236,7 @@ pub(crate) fn counter_get_info(counter_idx: usize) -> SbiRet {
     SbiRet::not_supported()
 }
 
+#[cfg(feature = "singleton")]
 #[inline]
 pub(crate) fn counter_config_matching(
     counter_idx_base: usize,
@@ -250,6 +257,7 @@ pub(crate) fn counter_config_matching(
     SbiRet::not_supported()
 }
 
+#[cfg(feature = "singleton")]
 #[inline]
 pub(crate) fn counter_start(
     counter_idx_base: usize,
@@ -268,6 +276,7 @@ pub(crate) fn counter_start(
     SbiRet::not_supported()
 }
 
+#[cfg(feature = "singleton")]
 #[inline]
 pub(crate) fn counter_stop(
     counter_idx_base: usize,
@@ -280,6 +289,7 @@ pub(crate) fn counter_stop(
     SbiRet::not_supported()
 }
 
+#[cfg(feature = "singleton")]
 #[inline]
 pub(crate) fn counter_fw_read(counter_idx: usize) -> SbiRet {
     if let Some(obj) = PMU.get() {

+ 6 - 1
src/reset.rs

@@ -50,10 +50,13 @@ pub trait Reset: Send + Sync {
     }
 }
 
+#[cfg(feature = "singleton")]
 use crate::util::AmoOnceRef;
 
+#[cfg(feature = "singleton")]
 static RESET: AmoOnceRef<dyn Reset> = AmoOnceRef::new();
 
+#[cfg(feature = "singleton")]
 /// Init SRST module
 pub fn init_reset(reset: &'static dyn Reset) {
     if !RESET.try_call_once(reset) {
@@ -61,11 +64,13 @@ pub fn init_reset(reset: &'static dyn Reset) {
     }
 }
 
+#[cfg(feature = "singleton")]
 #[inline]
 pub(crate) fn probe_reset() -> bool {
     RESET.get().is_some()
 }
 
+#[cfg(feature = "singleton")]
 #[inline]
 pub(crate) fn system_reset(reset_type: u32, reset_reason: u32) -> SbiRet {
     if let Some(obj) = RESET.get() {
@@ -74,7 +79,7 @@ pub(crate) fn system_reset(reset_type: u32, reset_reason: u32) -> SbiRet {
     SbiRet::not_supported()
 }
 
-#[cfg(feature = "legacy")]
+#[cfg(all(feature = "singleton", feature = "legacy"))]
 #[inline]
 pub(crate) fn legacy_reset() -> ! {
     if let Some(obj) = RESET.get() {

+ 11 - 0
src/rfence.rs

@@ -135,10 +135,13 @@ pub trait Rfence: Send + Sync {
     }
 }
 
+#[cfg(feature = "singleton")]
 use crate::util::AmoOnceRef;
 
+#[cfg(feature = "singleton")]
 static RFENCE: AmoOnceRef<dyn Rfence> = AmoOnceRef::new();
 
+#[cfg(feature = "singleton")]
 /// Init RFENCE module
 pub fn init_rfence(rfence: &'static dyn Rfence) {
     if !RFENCE.try_call_once(rfence) {
@@ -146,11 +149,13 @@ pub fn init_rfence(rfence: &'static dyn Rfence) {
     }
 }
 
+#[cfg(feature = "singleton")]
 #[inline]
 pub(crate) fn probe_rfence() -> bool {
     RFENCE.get().is_some()
 }
 
+#[cfg(feature = "singleton")]
 #[inline]
 pub(crate) fn remote_fence_i(hart_mask: HartMask) -> SbiRet {
     if let Some(rfence) = RFENCE.get() {
@@ -160,6 +165,7 @@ pub(crate) fn remote_fence_i(hart_mask: HartMask) -> SbiRet {
     }
 }
 
+#[cfg(feature = "singleton")]
 #[inline]
 pub(crate) fn remote_sfence_vma(hart_mask: HartMask, start_addr: usize, size: usize) -> SbiRet {
     if let Some(rfence) = RFENCE.get() {
@@ -169,6 +175,7 @@ pub(crate) fn remote_sfence_vma(hart_mask: HartMask, start_addr: usize, size: us
     }
 }
 
+#[cfg(feature = "singleton")]
 #[inline]
 pub(crate) fn remote_sfence_vma_asid(
     hart_mask: HartMask,
@@ -183,6 +190,7 @@ pub(crate) fn remote_sfence_vma_asid(
     }
 }
 
+#[cfg(feature = "singleton")]
 #[inline]
 pub(crate) fn remote_hfence_gvma_vmid(
     hart_mask: HartMask,
@@ -197,6 +205,7 @@ pub(crate) fn remote_hfence_gvma_vmid(
     }
 }
 
+#[cfg(feature = "singleton")]
 #[inline]
 pub(crate) fn remote_hfence_gvma(hart_mask: HartMask, start_addr: usize, size: usize) -> SbiRet {
     if let Some(rfence) = RFENCE.get() {
@@ -206,6 +215,7 @@ pub(crate) fn remote_hfence_gvma(hart_mask: HartMask, start_addr: usize, size: u
     }
 }
 
+#[cfg(feature = "singleton")]
 #[inline]
 pub(crate) fn remote_hfence_vvma_asid(
     hart_mask: HartMask,
@@ -220,6 +230,7 @@ pub(crate) fn remote_hfence_vvma_asid(
     }
 }
 
+#[cfg(feature = "singleton")]
 #[inline]
 pub(crate) fn remote_hfence_vvma(hart_mask: HartMask, start_addr: usize, size: usize) -> SbiRet {
     if let Some(rfence) = RFENCE.get() {

+ 5 - 0
src/timer.rs

@@ -1,3 +1,4 @@
+#[cfg(feature = "singleton")]
 use crate::util::AmoOnceRef;
 
 /// Timer programmer support
@@ -12,8 +13,10 @@ pub trait Timer: Send + Sync {
     fn set_timer(&self, stime_value: u64);
 }
 
+#[cfg(feature = "singleton")]
 static TIMER: AmoOnceRef<dyn Timer> = AmoOnceRef::new();
 
+#[cfg(feature = "singleton")]
 /// Init TIMER module
 pub fn init_timer(timer: &'static dyn Timer) {
     if !TIMER.try_call_once(timer) {
@@ -21,11 +24,13 @@ pub fn init_timer(timer: &'static dyn Timer) {
     }
 }
 
+#[cfg(feature = "singleton")]
 #[inline]
 pub(crate) fn probe_timer() -> bool {
     TIMER.get().is_some()
 }
 
+#[cfg(feature = "singleton")]
 #[inline]
 pub(crate) fn set_timer(time_value: u64) -> bool {
     if let Some(timer) = TIMER.get() {