use core::marker::PhantomData;
#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(C)]
pub struct SbiRet {
pub error: usize,
pub value: usize,
}
pub const RET_SUCCESS: usize = 0;
pub const RET_ERR_FAILED: usize = -1isize as _;
pub const RET_ERR_NOT_SUPPORTED: usize = -2isize as _;
pub const RET_ERR_INVALID_PARAM: usize = -3isize as _;
pub const RET_ERR_DENIED: usize = -4isize as _;
pub const RET_ERR_INVALID_ADDRESS: usize = -5isize as _;
pub const RET_ERR_ALREADY_AVAILABLE: usize = -6isize as _;
pub const RET_ERR_ALREADY_STARTED: usize = -7isize as _;
pub const RET_ERR_ALREADY_STOPPED: usize = -8isize as _;
pub const RET_ERR_NO_SHMEM: usize = -9isize as _;
impl core::fmt::Debug for SbiRet {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self.error {
RET_SUCCESS => self.value.fmt(f),
RET_ERR_FAILED => write!(f, "<SBI call failed>"),
RET_ERR_NOT_SUPPORTED => write!(f, "<SBI feature not supported>"),
RET_ERR_INVALID_PARAM => write!(f, "<SBI invalid parameter>"),
RET_ERR_DENIED => write!(f, "<SBI denied>"),
RET_ERR_INVALID_ADDRESS => write!(f, "<SBI invalid address>"),
RET_ERR_ALREADY_AVAILABLE => write!(f, "<SBI already available>"),
RET_ERR_ALREADY_STARTED => write!(f, "<SBI already started>"),
RET_ERR_ALREADY_STOPPED => write!(f, "<SBI already stopped>"),
RET_ERR_NO_SHMEM => write!(f, "<SBI shared memory not available>"),
unknown => write!(f, "[SBI Unknown error: {unknown:#x}]"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Error {
Failed,
NotSupported,
InvalidParam,
Denied,
InvalidAddress,
AlreadyAvailable,
AlreadyStarted,
AlreadyStopped,
NoShmem,
Custom(isize),
}
impl SbiRet {
#[inline]
pub const fn success(value: usize) -> Self {
Self {
error: RET_SUCCESS,
value,
}
}
#[inline]
pub const fn failed() -> Self {
Self {
error: RET_ERR_FAILED,
value: 0,
}
}
#[inline]
pub const fn not_supported() -> Self {
Self {
error: RET_ERR_NOT_SUPPORTED,
value: 0,
}
}
#[inline]
pub const fn invalid_param() -> Self {
Self {
error: RET_ERR_INVALID_PARAM,
value: 0,
}
}
#[inline]
pub const fn denied() -> Self {
Self {
error: RET_ERR_DENIED,
value: 0,
}
}
#[inline]
pub const fn invalid_address() -> Self {
Self {
error: RET_ERR_INVALID_ADDRESS,
value: 0,
}
}
#[inline]
pub const fn already_available() -> Self {
Self {
error: RET_ERR_ALREADY_AVAILABLE,
value: 0,
}
}
#[inline]
pub const fn already_started() -> Self {
Self {
error: RET_ERR_ALREADY_STARTED,
value: 0,
}
}
#[inline]
pub const fn already_stopped() -> Self {
Self {
error: RET_ERR_ALREADY_STOPPED,
value: 0,
}
}
#[inline]
pub const fn no_shmem() -> Self {
Self {
error: RET_ERR_NO_SHMEM,
value: 0,
}
}
}
impl SbiRet {
#[inline]
pub const fn into_result(self) -> Result<usize, Error> {
match self.error {
RET_SUCCESS => Ok(self.value),
RET_ERR_FAILED => Err(Error::Failed),
RET_ERR_NOT_SUPPORTED => Err(Error::NotSupported),
RET_ERR_INVALID_PARAM => Err(Error::InvalidParam),
RET_ERR_DENIED => Err(Error::Denied),
RET_ERR_INVALID_ADDRESS => Err(Error::InvalidAddress),
RET_ERR_ALREADY_AVAILABLE => Err(Error::AlreadyAvailable),
RET_ERR_ALREADY_STARTED => Err(Error::AlreadyStarted),
RET_ERR_ALREADY_STOPPED => Err(Error::AlreadyStopped),
RET_ERR_NO_SHMEM => Err(Error::NoShmem),
unknown => Err(Error::Custom(unknown as _)),
}
}
#[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)
}
#[must_use]
#[inline]
pub fn is_ok_and(self, f: impl FnOnce(usize) -> bool) -> bool {
self.into_result().is_ok_and(f)
}
#[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()
}
#[must_use]
#[inline]
pub fn is_err_and(self, f: impl FnOnce(Error) -> bool) -> bool {
self.into_result().is_err_and(f)
}
#[inline]
pub fn ok(self) -> Option<usize> {
self.into_result().ok()
}
#[inline]
pub fn err(self) -> Option<Error> {
self.into_result().err()
}
#[inline]
pub fn map<U, F: FnOnce(usize) -> U>(self, op: F) -> Result<U, Error> {
self.into_result().map(op)
}
#[inline]
pub fn map_or<U, F: FnOnce(usize) -> U>(self, default: U, f: F) -> U {
self.into_result().map_or(default, f)
}
#[inline]
pub fn map_or_else<U, D: FnOnce(Error) -> U, F: FnOnce(usize) -> U>(
self,
default: D,
f: F,
) -> U {
self.into_result().map_or_else(default, f)
}
#[inline]
pub fn map_err<F, O: FnOnce(Error) -> F>(self, op: O) -> Result<usize, F> {
self.into_result().map_err(op)
}
#[inline]
pub fn inspect<F: FnOnce(&usize)>(self, f: F) -> Self {
if let Ok(ref t) = self.into_result() {
f(t);
}
self
}
#[inline]
pub fn inspect_err<F: FnOnce(&Error)>(self, f: F) -> Self {
if let Err(ref e) = self.into_result() {
f(e);
}
self
}
#[inline]
pub fn expect(self, msg: &str) -> usize {
self.into_result().expect(msg)
}
#[inline]
pub fn unwrap(self) -> usize {
self.into_result().unwrap()
}
#[inline]
pub fn expect_err(self, msg: &str) -> Error {
self.into_result().expect_err(msg)
}
#[inline]
pub fn unwrap_err(self) -> Error {
self.into_result().unwrap_err()
}
#[inline]
pub fn and<U>(self, res: Result<U, Error>) -> Result<U, Error> {
self.into_result().and(res)
}
#[inline]
pub fn and_then<U, F: FnOnce(usize) -> Result<U, Error>>(self, op: F) -> Result<U, Error> {
self.into_result().and_then(op)
}
#[inline]
pub fn or<F>(self, res: Result<usize, F>) -> Result<usize, F> {
self.into_result().or(res)
}
#[inline]
pub fn or_else<F, O: FnOnce(Error) -> Result<usize, F>>(self, op: O) -> Result<usize, F> {
self.into_result().or_else(op)
}
#[inline]
pub fn unwrap_or(self, default: usize) -> usize {
self.into_result().unwrap_or(default)
}
#[inline]
pub fn unwrap_or_else<F: FnOnce(Error) -> usize>(self, op: F) -> usize {
self.into_result().unwrap_or_else(op)
}
}
#[inline]
pub(crate) const fn has_bit(mask: usize, base: usize, ignore: usize, bit: usize) -> bool {
if base == ignore {
true
} else if bit < base {
false
} else if (bit - base) >= usize::BITS as usize {
false
} else {
mask & (1 << (bit - base)) != 0
}
}
#[repr(C)]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct HartMask {
hart_mask: usize,
hart_mask_base: usize,
}
impl HartMask {
pub const IGNORE_MASK: usize = usize::MAX;
#[inline]
pub const fn from_mask_base(hart_mask: usize, hart_mask_base: usize) -> Self {
Self {
hart_mask,
hart_mask_base,
}
}
#[inline]
pub const fn ignore_mask(&self) -> usize {
Self::IGNORE_MASK
}
#[inline]
pub const fn into_inner(self) -> (usize, usize) {
(self.hart_mask, self.hart_mask_base)
}
#[inline]
pub const fn has_bit(self, hart_id: usize) -> bool {
has_bit(
self.hart_mask,
self.hart_mask_base,
Self::IGNORE_MASK,
hart_id,
)
}
}
#[repr(C)]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct CounterMask {
counter_idx_mask: usize,
counter_idx_base: usize,
}
impl CounterMask {
pub const IGNORE_MASK: usize = usize::MAX;
#[inline]
pub const fn from_mask_base(counter_idx_mask: usize, counter_idx_base: usize) -> Self {
Self {
counter_idx_mask,
counter_idx_base,
}
}
#[inline]
pub const fn ignore_mask(&self) -> usize {
Self::IGNORE_MASK
}
#[inline]
pub const fn into_inner(self) -> (usize, usize) {
(self.counter_idx_mask, self.counter_idx_base)
}
#[inline]
pub const fn has_bit(self, counter: usize) -> bool {
has_bit(
self.counter_idx_mask,
self.counter_idx_base,
Self::IGNORE_MASK,
counter,
)
}
}
#[derive(Clone, Copy)]
pub struct Physical<P> {
num_bytes: usize,
phys_addr_lo: usize,
phys_addr_hi: usize,
_marker: PhantomData<P>,
}
impl<P> Physical<P> {
#[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,
}
}
#[inline]
pub const fn num_bytes(&self) -> usize {
self.num_bytes
}
#[inline]
pub const fn phys_addr_lo(&self) -> usize {
self.phys_addr_lo
}
#[inline]
pub const fn phys_addr_hi(&self) -> usize {
self.phys_addr_hi
}
}
pub struct SharedPtr<T> {
phys_addr_lo: usize,
phys_addr_hi: usize,
_marker: PhantomData<*mut T>,
}
impl<T> SharedPtr<T> {
#[inline]
pub const fn new(phys_addr_lo: usize, phys_addr_hi: usize) -> Self {
Self {
phys_addr_lo,
phys_addr_hi,
_marker: PhantomData,
}
}
#[inline]
pub const fn phys_addr_lo(self) -> usize {
self.phys_addr_lo
}
#[inline]
pub const fn phys_addr_hi(self) -> usize {
self.phys_addr_hi
}
}
impl<T> Clone for SharedPtr<T> {
#[inline(always)]
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for SharedPtr<T> {}
#[cfg(test)]
mod tests {
use super::*;
#[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));
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));
}
#[test]
fn rustsbi_counter_index_mask() {
let mask = CounterMask::from_mask_base(0b1, 400);
assert!(!mask.has_bit(0));
assert!(mask.has_bit(400));
assert!(!mask.has_bit(401));
let mask = CounterMask::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 = CounterMask::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 = CounterMask::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));
let mask = CounterMask::from_mask_base(0, usize::MAX);
let null_mask = CounterMask::from_mask_base(0, 0);
(0..=usize::BITS as usize).for_each(|i| {
assert!(mask.has_bit(i));
assert!(!null_mask.has_bit(i));
});
assert!(mask.has_bit(usize::MAX));
}
}