Upgrade psci to 0.1.3 am: 077d73ad68 am: 0b118626ac am: 447753233b am: 3a87865f29

Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/psci/+/2537692

Change-Id: I9a95cca46b3c13889c5876d21694ffcdeea12942
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
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 @@
 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 @@
         "//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 @@
 ]
 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 @@
   }
   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 @@
 
 /// 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 @@
 
 /// 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 @@
 
 /// 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 @@
 /// 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 @@
 /// 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 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<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)
+    }
+}