diff --git a/riscv-pac/CHANGELOG.md b/riscv-pac/CHANGELOG.md index ab888408..0303047f 100644 --- a/riscv-pac/CHANGELOG.md +++ b/riscv-pac/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Added + +- Add `result` module for `Error` and `Result` types + ## [v0.1.1] - 2024-02-15 - Fix crates.io badge links diff --git a/riscv-pac/src/lib.rs b/riscv-pac/src/lib.rs index 934e7aad..4da1170a 100644 --- a/riscv-pac/src/lib.rs +++ b/riscv-pac/src/lib.rs @@ -1,5 +1,9 @@ #![no_std] +pub mod result; + +use result::Result; + /// Trait for enums of target-specific external interrupt numbers. /// /// This trait should be implemented by a peripheral access crate (PAC) @@ -23,7 +27,7 @@ pub unsafe trait InterruptNumber: Copy { /// Tries to convert a number to a valid interrupt source. /// If the conversion fails, it returns an error with the number back. - fn from_number(value: u16) -> Result; + fn from_number(value: u16) -> Result; } /// Trait for enums of priority levels. @@ -49,7 +53,7 @@ pub unsafe trait PriorityNumber: Copy { /// Tries to convert a number to a valid priority level. /// If the conversion fails, it returns an error with the number back. - fn from_number(value: u8) -> Result; + fn from_number(value: u8) -> Result; } /// Trait for enums of HART identifiers. @@ -75,5 +79,5 @@ pub unsafe trait HartIdNumber: Copy { /// Tries to convert a number to a valid HART ID. /// If the conversion fails, it returns an error with the number back. - fn from_number(value: u16) -> Result; + fn from_number(value: u16) -> Result; } diff --git a/riscv-pac/src/result.rs b/riscv-pac/src/result.rs new file mode 100644 index 00000000..b55c8beb --- /dev/null +++ b/riscv-pac/src/result.rs @@ -0,0 +1,58 @@ +use core::fmt; + +/// Convenience alias for the [Result](core::result::Result) type for the library. +pub type Result = core::result::Result; + +/// Represents error variants for the library. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum Error { + /// Attempted out-of-bounds access. + IndexOutOfBounds { + index: usize, + min: usize, + max: usize, + }, + /// Invalid field value. + InvalidFieldValue { + field: &'static str, + value: usize, + bitmask: usize, + }, + /// Invalid value of a register field that does not match any known variants. + InvalidFieldVariant { field: &'static str, value: usize }, + /// Invalid value. + InvalidValue { value: usize, bitmask: usize }, + /// Invalid value that does not match any known variants. + InvalidVariant(usize), + /// Unimplemented function or type. + Unimplemented, +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::IndexOutOfBounds { index, min, max } => write!( + f, + "out-of-bounds access, index: {index}, min: {min}, max: {max}" + ), + Self::InvalidFieldValue { + field, + value, + bitmask, + } => write!( + f, + "invalid {field} field value: {value:#x}, valid bitmask: {bitmask:#x}", + ), + Self::InvalidFieldVariant { field, value } => { + write!(f, "invalid {field} field variant: {value:#x}") + } + Self::InvalidValue { value, bitmask } => { + write!(f, "invalid value: {value:#x}, valid bitmask: {bitmask:#x}",) + } + Self::InvalidVariant(value) => { + write!(f, "invalid variant: {value:#x}") + } + Self::Unimplemented => write!(f, "unimplemented"), + } + } +} diff --git a/riscv-peripheral/CHANGELOG.md b/riscv-peripheral/CHANGELOG.md index b0038758..3feb9601 100644 --- a/riscv-peripheral/CHANGELOG.md +++ b/riscv-peripheral/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Added + +- use `riscv-pac` result types for trait implementations + ### Fixed - `clippy` fixes diff --git a/riscv-peripheral/examples/e310x.rs b/riscv-peripheral/examples/e310x.rs index c0801f24..6f608b6c 100644 --- a/riscv-peripheral/examples/e310x.rs +++ b/riscv-peripheral/examples/e310x.rs @@ -2,6 +2,7 @@ //! This is a simple example of how to use the `riscv-peripheral` crate to generate //! peripheral definitions for a target. +use riscv_pac::result::{Error, Result}; use riscv_pac::{HartIdNumber, InterruptNumber, PriorityNumber}; #[repr(u16)] @@ -19,9 +20,9 @@ unsafe impl HartIdNumber for HartId { } #[inline] - fn from_number(number: u16) -> Result { + fn from_number(number: u16) -> Result { if number > Self::MAX_HART_ID_NUMBER { - Err(number) + Err(Error::InvalidVariant(number as usize)) } else { // SAFETY: valid context number Ok(unsafe { core::mem::transmute(number) }) @@ -95,9 +96,9 @@ unsafe impl InterruptNumber for Interrupt { } #[inline] - fn from_number(number: u16) -> Result { + fn from_number(number: u16) -> Result { if number == 0 || number > Self::MAX_INTERRUPT_NUMBER { - Err(number) + Err(Error::InvalidVariant(number as usize)) } else { // SAFETY: valid interrupt number Ok(unsafe { core::mem::transmute(number) }) @@ -127,9 +128,9 @@ unsafe impl PriorityNumber for Priority { } #[inline] - fn from_number(number: u8) -> Result { + fn from_number(number: u8) -> Result { if number > Self::MAX_PRIORITY_NUMBER { - Err(number) + Err(Error::InvalidVariant(number as usize)) } else { // SAFETY: valid priority number Ok(unsafe { core::mem::transmute(number) }) diff --git a/riscv-peripheral/src/aclint.rs b/riscv-peripheral/src/aclint.rs index 5aa08239..873daeda 100644 --- a/riscv-peripheral/src/aclint.rs +++ b/riscv-peripheral/src/aclint.rs @@ -63,6 +63,7 @@ impl CLINT { #[cfg(test)] pub(crate) mod test { use super::HartIdNumber; + use riscv_pac::result::{Error, Result}; #[derive(Clone, Copy, Debug, Eq, PartialEq)] #[repr(u16)] @@ -81,9 +82,9 @@ pub(crate) mod test { } #[inline] - fn from_number(number: u16) -> Result { + fn from_number(number: u16) -> Result { if number > Self::MAX_HART_ID_NUMBER { - Err(number) + Err(Error::InvalidVariant(number as usize)) } else { // SAFETY: valid context number Ok(unsafe { core::mem::transmute(number) }) @@ -101,7 +102,7 @@ pub(crate) mod test { assert_eq!(HartId::from_number(1), Ok(HartId::H1)); assert_eq!(HartId::from_number(2), Ok(HartId::H2)); - assert_eq!(HartId::from_number(3), Err(3)); + assert_eq!(HartId::from_number(3), Err(Error::InvalidVariant(3))); } #[allow(dead_code)] diff --git a/riscv-peripheral/src/macros.rs b/riscv-peripheral/src/macros.rs index f4c6e8b4..eec2ac27 100644 --- a/riscv-peripheral/src/macros.rs +++ b/riscv-peripheral/src/macros.rs @@ -30,6 +30,7 @@ /// /// ``` /// use riscv_peripheral::clint_codegen; +/// use riscv_pac::result::{Error, Result}; /// /// /// HART IDs for the target CLINT peripheral /// #[derive(Clone, Copy, Debug, Eq, PartialEq)] @@ -40,9 +41,9 @@ /// unsafe impl riscv_peripheral::aclint::HartIdNumber for HartId { /// const MAX_HART_ID_NUMBER: u16 = 2; /// fn number(self) -> u16 { self as _ } -/// fn from_number(number: u16) -> Result { +/// fn from_number(number: u16) -> Result { /// if number > Self::MAX_HART_ID_NUMBER { -/// Err(number) +/// Err(Error::InvalidVariant(number as usize)) /// } else { /// // SAFETY: valid context number /// Ok(unsafe { core::mem::transmute(number) }) diff --git a/riscv-peripheral/src/plic.rs b/riscv-peripheral/src/plic.rs index 0619b0f7..116689a6 100644 --- a/riscv-peripheral/src/plic.rs +++ b/riscv-peripheral/src/plic.rs @@ -8,7 +8,8 @@ pub mod pendings; pub mod priorities; pub mod threshold; -pub use riscv_pac::{HartIdNumber, InterruptNumber, PriorityNumber}; // re-export useful riscv-pac traits +// re-export useful riscv-pac traits +pub use riscv_pac::{HartIdNumber, InterruptNumber, PriorityNumber}; /// Trait for a PLIC peripheral. /// @@ -145,6 +146,7 @@ impl CTX

