aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-04-18 03:47:17 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-04-18 03:47:17 +0000
commite8197aa9e81cfeefec5249a9e5fd350fb6830204 (patch)
tree1bc2c7528414de3900e3b868e851c80723feb7cb
parent17527effd9ef92ce76d8cb68735446ca0a08459b (diff)
parent627742b5f75b5c2fe5665799fd4da8dcbcc40f26 (diff)
downloadpsci-android14-d1-s1-release.tar.gz
Change-Id: Ib1f266ecee44cfe7b77d38a7fb2dd55b861a6048
-rw-r--r--.cargo_vcs_info.json2
-rw-r--r--Android.bp26
-rw-r--r--CHANGELOG.md26
-rw-r--r--Cargo.toml5
-rw-r--r--Cargo.toml.orig4
-rw-r--r--METADATA16
-rw-r--r--README.md15
-rw-r--r--cargo2android.json10
-rw-r--r--patches/Android.bp.patch17
-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
17 files changed, 428 insertions, 63 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index 5dc8b81..cea778b 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,6 +1,6 @@
{
"git": {
- "sha1": "12b9e3dc136b77b51b2a727408c7b894c0a10199"
+ "sha1": "bb36e58c69890d3b39e418f80ede8898e15aac44"
},
"path_in_vcs": ""
} \ No newline at end of file
diff --git a/Android.bp b/Android.bp
index 3545d62..2348c03 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1,4 +1,4 @@
-// This file is generated by cargo2android.py --run --device --tests --features=hvc --force-rlib.
+// This file is generated by cargo2android.py --config cargo2android.json.
// Do not modify this file as changes will be overridden on upgrade.
package {
@@ -41,10 +41,9 @@ license {
rust_library_rlib {
name: "libpsci",
// has rustc warnings
- host_supported: true,
crate_name: "psci",
cargo_env_compat: true,
- cargo_pkg_version: "0.1.1",
+ cargo_pkg_version: "0.1.3",
srcs: ["src/lib.rs"],
edition: "2021",
features: ["hvc"],
@@ -52,4 +51,25 @@ rust_library_rlib {
"//apex_available:platform",
"//apex_available:anyapex",
],
+ product_available: true,
+ vendor_available: true,
+ prefer_rlib: true,
+ no_stdlibs: true,
+ stdlibs: [
+ "libcompiler_builtins.rust_sysroot",
+ "libcore.rust_sysroot",
+ ],
+}
+
+rust_test {
+ name: "psci_test_src_lib",
+ // has rustc warnings
+ crate_name: "psci",
+ cargo_env_compat: true,
+ cargo_pkg_version: "0.1.3",
+ srcs: ["src/lib.rs"],
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ edition: "2021",
+ features: ["hvc"],
}
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..7f539a9
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,26 @@
+# Changelog
+
+## 0.1.3
+
+### Bugfixes
+
+- Fixed type of `smccc::error::success_or_error_64`. This is a breaking change relative to 0.1.2 but
+ it was yanked.
+
+## 0.1.2 (yanked)
+
+### New features
+
+- Added constants, types and functions for standard Arm architucture SMCCC calls, in `smccc::arch`
+ module.
+- Added helpers in `smccc::error` module for handling negative return values as errors.
+
+## 0.1.1
+
+### New features
+
+- Exposed functions for SMC and HVC calls for use outside of PSCI.
+
+## 0.1.0
+
+Initial release with PSCI constants and functions.
diff --git a/Cargo.toml b/Cargo.toml
index 2cf5f25..098acac 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,9 +12,9 @@
[package]
edition = "2021"
name = "psci"
-version = "0.1.1"
+version = "0.1.3"
authors = ["Andrew Walbran <qwandor@google.com>"]
-description = "Functions and constants for the Arm Power State Coordination Interface (PSCI) 1.1 on aarch64."
+description = "Functions and constants for the Arm SMC Calling Convention (SMCCC) 1.4 and Arm Power State Coordination Interface (PSCI) 1.1 on aarch64."
readme = "README.md"
keywords = [
"arm",
@@ -29,7 +29,6 @@ categories = [
]
license = "MIT OR Apache-2.0"
repository = "https://github.com/google/psci"
-resolver = "2"
[dependencies]
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index e329535..cd3e90c 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,9 +1,9 @@
[package]
name = "psci"
-version = "0.1.1"
+version = "0.1.3"
edition = "2021"
license = "MIT OR Apache-2.0"
-description = "Functions and constants for the Arm Power State Coordination Interface (PSCI) 1.1 on aarch64."
+description = "Functions and constants for the Arm SMC Calling Convention (SMCCC) 1.4 and Arm Power State Coordination Interface (PSCI) 1.1 on aarch64."
authors = ["Andrew Walbran <qwandor@google.com>"]
repository = "https://github.com/google/psci"
keywords = ["arm", "aarch64", "cortex-a", "psci"]
diff --git a/METADATA b/METADATA
index f317da4..ba79a5a 100644
--- a/METADATA
+++ b/METADATA
@@ -1,5 +1,9 @@
+# This project was upgraded with external_updater.
+# Usage: tools/external_updater/updater.sh update rust/crates/psci
+# For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md
+
name: "psci"
-description: "Functions and constants for the Arm Power State Coordination Interface (PSCI) 1.1 on aarch64."
+description: "Functions and constants for the Arm SMC Calling Convention (SMCCC) 1.4 and Arm Power State Coordination Interface (PSCI) 1.1 on aarch64."
third_party {
url {
type: HOMEPAGE
@@ -7,13 +11,13 @@ third_party {
}
url {
type: ARCHIVE
- value: "https://static.crates.io/crates/psci/psci-0.1.1.crate"
+ value: "https://static.crates.io/crates/psci/psci-0.1.3.crate"
}
- version: "0.1.1"
+ version: "0.1.3"
license_type: NOTICE
last_upgrade_date {
- year: 2022
- month: 10
- day: 6
+ year: 2023
+ month: 4
+ day: 14
}
}
diff --git a/README.md b/README.md
index 9b999b5..51fa00a 100644
--- a/README.md
+++ b/README.md
@@ -1,15 +1,16 @@
-# PSCI functions for bare-metal Rust on aarch64
+# SMCCC and PSCI functions for bare-metal Rust on aarch64
[![crates.io page](https://img.shields.io/crates/v/psci.svg)](https://crates.io/crates/psci)
[![docs.rs page](https://docs.rs/psci/badge.svg)](https://docs.rs/psci)
-This crate provides constants for version 1.1 of the Arm Power State Coordination Interface (PSCI),
-and functions to call them.
+This crate provides support for the Arm SMC Calling Convention version 1.4, including standard Arm
+Architecture Calls constants, and version 1.1 of the Arm Power State Coordination Interface (PSCI).
+It includes constants, functions to make the calls (on aarch64 targets), and error types.
-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 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/cargo2android.json b/cargo2android.json
new file mode 100644
index 0000000..f3802fd
--- /dev/null
+++ b/cargo2android.json
@@ -0,0 +1,10 @@
+{
+ "dependencies": true,
+ "device": true,
+ "features": "hvc",
+ "force-rlib": true,
+ "no-host": true,
+ "patch": "patches/Android.bp.patch",
+ "run": true,
+ "tests": true
+}
diff --git a/patches/Android.bp.patch b/patches/Android.bp.patch
new file mode 100644
index 0000000..1fa0e93
--- /dev/null
+++ b/patches/Android.bp.patch
@@ -0,0 +1,17 @@
+diff --git a/Android.bp b/Android.bp
+index 34d110a..2348c03 100644
+--- a/Android.bp
++++ b/Android.bp
+@@ -53,6 +53,12 @@ rust_library_rlib {
+ ],
+ product_available: true,
+ vendor_available: true,
++ prefer_rlib: true,
++ no_stdlibs: true,
++ stdlibs: [
++ "libcompiler_builtins.rust_sysroot",
++ "libcore.rust_sysroot",
++ ],
+ }
+
+ rust_test {
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)
+ }
+}