#![doc = include_str!("../README.md")]
#![warn(missing_docs)]
#![no_std]
#[cfg(all(not(target_arch = "riscv64"), not(target_arch = "riscv32")))]
compile_error!("SBI is only available on RISC-V platforms");
pub mod base;
pub mod collaborative_processor_performance_control;
pub mod debug_console;
pub mod hart_state_management;
pub mod ipi;
pub mod legacy;
pub mod performance_monitoring_unit;
pub mod rfence;
pub mod system_reset;
pub mod system_suspend;
pub mod timer;
use core::{num::NonZeroIsize, ptr::NonNull};
pub use collaborative_processor_performance_control as cbbc;
pub use hart_state_management as hsm;
pub use performance_monitoring_unit as pmu;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct SbiError(Option<NonZeroIsize>);
impl SbiError {
pub const FAILED: Self = Self(unsafe { Some(NonZeroIsize::new_unchecked(-1)) });
pub const NOT_SUPPORTED: Self = Self(unsafe { Some(NonZeroIsize::new_unchecked(-2)) });
pub const INVALID_PARAMETER: Self = Self(unsafe { Some(NonZeroIsize::new_unchecked(-3)) });
pub const DENIED: Self = Self(unsafe { Some(NonZeroIsize::new_unchecked(-4)) });
pub const INVALID_ADDRESS: Self = Self(unsafe { Some(NonZeroIsize::new_unchecked(-5)) });
pub const ALREADY_AVAILABLE: Self = Self(unsafe { Some(NonZeroIsize::new_unchecked(-6)) });
pub const ALREADY_STARTED: Self = Self(unsafe { Some(NonZeroIsize::new_unchecked(-7)) });
pub const ALREADY_STOPPED: Self = Self(unsafe { Some(NonZeroIsize::new_unchecked(-8)) });
pub const SHARED_MEMORY_UNAVAILABLE: Self =
Self(unsafe { Some(NonZeroIsize::new_unchecked(-9)) });
}
impl SbiError {
#[inline]
fn new(n: isize) -> Self {
match n {
n if n.is_negative() => Self(Some(unsafe { NonZeroIsize::new_unchecked(n) })),
_ => Self(None),
}
}
}
impl core::fmt::Display for SbiError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"{}",
match *self {
SbiError::ALREADY_AVAILABLE => "resource is already available",
SbiError::DENIED => "SBI implementation denied execution",
SbiError::FAILED => "call to SBI failed",
SbiError::INVALID_ADDRESS => "invalid address passed",
SbiError::INVALID_PARAMETER => "invalid parameter passed",
SbiError::NOT_SUPPORTED =>
"SBI call not implemented or functionality not available",
SbiError::ALREADY_STARTED => "resource was already started",
SbiError::ALREADY_STOPPED => "resource was already stopped",
_ => "unknown error",
}
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct HartMask {
base: usize,
mask: usize,
}
impl HartMask {
#[inline]
pub const fn new(base: usize) -> Self {
Self { base, mask: 0 }
}
#[inline]
pub const fn from(hart_id: usize) -> Self {
Self {
base: hart_id,
mask: 1,
}
}
#[inline]
#[must_use]
pub const fn with(mut self, hart_id: usize) -> Self {
if hart_id >= self.base && hart_id < (self.base + usize::BITS as usize) {
self.mask |= 1 << (hart_id - self.base);
}
self
}
}
#[macro_export]
macro_rules! hart_mask {
($hart_id1:expr $(, $($hart_id:expr),+ $(,)?)?) => {{
let mut hart_mask = $crate::HartMask::from($hart_id1);
$($(hart_mask = hart_mask.with($hart_id);)+)?
hart_mask
}};
(base: $base:literal, ids: $($hart_id:expr),* $(,)?) => {{
let mut hart_mask = $crate::HartMask::new($base);
$(hart_mask = hart_mask.with($hart_id);)*
hart_mask
}};
}
#[derive(PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
pub struct RestrictedRange<const MIN: u32, const MAX: u32>(u32);
impl<const MIN: u32, const MAX: u32> RestrictedRange<MIN, MAX> {
pub const fn new(value: u32) -> Self {
if value < MIN || value > MAX {
panic!("invalid value supplied to `PlatformSpecific::new`")
}
Self(value)
}
}
impl<const MIN: u32, const MAX: u32> From<RestrictedRange<MIN, MAX>> for u32 {
fn from(value: RestrictedRange<MIN, MAX>) -> Self {
value.0
}
}
impl<const MIN: u32, const MAX: u32> Clone for RestrictedRange<MIN, MAX> {
fn clone(&self) -> Self {
*self
}
}
impl<const MIN: u32, const MAX: u32> Copy for RestrictedRange<MIN, MAX> {}
impl<const MIN: u32, const MAX: u32> core::fmt::Debug for RestrictedRange<MIN, MAX> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"RestrictedRange<MIN={MIN:#X}, MAX={MAX:#X}>({:#X})",
self.0
)
}
}
#[repr(transparent)]
pub struct PhysicalAddress<T: ?Sized>(*mut T);
impl<T: ?Sized> core::fmt::Debug for PhysicalAddress<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
self.0.fmt(f)
}
}
impl<T: ?Sized> Copy for PhysicalAddress<T> {}
impl<T: ?Sized> Clone for PhysicalAddress<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: ?Sized> Eq for PhysicalAddress<T> {}
impl<T: ?Sized> PartialEq for PhysicalAddress<T> {
fn eq(&self, other: &Self) -> bool {
core::ptr::eq(self.0, other.0)
}
}
impl<T: ?Sized> Ord for PhysicalAddress<T> {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.0.cast::<()>().cmp(&other.0.cast::<()>())
}
}
impl<T: ?Sized> PartialOrd for PhysicalAddress<T> {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<T: ?Sized> PhysicalAddress<T> {
pub fn new(value: usize) -> Self
where
T: Sized,
{
Self(value as *mut T)
}
pub fn from_ptr(ptr: *mut T) -> Self {
Self(ptr)
}
}
impl<T: Sized> PhysicalAddress<T> {
pub fn as_ptr(self) -> *mut T {
self.0
}
}
impl<T> PhysicalAddress<[T]> {
pub fn as_ptr(self) -> *mut T {
self.0.cast()
}
#[allow(clippy::len_without_is_empty)]
pub fn len(self) -> usize {
match NonNull::new(self.0) {
Some(p) => p.len(),
None => NonNull::new(self.0.wrapping_byte_add(core::mem::size_of::<T>()))
.unwrap()
.len(),
}
}
}
impl<T> From<*mut T> for PhysicalAddress<T> {
fn from(value: *mut T) -> Self {
Self::from_ptr(value)
}
}
impl<T> From<NonNull<T>> for PhysicalAddress<T> {
fn from(value: NonNull<T>) -> Self {
Self::from_ptr(value.as_ptr())
}
}
#[inline]
pub unsafe fn ecall0(extension_id: usize, function_id: usize) -> Result<usize, SbiError> {
let error: isize;
let value: usize;
core::arch::asm!(
"ecall",
in("a6") function_id,
in("a7") extension_id,
lateout("a0") error,
lateout("a1") value,
);
match error {
0 => Result::Ok(value),
e => Result::Err(SbiError::new(e)),
}
}
#[inline]
pub unsafe fn ecall1(
arg: usize,
extension_id: usize,
function_id: usize,
) -> Result<usize, SbiError> {
let error: isize;
let value: usize;
core::arch::asm!(
"ecall",
inlateout("a0") arg => error,
in("a6") function_id,
in("a7") extension_id,
lateout("a1") value,
);
match error {
0 => Result::Ok(value),
e => Result::Err(SbiError::new(e)),
}
}
#[inline]
pub unsafe fn ecall2(
arg0: usize,
arg1: usize,
extension_id: usize,
function_id: usize,
) -> Result<usize, SbiError> {
let error: isize;
let value: usize;
core::arch::asm!(
"ecall",
inlateout("a0") arg0 => error,
inlateout("a1") arg1 => value,
in("a6") function_id,
in("a7") extension_id,
);
match error {
0 => Result::Ok(value),
e => Result::Err(SbiError::new(e)),
}
}
#[inline]
pub unsafe fn ecall3(
arg0: usize,
arg1: usize,
arg2: usize,
extension_id: usize,
function_id: usize,
) -> Result<usize, SbiError> {
let error: isize;
let value: usize;
core::arch::asm!(
"ecall",
inlateout("a0") arg0 => error,
inlateout("a1") arg1 => value,
in("a2") arg2,
in("a6") function_id,
in("a7") extension_id,
);
match error {
0 => Result::Ok(value),
e => Result::Err(SbiError::new(e)),
}
}
#[inline]
pub unsafe fn ecall4(
arg0: usize,
arg1: usize,
arg2: usize,
arg3: usize,
extension_id: usize,
function_id: usize,
) -> Result<usize, SbiError> {
let error: isize;
let value: usize;
core::arch::asm!(
"ecall",
inlateout("a0") arg0 => error,
inlateout("a1") arg1 => value,
in("a2") arg2,
in("a3") arg3,
in("a6") function_id,
in("a7") extension_id,
);
match error {
0 => Result::Ok(value),
e => Result::Err(SbiError::new(e)),
}
}
#[inline]
pub unsafe fn ecall5(
arg0: usize,
arg1: usize,
arg2: usize,
arg3: usize,
arg4: usize,
extension_id: usize,
function_id: usize,
) -> Result<usize, SbiError> {
let error: isize;
let value: usize;
core::arch::asm!(
"ecall",
inlateout("a0") arg0 => error,
inlateout("a1") arg1 => value,
in("a2") arg2,
in("a3") arg3,
in("a4") arg4,
in("a6") function_id,
in("a7") extension_id,
);
match error {
0 => Result::Ok(value),
e => Result::Err(SbiError::new(e)),
}
}
#[inline]
#[allow(clippy::too_many_arguments)]
pub unsafe fn ecall6(
arg0: usize,
arg1: usize,
arg2: usize,
arg3: usize,
arg4: usize,
arg5: usize,
extension_id: usize,
function_id: usize,
) -> Result<usize, SbiError> {
let error: isize;
let value: usize;
core::arch::asm!(
"ecall",
inlateout("a0") arg0 => error,
inlateout("a1") arg1 => value,
in("a2") arg2,
in("a3") arg3,
in("a4") arg4,
in("a5") arg5,
in("a6") function_id,
in("a7") extension_id,
);
match error {
0 => Result::Ok(value),
e => Result::Err(SbiError::new(e)),
}
}