aboutsummaryrefslogtreecommitdiff
path: root/src/smccc
diff options
context:
space:
mode:
Diffstat (limited to 'src/smccc')
-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
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)
+ }
+}