diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/calls.rs | 31 | ||||
-rw-r--r-- | src/error.rs | 58 | ||||
-rw-r--r-- | src/lib.rs | 12 | ||||
-rw-r--r-- | src/smccc.rs | 3 | ||||
-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 |
8 files changed, 329 insertions, 41 deletions
diff --git a/src/calls.rs b/src/calls.rs index 59ef1af..885a481 100644 --- a/src/calls.rs +++ b/src/calls.rs @@ -4,9 +4,12 @@ //! Functions to make PSCI calls. -use crate::error::{success_or_error_32, success_or_error_64, Error}; -use crate::smccc::{call32, call64}; use crate::{ + error::Error, + smccc::{ + call32, call64, + error::{positive_or_error_32, success_or_error_32, success_or_error_64}, + }, AffinityState, LowestAffinityLevel, MigrateType, PowerState, SuspendMode, PSCI_AFFINITY_INFO_64, PSCI_CPU_DEFAULT_SUSPEND_64, PSCI_CPU_FREEZE, PSCI_CPU_OFF, PSCI_CPU_ON_64, PSCI_CPU_SUSPEND_64, PSCI_FEATURES, PSCI_MEM_PROTECT, @@ -18,7 +21,7 @@ use crate::{ /// Returns the version of PSCI implemented. pub fn version() -> u32 { - call32(PSCI_VERSION, [0, 0, 0, 0, 0, 0, 0])[0] + call32(PSCI_VERSION, [0; 7])[0] } /// Suspends execution of a core or topology node. @@ -55,7 +58,7 @@ pub fn cpu_suspend( /// Powers down the current core. pub fn cpu_off() -> Result<(), Error> { - success_or_error_32(call32(PSCI_CPU_OFF, [0, 0, 0, 0, 0, 0, 0])[0]) + success_or_error_32(call32(PSCI_CPU_OFF, [0; 7])[0]) } /// Powers up a core. @@ -128,25 +131,22 @@ pub fn migrate(target_cpu: u64) -> Result<(), Error> { /// Identifies the levelof multicore support in the Trusted OS. pub fn migrate_info_type() -> Result<MigrateType, Error> { - (call32(PSCI_MIGRATE_INFO_TYPE, [0, 0, 0, 0, 0, 0, 0])[0] as i32).try_into() + (call32(PSCI_MIGRATE_INFO_TYPE, [0; 7])[0] as i32).try_into() } /// Returns the MPIDR value of the current resident core of the Trusted OS. pub fn migrate_info_up_cpu() -> u64 { - call64( - PSCI_MIGRATE_INFO_UP_CPU_64, - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - )[0] + call64(PSCI_MIGRATE_INFO_UP_CPU_64, [0; 17])[0] } /// Shuts down the system. pub fn system_off() -> Result<(), Error> { - success_or_error_32(call32(PSCI_SYSTEM_OFF, [0, 0, 0, 0, 0, 0, 0])[0]) + success_or_error_32(call32(PSCI_SYSTEM_OFF, [0; 7])[0]) } /// Resets the system. pub fn system_reset() -> Result<(), Error> { - success_or_error_32(call32(PSCI_SYSTEM_RESET, [0, 0, 0, 0, 0, 0, 0])[0]) + success_or_error_32(call32(PSCI_SYSTEM_RESET, [0; 7])[0]) } /// Resets the system in an architectural or vendor-specific way. @@ -199,17 +199,12 @@ pub fn mem_protect_check_range(base: u64, length: u64) -> Result<(), Error> { /// Queries whether `SMCCC_VERSION` or a specific PSCI function is implemented, and what features /// are supported. pub fn psci_features(psci_function_id: u32) -> Result<u32, Error> { - let result = call32(PSCI_FEATURES, [psci_function_id, 0, 0, 0, 0, 0, 0])[0] as i32; - if result >= 0 { - Ok(result as u32) - } else { - Err(result.into()) - } + positive_or_error_32(call32(PSCI_FEATURES, [psci_function_id, 0, 0, 0, 0, 0, 0])[0]) } /// Puts the current core into an implementation-defined low power state. pub fn cpu_freeze() -> Result<(), Error> { - success_or_error_32(call32(PSCI_CPU_FREEZE, [0, 0, 0, 0, 0, 0, 0])[0]) + success_or_error_32(call32(PSCI_CPU_FREEZE, [0; 7])[0]) } /// Puts the current core into an implementation-defined low power state. diff --git a/src/error.rs b/src/error.rs index da1fa80..fb57fe6 100644 --- a/src/error.rs +++ b/src/error.rs @@ -4,7 +4,9 @@ //! PSCI error codes. -pub const SUCCESS: i32 = 0; +pub use crate::smccc::error::SUCCESS; +use core::fmt::{self, Display, Formatter}; + pub const NOT_SUPPORTED: i32 = -1; pub const INVALID_PARAMETERS: i32 = -2; pub const DENIED: i32 = -3; @@ -18,35 +20,28 @@ pub const INVALID_ADDRESS: i32 = -9; /// Standard PSCI errors. #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum Error { + /// PSCI call not supported. NotSupported, + /// Invalid parameters to PSCI call. InvalidParameters, + /// PSCI call denied. Denied, + /// Core already on. AlreadyOn, + /// Core already being turned on. OnPending, + /// Internal failure in PSCI call. InternalFailure, + /// Trusted OS not present on target core. NotPresent, + /// Core disabled. Disabled, + /// Invalid address passed to PSCI call. InvalidAddress, /// An unexpected return value from a PSCI function. Unknown(i32), } -pub(crate) fn success_or_error_32(value: u32) -> Result<(), Error> { - success_or_error(value as i32) -} - -pub(crate) fn success_or_error_64(value: u64) -> Result<(), Error> { - success_or_error(value as i32) -} - -fn success_or_error(value: i32) -> Result<(), Error> { - if value == SUCCESS { - Ok(()) - } else { - Err(value.into()) - } -} - impl From<Error> for i32 { fn from(error: Error) -> i32 { match error { @@ -64,6 +59,12 @@ impl From<Error> for i32 { } } +impl From<Error> for i64 { + fn from(error: Error) -> i64 { + i32::from(error).into() + } +} + impl From<i32> for Error { fn from(value: i32) -> Self { match value { @@ -80,3 +81,26 @@ impl From<i32> for Error { } } } + +impl From<i64> for Error { + fn from(value: i64) -> Self { + Self::from(value as i32) + } +} + +impl Display for Error { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self { + Self::NotSupported => write!(f, "PSCI call not supported"), + Self::InvalidParameters => write!(f, "Invalid parameters to PSCI call"), + Self::Denied => write!(f, "PSCI call denied"), + Self::AlreadyOn => write!(f, "Core already on"), + Self::OnPending => write!(f, "Core already being turned on"), + Self::InternalFailure => write!(f, "Internal failure in PSCI call"), + Self::NotPresent => write!(f, "Trusted OS not present on target core"), + Self::Disabled => write!(f, "Core disabled"), + Self::InvalidAddress => write!(f, "Invalid address passed to PSCI call"), + Self::Unknown(e) => write!(f, "Unknown PSCI return value {} ({0:#x})", e), + } + } +} @@ -2,13 +2,13 @@ // This project is dual-licensed under Apache 2.0 and MIT terms. // See LICENSE-APACHE and LICENSE-MIT for details. -//! Constants for version 1.1 of the Arm Power State Coordination Interface (PSCI) version 1.1, and -//! functions to call them. +//! Constants for version 1.4 of the Arm SMC Calling Convention and version 1.1 of the Arm Power +//! State Coordination Interface (PSCI) version 1.1, and functions to call them. //! -//! Note that PSCI calls may be made via either HVC or SMC. You can choose which one to use by -//! building this crate with the corresponding feature (i.e. `hvc` or `smc`). By default `hvc` is -//! enabled. If neither feature is enabled then the functions to make calls will not be available, -//! but the constants are still provided. +//! Note that PSCI and other SMCCC calls may be made via either HVC or SMC. You can choose which one +//! to use by building this crate with the corresponding feature (i.e. `hvc` or `smc`). By default +//! `hvc` is enabled. If neither feature is enabled then the functions to make calls will not be +//! available, but the constants and types are still provided. //! //! This crate currently only supports aarch64 and the SMC64 versions of the various calls, in the //! cases that both SMC32 and SMC64 versions exist. diff --git a/src/smccc.rs b/src/smccc.rs index 4b1b4a4..01ff00f 100644 --- a/src/smccc.rs +++ b/src/smccc.rs @@ -4,6 +4,9 @@ //! Functions for making SMCCC calls. +pub mod arch; +pub mod error; + #[cfg(any(feature = "hvc", feature = "smc"))] #[inline(always)] pub(crate) fn call32(function: u32, args: [u32; 7]) -> [u32; 8] { 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) + } +} |