//! Chapter 3. Binary Encoding. use core::marker::PhantomData; /// SBI functions return type. /// /// > SBI functions must return a pair of values in a0 and a1, /// > with a0 returning an error code. /// > This is analogous to returning the C structure `SbiRet`. /// /// Note: if this structure is used in function return on conventional /// Rust code, it would not require to pin memory representation as /// extern C. The `repr(C)` is set in case that some users want to use /// this structure in FFI code. #[derive(Clone, Copy, PartialEq, Eq)] #[repr(C)] pub struct SbiRet { /// Error number. pub error: usize, /// Result value. pub value: usize, } /// SBI success state return value. pub const RET_SUCCESS: usize = 0; /// Error for SBI call failed for unknown reasons. pub const RET_ERR_FAILED: usize = -1isize as _; /// Error for target operation not supported. pub const RET_ERR_NOT_SUPPORTED: usize = -2isize as _; /// Error for invalid parameter. pub const RET_ERR_INVALID_PARAM: usize = -3isize as _; /// Error for denied (unused in standard extensions). pub const RET_ERR_DENIED: usize = -4isize as _; /// Error for invalid address. pub const RET_ERR_INVALID_ADDRESS: usize = -5isize as _; /// Error for resource already available. pub const RET_ERR_ALREADY_AVAILABLE: usize = -6isize as _; /// Error for resource already started. pub const RET_ERR_ALREADY_STARTED: usize = -7isize as _; /// Error for resource already stopped. pub const RET_ERR_ALREADY_STOPPED: usize = -8isize as _; /// Error for shared memory not available. pub const RET_ERR_NO_SHMEM: usize = -9isize as _; impl core::fmt::Debug for SbiRet { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self.error { RET_SUCCESS => self.value.fmt(f), RET_ERR_FAILED => write!(f, ""), RET_ERR_NOT_SUPPORTED => write!(f, ""), RET_ERR_INVALID_PARAM => write!(f, ""), RET_ERR_DENIED => write!(f, ""), RET_ERR_INVALID_ADDRESS => write!(f, ""), RET_ERR_ALREADY_AVAILABLE => write!(f, ""), RET_ERR_ALREADY_STARTED => write!(f, ""), RET_ERR_ALREADY_STOPPED => write!(f, ""), RET_ERR_NO_SHMEM => write!(f, ""), unknown => write!(f, "[SBI Unknown error: {unknown:#x}]"), } } } /// RISC-V SBI error in enumeration. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Error { /// Error for SBI call failed for unknown reasons. Failed, /// Error for target operation not supported. NotSupported, /// Error for invalid parameter. InvalidParam, /// Error for denied (unused in standard extensions). Denied, /// Error for invalid address. InvalidAddress, /// Error for resource already available. AlreadyAvailable, /// Error for resource already started. AlreadyStarted, /// Error for resource already stopped. AlreadyStopped, /// Error for shared memory not available. NoShmem, /// Custom error code. Custom(isize), } impl SbiRet { /// Returns success SBI state with given `value`. #[inline] pub const fn success(value: usize) -> Self { Self { error: RET_SUCCESS, value, } } /// The SBI call request failed for unknown reasons. #[inline] pub const fn failed() -> Self { Self { error: RET_ERR_FAILED, value: 0, } } /// SBI call failed due to not supported by target ISA, /// operation type not supported, /// or target operation type not implemented on purpose. #[inline] pub const fn not_supported() -> Self { Self { error: RET_ERR_NOT_SUPPORTED, value: 0, } } /// SBI call failed due to invalid hart mask parameter, /// invalid target hart id, /// invalid operation type, /// or invalid resource index. #[inline] pub const fn invalid_param() -> Self { Self { error: RET_ERR_INVALID_PARAM, value: 0, } } /// SBI call failed due to denied. /// /// As the time this document was written, /// there is currently no function in SBI standard that returns this error. /// However, custom extensions or future standard functions may return this /// error if appropriate. #[inline] pub const fn denied() -> Self { Self { error: RET_ERR_DENIED, value: 0, } } /// SBI call failed for invalid mask start address, /// not a valid physical address parameter, /// or the target address is prohibited by PMP to run in supervisor mode. #[inline] pub const fn invalid_address() -> Self { Self { error: RET_ERR_INVALID_ADDRESS, value: 0, } } /// SBI call failed for the target resource is already available, /// e.g. the target hart is already started when caller still request it to start. #[inline] pub const fn already_available() -> Self { Self { error: RET_ERR_ALREADY_AVAILABLE, value: 0, } } /// SBI call failed for the target resource is already started, /// e.g. target performance counter is started. #[inline] pub const fn already_started() -> Self { Self { error: RET_ERR_ALREADY_STARTED, value: 0, } } /// SBI call failed for the target resource is already stopped, /// e.g. target performance counter is stopped. #[inline] pub const fn already_stopped() -> Self { Self { error: RET_ERR_ALREADY_STOPPED, value: 0, } } /// SBI call failed for shared memory is not available, /// e.g. nested acceleration shared memory is not available. #[inline] pub const fn no_shmem() -> Self { Self { error: RET_ERR_NO_SHMEM, value: 0, } } } impl SbiRet { /// Converts to a [`Result`] of value and error. #[inline] pub const fn into_result(self) -> Result { match self.error { RET_SUCCESS => Ok(self.value), RET_ERR_FAILED => Err(Error::Failed), RET_ERR_NOT_SUPPORTED => Err(Error::NotSupported), RET_ERR_INVALID_PARAM => Err(Error::InvalidParam), RET_ERR_DENIED => Err(Error::Denied), RET_ERR_INVALID_ADDRESS => Err(Error::InvalidAddress), RET_ERR_ALREADY_AVAILABLE => Err(Error::AlreadyAvailable), RET_ERR_ALREADY_STARTED => Err(Error::AlreadyStarted), RET_ERR_ALREADY_STOPPED => Err(Error::AlreadyStopped), RET_ERR_NO_SHMEM => Err(Error::NoShmem), unknown => Err(Error::Custom(unknown as _)), } } /// Returns `true` if current SBI return succeeded. /// /// # Examples /// /// Basic usage: /// /// ``` /// # use sbi_spec::binary::SbiRet; /// let x = SbiRet::success(0); /// assert_eq!(x.is_ok(), true); /// /// let x = SbiRet::failed(); /// assert_eq!(x.is_ok(), false); /// ``` #[must_use = "if you intended to assert that this is ok, consider `.unwrap()` instead"] #[inline] pub const fn is_ok(&self) -> bool { matches!(self.error, RET_SUCCESS) } /// Returns `true` if current SBI return is an error. /// /// # Examples /// /// Basic usage: /// /// ``` /// # use sbi_spec::binary::SbiRet; /// let x = SbiRet::success(0); /// assert_eq!(x.is_err(), false); /// /// let x = SbiRet::not_supported(); /// assert_eq!(x.is_err(), true); /// ``` #[must_use = "if you intended to assert that this is err, consider `.unwrap_err()` instead"] #[inline] pub const fn is_err(&self) -> bool { !self.is_ok() } /// Converts from `SbiRet` to [`Option`]. /// /// Converts `self` into an [`Option`], consuming `self`, /// and discarding the error, if any. /// /// # Examples /// /// Basic usage: /// /// ``` /// # use sbi_spec::binary::SbiRet; /// let x = SbiRet::success(2); /// assert_eq!(x.ok(), Some(2)); /// /// let x = SbiRet::invalid_param(); /// assert_eq!(x.ok(), None); /// ``` // fixme: should be pub const fn once this function in Result is stablized in constant #[inline] pub fn ok(self) -> Option { self.into_result().ok() } /// Converts from `SbiRet` to [`Option`]. /// /// Converts `self` into an [`Option`], consuming `self`, /// and discarding the success value, if any. /// /// # Examples /// /// Basic usage: /// /// ``` /// # use sbi_spec::binary::{SbiRet, Error}; /// let x = SbiRet::success(2); /// assert_eq!(x.err(), None); /// /// let x = SbiRet::denied(); /// assert_eq!(x.err(), Some(Error::Denied)); /// ``` // fixme: should be pub const fn once this function in Result is stablized in constant #[inline] pub fn err(self) -> Option { self.into_result().err() } /// Maps a `SbiRet` to `Result` by applying a function to a /// contained success value, leaving an error value untouched. /// /// This function can be used to compose the results of two functions. /// /// # Examples /// /// Gets detail of a PMU counter and judge if it is a firmware counter. /// /// ``` /// # use sbi_spec::binary::SbiRet; /// # use core::mem::size_of; /// # mod sbi_rt { /// # use sbi_spec::binary::SbiRet; /// # const TYPE_MASK: usize = 1 << (core::mem::size_of::() - 1); /// # pub fn pmu_counter_get_info(_: usize) -> SbiRet { SbiRet::success(TYPE_MASK) } /// # } /// // We assume that counter index 42 is a firmware counter. /// let counter_idx = 42; /// // Masks PMU counter type by setting highest bit in `usize`. /// const TYPE_MASK: usize = 1 << (size_of::() - 1); /// // Highest bit of returned `counter_info` represents whether it's /// // a firmware counter or a hardware counter. /// let is_firmware_counter = sbi_rt::pmu_counter_get_info(counter_idx) /// .map(|counter_info| counter_info & TYPE_MASK != 0); /// // If that bit is set, it is a firmware counter. /// assert_eq!(is_firmware_counter, Ok(true)); /// ``` #[inline] pub fn map U>(self, op: F) -> Result { self.into_result().map(op) } /// Returns the provided default (if error), /// or applies a function to the contained value (if success). /// /// Arguments passed to `map_or` are eagerly evaluated; /// if you are passing the result of a function call, /// it is recommended to use [`map_or_else`], /// which is lazily evaluated. /// /// [`map_or_else`]: SbiRet::map_or_else /// /// # Examples /// /// ``` /// # use sbi_spec::binary::SbiRet; /// let x = SbiRet::success(3); /// assert_eq!(x.map_or(42, |v| v & 0b1), 1); /// /// let x = SbiRet::invalid_address(); /// assert_eq!(x.map_or(42, |v| v & 0b1), 42); /// ``` #[inline] pub fn map_or U>(self, default: U, f: F) -> U { self.into_result().map_or(default, f) } /// Maps a `SbiRet` to `usize` value by applying fallback function `default` to /// a contained error, or function `f` to a contained success value. /// /// This function can be used to unpack a successful result /// while handling an error. /// /// # Examples /// /// Basic usage: /// /// ``` /// # use sbi_spec::binary::SbiRet; /// let k = 21; /// /// let x = SbiRet::success(3); /// assert_eq!(x.map_or_else(|e| k * 2, |v| v & 0b1), 1); /// /// let x = SbiRet::already_available(); /// assert_eq!(x.map_or_else(|e| k * 2, |v| v & 0b1), 42); /// ``` #[inline] pub fn map_or_else U, F: FnOnce(usize) -> U>( self, default: D, f: F, ) -> U { self.into_result().map_or_else(default, f) } /// Maps a `SbiRet` to `Result` by applying a function to a /// contained error as [`Error`] struct, leaving success value untouched. /// /// This function can be used to pass through a successful result while handling /// an error. /// /// # Examples /// /// Basic usage: /// /// ``` /// # use sbi_spec::binary::{SbiRet, Error}; /// fn stringify(x: Error) -> String { /// if x == Error::AlreadyStarted { /// format!("error: already started!") /// } else { /// format!("error: other error!") /// } /// } /// /// let x = SbiRet::success(2); /// assert_eq!(x.map_err(stringify), Ok(2)); /// /// let x = SbiRet::already_started(); /// assert_eq!(x.map_err(stringify), Err("error: already started!".to_string())); /// ``` #[inline] pub fn map_err F>(self, op: O) -> Result { self.into_result().map_err(op) } /// Returns the contained success value, consuming the `self` value. /// /// # Panics /// /// Panics if self is an SBI error with a panic message including the /// passed message, and the content of the SBI state. /// /// # Examples /// /// Basic usage: /// /// ```should_panic /// # use sbi_spec::binary::SbiRet; /// let x = SbiRet::already_stopped(); /// x.expect("Testing expect"); // panics with `Testing expect` /// ``` #[inline] pub fn expect(self, msg: &str) -> usize { self.into_result().expect(msg) } /// Returns the contained success value, consuming the `self` value. /// /// # Panics /// /// Panics if self is an SBI error, with a panic message provided by the /// SBI error converted into [`Error`] struct. /// /// # Examples /// /// Basic usage: /// /// ``` /// # use sbi_spec::binary::SbiRet; /// let x = SbiRet::success(2); /// assert_eq!(x.unwrap(), 2); /// ``` /// /// ```should_panic /// # use sbi_spec::binary::SbiRet; /// let x = SbiRet::failed(); /// x.unwrap(); // panics /// ``` #[inline] pub fn unwrap(self) -> usize { self.into_result().unwrap() } /// Returns the contained error as [`Error`] struct, consuming the `self` value. /// /// # Panics /// /// Panics if the self is SBI success value, with a panic message /// including the passed message, and the content of the success value. /// /// # Examples /// /// Basic usage: /// /// ```should_panic /// # use sbi_spec::binary::SbiRet; /// let x = SbiRet::success(10); /// x.expect_err("Testing expect_err"); // panics with `Testing expect_err` /// ``` #[inline] pub fn expect_err(self, msg: &str) -> Error { self.into_result().expect_err(msg) } /// Returns the contained error as [`Error`] struct, consuming the `self` value. /// /// # Panics /// /// Panics if the self is SBI success value, with a custom panic message provided /// by the success value. /// /// # Examples /// /// ```should_panic /// # use sbi_spec::binary::SbiRet; /// let x = SbiRet::success(2); /// x.unwrap_err(); // panics with `2` /// ``` /// /// ``` /// # use sbi_spec::binary::{SbiRet, Error}; /// let x = SbiRet::not_supported(); /// assert_eq!(x.unwrap_err(), Error::NotSupported); /// ``` #[inline] pub fn unwrap_err(self) -> Error { self.into_result().unwrap_err() } /// Returns `res` if self is success value, otherwise otherwise returns the contained error /// of `self` as [`Error`] struct. /// /// Arguments passed to `and` are eagerly evaluated; if you are passing the /// result of a function call, it is recommended to use [`and_then`], which is /// lazily evaluated. /// /// [`and_then`]: SbiRet::and_then /// /// # Examples /// /// Basic usage: /// /// ``` /// # use sbi_spec::binary::{SbiRet, Error}; /// let x = SbiRet::success(2); /// let y = SbiRet::invalid_param().into_result(); /// assert_eq!(x.and(y), Err(Error::InvalidParam)); /// /// let x = SbiRet::denied(); /// let y = SbiRet::success(3).into_result(); /// assert_eq!(x.and(y), Err(Error::Denied)); /// /// let x = SbiRet::invalid_address(); /// let y = SbiRet::already_available().into_result(); /// assert_eq!(x.and(y), Err(Error::InvalidAddress)); /// /// let x = SbiRet::success(4); /// let y = SbiRet::success(5).into_result(); /// assert_eq!(x.and(y), Ok(5)); /// ``` // fixme: should be pub const fn once this function in Result is stablized in constant // fixme: should parameter be `res: SbiRet`? #[inline] pub fn and(self, res: Result) -> Result { self.into_result().and(res) } /// Calls `op` if self is success value, otherwise returns the contained error /// as [`Error`] struct. /// /// This function can be used for control flow based on `SbiRet` values. /// /// # Examples /// /// ``` /// # use sbi_spec::binary::{SbiRet, Error}; /// fn sq_then_to_string(x: usize) -> Result { /// x.checked_mul(x).map(|sq| sq.to_string()).ok_or(Error::Failed) /// } /// /// assert_eq!(SbiRet::success(2).and_then(sq_then_to_string), Ok(4.to_string())); /// assert_eq!(SbiRet::success(1_000_000_000_000).and_then(sq_then_to_string), Err(Error::Failed)); /// assert_eq!(SbiRet::invalid_param().and_then(sq_then_to_string), Err(Error::InvalidParam)); /// ``` #[inline] pub fn and_then Result>(self, op: F) -> Result { self.into_result().and_then(op) } /// Returns `res` if self is SBI error, otherwise returns the success value of `self`. /// /// Arguments passed to `or` are eagerly evaluated; if you are passing the /// result of a function call, it is recommended to use [`or_else`], which is /// lazily evaluated. /// /// [`or_else`]: Result::or_else /// /// # Examples /// /// Basic usage: /// /// ``` /// # use sbi_spec::binary::{SbiRet, Error}; /// let x = SbiRet::success(2); /// let y = SbiRet::invalid_param().into_result(); /// assert_eq!(x.or(y), Ok(2)); /// /// let x = SbiRet::denied(); /// let y = SbiRet::success(3).into_result(); /// assert_eq!(x.or(y), Ok(3)); /// /// let x = SbiRet::invalid_address(); /// let y = SbiRet::already_available().into_result(); /// assert_eq!(x.or(y), Err(Error::AlreadyAvailable)); /// /// let x = SbiRet::success(4); /// let y = SbiRet::success(100).into_result(); /// assert_eq!(x.or(y), Ok(4)); /// ``` // fixme: should be pub const fn once this function in Result is stablized in constant // fixme: should parameter be `res: SbiRet`? #[inline] pub fn or(self, res: Result) -> Result { self.into_result().or(res) } /// Calls `op` if self is SBI error, otherwise returns the success value of `self`. /// /// This function can be used for control flow based on result values. /// /// /// # Examples /// /// Basic usage: /// /// ``` /// # use sbi_spec::binary::{SbiRet, Error}; /// fn is_failed(x: Error) -> Result { Err(x == Error::Failed) } /// /// assert_eq!(SbiRet::success(2).or_else(is_failed), Ok(2)); /// assert_eq!(SbiRet::failed().or_else(is_failed), Err(true)); /// ``` #[inline] pub fn or_else Result>(self, op: O) -> Result { self.into_result().or_else(op) } /// Returns the contained success value or a provided default. /// /// Arguments passed to `unwrap_or` are eagerly evaluated; if you are passing /// the result of a function call, it is recommended to use [`unwrap_or_else`], /// which is lazily evaluated. /// /// [`unwrap_or_else`]: SbiRet::unwrap_or_else /// /// # Examples /// /// Basic usage: /// /// ``` /// # use sbi_spec::binary::SbiRet; /// let default = 2; /// let x = SbiRet::success(9); /// assert_eq!(x.unwrap_or(default), 9); /// /// let x = SbiRet::invalid_param(); /// assert_eq!(x.unwrap_or(default), default); /// ``` // fixme: should be pub const fn once this function in Result is stablized in constant #[inline] pub fn unwrap_or(self, default: usize) -> usize { self.into_result().unwrap_or(default) } /// Returns the contained success value or computes it from a closure. /// /// # Examples /// /// Basic usage: /// /// ``` /// # use sbi_spec::binary::{SbiRet, Error}; /// fn invalid_use_zero(x: Error) -> usize { if x == Error::InvalidParam { 0 } else { 3 } } /// /// assert_eq!(SbiRet::success(2).unwrap_or_else(invalid_use_zero), 2); /// assert_eq!(SbiRet::invalid_param().unwrap_or_else(invalid_use_zero), 0); /// ``` #[inline] pub fn unwrap_or_else usize>(self, op: F) -> usize { self.into_result().unwrap_or_else(op) } } /// Hart mask structure in SBI function calls. #[derive(Debug, Copy, Clone)] pub struct HartMask { inner: BitVector, } impl HartMask { /// Construct a hart mask from mask value and base hart id. #[inline] pub const fn from_mask_base(hart_mask: usize, hart_mask_base: usize) -> HartMask { HartMask { inner: BitVector { hart_mask, hart_mask_base, }, } } /// Returns `hart_mask` and `hart_mask_base` parameters from the hart mask structure. #[inline] pub const fn into_inner(self) -> (usize, usize) { (self.inner.hart_mask, self.inner.hart_mask_base) } /// Check if the `hart_id` is included in this hart mask structure. #[inline] pub const fn has_bit(&self, hart_id: usize) -> bool { let BitVector { hart_mask, hart_mask_base, } = self.inner; if hart_mask_base == usize::MAX { // If `hart_mask_base` equals `usize::MAX`, that means `hart_mask` is ignored // and all available harts must be considered. return true; } let Some(idx) = hart_id.checked_sub(hart_mask_base) else { // hart_id < hart_mask_base, not in current mask range return false; }; if idx >= usize::BITS as usize { // hart_idx >= hart_mask_base + XLEN, not in current mask range return false; } hart_mask & (1 << idx) != 0 } } #[derive(Debug, Copy, Clone)] struct BitVector { hart_mask: usize, hart_mask_base: usize, } /// Physical slice wrapper with type annotation. /// /// This struct wraps slices in RISC-V physical memory by low and high part of the /// physical base address as well as its length. It is usually used by SBI extensions /// as parameter types to pass base address and length parameters on physical memory /// other than a virtual one. /// /// Generic parameter `P` represents a hint of how this physical slice would be used. /// For example, `Physical<&[u8]>` represents an immutable reference to physical byte slice, /// while `Physical<&mut [u8]>` represents a mutable one. /// /// An SBI implementation should load or store memory using both `phys_addr_lo` and /// `phys_addr_hi` combined as base address. A supervisor program (kernels etc.) /// should provide continuous physical memory, wrapping its reference using this structure /// before passing into SBI runtime. #[derive(Clone, Copy)] pub struct Physical

{ num_bytes: usize, phys_addr_lo: usize, phys_addr_hi: usize, _marker: PhantomData

, } impl

Physical

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