#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(C)]
pub struct SbiRet<T = usize> {
pub error: T,
pub value: T,
}
pub mod id {
use super::SbiRegister;
#[doc(alias = "SBI_SUCCESS")]
pub const RET_SUCCESS: usize = <usize as SbiRegister>::RET_SUCCESS;
#[doc(alias = "SBI_ERR_FAILED")]
pub const RET_ERR_FAILED: usize = <usize as SbiRegister>::RET_ERR_FAILED;
#[doc(alias = "SBI_ERR_NOT_SUPPORTED")]
pub const RET_ERR_NOT_SUPPORTED: usize = <usize as SbiRegister>::RET_ERR_NOT_SUPPORTED;
#[doc(alias = "SBI_ERR_INVALID_PARAM")]
pub const RET_ERR_INVALID_PARAM: usize = <usize as SbiRegister>::RET_ERR_INVALID_PARAM;
#[doc(alias = "SBI_ERR_DENIED")]
pub const RET_ERR_DENIED: usize = <usize as SbiRegister>::RET_ERR_DENIED;
#[doc(alias = "SBI_ERR_INVALID_ADDRESS")]
pub const RET_ERR_INVALID_ADDRESS: usize = <usize as SbiRegister>::RET_ERR_INVALID_ADDRESS;
#[doc(alias = "SBI_ERR_ALREADY_AVAILABLE")]
pub const RET_ERR_ALREADY_AVAILABLE: usize = <usize as SbiRegister>::RET_ERR_ALREADY_AVAILABLE;
#[doc(alias = "SBI_ERR_ALREADY_STARTED")]
pub const RET_ERR_ALREADY_STARTED: usize = <usize as SbiRegister>::RET_ERR_ALREADY_STARTED;
#[doc(alias = "SBI_ERR_ALREADY_STOPPED")]
pub const RET_ERR_ALREADY_STOPPED: usize = <usize as SbiRegister>::RET_ERR_ALREADY_STOPPED;
#[doc(alias = "SBI_ERR_NO_SHMEM")]
pub const RET_ERR_NO_SHMEM: usize = <usize as SbiRegister>::RET_ERR_NO_SHMEM;
#[doc(alias = "SBI_ERR_INVALID_STATE")]
pub const RET_ERR_INVALID_STATE: usize = <usize as SbiRegister>::RET_ERR_INVALID_STATE;
#[doc(alias = "SBI_ERR_BAD_RANGE")]
pub const RET_ERR_BAD_RANGE: usize = <usize as SbiRegister>::RET_ERR_BAD_RANGE;
#[doc(alias = "SBI_ERR_TIMEOUT")]
pub const RET_ERR_TIMEOUT: usize = <usize as SbiRegister>::RET_ERR_TIMEOUT;
#[doc(alias = "SBI_ERR_IO")]
pub const RET_ERR_IO: usize = <usize as SbiRegister>::RET_ERR_IO;
#[doc(alias = "SBI_ERR_DENIED_LOCKED")]
pub const RET_ERR_DENIED_LOCKED: usize = <usize as SbiRegister>::RET_ERR_DENIED_LOCKED;
}
use id::*;
pub trait SbiRegister: Copy + Eq + Ord + core::fmt::Debug {
const RET_SUCCESS: Self;
const RET_ERR_FAILED: Self;
const RET_ERR_NOT_SUPPORTED: Self;
const RET_ERR_INVALID_PARAM: Self;
const RET_ERR_DENIED: Self;
const RET_ERR_INVALID_ADDRESS: Self;
const RET_ERR_ALREADY_AVAILABLE: Self;
const RET_ERR_ALREADY_STARTED: Self;
const RET_ERR_ALREADY_STOPPED: Self;
const RET_ERR_NO_SHMEM: Self;
const RET_ERR_INVALID_STATE: Self;
const RET_ERR_BAD_RANGE: Self;
const RET_ERR_TIMEOUT: Self;
const RET_ERR_IO: Self;
const RET_ERR_DENIED_LOCKED: Self;
const ZERO: Self;
const FULL_MASK: Self;
fn into_result(ret: SbiRet<Self>) -> Result<Self, Error<Self>>;
}
macro_rules! impl_sbi_register {
($ty:ty, $signed:ty) => {
impl SbiRegister for $ty {
const RET_SUCCESS: Self = 0;
const RET_ERR_FAILED: Self = -1 as $signed as $ty;
const RET_ERR_NOT_SUPPORTED: Self = -2 as $signed as $ty;
const RET_ERR_INVALID_PARAM: Self = -3 as $signed as $ty;
const RET_ERR_DENIED: Self = -4 as $signed as $ty;
const RET_ERR_INVALID_ADDRESS: Self = -5 as $signed as $ty;
const RET_ERR_ALREADY_AVAILABLE: Self = -6 as $signed as $ty;
const RET_ERR_ALREADY_STARTED: Self = -7 as $signed as $ty;
const RET_ERR_ALREADY_STOPPED: Self = -8 as $signed as $ty;
const RET_ERR_NO_SHMEM: Self = -9 as $signed as $ty;
const RET_ERR_INVALID_STATE: Self = -10 as $signed as $ty;
const RET_ERR_BAD_RANGE: Self = -11 as $signed as $ty;
const RET_ERR_TIMEOUT: Self = -12 as $signed as $ty;
const RET_ERR_IO: Self = -13 as $signed as $ty;
const RET_ERR_DENIED_LOCKED: Self = -14 as $signed as $ty;
const ZERO: Self = 0;
const FULL_MASK: Self = !0;
fn into_result(ret: SbiRet<Self>) -> Result<Self, Error<Self>> {
match ret.error {
Self::RET_SUCCESS => Ok(ret.value),
Self::RET_ERR_FAILED => Err(Error::Failed),
Self::RET_ERR_NOT_SUPPORTED => Err(Error::NotSupported),
Self::RET_ERR_INVALID_PARAM => Err(Error::InvalidParam),
Self::RET_ERR_DENIED => Err(Error::Denied),
Self::RET_ERR_INVALID_ADDRESS => Err(Error::InvalidAddress),
Self::RET_ERR_ALREADY_AVAILABLE => Err(Error::AlreadyAvailable),
Self::RET_ERR_ALREADY_STARTED => Err(Error::AlreadyStarted),
Self::RET_ERR_ALREADY_STOPPED => Err(Error::AlreadyStopped),
Self::RET_ERR_NO_SHMEM => Err(Error::NoShmem),
Self::RET_ERR_INVALID_STATE => Err(Error::InvalidState),
Self::RET_ERR_BAD_RANGE => Err(Error::BadRange),
Self::RET_ERR_TIMEOUT => Err(Error::Timeout),
Self::RET_ERR_IO => Err(Error::Io),
Self::RET_ERR_DENIED_LOCKED => Err(Error::DeniedLocked),
unknown => Err(Error::Custom(unknown as _)),
}
}
}
};
}
impl_sbi_register!(usize, isize);
impl_sbi_register!(isize, isize);
impl_sbi_register!(u32, i32);
impl_sbi_register!(i32, i32);
impl_sbi_register!(u64, i64);
impl_sbi_register!(i64, i64);
impl_sbi_register!(u128, i128);
impl_sbi_register!(i128, i128);
impl<T: SbiRegister + core::fmt::LowerHex> core::fmt::Debug for SbiRet<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match T::into_result(*self) {
Ok(value) => write!(f, "{:?}", value),
Err(err) => match err {
Error::Failed => write!(f, "<SBI call failed>"),
Error::NotSupported => write!(f, "<SBI feature not supported>"),
Error::InvalidParam => write!(f, "<SBI invalid parameter>"),
Error::Denied => write!(f, "<SBI denied>"),
Error::InvalidAddress => write!(f, "<SBI invalid address>"),
Error::AlreadyAvailable => write!(f, "<SBI already available>"),
Error::AlreadyStarted => write!(f, "<SBI already started>"),
Error::AlreadyStopped => write!(f, "<SBI already stopped>"),
Error::NoShmem => write!(f, "<SBI shared memory not available>"),
Error::InvalidState => write!(f, "<SBI invalid state>"),
Error::BadRange => write!(f, "<SBI bad range>"),
Error::Timeout => write!(f, "<SBI timeout>"),
Error::Io => write!(f, "<SBI input/output error>"),
Error::DeniedLocked => write!(f, "<SBI denied due to locked status>"),
Error::Custom(unknown) => write!(f, "[SBI Unknown error: {:#x}]", unknown),
},
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Error<T = usize> {
Failed,
NotSupported,
InvalidParam,
Denied,
InvalidAddress,
AlreadyAvailable,
AlreadyStarted,
AlreadyStopped,
NoShmem,
InvalidState,
BadRange,
Timeout,
Io,
DeniedLocked,
Custom(T),
}
impl<T: SbiRegister> SbiRet<T> {
#[inline]
pub const fn success(value: T) -> Self {
Self {
error: T::RET_SUCCESS,
value,
}
}
#[inline]
pub const fn failed() -> Self {
Self {
error: T::RET_ERR_FAILED,
value: T::ZERO,
}
}
#[inline]
pub const fn not_supported() -> Self {
Self {
error: T::RET_ERR_NOT_SUPPORTED,
value: T::ZERO,
}
}
#[inline]
pub const fn invalid_param() -> Self {
Self {
error: T::RET_ERR_INVALID_PARAM,
value: T::ZERO,
}
}
#[inline]
pub const fn denied() -> Self {
Self {
error: T::RET_ERR_DENIED,
value: T::ZERO,
}
}
#[inline]
pub const fn invalid_address() -> Self {
Self {
error: T::RET_ERR_INVALID_ADDRESS,
value: T::ZERO,
}
}
#[inline]
pub const fn already_available() -> Self {
Self {
error: T::RET_ERR_ALREADY_AVAILABLE,
value: T::ZERO,
}
}
#[inline]
pub const fn already_started() -> Self {
Self {
error: T::RET_ERR_ALREADY_STARTED,
value: T::ZERO,
}
}
#[inline]
pub const fn already_stopped() -> Self {
Self {
error: T::RET_ERR_ALREADY_STOPPED,
value: T::ZERO,
}
}
#[inline]
pub const fn no_shmem() -> Self {
Self {
error: T::RET_ERR_NO_SHMEM,
value: T::ZERO,
}
}
#[inline]
pub const fn invalid_state() -> Self {
Self {
error: T::RET_ERR_INVALID_STATE,
value: T::ZERO,
}
}
#[inline]
pub const fn bad_range() -> Self {
Self {
error: T::RET_ERR_BAD_RANGE,
value: T::ZERO,
}
}
#[inline]
pub const fn timeout() -> Self {
Self {
error: T::RET_ERR_TIMEOUT,
value: T::ZERO,
}
}
#[inline]
pub const fn io() -> Self {
Self {
error: T::RET_ERR_IO,
value: T::ZERO,
}
}
#[inline]
pub const fn denied_locked() -> Self {
Self {
error: T::RET_ERR_DENIED_LOCKED,
value: T::ZERO,
}
}
}
impl<T: SbiRegister> From<Error<T>> for SbiRet<T> {
#[inline]
fn from(value: Error<T>) -> Self {
match value {
Error::Failed => SbiRet::failed(),
Error::NotSupported => SbiRet::not_supported(),
Error::InvalidParam => SbiRet::invalid_param(),
Error::Denied => SbiRet::denied(),
Error::InvalidAddress => SbiRet::invalid_address(),
Error::AlreadyAvailable => SbiRet::already_available(),
Error::AlreadyStarted => SbiRet::already_started(),
Error::AlreadyStopped => SbiRet::already_stopped(),
Error::NoShmem => SbiRet::no_shmem(),
Error::InvalidState => SbiRet::invalid_state(),
Error::BadRange => SbiRet::bad_range(),
Error::Timeout => SbiRet::timeout(),
Error::Io => SbiRet::io(),
Error::DeniedLocked => SbiRet::denied_locked(),
Error::Custom(error) => SbiRet {
error,
value: T::ZERO,
},
}
}
}
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),
RET_ERR_INVALID_STATE => Err(Error::InvalidState),
RET_ERR_BAD_RANGE => Err(Error::BadRange),
RET_ERR_TIMEOUT => Err(Error::Timeout),
RET_ERR_IO => Err(Error::Io),
RET_ERR_DENIED_LOCKED => Err(Error::DeniedLocked),
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 unsafe fn unwrap_unchecked(self) -> usize {
unsafe { self.into_result().unwrap_unchecked() }
}
#[inline]
pub unsafe fn unwrap_err_unchecked(self) -> Error {
unsafe { self.into_result().unwrap_err_unchecked() }
}
}
impl IntoIterator for SbiRet {
type Item = usize;
type IntoIter = core::result::IntoIter<usize>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.into_result().into_iter()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[rustfmt::skip]
fn rustsbi_sbi_ret_constructors() {
assert_eq!(SbiRet::success(0), SbiRet { value: 0, error: 0 });
assert_eq!(SbiRet::success(1037), SbiRet { value: 1037, error: 0 });
assert_eq!(SbiRet::success(usize::MAX), SbiRet { value: usize::MAX, error: 0 });
assert_eq!(SbiRet::failed(), SbiRet { value: 0, error: usize::MAX - 1 + 1 });
assert_eq!(SbiRet::not_supported(), SbiRet { value: 0, error: usize::MAX - 2 + 1 });
assert_eq!(SbiRet::invalid_param(), SbiRet { value: 0, error: usize::MAX - 3 + 1 });
assert_eq!(SbiRet::denied(), SbiRet { value: 0, error: usize::MAX - 4 + 1 });
assert_eq!(SbiRet::invalid_address(), SbiRet { value: 0, error: usize::MAX - 5 + 1 });
assert_eq!(SbiRet::already_available(), SbiRet { value: 0, error: usize::MAX - 6 + 1 });
assert_eq!(SbiRet::already_started(), SbiRet { value: 0, error: usize::MAX - 7 + 1 });
assert_eq!(SbiRet::already_stopped(), SbiRet { value: 0, error: usize::MAX - 8 + 1 });
assert_eq!(SbiRet::no_shmem(), SbiRet { value: 0, error: usize::MAX - 9 + 1 });
assert_eq!(SbiRet::invalid_state(), SbiRet { value: 0, error: usize::MAX - 10 + 1 });
assert_eq!(SbiRet::bad_range(), SbiRet { value: 0, error: usize::MAX - 11 + 1 });
assert_eq!(SbiRet::timeout(), SbiRet { value: 0, error: usize::MAX - 12 + 1 });
assert_eq!(SbiRet::io(), SbiRet { value: 0, error: usize::MAX - 13 + 1 });
assert_eq!(SbiRet::denied_locked(), SbiRet { value: 0, error: usize::MAX - 14 + 1 });
}
}