aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/calls.rs31
-rw-r--r--src/error.rs58
-rw-r--r--src/lib.rs12
-rw-r--r--src/smccc.rs3
-rw-r--r--src/smccc/arch.rs90
-rw-r--r--src/smccc/arch/calls.rs43
-rw-r--r--src/smccc/arch/error.rs58
-rw-r--r--src/smccc/error.rs75
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),
+ }
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index be52908..ee5167b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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)
+ }
+}