Initial version of the PSCI crate
Implement functions for parsing and building register arguments for PSCI
calls along all the necessary types.
Signed-off-by: Imre Kis <imre.kis@arm.com>
Change-Id: Ib763a1be81575cb53669ac69e532a0bd69883141
diff --git a/Cargo.lock b/Cargo.lock
index d4505c0..edcd3a2 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -5,3 +5,89 @@
[[package]]
name = "arm-psci"
version = "0.1.0"
+dependencies = [
+ "bitflags",
+ "num_enum",
+ "thiserror",
+]
+
+[[package]]
+name = "bitflags"
+version = "2.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36"
+
+[[package]]
+name = "num_enum"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179"
+dependencies = [
+ "num_enum_derive",
+]
+
+[[package]]
+name = "num_enum_derive"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.93"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.38"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.98"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "thiserror"
+version = "2.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "2.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"
diff --git a/Cargo.toml b/Cargo.toml
index 56017fb..9f32543 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -18,3 +18,6 @@
rust-version = "1.82"
[dependencies]
+bitflags = "2.8"
+num_enum = { version = "0.7", default-features = false }
+thiserror = { version = "2.0", default-features = false }
diff --git a/README.md b/README.md
index ff9e6ff..0fc8f6a 100644
--- a/README.md
+++ b/README.md
@@ -1,14 +1,22 @@
# Arm Power State Coordination Interface (PSCI) library
-Implementation of [Arm Power State Coordination Interface](https://developer.arm.com/documentation/den0022/latest/)
-primitives.
+This crate aims to offer functions and user-friendly types for parsing and constructing arguments
+of [Arm Power State Coordination Interface](https://developer.arm.com/documentation/den0022/latest/)
+(PSCI) calls. This functionality can be beneficial for both firmware and OS components.
+However, doing the actual `SMC`/`HVC`/`ERET` calls, or implementing power management logic is beyond
+the scope of this crate.
## Implemented features
+* Handling all PSCI 1.3 mandatory and optional functions
+* Handling both 32-bit and 64-bit call formats
+* Dedicated types for common PSCI call arguments
+* Unit tests
-## Future plans
+## Limitations
+* The implementation does not handle pre-1.0 format suspend power state (see 5.4.2.1 Original format)
## License
@@ -28,11 +36,13 @@
## Contributing
-Please follow the directions of the [Trusted Firmware Processes](https://trusted-firmware-docs.readthedocs.io/en/latest/generic_processes/index.html)
+Please follow the directions of the [Trusted Firmware Processes](https://trusted-firmware-docs.readthedocs.io/en/latest/generic_processes/index.html).
+
+Contributions are handled through [review.trustedfirmware.org](https://review.trustedfirmware.org/q/project:rust-spmc/arm-psci).
## Reporting Security Issues
-Please follow the directions of the [Trusted Firmware Security Center](https://trusted-firmware-docs.readthedocs.io/en/latest/security_center/index.html)
+Please follow the directions of the [Trusted Firmware Security Center](https://trusted-firmware-docs.readthedocs.io/en/latest/security_center/index.html).
--------------
diff --git a/src/lib.rs b/src/lib.rs
index aae9e20..e834c25 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -5,3 +5,980 @@
#![doc = include_str!("../README.md")]
#![deny(clippy::undocumented_unsafe_blocks)]
#![deny(unsafe_op_in_unsafe_fn)]
+
+//! # Specification
+//!
+//! This implementation is based on
+//! [Arm Power State Coordination Interface](https://developer.arm.com/documentation/den0022/latest/)
+//! Platform Design Document Version 1.3 issue F.b. (DEN0022).
+//!
+//! The type descriptions below refer to sections of this particular version of the specification.
+
+use bitflags::bitflags;
+use core::fmt::Debug;
+use num_enum::{IntoPrimitive, TryFromPrimitive};
+use thiserror::Error;
+
+/// Internal error type of the PSCI module
+#[derive(Debug, Error, PartialEq, Eq)]
+pub enum Error {
+ #[error("unrecognised PSCI function ID {0}")]
+ UnrecognisedFunctionId(u32),
+ #[error("unrecognised PSCI error code {0}")]
+ UnrecognisedErrorCode(i32),
+ #[error("invalid PSCI version {0}")]
+ InvalidVersion(u32),
+ #[error("invalid power state value {0}")]
+ InvalidPowerState(u32),
+ #[error("invalid 32 bit CPU MPIDR value {0}")]
+ InvalidMpidr32(u32),
+ #[error("invalid 64 bit CPU MPIDR value {0}")]
+ InvalidMpidr64(u64),
+ #[error("unrecognised SYSTEM_OFF2 type {0}")]
+ UnrecognisedSystemOff2Type(u32),
+ #[error("unrecognised SYSTEM_RESET2 type {0}")]
+ UnrecognisedSystemReset2Type(u32),
+ #[error("unrecognised PSCI_FEATURES flags {0}")]
+ UnrecognisedPsciFeaturesFlags(u32),
+ #[error("unrecognised NODE_HW_STATE {0}")]
+ UnrecognisedHwState(u32),
+ #[error("unrecognised PSCI_SET_SUSPEND_MODE mode {0}")]
+ UnrecognisedSuspendMode(u32),
+ #[error("invalid lower affinity level {0}")]
+ InvalidLowerAffinityLevel(u32),
+}
+
+impl From<Error> for ErrorCode {
+ fn from(value: Error) -> Self {
+ match value {
+ Error::UnrecognisedFunctionId(_) => Self::NotSupported,
+ _ => Self::InvalidParameters,
+ }
+ }
+}
+
+/// 5.1 Function prototypes
+#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
+#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedFunctionId))]
+#[repr(u32)]
+pub enum FunctionId {
+ PsciVersion = 0x84000000,
+ CpuSuspend32 = 0x84000001,
+ CpuSuspend64 = 0xc4000001,
+ CpuOff = 0x84000002,
+ CpuOn32 = 0x84000003,
+ CpuOn64 = 0xc4000003,
+ AffinityInfo32 = 0x84000004,
+ AffinityInfo64 = 0xc4000004,
+ Migrate32 = 0x84000005,
+ Migrate64 = 0xc4000005,
+ MigrateInfoType = 0x84000006,
+ MigrateInfoUpCpu32 = 0x84000007,
+ MigrateInfoUpCpu64 = 0xc4000007,
+ SystemOff = 0x84000008,
+ SystemOff232 = 0x84000015,
+ SystemOff264 = 0xc4000015,
+ SystemReset = 0x84000009,
+ SystemReset232 = 0x84000012,
+ SystemReset264 = 0xc4000012,
+ MemProtect = 0x84000013,
+ MemProtectCheckRange32 = 0x84000014,
+ MemProtectCheckRange64 = 0xc4000014,
+ PsciFeatures = 0x8400000a,
+ CpuFreeze = 0x8400000b,
+ CpuDefaultSuspend32 = 0x8400000c,
+ CpuDefaultSuspend64 = 0xc400000c,
+ NodeHwState32 = 0x8400000d,
+ NodeHwState64 = 0xc400000d,
+ SystemSuspend32 = 0x8400000e,
+ SystemSuspend64 = 0xc400000e,
+ PsciSetSuspendMode = 0x8400000f,
+ PsciStatResidency32 = 0x84000010,
+ PsciStatResidency64 = 0xc4000010,
+ PsciStatCount32 = 0x84000011,
+ PsciStatCount64 = 0xc4000011,
+}
+
+/// Composite type for capturing success and error return codes.
+/// See Table 5 Return error codes
+///
+/// Clients can use `ReturnCode` to parse the result register value of a PSCI calls and easily
+/// determine if it was a success or an error.
+///
+/// Error codes are handled by the `ErrorCode` type. Having a separate type for errors helps using
+/// `Result<(), ErrorCode>`. If a single type would include both success and error values,
+/// then `Err(ErrorCode::Success)` would be incomprehensible.
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub enum ReturnCode {
+ Success,
+ Error(ErrorCode),
+}
+
+impl TryFrom<i32> for ReturnCode {
+ type Error = Error;
+
+ fn try_from(value: i32) -> Result<Self, <Self as TryFrom<i32>>::Error> {
+ Ok(match value {
+ 0 => Self::Success,
+ error_code => Self::Error(ErrorCode::try_from(error_code)?),
+ })
+ }
+}
+
+impl From<ReturnCode> for i32 {
+ fn from(value: ReturnCode) -> Self {
+ match value {
+ ReturnCode::Success => 0,
+ ReturnCode::Error(error_code) => error_code.into(),
+ }
+ }
+}
+
+/// Error codes
+/// See Table 5 Return error codes
+#[derive(Clone, Copy, Debug, Eq, Error, IntoPrimitive, PartialEq, TryFromPrimitive)]
+#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedErrorCode))]
+#[repr(i32)]
+pub enum ErrorCode {
+ #[error("Not supported")]
+ NotSupported = -1,
+ #[error("Invalid parameters")]
+ InvalidParameters = -2,
+ #[error("Denied")]
+ Denied = -3,
+ #[error("Already on")]
+ AlreadyOn = -4,
+ #[error("On pending")]
+ OnPending = -5,
+ #[error("Internal failure")]
+ InternalFailure = -6,
+ #[error("Not present")]
+ NotPresent = -7,
+ #[error("Disabled")]
+ Disabled = -8,
+ #[error("Invalid address")]
+ InvalidAddress = -9,
+}
+
+/// Structure for describing PSCI major and minor version.
+#[derive(Debug, Eq, PartialEq, Clone, Copy)]
+pub struct Version {
+ pub major: u16,
+ pub minor: u16,
+}
+
+impl TryFrom<u32> for Version {
+ type Error = Error;
+
+ fn try_from(value: u32) -> Result<Self, Self::Error> {
+ const MBZ_BITS: u32 = 0x8000_0000;
+
+ if value & MBZ_BITS != 0 {
+ Err(Error::InvalidVersion(value))
+ } else {
+ Ok(Self {
+ major: (value >> 16) as u16,
+ minor: value as u16,
+ })
+ }
+ }
+}
+
+impl From<Version> for u32 {
+ fn from(value: Version) -> Self {
+ const MAJOR_MBZ_BITS: u16 = 0x8000;
+
+ assert!((value.major & MAJOR_MBZ_BITS) == 0);
+
+ ((value.major as u32) << 16) | value.minor as u32
+ }
+}
+
+/// Table 8 power_state parameter bit fields in Extended StateID format.
+#[derive(Debug, Eq, PartialEq, Clone, Copy)]
+pub enum PowerState {
+ StandbyOrRetention(u32),
+ PowerDown(u32),
+}
+
+impl PowerState {
+ const STATE_TYPE_PD_BIT: u32 = 1 << 30;
+ const STATE_ID_MASK: u32 = 0x0fff_ffff;
+ const MBZ_BITS: u32 = !(Self::STATE_TYPE_PD_BIT | Self::STATE_ID_MASK);
+}
+
+impl TryFrom<u32> for PowerState {
+ type Error = Error;
+
+ fn try_from(value: u32) -> Result<Self, Self::Error> {
+ if value & Self::MBZ_BITS != 0 {
+ return Err(Error::InvalidPowerState(value));
+ }
+
+ let state_id = value & Self::STATE_ID_MASK;
+
+ Ok(if value & Self::STATE_TYPE_PD_BIT != 0 {
+ Self::PowerDown(state_id)
+ } else {
+ Self::StandbyOrRetention(state_id)
+ })
+ }
+}
+
+impl From<PowerState> for u32 {
+ fn from(value: PowerState) -> Self {
+ let (state_type_bit, state_id) = match value {
+ PowerState::StandbyOrRetention(state_id) => (0, state_id),
+ PowerState::PowerDown(state_id) => (PowerState::STATE_TYPE_PD_BIT, state_id),
+ };
+
+ assert_eq!(state_id & !PowerState::STATE_ID_MASK, 0x0000_0000);
+
+ state_type_bit | state_id
+ }
+}
+
+/// Entry point descriptor
+#[derive(Debug, Eq, PartialEq, Clone, Copy)]
+pub enum EntryPoint {
+ Entry32 {
+ entry_point_address: u32,
+ context_id: u32,
+ },
+ Entry64 {
+ entry_point_address: u64,
+ context_id: u64,
+ },
+}
+
+/// The type contains the affinity fields of the MPIDR register.
+/// For AArch32 callers this contains Affinity 0, 1, 2 fields and for AAarch64 callers it has
+/// Affinity 0, 1, 2, 3 fields.
+#[derive(Debug, Eq, PartialEq, Clone, Copy)]
+pub struct Mpidr {
+ pub aff0: u8,
+ pub aff1: u8,
+ pub aff2: u8,
+ pub aff3: Option<u8>,
+}
+
+impl TryFrom<u32> for Mpidr {
+ type Error = Error;
+
+ fn try_from(value: u32) -> Result<Self, Self::Error> {
+ const MBZ_BITS: u32 = 0xff00_0000;
+
+ if value & MBZ_BITS != 0 {
+ Err(Error::InvalidMpidr32(value))
+ } else {
+ Ok(Self {
+ aff0: value as u8,
+ aff1: (value >> 8) as u8,
+ aff2: (value >> 16) as u8,
+ aff3: None,
+ })
+ }
+ }
+}
+
+impl TryFrom<u64> for Mpidr {
+ type Error = Error;
+
+ fn try_from(value: u64) -> Result<Self, Self::Error> {
+ const MBZ_BITS: u64 = 0xffff_ff00_ff00_0000;
+
+ if value & MBZ_BITS != 0 {
+ Err(Error::InvalidMpidr64(value))
+ } else {
+ Ok(Self {
+ aff0: value as u8,
+ aff1: (value >> 8) as u8,
+ aff2: (value >> 16) as u8,
+ aff3: Some((value >> 32) as u8),
+ })
+ }
+ }
+}
+
+impl From<Mpidr> for u32 {
+ fn from(value: Mpidr) -> Self {
+ assert_eq!(value.aff3, None);
+ ((value.aff2 as u32) << 16) | ((value.aff1 as u32) << 8) | value.aff0 as u32
+ }
+}
+
+impl From<Mpidr> for u64 {
+ fn from(value: Mpidr) -> Self {
+ assert!(value.aff3.is_some());
+
+ ((value.aff3.unwrap() as u64) << 32)
+ | ((value.aff2 as u64) << 16)
+ | ((value.aff1 as u64) << 8)
+ | value.aff0 as u64
+ }
+}
+
+/// 5.1.5 AFFINITY_INFO return value
+#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
+#[repr(u32)]
+pub enum AffinityInfo {
+ On = 0,
+ Off = 1,
+ OnPending = 2,
+}
+
+/// 5.1.8 MIGRATE_INFO_UP_CPU return value
+#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
+#[repr(u32)]
+pub enum MigrateInfoType {
+ MigrateCapable = 0,
+ NotMigrateCapable = 1,
+ MigrationNotRequired = 2,
+}
+
+/// 5.1.10 SYSTEM_OFF2 type field
+#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
+#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedSystemOff2Type))]
+#[repr(u32)]
+pub enum SystemOff2Type {
+ HibernateOff = 0x00000001,
+}
+
+/// Additional off/reset parameter
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub enum Cookie {
+ Cookie32(u32),
+ Cookie64(u64),
+}
+
+/// 5.1.12 SYSTEM_RESET2 architectural reset type
+#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
+#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedSystemReset2Type))]
+#[repr(u32)]
+pub enum ArchitecturalResetType {
+ SystemWarmReset = 0x00000000,
+}
+
+/// 5.1.12 SYSTEM_RESET2 reset type
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub enum ResetType {
+ Architectural(ArchitecturalResetType),
+ VendorSpecific(u32),
+}
+
+impl ResetType {
+ const VENDOR_SPECIFIC_BIT: u32 = 0x8000_0000;
+}
+
+impl TryFrom<u32> for ResetType {
+ type Error = Error;
+
+ fn try_from(value: u32) -> Result<Self, Self::Error> {
+ Ok(if value & Self::VENDOR_SPECIFIC_BIT == 0 {
+ Self::Architectural(value.try_into()?)
+ } else {
+ Self::VendorSpecific(value & !Self::VENDOR_SPECIFIC_BIT)
+ })
+ }
+}
+
+impl From<ResetType> for u32 {
+ fn from(value: ResetType) -> Self {
+ match value {
+ ResetType::Architectural(architectural_reset_type) => architectural_reset_type.into(),
+ ResetType::VendorSpecific(vendor_specific_type) => {
+ vendor_specific_type | ResetType::VENDOR_SPECIFIC_BIT
+ }
+ }
+ }
+}
+
+/// 5.1.14 MEM_PROTECT_CHECK_RANGE memory range descriptor
+#[derive(Debug, Eq, PartialEq, Clone, Copy)]
+pub enum MemProtectRange {
+ Range32 { base: u32, length: u32 },
+ Range64 { base: u64, length: u64 },
+}
+
+/// 5.1.15 PSCI_FEATURES psci_func_id field
+#[derive(Debug, Eq, PartialEq, Clone, Copy)]
+pub enum PsciFeature {
+ PsciFunction(FunctionId),
+ SmcccVersion,
+}
+
+impl PsciFeature {
+ const SMCCC_VERSION: u32 = 0x8000_0000;
+}
+
+impl TryFrom<u32> for PsciFeature {
+ type Error = Error;
+
+ fn try_from(value: u32) -> Result<Self, Self::Error> {
+ Ok(if value == Self::SMCCC_VERSION {
+ Self::SmcccVersion
+ } else {
+ Self::PsciFunction(value.try_into()?)
+ })
+ }
+}
+
+impl From<PsciFeature> for u32 {
+ fn from(value: PsciFeature) -> u32 {
+ match value {
+ PsciFeature::PsciFunction(function_id) => function_id.into(),
+ PsciFeature::SmcccVersion => PsciFeature::SMCCC_VERSION,
+ }
+ }
+}
+
+/// Table 11 Return values if a function is implemented / CPU_SUSPEND
+#[derive(Debug, Eq, PartialEq, Clone, Copy)]
+#[repr(transparent)]
+pub struct FeatureFlagsCpuSuspend(u32);
+
+bitflags! {
+ impl FeatureFlagsCpuSuspend : u32 {
+ const EXTENDED_POWER_STATE = 0x0000_0002;
+ const OS_INITIATED_MODE = 0x0000_0001;
+ }
+}
+
+impl TryFrom<u32> for FeatureFlagsCpuSuspend {
+ type Error = Error;
+
+ fn try_from(value: u32) -> Result<Self, Self::Error> {
+ Self::from_bits(value).ok_or(Error::UnrecognisedPsciFeaturesFlags(value))
+ }
+}
+
+impl From<FeatureFlagsCpuSuspend> for u32 {
+ fn from(value: FeatureFlagsCpuSuspend) -> Self {
+ value.bits()
+ }
+}
+
+/// Table 11 Return values if a function is implemented / SYSTEM_OFF2
+#[derive(Debug, Eq, PartialEq, Clone, Copy)]
+#[repr(transparent)]
+pub struct FeatureFlagsSystemOff2(u32);
+
+bitflags! {
+ impl FeatureFlagsSystemOff2 : u32 {
+ const HIBERNATE_OFF = 0x0000_0001;
+ }
+}
+
+impl TryFrom<u32> for FeatureFlagsSystemOff2 {
+ type Error = Error;
+
+ fn try_from(value: u32) -> Result<Self, Self::Error> {
+ Self::from_bits(value).ok_or(Error::UnrecognisedPsciFeaturesFlags(value))
+ }
+}
+
+impl From<FeatureFlagsSystemOff2> for u32 {
+ fn from(value: FeatureFlagsSystemOff2) -> Self {
+ value.bits()
+ }
+}
+
+/// 5.1.18 NODE_HW_STATE return value
+#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
+#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedHwState))]
+#[repr(u32)]
+pub enum HwState {
+ On = 0,
+ Off = 1,
+ Standby = 2,
+}
+
+/// 5.1.20 PSCI_SET_SUSPEND_MODE mode field
+#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
+#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedSuspendMode))]
+#[repr(u32)]
+pub enum SuspendMode {
+ PlatformCoordinated = 0,
+ OsInitiated = 1,
+}
+
+/// Enum for representing PSCI requests and their arguments.
+#[derive(Debug, Eq, PartialEq, Clone, Copy)]
+pub enum Function {
+ Version,
+ CpuSuspend {
+ state: PowerState,
+ entry: EntryPoint,
+ },
+ CpuOff,
+ CpuOn {
+ target_cpu: Mpidr,
+ entry: EntryPoint,
+ },
+ AffinityInfo {
+ mpidr: Mpidr,
+ lowest_affinity_level: u32,
+ },
+ Migrate {
+ target_affinity: Mpidr,
+ },
+ MigrateInfoType,
+ MigrateInfoUpCpu {
+ is_32bit: bool,
+ },
+ SystemOff,
+ SystemOff2 {
+ off_type: SystemOff2Type,
+ cookie: Cookie,
+ },
+ SystemReset,
+ SystemReset2 {
+ reset_type: ResetType,
+ cookie: Cookie,
+ },
+ MemProtect {
+ enabled: bool,
+ },
+ MemProtectCheckRange {
+ range: MemProtectRange,
+ },
+ Features {
+ psci_func_id: PsciFeature,
+ },
+ CpuFreeze,
+ CpuDefaultSuspend {
+ entry: EntryPoint,
+ },
+ NodeHwState {
+ target_cpu: Mpidr,
+ power_level: u32,
+ },
+ SystemSuspend {
+ entry: EntryPoint,
+ },
+ SetSuspendMode {
+ mode: SuspendMode,
+ },
+ StatResidency {
+ target_cpu: Mpidr,
+ power_state: PowerState,
+ },
+ StatCount {
+ target_cpu: Mpidr,
+ power_state: PowerState,
+ },
+}
+
+impl TryFrom<[u64; 4]> for Function {
+ type Error = Error;
+
+ fn try_from(regs: [u64; 4]) -> Result<Self, Error> {
+ let fid = FunctionId::try_from(regs[0] as u32)?;
+
+ let msg = match fid {
+ FunctionId::PsciVersion => Self::Version,
+ FunctionId::CpuSuspend32 => Self::CpuSuspend {
+ state: PowerState::try_from(regs[1] as u32)?,
+ entry: EntryPoint::Entry32 {
+ entry_point_address: regs[2] as u32,
+ context_id: regs[3] as u32,
+ },
+ },
+ FunctionId::CpuSuspend64 => Self::CpuSuspend {
+ state: PowerState::try_from(regs[1] as u32)?,
+ entry: EntryPoint::Entry64 {
+ entry_point_address: regs[2],
+ context_id: regs[3],
+ },
+ },
+ FunctionId::CpuOff => Self::CpuOff,
+ FunctionId::CpuOn32 => Self::CpuOn {
+ target_cpu: (regs[1] as u32).try_into()?,
+ entry: EntryPoint::Entry32 {
+ entry_point_address: regs[2] as u32,
+ context_id: regs[3] as u32,
+ },
+ },
+ FunctionId::CpuOn64 => Self::CpuOn {
+ target_cpu: regs[1].try_into()?,
+ entry: EntryPoint::Entry64 {
+ entry_point_address: regs[2],
+ context_id: regs[3],
+ },
+ },
+ FunctionId::AffinityInfo32 => {
+ let lowest_affinity_level = regs[2] as u32;
+ if lowest_affinity_level > 2 {
+ return Err(Error::InvalidLowerAffinityLevel(lowest_affinity_level));
+ }
+ Self::AffinityInfo {
+ mpidr: (regs[1] as u32).try_into()?,
+ lowest_affinity_level,
+ }
+ }
+ FunctionId::AffinityInfo64 => {
+ let lowest_affinity_level = regs[2] as u32;
+ if lowest_affinity_level > 3 {
+ return Err(Error::InvalidLowerAffinityLevel(lowest_affinity_level));
+ }
+ Self::AffinityInfo {
+ mpidr: regs[1].try_into()?,
+ lowest_affinity_level,
+ }
+ }
+ FunctionId::Migrate32 => Self::Migrate {
+ target_affinity: (regs[1] as u32).try_into()?,
+ },
+ FunctionId::Migrate64 => Self::Migrate {
+ target_affinity: regs[1].try_into()?,
+ },
+ FunctionId::MigrateInfoType => Self::MigrateInfoType,
+ FunctionId::MigrateInfoUpCpu32 => Self::MigrateInfoUpCpu { is_32bit: true },
+ FunctionId::MigrateInfoUpCpu64 => Self::MigrateInfoUpCpu { is_32bit: false },
+ FunctionId::SystemOff => Self::SystemOff,
+ FunctionId::SystemOff232 => Self::SystemOff2 {
+ off_type: SystemOff2Type::try_from_primitive(regs[1] as u32)?,
+ cookie: Cookie::Cookie32(regs[2] as u32),
+ },
+ FunctionId::SystemOff264 => Self::SystemOff2 {
+ off_type: SystemOff2Type::try_from_primitive(regs[1] as u32)?,
+ cookie: Cookie::Cookie64(regs[2]),
+ },
+ FunctionId::SystemReset => Self::SystemReset,
+ FunctionId::SystemReset232 => Self::SystemReset2 {
+ reset_type: (regs[1] as u32).try_into()?,
+ cookie: Cookie::Cookie32(regs[2] as u32),
+ },
+ FunctionId::SystemReset264 => Self::SystemReset2 {
+ reset_type: (regs[1] as u32).try_into()?,
+ cookie: Cookie::Cookie64(regs[2]),
+ },
+ FunctionId::MemProtect => Self::MemProtect {
+ enabled: regs[1] != 0,
+ },
+ FunctionId::MemProtectCheckRange32 => Self::MemProtectCheckRange {
+ range: MemProtectRange::Range32 {
+ base: regs[1] as u32,
+ length: regs[2] as u32,
+ },
+ },
+ FunctionId::MemProtectCheckRange64 => Self::MemProtectCheckRange {
+ range: MemProtectRange::Range64 {
+ base: regs[1],
+ length: regs[2],
+ },
+ },
+ FunctionId::PsciFeatures => Self::Features {
+ psci_func_id: (regs[1] as u32).try_into()?,
+ },
+ FunctionId::CpuFreeze => Self::CpuFreeze,
+ FunctionId::CpuDefaultSuspend32 => Self::CpuDefaultSuspend {
+ entry: EntryPoint::Entry32 {
+ entry_point_address: regs[1] as u32,
+ context_id: regs[2] as u32,
+ },
+ },
+ FunctionId::CpuDefaultSuspend64 => Self::CpuDefaultSuspend {
+ entry: EntryPoint::Entry64 {
+ entry_point_address: regs[1],
+ context_id: regs[2],
+ },
+ },
+ FunctionId::NodeHwState32 => Self::NodeHwState {
+ target_cpu: (regs[1] as u32).try_into()?,
+ power_level: regs[2] as u32,
+ },
+ FunctionId::NodeHwState64 => Self::NodeHwState {
+ target_cpu: regs[1].try_into()?,
+ power_level: regs[2] as u32,
+ },
+ FunctionId::SystemSuspend32 => Self::SystemSuspend {
+ entry: EntryPoint::Entry32 {
+ entry_point_address: regs[1] as u32,
+ context_id: regs[2] as u32,
+ },
+ },
+ FunctionId::SystemSuspend64 => Self::SystemSuspend {
+ entry: EntryPoint::Entry64 {
+ entry_point_address: regs[1],
+ context_id: regs[2],
+ },
+ },
+ FunctionId::PsciSetSuspendMode => Self::SetSuspendMode {
+ mode: SuspendMode::try_from_primitive(regs[1] as u32)?,
+ },
+ FunctionId::PsciStatResidency32 => Self::StatResidency {
+ target_cpu: (regs[1] as u32).try_into()?,
+ power_state: PowerState::try_from(regs[2] as u32)?,
+ },
+ FunctionId::PsciStatResidency64 => Self::StatResidency {
+ target_cpu: regs[1].try_into()?,
+ power_state: PowerState::try_from(regs[2] as u32)?,
+ },
+ FunctionId::PsciStatCount32 => Self::StatCount {
+ target_cpu: (regs[1] as u32).try_into()?,
+ power_state: PowerState::try_from(regs[2] as u32)?,
+ },
+ FunctionId::PsciStatCount64 => Self::StatCount {
+ target_cpu: regs[1].try_into()?,
+ power_state: PowerState::try_from(regs[2] as u32)?,
+ },
+ };
+
+ Ok(msg)
+ }
+}
+
+impl Function {
+ /// Returns the function ID for the call.
+ pub fn function_id(&self) -> FunctionId {
+ match self {
+ Function::Version => FunctionId::PsciVersion,
+ Function::CpuSuspend {
+ entry: EntryPoint::Entry32 { .. },
+ ..
+ } => FunctionId::CpuSuspend32,
+ Function::CpuSuspend {
+ entry: EntryPoint::Entry64 { .. },
+ ..
+ } => FunctionId::CpuSuspend64,
+ Function::CpuOff => FunctionId::CpuOff,
+ Function::CpuOn {
+ target_cpu: Mpidr { aff3: None, .. },
+ entry: EntryPoint::Entry32 { .. },
+ } => FunctionId::CpuOn32,
+ Function::CpuOn {
+ target_cpu: Mpidr { aff3: Some(_), .. },
+ entry: EntryPoint::Entry64 { .. },
+ } => FunctionId::CpuOn64,
+ Function::CpuOn { .. } => panic!("Mixed 32 bit and 64 bit CpuOn arguments"),
+ Function::AffinityInfo {
+ mpidr: Mpidr { aff3: None, .. },
+ ..
+ } => FunctionId::AffinityInfo32,
+ Function::AffinityInfo {
+ mpidr: Mpidr { aff3: Some(_), .. },
+ ..
+ } => FunctionId::AffinityInfo64,
+ Function::Migrate {
+ target_affinity: Mpidr { aff3: None, .. },
+ } => FunctionId::Migrate32,
+ Function::Migrate {
+ target_affinity: Mpidr { aff3: Some(_), .. },
+ } => FunctionId::Migrate64,
+ Function::MigrateInfoType => FunctionId::MigrateInfoType,
+ Function::MigrateInfoUpCpu { is_32bit: true } => FunctionId::MigrateInfoUpCpu32,
+ Function::MigrateInfoUpCpu { is_32bit: false } => FunctionId::MigrateInfoUpCpu64,
+ Function::SystemOff => FunctionId::SystemOff,
+ Function::SystemOff2 {
+ cookie: Cookie::Cookie32(_),
+ ..
+ } => FunctionId::SystemOff232,
+ Function::SystemOff2 {
+ cookie: Cookie::Cookie64(_),
+ ..
+ } => FunctionId::SystemOff264,
+ Function::SystemReset => FunctionId::SystemReset,
+ Function::SystemReset2 {
+ cookie: Cookie::Cookie32(_),
+ ..
+ } => FunctionId::SystemReset232,
+ Function::SystemReset2 {
+ cookie: Cookie::Cookie64(_),
+ ..
+ } => FunctionId::SystemReset264,
+ Function::MemProtect { .. } => FunctionId::MemProtect,
+ Function::MemProtectCheckRange {
+ range: MemProtectRange::Range32 { .. },
+ } => FunctionId::MemProtectCheckRange32,
+ Function::MemProtectCheckRange {
+ range: MemProtectRange::Range64 { .. },
+ } => FunctionId::MemProtectCheckRange64,
+ Function::Features { .. } => FunctionId::PsciFeatures,
+ Function::CpuFreeze => FunctionId::CpuFreeze,
+ Function::CpuDefaultSuspend {
+ entry: EntryPoint::Entry32 { .. },
+ } => FunctionId::CpuDefaultSuspend32,
+ Function::CpuDefaultSuspend {
+ entry: EntryPoint::Entry64 { .. },
+ } => FunctionId::CpuDefaultSuspend64,
+ Function::NodeHwState {
+ target_cpu: Mpidr { aff3: None, .. },
+ ..
+ } => FunctionId::NodeHwState32,
+ Function::NodeHwState {
+ target_cpu: Mpidr { aff3: Some(_), .. },
+ ..
+ } => FunctionId::NodeHwState64,
+ Function::SystemSuspend {
+ entry: EntryPoint::Entry32 { .. },
+ } => FunctionId::SystemSuspend32,
+ Function::SystemSuspend {
+ entry: EntryPoint::Entry64 { .. },
+ } => FunctionId::SystemSuspend64,
+ Function::SetSuspendMode { .. } => FunctionId::PsciSetSuspendMode,
+ Function::StatResidency {
+ target_cpu: Mpidr { aff3: None, .. },
+ ..
+ } => FunctionId::PsciStatResidency32,
+ Function::StatResidency {
+ target_cpu: Mpidr { aff3: Some(_), .. },
+ ..
+ } => FunctionId::PsciStatResidency64,
+ Function::StatCount {
+ target_cpu: Mpidr { aff3: None, .. },
+ ..
+ } => FunctionId::PsciStatCount32,
+ Function::StatCount {
+ target_cpu: Mpidr { aff3: Some(_), .. },
+ ..
+ } => FunctionId::PsciStatCount64,
+ }
+ }
+
+ pub fn copy_to_array(&self, a: &mut [u64; 4]) {
+ a.fill(0);
+ a[0] = u32::from(self.function_id()).into();
+
+ match *self {
+ Function::Version
+ | Function::CpuOff
+ | Function::MigrateInfoType
+ | Function::MigrateInfoUpCpu { .. }
+ | Function::SystemOff
+ | Function::SystemReset
+ | Function::CpuFreeze => {}
+ Function::CpuSuspend { state, entry } => {
+ a[1] = u32::from(state).into();
+ (a[2], a[3]) = match entry {
+ EntryPoint::Entry32 {
+ entry_point_address,
+ context_id,
+ } => (entry_point_address.into(), context_id.into()),
+ EntryPoint::Entry64 {
+ entry_point_address,
+ context_id,
+ } => (entry_point_address, context_id),
+ }
+ }
+ Function::CpuOn { target_cpu, entry } => {
+ a[1] = match target_cpu {
+ Mpidr { aff3: None, .. } => u32::from(target_cpu).into(),
+ Mpidr { aff3: Some(_), .. } => u64::from(target_cpu),
+ };
+ (a[2], a[3]) = match entry {
+ EntryPoint::Entry32 {
+ entry_point_address,
+ context_id,
+ } => (entry_point_address.into(), context_id.into()),
+ EntryPoint::Entry64 {
+ entry_point_address,
+ context_id,
+ } => (entry_point_address, context_id),
+ }
+ }
+ Function::AffinityInfo {
+ mpidr,
+ lowest_affinity_level,
+ } => {
+ a[1] = match mpidr {
+ Mpidr { aff3: None, .. } => u32::from(mpidr).into(),
+ Mpidr { aff3: Some(_), .. } => u64::from(mpidr),
+ };
+ a[2] = lowest_affinity_level.into();
+ }
+ Function::Migrate { target_affinity } => {
+ a[1] = match target_affinity {
+ Mpidr { aff3: None, .. } => u32::from(target_affinity).into(),
+ Mpidr { aff3: Some(_), .. } => u64::from(target_affinity),
+ };
+ }
+ Function::SystemOff2 {
+ off_type: hibernate_type,
+ cookie,
+ } => {
+ a[1] = u32::from(hibernate_type).into();
+ a[2] = match cookie {
+ Cookie::Cookie32(value) => value.into(),
+ Cookie::Cookie64(value) => value,
+ };
+ }
+ Function::SystemReset2 { reset_type, cookie } => {
+ a[1] = u32::from(reset_type).into();
+ a[2] = match cookie {
+ Cookie::Cookie32(value) => value.into(),
+ Cookie::Cookie64(value) => value,
+ };
+ }
+ Function::MemProtect { enabled } => {
+ a[1] = if enabled { 0x0000_0001 } else { 0x0000_0000 };
+ }
+ Function::MemProtectCheckRange { range } => {
+ (a[1], a[2]) = match range {
+ MemProtectRange::Range32 { base, length } => (base.into(), length.into()),
+ MemProtectRange::Range64 { base, length } => (base, length),
+ }
+ }
+ Function::Features { psci_func_id } => {
+ a[1] = u32::from(psci_func_id).into();
+ }
+ Function::CpuDefaultSuspend { entry } => {
+ (a[1], a[2]) = match entry {
+ EntryPoint::Entry32 {
+ entry_point_address,
+ context_id,
+ } => (entry_point_address.into(), context_id.into()),
+ EntryPoint::Entry64 {
+ entry_point_address,
+ context_id,
+ } => (entry_point_address, context_id),
+ }
+ }
+ Function::NodeHwState {
+ target_cpu,
+ power_level,
+ } => {
+ a[1] = match target_cpu {
+ Mpidr { aff3: None, .. } => u32::from(target_cpu).into(),
+ Mpidr { aff3: Some(_), .. } => u64::from(target_cpu),
+ };
+ a[2] = power_level.into();
+ }
+ Function::SystemSuspend { entry } => {
+ (a[1], a[2]) = match entry {
+ EntryPoint::Entry32 {
+ entry_point_address,
+ context_id,
+ } => (entry_point_address.into(), context_id.into()),
+ EntryPoint::Entry64 {
+ entry_point_address,
+ context_id,
+ } => (entry_point_address, context_id),
+ }
+ }
+ Function::SetSuspendMode { mode } => {
+ a[1] = u32::from(mode).into();
+ }
+ Function::StatResidency {
+ target_cpu,
+ power_state,
+ } => {
+ a[1] = match target_cpu {
+ Mpidr { aff3: None, .. } => u32::from(target_cpu).into(),
+ Mpidr { aff3: Some(_), .. } => u64::from(target_cpu),
+ };
+ a[2] = u32::from(power_state).into();
+ }
+ Function::StatCount {
+ target_cpu,
+ power_state,
+ } => {
+ a[1] = match target_cpu {
+ Mpidr { aff3: None, .. } => u32::from(target_cpu).into(),
+ Mpidr { aff3: Some(_), .. } => u64::from(target_cpu),
+ };
+ a[2] = u32::from(power_state).into();
+ }
+ }
+ }
+}