{ #[cfg(test)] pub(crate) mod test { use super::{HartIdNumber, InterruptNumber, PriorityNumber}; + use riscv_pac::result::{Error, Result}; #[derive(Clone, Copy, Debug, Eq, PartialEq)] #[repr(u16)] @@ -181,9 +183,9 @@ pub(crate) mod test { } #[inline] - fn from_number(number: u16) -> Result { + fn from_number(number: u16) -> Result { if number > Self::MAX_INTERRUPT_NUMBER || number == 0 { - Err(number) + Err(Error::InvalidVariant(number as usize)) } else { // SAFETY: valid interrupt number Ok(unsafe { core::mem::transmute(number) }) @@ -200,9 +202,9 @@ pub(crate) mod test { } #[inline] - fn from_number(number: u8) -> Result { + fn from_number(number: u8) -> Result { if number > Self::MAX_PRIORITY_NUMBER { - Err(number) + Err(Error::InvalidVariant(number as usize)) } else { // SAFETY: valid priority number Ok(unsafe { core::mem::transmute(number) }) @@ -219,9 +221,9 @@ pub(crate) mod test { } #[inline] - fn from_number(number: u16) -> Result { + fn from_number(number: u16) -> Result { if number > Self::MAX_HART_ID_NUMBER { - Err(number) + Err(Error::InvalidVariant(number as usize)) } else { // SAFETY: valid context number Ok(unsafe { core::mem::transmute(number) }) @@ -241,8 +243,8 @@ pub(crate) mod test { assert_eq!(Interrupt::from_number(3), Ok(Interrupt::I3)); assert_eq!(Interrupt::from_number(4), Ok(Interrupt::I4)); - assert_eq!(Interrupt::from_number(0), Err(0)); - assert_eq!(Interrupt::from_number(5), Err(5)); + assert_eq!(Interrupt::from_number(0), Err(Error::InvalidVariant(0)),); + assert_eq!(Interrupt::from_number(5), Err(Error::InvalidVariant(5)),); } #[test] @@ -257,7 +259,7 @@ pub(crate) mod test { assert_eq!(Priority::from_number(2), Ok(Priority::P2)); assert_eq!(Priority::from_number(3), Ok(Priority::P3)); - assert_eq!(Priority::from_number(4), Err(4)); + assert_eq!(Priority::from_number(4), Err(Error::InvalidVariant(4)),); } #[test] @@ -270,7 +272,7 @@ pub(crate) mod test { assert_eq!(Context::from_number(1), Ok(Context::C1)); assert_eq!(Context::from_number(2), Ok(Context::C2)); - assert_eq!(Context::from_number(3), Err(3)); + assert_eq!(Context::from_number(3), Err(Error::InvalidVariant(3)),); } #[allow(dead_code)] diff --git a/riscv/CHANGELOG.md b/riscv/CHANGELOG.md index f40a3202..7b2aa2fb 100644 --- a/riscv/CHANGELOG.md +++ b/riscv/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Add `riscv::register::mcountinhibit` module for `mcountinhibit` CSR - Add `Mcounteren` in-memory update functions - Add `Mstatus` vector extension support +- Add fallible counterparts to all functions that `panic` ### Fixed diff --git a/riscv/Cargo.toml b/riscv/Cargo.toml index 45f07f36..a783bb1c 100644 --- a/riscv/Cargo.toml +++ b/riscv/Cargo.toml @@ -26,3 +26,4 @@ critical-section-single-hart = ["critical-section/restore-state-bool"] [dependencies] critical-section = "1.1.2" embedded-hal = "1.0.0" +riscv-pac = { path = "../riscv-pac", version = "0.1.1", default-features = false } diff --git a/riscv/src/lib.rs b/riscv/src/lib.rs index cb6f6ed8..daa859f5 100644 --- a/riscv/src/lib.rs +++ b/riscv/src/lib.rs @@ -40,6 +40,7 @@ pub(crate) mod bits; pub mod delay; pub mod interrupt; pub mod register; +pub use riscv_pac::*; #[macro_use] mod macros; diff --git a/riscv/src/register/macros.rs b/riscv/src/register/macros.rs index d2dfcb49..1153dedc 100644 --- a/riscv/src/register/macros.rs +++ b/riscv/src/register/macros.rs @@ -6,19 +6,27 @@ #[macro_export] macro_rules! read_csr { ($csr_number:literal) => { - /// Reads the CSR + /// Reads the CSR. + /// + /// **WARNING**: panics on non-`riscv` targets. #[inline] unsafe fn _read() -> usize { + _try_read().unwrap() + } + + /// Attempts to read the CSR. + #[inline] + unsafe fn _try_read() -> $crate::result::Result { match () { #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] () => { let r: usize; core::arch::asm!(concat!("csrrs {0}, ", stringify!($csr_number), ", x0"), out(reg) r); - r + Ok(r) } #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))] - () => unimplemented!(), + () => Err($crate::result::Error::Unimplemented), } } }; @@ -32,19 +40,27 @@ macro_rules! read_csr { #[macro_export] macro_rules! read_csr_rv32 { ($csr_number:literal) => { - /// Reads the CSR + /// Reads the CSR. + /// + /// **WARNING**: panics on non-`riscv` targets. #[inline] unsafe fn _read() -> usize { + _try_read().unwrap() + } + + /// Attempts to read the CSR. + #[inline] + unsafe fn _try_read() -> $crate::result::Result { match () { #[cfg(target_arch = "riscv32")] () => { let r: usize; core::arch::asm!(concat!("csrrs {0}, ", stringify!($csr_number), ", x0"), out(reg) r); - r + Ok(r) } #[cfg(not(target_arch = "riscv32"))] - () => unimplemented!(), + () => Err($crate::result::Error::Unimplemented), } } }; @@ -58,13 +74,23 @@ macro_rules! read_csr_as { ($register:ident, $csr_number:literal) => { $crate::read_csr!($csr_number); - /// Reads the CSR + /// Reads the CSR. + /// + /// **WARNING**: panics on non-`riscv` targets. #[inline] pub fn read() -> $register { $register { bits: unsafe { _read() }, } } + + /// Attempts to reads the CSR. + #[inline] + pub fn try_read() -> $crate::result::Result<$register> { + Ok($register { + bits: unsafe { _try_read()? }, + }) + } }; } @@ -76,13 +102,23 @@ macro_rules! read_csr_as_rv32 { ($register:ident, $csr_number:literal) => { $crate::read_csr_rv32!($csr_number); - /// Reads the CSR + /// Reads the CSR. + /// + /// **WARNING**: panics on non-`riscv` targets. #[inline] pub fn read() -> $register { $register { bits: unsafe { _read() }, } } + + /// Attempts to reads the CSR. + #[inline] + pub fn try_read() -> $crate::result::Result<$register> { + Ok($register { + bits: unsafe { _try_read()? }, + }) + } }; } @@ -92,11 +128,19 @@ macro_rules! read_csr_as_usize { ($csr_number:literal) => { $crate::read_csr!($csr_number); - /// Reads the CSR + /// Reads the CSR. + /// + /// **WARNING**: panics on non-`riscv` targets. #[inline] pub fn read() -> usize { unsafe { _read() } } + + /// Attempts to read the CSR. + #[inline] + pub fn try_read() -> $crate::result::Result { + unsafe { _try_read() } + } }; } @@ -106,11 +150,19 @@ macro_rules! read_csr_as_usize_rv32 { ($csr_number:literal) => { $crate::read_csr_rv32!($csr_number); - /// Reads the CSR + /// Reads the CSR. + /// + /// **WARNING**: panics on non-`riscv` targets. #[inline] pub fn read() -> usize { unsafe { _read() } } + + /// Attempts to reads the CSR. + #[inline] + pub fn try_read() -> $crate::result::Result { + unsafe { _try_read() } + } }; } @@ -122,16 +174,28 @@ macro_rules! read_csr_as_usize_rv32 { #[macro_export] macro_rules! write_csr { ($csr_number:literal) => { - /// Writes the CSR + /// Writes the CSR. + /// + /// **WARNING**: panics on non-`riscv` targets. #[inline] #[allow(unused_variables)] unsafe fn _write(bits: usize) { + _try_write(bits).unwrap(); + } + + /// Attempts to write the CSR. + #[inline] + #[allow(unused_variables)] + unsafe fn _try_write(bits: usize) -> $crate::result::Result<()> { match () { #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] - () => core::arch::asm!(concat!("csrrw x0, ", stringify!($csr_number), ", {0}"), in(reg) bits), + () => { + core::arch::asm!(concat!("csrrw x0, ", stringify!($csr_number), ", {0}"), in(reg) bits); + Ok(()) + } #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))] - () => unimplemented!(), + () => Err($crate::result::Error::Unimplemented), } } }; @@ -145,16 +209,28 @@ macro_rules! write_csr { #[macro_export] macro_rules! write_csr_rv32 { ($csr_number:literal) => { - /// Writes the CSR + /// Writes the CSR. + /// + /// **WARNING**: panics on non-`riscv` targets. #[inline] #[allow(unused_variables)] unsafe fn _write(bits: usize) { + _try_write(bits).unwrap(); + } + + /// Attempts to write the CSR. + #[inline] + #[allow(unused_variables)] + unsafe fn _try_write(bits: usize) -> $crate::result::Result<()> { match () { #[cfg(target_arch = "riscv32")] - () => core::arch::asm!(concat!("csrrw x0, ", stringify!($csr_number), ", {0}"), in(reg) bits), + () => { + core::arch::asm!(concat!("csrrw x0, ", stringify!($csr_number), ", {0}"), in(reg) bits); + Ok(()) + } #[cfg(not(target_arch = "riscv32"))] - () => unimplemented!(), + () => Err($crate::result::Error::Unimplemented), } } }; @@ -166,11 +242,19 @@ macro_rules! write_csr_as { ($csr_type:ty, $csr_number:literal) => { $crate::write_csr!($csr_number); - /// Writes the CSR + /// Writes the CSR. + /// + /// **WARNING**: panics on non-`riscv` targets. #[inline] pub fn write(value: $csr_type) { unsafe { _write(value.bits) } } + + /// Attempts to write the CSR. + #[inline] + pub fn try_write(value: $csr_type) -> $crate::result::Result<()> { + unsafe { _try_write(value.bits) } + } }; } @@ -180,11 +264,19 @@ macro_rules! write_csr_as_rv32 { ($csr_type:ty, $csr_number:literal) => { $crate::write_csr_rv32!($csr_number); - /// Writes the CSR + /// Writes the CSR. + /// + /// **WARNING**: panics on non-`riscv` targets. #[inline] pub fn write(value: $csr_type) { unsafe { _write(value.bits) } } + + /// Attempts to write the CSR. + #[inline] + pub fn try_write(value: $csr_type) -> $crate::result::Result<()> { + unsafe { _try_write(value.bits) } + } }; } @@ -194,11 +286,19 @@ macro_rules! write_csr_as_usize { ($csr_number:literal) => { $crate::write_csr!($csr_number); - /// Writes the CSR + /// Writes the CSR. + /// + /// **WARNING**: panics on non-`riscv` targets. #[inline] pub fn write(bits: usize) { unsafe { _write(bits) } } + + /// Attempts to write the CSR. + #[inline] + pub fn try_write(bits: usize) -> $crate::result::Result<()> { + unsafe { _try_write(bits) } + } }; } @@ -208,11 +308,19 @@ macro_rules! write_csr_as_usize_rv32 { ($csr_number:literal) => { $crate::write_csr_rv32!($csr_number); - /// Writes the CSR + /// Writes the CSR. + /// + /// **WARNING**: panics on non-`riscv` targets. #[inline] pub fn write(bits: usize) { unsafe { _write(bits) } } + + /// Attempts to write the CSR. + #[inline] + pub fn try_write(bits: usize) -> $crate::result::Result<()> { + unsafe { _try_write(bits) } + } }; } @@ -222,16 +330,28 @@ macro_rules! write_csr_as_usize_rv32 { #[macro_export] macro_rules! set { ($csr_number:literal) => { - /// Set the CSR + /// Set the CSR. + /// + /// **WARNING**: panics on non-`riscv` targets. #[inline] #[allow(unused_variables)] unsafe fn _set(bits: usize) { + _try_set(bits).unwrap(); + } + + /// Attempts to set the CSR. + #[inline] + #[allow(unused_variables)] + unsafe fn _try_set(bits: usize) -> $crate::result::Result<()> { match () { #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] - () => core::arch::asm!(concat!("csrrs x0, ", stringify!($csr_number), ", {0}"), in(reg) bits), + () => { + core::arch::asm!(concat!("csrrs x0, ", stringify!($csr_number), ", {0}"), in(reg) bits); + Ok(()) + } #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))] - () => unimplemented!(), + () => Err($crate::result::Error::Unimplemented), } } }; @@ -243,16 +363,28 @@ macro_rules! set { #[macro_export] macro_rules! set_rv32 { ($csr_number:literal) => { - /// Set the CSR + /// Set the CSR. + /// + /// **WARNING**: panics on non-`riscv` targets. #[inline] #[allow(unused_variables)] unsafe fn _set(bits: usize) { + _try_set(bits).unwrap(); + } + + /// Attempts to set the CSR. + #[inline] + #[allow(unused_variables)] + unsafe fn _try_set(bits: usize) -> $crate::result::Result<()> { match () { #[cfg(target_arch = "riscv32")] - () => core::arch::asm!(concat!("csrrs x0, ", stringify!($csr_number), ", {0}"), in(reg) bits), + () => { + core::arch::asm!(concat!("csrrs x0, ", stringify!($csr_number), ", {0}"), in(reg) bits); + Ok(()) + } #[cfg(not(target_arch = "riscv32"))] - () => unimplemented!(), + () => Err($crate::result::Error::Unimplemented), } } }; @@ -264,16 +396,28 @@ macro_rules! set_rv32 { #[macro_export] macro_rules! clear { ($csr_number:literal) => { - /// Clear the CSR + /// Clear the CSR. + /// + /// **WARNING**: panics on non-`riscv` targets. #[inline] #[allow(unused_variables)] unsafe fn _clear(bits: usize) { + _try_clear(bits).unwrap(); + } + + /// Attempts to clear the CSR. + #[inline] + #[allow(unused_variables)] + unsafe fn _try_clear(bits: usize) -> $crate::result::Result<()> { match () { #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] - () => core::arch::asm!(concat!("csrrc x0, ", stringify!($csr_number), ", {0}"), in(reg) bits), + () => { + core::arch::asm!(concat!("csrrc x0, ", stringify!($csr_number), ", {0}"), in(reg) bits); + Ok(()) + } #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))] - () => unimplemented!(), + () => Err($crate::result::Error::Unimplemented), } } }; @@ -285,16 +429,28 @@ macro_rules! clear { #[macro_export] macro_rules! clear_rv32 { ($csr_number:literal) => { - /// Clear the CSR + /// Clear the CSR. + /// + /// **WARNING**: panics on non-`riscv` targets. #[inline] #[allow(unused_variables)] unsafe fn _clear(bits: usize) { + _try_clear(bits).unwrap(); + } + + /// Attempts to clear the CSR. + #[inline] + #[allow(unused_variables)] + unsafe fn _try_clear(bits: usize) -> $crate::result::Result<()> { match () { #[cfg(target_arch = "riscv32")] - () => core::arch::asm!(concat!("csrrc x0, ", stringify!($csr_number), ", {0}"), in(reg) bits), + () => { + core::arch::asm!(concat!("csrrc x0, ", stringify!($csr_number), ", {0}"), in(reg) bits); + Ok(()) + } #[cfg(not(target_arch = "riscv32"))] - () => unimplemented!(), + () => Err($crate::result::Error::Unimplemented), } } }; @@ -362,38 +518,85 @@ macro_rules! read_composite_csr { macro_rules! set_pmp { () => { - /// Set the pmp configuration corresponding to the index + /// Set the pmp configuration corresponding to the index. + /// + /// **WARNING**: panics on non-`riscv` targets, and/or if `index` is out-of-bounds. #[inline] pub unsafe fn set_pmp(index: usize, range: Range, permission: Permission, locked: bool) { - #[cfg(target_arch = "riscv32")] - assert!(index < 4); - - #[cfg(target_arch = "riscv64")] - assert!(index < 8); + try_set_pmp(index, range, permission, locked).unwrap() + } - let mut value = _read(); - value &= !(0xFF << (8 * index)); // clear previous value - let byte = (locked as usize) << 7 | (range as usize) << 3 | (permission as usize); - value |= byte << (8 * index); - _write(value); + /// Attempts to set the pmp configuration corresponding to the index. + /// + /// Returns an error if the index is invalid. + #[inline] + pub unsafe fn try_set_pmp( + index: usize, + range: Range, + permission: Permission, + locked: bool, + ) -> $crate::result::Result<()> { + let max = match () { + #[cfg(target_arch = "riscv32")] + () => Ok(4usize), + #[cfg(target_arch = "riscv64")] + () => Ok(8usize), + #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))] + () => Err($crate::result::Error::Unimplemented), + }?; + + if index < max { + let mut value = _try_read()?; + value &= !(0xFF << (8 * index)); // clear previous value + let byte = (locked as usize) << 7 | (range as usize) << 3 | (permission as usize); + value |= byte << (8 * index); + _try_write(value) + } else { + Err($crate::result::Error::IndexOutOfBounds { + index, + min: 0, + max: max - 1, + }) + } } }; } macro_rules! clear_pmp { () => { - /// Clear the pmp configuration corresponding to the index + /// Clear the pmp configuration corresponding to the index. + /// + /// **WARNING**: panics on non-`riscv` targets, and/or if `index` is out-of-bounds. #[inline] pub unsafe fn clear_pmp(index: usize) { - #[cfg(target_arch = "riscv32")] - assert!(index < 4); - - #[cfg(target_arch = "riscv64")] - assert!(index < 8); + try_clear_pmp(index).unwrap(); + } - let mut value = _read(); - value &= !(0xFF << (8 * index)); // clear previous value - _write(value); + /// Attempts to clear the pmp configuration corresponding to the index. + /// + /// Returns an error if the index is invalid. + #[inline] + pub unsafe fn try_clear_pmp(index: usize) -> $crate::result::Result<()> { + let max = match () { + #[cfg(target_arch = "riscv32")] + () => Ok(4usize), + #[cfg(target_arch = "riscv64")] + () => Ok(8usize), + #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))] + () => Err($crate::result::Error::Unimplemented), + }?; + + if index < max { + let mut value = _try_read()?; + value &= !(0xFF << (8 * index)); // clear previous value + _try_write(value) + } else { + Err($crate::result::Error::IndexOutOfBounds { + index, + min: 0, + max: max - 1, + }) + } } }; } diff --git a/riscv/src/register/mcounteren.rs b/riscv/src/register/mcounteren.rs index da9df7a5..b9008f8a 100644 --- a/riscv/src/register/mcounteren.rs +++ b/riscv/src/register/mcounteren.rs @@ -1,6 +1,7 @@ //! mcounteren register use crate::bits::{bf_extract, bf_insert}; +use crate::result::{Error, Result}; /// mcounteren register #[derive(Clone, Copy, Debug)] @@ -52,19 +53,56 @@ impl Mcounteren { } /// Supervisor "hpm\[x\]" Enable (bits 3-31) + /// + /// **WARNING**: panics on `index` out-of-bounds #[inline] pub fn hpm(&self, index: usize) -> bool { - assert!((3..32).contains(&index)); - bf_extract(self.bits, index, 1) != 0 + self.try_hpm(index).unwrap() + } + + /// Fallible Supervisor "hpm\[x\]" Enable (bits 3-31). + /// + /// Attempts to read the "hpm\[x\]" value, and returns an error if the `index` is invalid. + #[inline] + pub fn try_hpm(&self, index: usize) -> Result { + if (3..32).contains(&index) { + Ok(bf_extract(self.bits, index, 1) != 0) + } else { + Err(Error::IndexOutOfBounds { + index, + min: 3, + max: 31, + }) + } } /// Sets whether to enable the "hpm\[X\]" counter. /// /// Only updates the in-memory value, does not modify the `mcounteren` register. + /// + /// **WARNING**: panics on `index` out-of-bounds #[inline] pub fn set_hpm(&mut self, index: usize, hpm: bool) { - assert!((3..32).contains(&index)); - self.bits = bf_insert(self.bits, index, 1, hpm as usize); + self.try_set_hpm(index, hpm).unwrap() + } + + /// Sets whether to enable the "hpm\[X\]" counter. + /// + /// Only updates the in-memory value, does not modify the `mcounteren` register. + /// + /// Attempts to update the "hpm\[x\]" value, and returns an error if the `index` is invalid. + #[inline] + pub fn try_set_hpm(&mut self, index: usize, hpm: bool) -> Result<()> { + if (3..32).contains(&index) { + self.bits = bf_insert(self.bits, index, 1, hpm as usize); + Ok(()) + } else { + Err(Error::IndexOutOfBounds { + index, + min: 3, + max: 31, + }) + } } } @@ -85,16 +123,62 @@ set_clear_csr!( /// Supervisor instret Enable , set_ir, clear_ir, 1 << 2); +/// Enables the "hpm\[X\]" counter. +/// +/// Updates the `mcounteren` register. +/// +/// **WARNING**: panics on: +/// +/// - non-`riscv` targets +/// - `index` out-of-bounds #[inline] pub unsafe fn set_hpm(index: usize) { - assert!((3..32).contains(&index)); - _set(1 << index); + try_set_hpm(index).unwrap(); +} + +/// Attempts to enable the "hpm\[X\]" counter. +/// +/// Updates the `mcounteren` register. +#[inline] +pub unsafe fn try_set_hpm(index: usize) -> Result<()> { + if (3..32).contains(&index) { + _try_set(1 << index) + } else { + Err(Error::IndexOutOfBounds { + index, + min: 3, + max: 31, + }) + } } +/// Disables the "hpm\[X\]" counter. +/// +/// Updates the `mcounteren` register. +/// +/// **WARNING**: panics on: +/// +/// - non-`riscv` targets +/// - `index` out-of-bounds #[inline] pub unsafe fn clear_hpm(index: usize) { - assert!((3..32).contains(&index)); - _clear(1 << index); + try_clear_hpm(index).unwrap(); +} + +/// Attempts to disable the "hpm\[X\]" counter. +/// +/// Updates the `mcounteren` register. +#[inline] +pub unsafe fn try_clear_hpm(index: usize) -> Result<()> { + if (3..32).contains(&index) { + _try_clear(1 << index) + } else { + Err(Error::IndexOutOfBounds { + index, + min: 3, + max: 31, + }) + } } #[cfg(test)] @@ -131,12 +215,34 @@ mod tests { (3..32).for_each(|i| { assert!(!m.hpm(i)); + assert_eq!(m.try_hpm(i), Ok(false)); m.set_hpm(i, true); assert!(m.hpm(i)); - m.set_hpm(i, false); + assert_eq!(m.try_set_hpm(i, false), Ok(())); + assert_eq!(m.try_hpm(i), Ok(false)); + assert!(!m.hpm(i)); }); + + (0..3).chain(32..64).for_each(|index| { + assert_eq!( + m.try_hpm(index), + Err(Error::IndexOutOfBounds { + index, + min: 3, + max: 31 + }) + ); + assert_eq!( + m.try_set_hpm(index, false), + Err(Error::IndexOutOfBounds { + index, + min: 3, + max: 31 + }) + ); + }) } } diff --git a/riscv/src/register/mcountinhibit.rs b/riscv/src/register/mcountinhibit.rs index fca8cce7..60d32af3 100644 --- a/riscv/src/register/mcountinhibit.rs +++ b/riscv/src/register/mcountinhibit.rs @@ -1,6 +1,7 @@ //! `mcountinhibit` register use crate::bits::{bf_extract, bf_insert}; +use crate::result::{Error, Result}; /// `mcountinhibit` register #[derive(Clone, Copy, Debug)] @@ -44,6 +45,22 @@ impl Mcountinhibit { bf_extract(self.bits, index, 1) != 0 } + /// Machine "hpm\[x\]" Disable (bits 3-31) + /// + /// Attempts to read the "hpm\[x\]" value, and returns an error if the index is invalid. + #[inline] + pub fn try_hpm(&self, index: usize) -> Result { + if (3..32).contains(&index) { + Ok(bf_extract(self.bits, index, 1) != 0) + } else { + Err(Error::IndexOutOfBounds { + index, + min: 3, + max: 31, + }) + } + } + /// Sets whether to inhibit the "hpm\[X\]" counter. /// /// Only updates the in-memory value, does not modify the `mcountinhibit` register. @@ -52,6 +69,25 @@ impl Mcountinhibit { assert!((3..32).contains(&index)); self.bits = bf_insert(self.bits, index, 1, hpm as usize); } + + /// Sets whether to inhibit the "hpm\[X\]" counter. + /// + /// Only updates the in-memory value, does not modify the `mcountinhibit` register. + /// + /// Attempts to update the "hpm\[x\]" value, and returns an error if the index is invalid. + #[inline] + pub fn try_set_hpm(&mut self, index: usize, hpm: bool) -> Result<()> { + if (3..32).contains(&index) { + self.bits = bf_insert(self.bits, index, 1, hpm as usize); + Ok(()) + } else { + Err(Error::IndexOutOfBounds { + index, + min: 3, + max: 31, + }) + } + } } read_csr_as!(Mcountinhibit, 0x320); @@ -73,12 +109,38 @@ pub unsafe fn set_hpm(index: usize) { _set(1 << index); } +#[inline] +pub unsafe fn try_set_hpm(index: usize) -> Result<()> { + if (3..32).contains(&index) { + _try_set(1 << index) + } else { + Err(Error::IndexOutOfBounds { + index, + min: 3, + max: 31, + }) + } +} + #[inline] pub unsafe fn clear_hpm(index: usize) { assert!((3..32).contains(&index)); _clear(1 << index); } +#[inline] +pub unsafe fn try_clear_hpm(index: usize) -> Result<()> { + if (3..32).contains(&index) { + _try_clear(1 << index) + } else { + Err(Error::IndexOutOfBounds { + index, + min: 3, + max: 31, + }) + } +} + #[cfg(test)] mod tests { use super::*; @@ -105,12 +167,33 @@ mod tests { (3..32).for_each(|i| { assert!(!m.hpm(i)); + assert_eq!(m.try_hpm(i), Ok(false)); m.set_hpm(i, true); assert!(m.hpm(i)); + assert_eq!(m.try_hpm(i), Ok(true)); - m.set_hpm(i, false); - assert!(!m.hpm(i)); + assert_eq!(m.try_set_hpm(i, false), Ok(())); + assert_eq!(m.try_hpm(i), Ok(false)); + }); + + (0..2).chain(32..64).for_each(|index| { + assert_eq!( + m.try_hpm(index), + Err(Error::IndexOutOfBounds { + index, + min: 3, + max: 31 + }) + ); + assert_eq!( + m.try_set_hpm(index, false), + Err(Error::IndexOutOfBounds { + index, + min: 3, + max: 31 + }) + ); }); } } diff --git a/riscv/src/register/pmpcfgx.rs b/riscv/src/register/pmpcfgx.rs index 4d973e1a..fe7ede03 100644 --- a/riscv/src/register/pmpcfgx.rs +++ b/riscv/src/register/pmpcfgx.rs @@ -1,5 +1,7 @@ //! Physical memory protection configuration +use crate::result::{Error, Result}; + /// Permission enum contains all possible permission modes for pmp registers #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Permission { @@ -13,6 +15,28 @@ pub enum Permission { RWX = 0b111, } +impl TryFrom for Permission { + type Error = Error; + + fn try_from(val: u8) -> Result { + match val { + 0b000 => Ok(Self::NONE), + 0b001 => Ok(Self::R), + 0b010 => Ok(Self::W), + 0b011 => Ok(Self::RW), + 0b100 => Ok(Self::X), + 0b101 => Ok(Self::RX), + 0b110 => Ok(Self::WX), + 0b111 => Ok(Self::RWX), + _ => Err(Error::InvalidFieldValue { + field: "permission", + value: val as usize, + bitmask: 0b111, + }), + } + } +} + /// Range enum contains all possible addressing modes for pmp registers #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Range { @@ -22,6 +46,24 @@ pub enum Range { NAPOT = 0b11, } +impl TryFrom for Range { + type Error = Error; + + fn try_from(val: u8) -> Result { + match val { + 0b00 => Ok(Self::OFF), + 0b01 => Ok(Self::TOR), + 0b10 => Ok(Self::NA4), + 0b11 => Ok(Self::NAPOT), + _ => Err(Error::InvalidFieldValue { + field: "range", + value: val as usize, + bitmask: 0b11, + }), + } + } +} + /// Pmp struct holds a high-level representation of a single pmp configuration #[derive(Clone, Copy, Debug)] pub struct Pmp { @@ -42,38 +84,46 @@ pub struct Pmpcsr { impl Pmpcsr { /// Take the register contents and translate into a Pmp configuration struct + /// + /// **WARNING**: panics on: + /// + /// - non-`riscv` targets + /// - `index` is out of bounds + /// - register fields contain invalid values #[inline] pub fn into_config(&self, index: usize) -> Pmp { - #[cfg(riscv32)] - assert!(index < 4); - - #[cfg(riscv64)] - assert!(index < 8); - - let byte = (self.bits >> (8 * index)) as u8; // move config to LSB and drop the rest - let permission = byte & 0x7; // bits 0-2 - let range = (byte >> 3) & 0x3; // bits 3-4 - Pmp { - byte, - permission: match permission { - 0 => Permission::NONE, - 1 => Permission::R, - 2 => Permission::W, - 3 => Permission::RW, - 4 => Permission::X, - 5 => Permission::RX, - 6 => Permission::WX, - 7 => Permission::RWX, - _ => unreachable!(), - }, - range: match range { - 0 => Range::OFF, - 1 => Range::TOR, - 2 => Range::NA4, - 3 => Range::NAPOT, - _ => unreachable!(), - }, - locked: (byte & (1 << 7)) != 0, + self.try_into_config(index).unwrap() + } + + /// Attempts to take the register contents, and translate into a Pmp configuration struct. + #[inline] + pub fn try_into_config(&self, index: usize) -> Result { + let max = match () { + #[cfg(riscv32)] + () => Ok(4usize), + #[cfg(riscv64)] + () => Ok(8usize), + #[cfg(not(any(riscv32, riscv64)))] + () => Err(Error::Unimplemented), + }?; + + if index < max { + let byte = (self.bits >> (8 * index)) as u8; // move config to LSB and drop the rest + let permission = byte & 0x7; // bits 0-2 + let range = (byte >> 3) & 0x3; // bits 3-4 + + Ok(Pmp { + byte, + permission: permission.try_into()?, + range: range.try_into()?, + locked: (byte & (1 << 7)) != 0, + }) + } else { + Err(Error::IndexOutOfBounds { + index, + min: 0, + max: max - 1, + }) } } } diff --git a/riscv/src/register/satp.rs b/riscv/src/register/satp.rs index c23041fa..16f3aeb2 100644 --- a/riscv/src/register/satp.rs +++ b/riscv/src/register/satp.rs @@ -1,5 +1,7 @@ //! satp register +use crate::result::{Error, Result}; + /// satp register #[derive(Clone, Copy, Debug)] pub struct Satp { @@ -14,27 +16,35 @@ impl Satp { } /// Current address-translation scheme + /// + /// **WARNING**: panics if the field has an invalid variant. #[inline] #[cfg(target_pointer_width = "32")] pub fn mode(&self) -> Mode { - match self.bits & (1 << 31) != 0 { - false => Mode::Bare, - true => Mode::Sv32, - } + self.try_mode().unwrap() + } + + /// Attempts to get the current address-translation scheme. + #[inline] + #[cfg(target_pointer_width = "32")] + pub fn try_mode(&self) -> Result { + ((self.bits >> 31) as u8).try_into() } /// Current address-translation scheme + /// + /// **WARNING**: panics if the field has an invalid variant. #[inline] #[cfg(target_pointer_width = "64")] pub fn mode(&self) -> Mode { - match self.bits >> 60 { - 0 => Mode::Bare, - 8 => Mode::Sv39, - 9 => Mode::Sv48, - 10 => Mode::Sv57, - 11 => Mode::Sv64, - _ => unreachable!(), - } + self.try_mode().unwrap() + } + + /// Attempts to get the current address-translation scheme. + #[inline] + #[cfg(target_pointer_width = "64")] + pub fn try_mode(&self) -> Result { + ((self.bits >> 60) as u8).try_into() } /// Address space identifier @@ -92,25 +102,108 @@ pub enum Mode { Sv64 = 11, } +#[cfg(target_pointer_width = "32")] +impl TryFrom for Mode { + type Error = Error; + + fn try_from(val: u8) -> Result { + match val { + 0 => Ok(Mode::Bare), + 1 => Ok(Mode::Sv32), + _ => Err(Error::InvalidFieldVariant { + field: "mode", + value: val as usize, + }), + } + } +} + +#[cfg(target_pointer_width = "64")] +impl TryFrom for Mode { + type Error = Error; + + fn try_from(val: u8) -> Result { + match val { + 0 => Ok(Mode::Bare), + 8 => Ok(Mode::Sv39), + 9 => Ok(Mode::Sv48), + 10 => Ok(Mode::Sv57), + 11 => Ok(Mode::Sv64), + _ => Err(Error::InvalidFieldVariant { + field: "mode", + value: val as usize, + }), + } + } +} + read_csr_as!(Satp, 0x180); write_csr_as_usize!(0x180); /// Sets the register to corresponding page table mode, physical page number and address space id. +/// +/// **WARNING**: panics on: +/// +/// - non-`riscv` targets +/// - invalid field values #[inline] #[cfg(target_pointer_width = "32")] pub unsafe fn set(mode: Mode, asid: usize, ppn: usize) { - assert_eq!(asid, asid & 0x1FF, "invalid value for asid"); - assert_eq!(ppn, ppn & 0x3F_FFFF, "invalid value for ppn"); - let bits = (mode as usize) << 31 | (asid << 22) | ppn; - _write(bits); + try_set(mode, asid, ppn).unwrap(); +} + +/// Attempts to set the register to corresponding page table mode, physical page number and address space id. +#[inline] +#[cfg(target_pointer_width = "32")] +pub unsafe fn try_set(mode: Mode, asid: usize, ppn: usize) -> Result<()> { + if asid != asid & 0x1FF { + Err(Error::InvalidFieldValue { + field: "asid", + value: asid, + bitmask: 0x1FF, + }) + } else if ppn != ppn & 0x3F_FFFF { + Err(Error::InvalidFieldValue { + field: "ppn", + value: ppn, + bitmask: 0x3F_FFFF, + }) + } else { + let bits = (mode as usize) << 31 | (asid << 22) | ppn; + _try_write(bits) + } } /// Sets the register to corresponding page table mode, physical page number and address space id. +/// +/// **WARNING**: panics on: +/// +/// - non-`riscv` targets +/// - invalid field values #[inline] #[cfg(target_pointer_width = "64")] pub unsafe fn set(mode: Mode, asid: usize, ppn: usize) { - assert_eq!(asid, asid & 0xFFFF, "invalid value for asid"); - assert_eq!(ppn, ppn & 0xFFF_FFFF_FFFF, "invalid value for ppn"); - let bits = (mode as usize) << 60 | (asid << 44) | ppn; - _write(bits); + try_set(mode, asid, ppn).unwrap() +} + +/// Attempts to set the register to corresponding page table mode, physical page number and address space id. +#[inline] +#[cfg(target_pointer_width = "64")] +pub unsafe fn try_set(mode: Mode, asid: usize, ppn: usize) -> Result<()> { + if asid != asid & 0xFFFF { + Err(Error::InvalidFieldValue { + field: "asid", + value: asid, + bitmask: 0xFFFF, + }) + } else if ppn != ppn & 0xFFF_FFFF_FFFF { + Err(Error::InvalidFieldValue { + field: "ppn", + value: ppn, + bitmask: 0xFFF_FFFF_FFFF, + }) + } else { + let bits = (mode as usize) << 60 | (asid << 44) | ppn; + _try_write(bits) + } } diff --git a/riscv/src/register/scounteren.rs b/riscv/src/register/scounteren.rs index 5242734b..8559428c 100644 --- a/riscv/src/register/scounteren.rs +++ b/riscv/src/register/scounteren.rs @@ -1,5 +1,7 @@ //! scounteren register +use crate::result::{Error, Result}; + /// scounteren register #[derive(Clone, Copy, Debug)] pub struct Scounteren { @@ -31,6 +33,22 @@ impl Scounteren { assert!((3..32).contains(&index)); self.bits & (1 << index) != 0 } + + /// User "hpm\[x\]" Enable (bits 3-31) + /// + /// Attempts to read the "hpm\[x\]" value, and returns an error if the index is invalid. + #[inline] + pub fn try_hpm(&self, index: usize) -> Result { + if (3..32).contains(&index) { + Ok(self.bits & (1 << index) != 0) + } else { + Err(Error::IndexOutOfBounds { + index, + min: 3, + max: 31, + }) + } + } } read_csr_as!(Scounteren, 0x106); @@ -56,8 +74,34 @@ pub unsafe fn set_hpm(index: usize) { _set(1 << index); } +#[inline] +pub unsafe fn try_set_hpm(index: usize) -> Result<()> { + if (3..32).contains(&index) { + _try_set(1 << index) + } else { + Err(Error::IndexOutOfBounds { + index, + min: 3, + max: 31, + }) + } +} + #[inline] pub unsafe fn clear_hpm(index: usize) { assert!((3..32).contains(&index)); _clear(1 << index); } + +#[inline] +pub unsafe fn try_clear_hpm(index: usize) -> Result<()> { + if (3..32).contains(&index) { + _try_clear(1 << index) + } else { + Err(Error::IndexOutOfBounds { + index, + min: 3, + max: 31, + }) + } +}