diff options
Diffstat (limited to 'src/smccc')
-rw-r--r-- | src/smccc/arch.rs | 90 | ||||
-rw-r--r-- | src/smccc/arch/calls.rs | 43 | ||||
-rw-r--r-- | src/smccc/arch/error.rs | 58 | ||||
-rw-r--r-- | src/smccc/error.rs | 75 |
4 files changed, 266 insertions, 0 deletions
diff --git a/src/smccc/arch.rs b/src/smccc/arch.rs new file mode 100644 index 0000000..feaf590 --- /dev/null +++ b/src/smccc/arch.rs @@ -0,0 +1,90 @@ +// Copyright 2023 the authors. +// This project is dual-licensed under Apache 2.0 and MIT terms. +// See LICENSE-APACHE and LICENSE-MIT for details. + +//! Standard Arm architecture calls. + +#[cfg(any(feature = "hvc", feature = "smc"))] +mod calls; +pub mod error; + +#[cfg(any(feature = "hvc", feature = "smc"))] +pub use calls::{ + arch_workaround_1, arch_workaround_2, arch_workaround_3, features, soc_id, version, +}; +use core::fmt::{self, Debug, Display, Formatter}; +use error::Error; + +pub const SMCCC_VERSION: u32 = 0x8000_0000; +pub const SMCCC_ARCH_FEATURES: u32 = 0x8000_0001; +pub const SMCCC_ARCH_SOC_ID: u32 = 0x8000_0002; +pub const SMCCC_ARCH_WORKAROUND_1: u32 = 0x8000_8000; +pub const SMCCC_ARCH_WORKAROUND_2: u32 = 0x8000_7FFF; +pub const SMCCC_ARCH_WORKAROUND_3: u32 = 0x8000_3FFF; + +/// A version of the SMC Calling Convention. +#[derive(Copy, Clone, Eq, Ord, PartialEq, PartialOrd)] +pub struct Version { + pub major: u16, + pub minor: u16, +} + +impl Display for Version { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "{}.{}", self.major, self.minor) + } +} + +impl Debug for Version { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + Display::fmt(self, f) + } +} + +impl TryFrom<i32> for Version { + type Error = Error; + + fn try_from(value: i32) -> Result<Self, Error> { + if value < 0 { + Err(value.into()) + } else { + Ok(Self { + major: (value >> 16) as u16, + minor: value as u16, + }) + } + } +} + +impl From<Version> for u32 { + fn from(version: Version) -> Self { + u32::from(version.major) << 16 | u32::from(version.minor) + } +} + +#[derive(Copy, Clone, Eq, Ord, PartialEq, PartialOrd)] +#[repr(u32)] +pub enum SocIdType { + /// The SoC version. + Version, + /// The SoC revision. + Revision, +} + +impl From<SocIdType> for u32 { + fn from(id_type: SocIdType) -> Self { + id_type as Self + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn convert_version() { + let version = Version { major: 1, minor: 2 }; + assert_eq!(u32::from(version), 0x0001_0002); + assert_eq!(0x0001_0002.try_into(), Ok(version)); + } +} diff --git a/src/smccc/arch/calls.rs b/src/smccc/arch/calls.rs new file mode 100644 index 0000000..0164616 --- /dev/null +++ b/src/smccc/arch/calls.rs @@ -0,0 +1,43 @@ +// Copyright 2023 the authors. +// This project is dual-licensed under Apache 2.0 and MIT terms. +// See LICENSE-APACHE and LICENSE-MIT for details. + +use super::{ + error::Error, SocIdType, Version, SMCCC_ARCH_FEATURES, SMCCC_ARCH_SOC_ID, + SMCCC_ARCH_WORKAROUND_1, SMCCC_ARCH_WORKAROUND_2, SMCCC_ARCH_WORKAROUND_3, SMCCC_VERSION, +}; +use crate::smccc::{ + call32, + error::{positive_or_error_32, success_or_error_32}, +}; + +/// Returns the implemented version of the SMC Calling Convention. +pub fn version() -> Result<Version, Error> { + (call32(SMCCC_VERSION, [0; 7])[0] as i32).try_into() +} + +/// Returns whether the given Arm Architecture Service function is implemented, and any feature +/// flags specific to the function. +pub fn features(arch_func_id: u32) -> Result<u32, Error> { + positive_or_error_32(call32(SMCCC_ARCH_FEATURES, [arch_func_id, 0, 0, 0, 0, 0, 0])[0]) +} + +/// Returns the SiP defined SoC identification details. +pub fn soc_id(soc_id_type: SocIdType) -> Result<u32, Error> { + positive_or_error_32(call32(SMCCC_ARCH_SOC_ID, [soc_id_type.into(), 0, 0, 0, 0, 0, 0])[0]) +} + +/// Executes a firmware workaround to mitigate CVE-2017-5715. +pub fn arch_workaround_1() -> Result<(), Error> { + success_or_error_32(call32(SMCCC_ARCH_WORKAROUND_1, [0; 7])[0]) +} + +/// Enables or disables the mitigation for CVE-2018-3639. +pub fn arch_workaround_2(enable: bool) -> Result<(), Error> { + success_or_error_32(call32(SMCCC_ARCH_WORKAROUND_2, [enable.into(), 0, 0, 0, 0, 0, 0])[0]) +} + +/// Executes a firmware workaround to mitigate CVE-2017-5715 and CVE-2022-23960. +pub fn arch_workaround_3() -> Result<(), Error> { + success_or_error_32(call32(SMCCC_ARCH_WORKAROUND_3, [0; 7])[0]) +} diff --git a/src/smccc/arch/error.rs b/src/smccc/arch/error.rs new file mode 100644 index 0000000..fa2fd4b --- /dev/null +++ b/src/smccc/arch/error.rs @@ -0,0 +1,58 @@ +// Copyright 2023 the authors. +// This project is dual-licensed under Apache 2.0 and MIT terms. +// See LICENSE-APACHE and LICENSE-MIT for details. + +//! Error codes for standard Arm Architecture SMCCC calls. + +pub use crate::smccc::error::SUCCESS; +use core::fmt::{self, Display, Formatter}; + +pub const NOT_SUPPORTED: i32 = -1; +pub const NOT_REQUIRED: i32 = -2; +pub const INVALID_PARAMETER: i32 = -3; + +/// Errors for standard Arm Architecture calls. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum Error { + /// The call is not supported by the implementation. + NotSupported, + /// The call is deemed not required by the implementation. + NotRequired, + /// One of the call parameters has a non-supported value. + InvalidParameter, + /// There was an unexpected return value. + Unknown(i32), +} + +impl From<Error> for i32 { + fn from(error: Error) -> i32 { + match error { + Error::NotSupported => NOT_SUPPORTED, + Error::NotRequired => NOT_REQUIRED, + Error::InvalidParameter => INVALID_PARAMETER, + Error::Unknown(value) => value, + } + } +} + +impl From<i32> for Error { + fn from(value: i32) -> Self { + match value { + NOT_SUPPORTED => Error::NotSupported, + NOT_REQUIRED => Error::NotRequired, + INVALID_PARAMETER => Error::InvalidParameter, + _ => Error::Unknown(value), + } + } +} + +impl Display for Error { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self { + Self::NotSupported => write!(f, "SMCCC call not supported"), + Self::NotRequired => write!(f, "SMCCC call not required"), + Self::InvalidParameter => write!(f, "SMCCC call received non-supported value"), + Self::Unknown(e) => write!(f, "Unknown SMCCC return value {} ({0:#x})", e), + } + } +} diff --git a/src/smccc/error.rs b/src/smccc/error.rs new file mode 100644 index 0000000..2fce48a --- /dev/null +++ b/src/smccc/error.rs @@ -0,0 +1,75 @@ +// Copyright 2023 the authors. +// This project is dual-licensed under Apache 2.0 and MIT terms. +// See LICENSE-APACHE and LICENSE-MIT for details. + +//! Utility functions for error handling. +//! +//! These functions can be combined with the appropriate HVC or SMC functions to wrap calls which +//! return a single value where negative values indicate an error. +//! +//! For example, the [`system_off`](crate::system_off) function is implemented approximately as: +//! +//! ``` +//! use psci::{ +//! error::Error, +//! smccc::{error::success_or_error_32, smc32}, +//! PSCI_SYSTEM_OFF, +//! }; +//! +//! pub fn system_off() -> Result<(), Error> { +//! success_or_error_32(smc32(PSCI_SYSTEM_OFF, [0; 7])[0]) +//! } +//! ``` + +/// A value commonly returned to indicate a successful SMCCC call. +pub const SUCCESS: i32 = 0; + +/// Converts the given value (returned from an HVC32 or SMC32 call) either to `Ok(())` if it is +/// equal to [`SUCCESS`], or else an error of the given type. +pub fn success_or_error_32<E: From<i32>>(value: u32) -> Result<(), E> { + let value = value as i32; + if value == SUCCESS { + Ok(()) + } else { + Err(value.into()) + } +} + +/// Converts the given value (returned from an HVC64 or SMC64 call) either to `Ok(())` if it is +/// equal to [`SUCCESS`], or else an error of the given type. +pub fn success_or_error_64<E: From<i64>>(value: u64) -> Result<(), E> { + let value = value as i64; + if value == SUCCESS.into() { + Ok(()) + } else { + Err(value.into()) + } +} + +/// Returns `Ok(value)` if the given value has its high bit unset (i.e. would be positive when +/// treated as a signed value), or an error of the given type if the high bit is set. +/// +/// This is intended to be used with the return value of [`hvc32`](super::hvc32) or +/// [`smc32`](super::smc32). +pub fn positive_or_error_32<E: From<i32>>(value: u32) -> Result<u32, E> { + let signed = value as i32; + if signed < 0 { + Err(signed.into()) + } else { + Ok(value) + } +} + +/// Returns `Ok(value)` if the given value has its high bit unset (i.e. would be positive when +/// treated as a signed value), or an error of the given type if the high bit is set. +/// +/// This is intended to be used with the return value of [`hvc64`](super::hvc64) or +/// [`smc64`](super::smc64). +pub fn positive_or_error_64<E: From<i64>>(value: u64) -> Result<u64, E> { + let signed = value as i64; + if signed < 0 { + Err(signed.into()) + } else { + Ok(value) + } +} |