arm_psci/
lib.rs

1// SPDX-FileCopyrightText: Copyright 2025 Arm Limited and/or its affiliates <open-source-office@arm.com>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4#![no_std]
5#![doc = include_str!("../README.md")]
6#![deny(clippy::undocumented_unsafe_blocks)]
7#![deny(unsafe_op_in_unsafe_fn)]
8
9//! # Specification
10//!
11//! This implementation is based on
12//! [Arm Power State Coordination Interface](https://developer.arm.com/documentation/den0022/latest/)
13//! Platform Design Document Version 1.3 issue F.b. (DEN0022).
14//!
15//! The type descriptions below refer to sections of this particular version of the specification.
16
17use bitflags::bitflags;
18use core::fmt::Debug;
19use num_enum::{IntoPrimitive, TryFromPrimitive};
20use thiserror::Error;
21
22/// Internal error type of the PSCI module
23#[derive(Debug, Error, PartialEq, Eq)]
24pub enum Error {
25    #[error("unrecognised PSCI function ID {0}")]
26    UnrecognisedFunctionId(u32),
27    #[error("unrecognised PSCI error code {0}")]
28    UnrecognisedErrorCode(i32),
29    #[error("overflowing PSCI error code {0}")]
30    OverflowingErrorCode(i64),
31    #[error("invalid PSCI version {0}")]
32    InvalidVersion(u32),
33    #[error("invalid power state value {0}")]
34    InvalidPowerState(u32),
35    #[error("invalid 32 bit CPU MPIDR value {0}")]
36    InvalidMpidr32(u32),
37    #[error("invalid 64 bit CPU MPIDR value {0}")]
38    InvalidMpidr64(u64),
39    #[error("unrecognised SYSTEM_OFF2 type {0}")]
40    UnrecognisedSystemOff2Type(u32),
41    #[error("unrecognised SYSTEM_RESET2 type {0}")]
42    UnrecognisedSystemReset2Type(u32),
43    #[error("unrecognised PSCI_FEATURES flags {0}")]
44    UnrecognisedPsciFeaturesFlags(u32),
45    #[error("unrecognised NODE_HW_STATE {0}")]
46    UnrecognisedHwState(u32),
47    #[error("unrecognised PSCI_SET_SUSPEND_MODE mode {0}")]
48    UnrecognisedSuspendMode(u32),
49    #[error("invalid lower affinity level {0}")]
50    InvalidLowerAffinityLevel(u32),
51    #[error("ignored non-zero aff3 when converting to u32 {0}")]
52    IgnoredNonZeroAff3(u8),
53}
54
55impl From<Error> for ErrorCode {
56    fn from(value: Error) -> Self {
57        match value {
58            Error::UnrecognisedFunctionId(_) => Self::NotSupported,
59            _ => Self::InvalidParameters,
60        }
61    }
62}
63
64/// 5.1 Function prototypes
65#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
66#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedFunctionId))]
67#[repr(u32)]
68pub enum FunctionId {
69    PsciVersion = 0x84000000,
70    CpuSuspend32 = 0x84000001,
71    CpuSuspend64 = 0xc4000001,
72    CpuOff = 0x84000002,
73    CpuOn32 = 0x84000003,
74    CpuOn64 = 0xc4000003,
75    AffinityInfo32 = 0x84000004,
76    AffinityInfo64 = 0xc4000004,
77    Migrate32 = 0x84000005,
78    Migrate64 = 0xc4000005,
79    MigrateInfoType = 0x84000006,
80    MigrateInfoUpCpu32 = 0x84000007,
81    MigrateInfoUpCpu64 = 0xc4000007,
82    SystemOff = 0x84000008,
83    SystemOff232 = 0x84000015,
84    SystemOff264 = 0xc4000015,
85    SystemReset = 0x84000009,
86    SystemReset232 = 0x84000012,
87    SystemReset264 = 0xc4000012,
88    MemProtect = 0x84000013,
89    MemProtectCheckRange32 = 0x84000014,
90    MemProtectCheckRange64 = 0xc4000014,
91    PsciFeatures = 0x8400000a,
92    CpuFreeze = 0x8400000b,
93    CpuDefaultSuspend32 = 0x8400000c,
94    CpuDefaultSuspend64 = 0xc400000c,
95    NodeHwState32 = 0x8400000d,
96    NodeHwState64 = 0xc400000d,
97    SystemSuspend32 = 0x8400000e,
98    SystemSuspend64 = 0xc400000e,
99    PsciSetSuspendMode = 0x8400000f,
100    PsciStatResidency32 = 0x84000010,
101    PsciStatResidency64 = 0xc4000010,
102    PsciStatCount32 = 0x84000011,
103    PsciStatCount64 = 0xc4000011,
104}
105
106/// Composite type for capturing success and error return codes.
107/// See Table 5 Return error codes
108///
109/// Clients can use `ReturnCode` to parse the result register value of a PSCI calls and easily
110/// determine if it was a success or an error.
111///
112/// Error codes are handled by the `ErrorCode` type. Having a separate type for errors helps using
113/// `Result<(), ErrorCode>`. If a single type would include both success and error values,
114/// then `Err(ErrorCode::Success)` would be incomprehensible.
115#[derive(Clone, Copy, Debug, Eq, PartialEq)]
116pub enum ReturnCode {
117    Success,
118    Error(ErrorCode),
119}
120
121impl TryFrom<i32> for ReturnCode {
122    type Error = Error;
123
124    fn try_from(value: i32) -> Result<Self, <Self as TryFrom<i32>>::Error> {
125        Ok(match value {
126            0 => Self::Success,
127            error_code => Self::Error(ErrorCode::try_from(error_code)?),
128        })
129    }
130}
131
132impl From<ReturnCode> for i32 {
133    fn from(value: ReturnCode) -> Self {
134        match value {
135            ReturnCode::Success => 0,
136            ReturnCode::Error(error_code) => error_code.into(),
137        }
138    }
139}
140
141/// Error codes
142/// See Table 5 Return error codes
143#[derive(Clone, Copy, Debug, Eq, Error, IntoPrimitive, PartialEq, TryFromPrimitive)]
144#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedErrorCode))]
145#[repr(i32)]
146pub enum ErrorCode {
147    #[error("Not supported")]
148    NotSupported = -1,
149    #[error("Invalid parameters")]
150    InvalidParameters = -2,
151    #[error("Denied")]
152    Denied = -3,
153    #[error("Already on")]
154    AlreadyOn = -4,
155    #[error("On pending")]
156    OnPending = -5,
157    #[error("Internal failure")]
158    InternalFailure = -6,
159    #[error("Not present")]
160    NotPresent = -7,
161    #[error("Disabled")]
162    Disabled = -8,
163    #[error("Invalid address")]
164    InvalidAddress = -9,
165}
166
167impl TryFrom<u32> for ErrorCode {
168    type Error = Error;
169
170    fn try_from(value: u32) -> Result<Self, Self::Error> {
171        Self::try_from_primitive(value as i32)
172    }
173}
174
175impl TryFrom<u64> for ErrorCode {
176    type Error = Error;
177
178    fn try_from(value: u64) -> Result<Self, Self::Error> {
179        let signed_value =
180            i32::try_from(value as i64).map_err(|_| Error::OverflowingErrorCode(value as i64))?;
181        Self::try_from_primitive(signed_value)
182    }
183}
184
185impl From<ErrorCode> for u32 {
186    /// Converts `ErrorCode` to be suitable for returning it in a 32-bit register.
187    /// See 5.2.2 Return error codes
188    fn from(value: ErrorCode) -> Self {
189        i32::from(value) as u32
190    }
191}
192
193impl From<ErrorCode> for u64 {
194    /// Converts `ErrorCode` to be suitable for returning it in a 64-bit register, i.e. it does
195    /// sign extension to 64 bits.
196    /// See 5.2.2 Return error codes
197    fn from(value: ErrorCode) -> Self {
198        i32::from(value) as u64
199    }
200}
201
202/// Structure for describing PSCI major and minor version.
203#[derive(Debug, Eq, PartialEq, Clone, Copy)]
204pub struct Version {
205    pub major: u16,
206    pub minor: u16,
207}
208
209impl TryFrom<u32> for Version {
210    type Error = Error;
211
212    fn try_from(value: u32) -> Result<Self, Self::Error> {
213        const MBZ_BITS: u32 = 0x8000_0000;
214
215        if value & MBZ_BITS != 0 {
216            Err(Error::InvalidVersion(value))
217        } else {
218            Ok(Self {
219                major: (value >> 16) as u16,
220                minor: value as u16,
221            })
222        }
223    }
224}
225
226impl From<Version> for u32 {
227    fn from(value: Version) -> Self {
228        const MAJOR_MBZ_BITS: u16 = 0x8000;
229
230        assert!((value.major & MAJOR_MBZ_BITS) == 0);
231
232        ((value.major as u32) << 16) | value.minor as u32
233    }
234}
235
236/// Table 8 power_state parameter bit fields in Extended StateID format.
237#[derive(Debug, Eq, PartialEq, Clone, Copy)]
238pub enum PowerState {
239    StandbyOrRetention(u32),
240    PowerDown(u32),
241}
242
243impl PowerState {
244    const STATE_TYPE_PD_BIT: u32 = 1 << 30;
245    const STATE_ID_MASK: u32 = 0x0fff_ffff;
246    const MBZ_BITS: u32 = !(Self::STATE_TYPE_PD_BIT | Self::STATE_ID_MASK);
247}
248
249impl TryFrom<u32> for PowerState {
250    type Error = Error;
251
252    fn try_from(value: u32) -> Result<Self, Self::Error> {
253        if value & Self::MBZ_BITS != 0 {
254            return Err(Error::InvalidPowerState(value));
255        }
256
257        let state_id = value & Self::STATE_ID_MASK;
258
259        Ok(if value & Self::STATE_TYPE_PD_BIT != 0 {
260            Self::PowerDown(state_id)
261        } else {
262            Self::StandbyOrRetention(state_id)
263        })
264    }
265}
266
267impl From<PowerState> for u32 {
268    fn from(value: PowerState) -> Self {
269        let (state_type_bit, state_id) = match value {
270            PowerState::StandbyOrRetention(state_id) => (0, state_id),
271            PowerState::PowerDown(state_id) => (PowerState::STATE_TYPE_PD_BIT, state_id),
272        };
273
274        assert_eq!(state_id & !PowerState::STATE_ID_MASK, 0x0000_0000);
275
276        state_type_bit | state_id
277    }
278}
279
280/// Entry point descriptor
281#[derive(Debug, Eq, PartialEq, Clone, Copy)]
282pub enum EntryPoint {
283    Entry32 {
284        entry_point_address: u32,
285        context_id: u32,
286    },
287    Entry64 {
288        entry_point_address: u64,
289        context_id: u64,
290    },
291}
292
293impl EntryPoint {
294    /// Returns the entry point address.
295    pub fn entry_point_address(&self) -> u64 {
296        match self {
297            Self::Entry32 {
298                entry_point_address,
299                ..
300            } => (*entry_point_address).into(),
301            Self::Entry64 {
302                entry_point_address,
303                ..
304            } => *entry_point_address,
305        }
306    }
307
308    /// Returns the context ID.
309    pub fn context_id(&self) -> u64 {
310        match self {
311            Self::Entry32 { context_id, .. } => (*context_id).into(),
312            Self::Entry64 { context_id, .. } => *context_id,
313        }
314    }
315}
316
317/// The type contains the affinity fields of the MPIDR register.
318/// For AArch32 callers this contains Affinity 0, 1, 2 fields and for AAarch64 callers it has
319/// Affinity 0, 1, 2, 3 fields.
320#[derive(Debug, Eq, PartialEq, Clone, Copy)]
321pub struct Mpidr {
322    pub aff0: u8,
323    pub aff1: u8,
324    pub aff2: u8,
325    pub aff3: Option<u8>,
326}
327
328impl Mpidr {
329    const MBZ_BITS_64: u64 = 0xffff_ff00_ff00_0000;
330
331    /// Create Mpidr instance from aff3-0 values
332    pub const fn from_aff3210(aff3: u8, aff2: u8, aff1: u8, aff0: u8) -> Self {
333        Self {
334            aff0,
335            aff1,
336            aff2,
337            aff3: Some(aff3),
338        }
339    }
340
341    /// Create Mpidr instance from aff2-0 values
342    pub const fn from_aff210(aff2: u8, aff1: u8, aff0: u8) -> Self {
343        Self {
344            aff0,
345            aff1,
346            aff2,
347            aff3: None,
348        }
349    }
350
351    /// The `MPIDR_EL1` register contains bits other then the aff3-0 fields. However the PSCI
352    /// specification request bits\[40:63\] and bits\[24:31\] to be set to zero when forwarding an
353    /// MPIDR value as an argument of a PSCI function call. Because of this, the `TryFrom`
354    /// implementation returns an error if these bits are set. In other cases the `Mpidr` value is
355    /// constructed from the `MPIDR_EL1` register value of the local core. This function does this
356    /// by ignoring other bits. Do not use this function for creating `Mpidr` instance from a PSCI
357    /// function argument.
358    pub fn from_register_value(mpidr_el1: u64) -> Self {
359        Self::try_from(mpidr_el1 & !Self::MBZ_BITS_64).unwrap()
360    }
361}
362
363impl TryFrom<u32> for Mpidr {
364    type Error = Error;
365
366    fn try_from(value: u32) -> Result<Self, Self::Error> {
367        const MBZ_BITS: u32 = 0xff00_0000;
368
369        if value & MBZ_BITS != 0 {
370            Err(Error::InvalidMpidr32(value))
371        } else {
372            Ok(Self {
373                aff0: value as u8,
374                aff1: (value >> 8) as u8,
375                aff2: (value >> 16) as u8,
376                aff3: None,
377            })
378        }
379    }
380}
381
382impl TryFrom<u64> for Mpidr {
383    type Error = Error;
384
385    fn try_from(value: u64) -> Result<Self, Self::Error> {
386        if value & Self::MBZ_BITS_64 != 0 {
387            Err(Error::InvalidMpidr64(value))
388        } else {
389            Ok(Self {
390                aff0: value as u8,
391                aff1: (value >> 8) as u8,
392                aff2: (value >> 16) as u8,
393                aff3: Some((value >> 32) as u8),
394            })
395        }
396    }
397}
398
399impl TryFrom<Mpidr> for u32 {
400    type Error = Error;
401
402    fn try_from(value: Mpidr) -> Result<Self, Self::Error> {
403        match value.aff3 {
404            // Allow converting Mpidr to u32 if aff3 is not set or set to zero.
405            Some(0) | None => {
406                Ok(((value.aff2 as u32) << 16) | ((value.aff1 as u32) << 8) | value.aff0 as u32)
407            }
408            // Aff3 is non zero, we would lose it when converting to u32.
409            Some(aff3) => Err(Error::IgnoredNonZeroAff3(aff3)),
410        }
411    }
412}
413
414impl From<Mpidr> for u64 {
415    fn from(value: Mpidr) -> Self {
416        ((value.aff3.unwrap_or(0) as u64) << 32)
417            | ((value.aff2 as u64) << 16)
418            | ((value.aff1 as u64) << 8)
419            | value.aff0 as u64
420    }
421}
422
423/// 5.1.5 AFFINITY_INFO return value
424#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
425#[repr(u32)]
426pub enum AffinityInfo {
427    On = 0,
428    Off = 1,
429    OnPending = 2,
430}
431
432/// 5.1.8 MIGRATE_INFO_UP_CPU return value
433#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
434#[repr(u32)]
435pub enum MigrateInfoType {
436    MigrateCapable = 0,
437    NotMigrateCapable = 1,
438    MigrationNotRequired = 2,
439}
440
441/// 5.1.10 SYSTEM_OFF2 type field
442#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
443#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedSystemOff2Type))]
444#[repr(u32)]
445pub enum SystemOff2Type {
446    HibernateOff = 0x00000001,
447}
448
449/// Additional off/reset parameter
450#[derive(Clone, Copy, Debug, Eq, PartialEq)]
451pub enum Cookie {
452    Cookie32(u32),
453    Cookie64(u64),
454}
455
456/// 5.1.12 SYSTEM_RESET2 architectural reset type
457#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
458#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedSystemReset2Type))]
459#[repr(u32)]
460pub enum ArchitecturalResetType {
461    SystemWarmReset = 0x00000000,
462}
463
464/// 5.1.12 SYSTEM_RESET2 reset type
465#[derive(Clone, Copy, Debug, Eq, PartialEq)]
466pub enum ResetType {
467    Architectural(ArchitecturalResetType),
468    VendorSpecific(u32),
469}
470
471impl ResetType {
472    const VENDOR_SPECIFIC_BIT: u32 = 0x8000_0000;
473}
474
475impl TryFrom<u32> for ResetType {
476    type Error = Error;
477
478    fn try_from(value: u32) -> Result<Self, Self::Error> {
479        Ok(if value & Self::VENDOR_SPECIFIC_BIT == 0 {
480            Self::Architectural(value.try_into()?)
481        } else {
482            Self::VendorSpecific(value & !Self::VENDOR_SPECIFIC_BIT)
483        })
484    }
485}
486
487impl From<ResetType> for u32 {
488    fn from(value: ResetType) -> Self {
489        match value {
490            ResetType::Architectural(architectural_reset_type) => architectural_reset_type.into(),
491            ResetType::VendorSpecific(vendor_specific_type) => {
492                vendor_specific_type | ResetType::VENDOR_SPECIFIC_BIT
493            }
494        }
495    }
496}
497
498/// 5.1.14 MEM_PROTECT_CHECK_RANGE memory range descriptor
499#[derive(Debug, Eq, PartialEq, Clone, Copy)]
500pub enum MemProtectRange {
501    Range32 { base: u32, length: u32 },
502    Range64 { base: u64, length: u64 },
503}
504
505/// 5.1.15 PSCI_FEATURES psci_func_id field
506#[derive(Debug, Eq, PartialEq, Clone, Copy)]
507pub enum PsciFeature {
508    PsciFunction(FunctionId),
509    SmcccVersion,
510}
511
512impl PsciFeature {
513    const SMCCC_VERSION: u32 = 0x8000_0000;
514}
515
516impl TryFrom<u32> for PsciFeature {
517    type Error = Error;
518
519    fn try_from(value: u32) -> Result<Self, Self::Error> {
520        Ok(if value == Self::SMCCC_VERSION {
521            Self::SmcccVersion
522        } else {
523            Self::PsciFunction(value.try_into()?)
524        })
525    }
526}
527
528impl From<PsciFeature> for u32 {
529    fn from(value: PsciFeature) -> u32 {
530        match value {
531            PsciFeature::PsciFunction(function_id) => function_id.into(),
532            PsciFeature::SmcccVersion => PsciFeature::SMCCC_VERSION,
533        }
534    }
535}
536
537/// Table 11 Return values if a function is implemented / CPU_SUSPEND
538#[derive(Debug, Eq, PartialEq, Clone, Copy)]
539#[repr(transparent)]
540pub struct FeatureFlagsCpuSuspend(u32);
541
542bitflags! {
543    impl FeatureFlagsCpuSuspend : u32 {
544        const EXTENDED_POWER_STATE = 0x0000_0002;
545        const OS_INITIATED_MODE = 0x0000_0001;
546    }
547}
548
549impl TryFrom<u32> for FeatureFlagsCpuSuspend {
550    type Error = Error;
551
552    fn try_from(value: u32) -> Result<Self, Self::Error> {
553        Self::from_bits(value).ok_or(Error::UnrecognisedPsciFeaturesFlags(value))
554    }
555}
556
557impl From<FeatureFlagsCpuSuspend> for u32 {
558    fn from(value: FeatureFlagsCpuSuspend) -> Self {
559        value.bits()
560    }
561}
562
563/// Table 11 Return values if a function is implemented / SYSTEM_OFF2
564#[derive(Debug, Eq, PartialEq, Clone, Copy)]
565#[repr(transparent)]
566pub struct FeatureFlagsSystemOff2(u32);
567
568bitflags! {
569    impl FeatureFlagsSystemOff2 : u32 {
570        const HIBERNATE_OFF = 0x0000_0001;
571    }
572}
573
574impl TryFrom<u32> for FeatureFlagsSystemOff2 {
575    type Error = Error;
576
577    fn try_from(value: u32) -> Result<Self, Self::Error> {
578        Self::from_bits(value).ok_or(Error::UnrecognisedPsciFeaturesFlags(value))
579    }
580}
581
582impl From<FeatureFlagsSystemOff2> for u32 {
583    fn from(value: FeatureFlagsSystemOff2) -> Self {
584        value.bits()
585    }
586}
587
588/// 5.1.18 NODE_HW_STATE return value
589#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
590#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedHwState))]
591#[repr(u32)]
592pub enum HwState {
593    On = 0,
594    Off = 1,
595    Standby = 2,
596}
597
598/// 5.1.20 PSCI_SET_SUSPEND_MODE mode field
599#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
600#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedSuspendMode))]
601#[repr(u32)]
602pub enum SuspendMode {
603    PlatformCoordinated = 0,
604    OsInitiated = 1,
605}
606
607/// Enum for representing PSCI requests and their arguments.
608#[derive(Debug, Eq, PartialEq, Clone, Copy)]
609pub enum Function {
610    Version,
611    CpuSuspend {
612        state: PowerState,
613        entry: EntryPoint,
614    },
615    CpuOff,
616    CpuOn {
617        target_cpu: Mpidr,
618        entry: EntryPoint,
619    },
620    AffinityInfo {
621        mpidr: Mpidr,
622        lowest_affinity_level: u32,
623    },
624    Migrate {
625        target_affinity: Mpidr,
626    },
627    MigrateInfoType,
628    MigrateInfoUpCpu {
629        is_32bit: bool,
630    },
631    SystemOff,
632    SystemOff2 {
633        off_type: SystemOff2Type,
634        cookie: Cookie,
635    },
636    SystemReset,
637    SystemReset2 {
638        reset_type: ResetType,
639        cookie: Cookie,
640    },
641    MemProtect {
642        enabled: bool,
643    },
644    MemProtectCheckRange {
645        range: MemProtectRange,
646    },
647    Features {
648        psci_func_id: PsciFeature,
649    },
650    CpuFreeze,
651    CpuDefaultSuspend {
652        entry: EntryPoint,
653    },
654    NodeHwState {
655        target_cpu: Mpidr,
656        power_level: u32,
657    },
658    SystemSuspend {
659        entry: EntryPoint,
660    },
661    SetSuspendMode {
662        mode: SuspendMode,
663    },
664    StatResidency {
665        target_cpu: Mpidr,
666        power_state: PowerState,
667    },
668    StatCount {
669        target_cpu: Mpidr,
670        power_state: PowerState,
671    },
672}
673
674impl TryFrom<[u64; 4]> for Function {
675    type Error = Error;
676
677    fn try_from(regs: [u64; 4]) -> Result<Self, Error> {
678        let fid = FunctionId::try_from(regs[0] as u32)?;
679
680        let msg = match fid {
681            FunctionId::PsciVersion => Self::Version,
682            FunctionId::CpuSuspend32 => Self::CpuSuspend {
683                state: PowerState::try_from(regs[1] as u32)?,
684                entry: EntryPoint::Entry32 {
685                    entry_point_address: regs[2] as u32,
686                    context_id: regs[3] as u32,
687                },
688            },
689            FunctionId::CpuSuspend64 => Self::CpuSuspend {
690                state: PowerState::try_from(regs[1] as u32)?,
691                entry: EntryPoint::Entry64 {
692                    entry_point_address: regs[2],
693                    context_id: regs[3],
694                },
695            },
696            FunctionId::CpuOff => Self::CpuOff,
697            FunctionId::CpuOn32 => Self::CpuOn {
698                target_cpu: (regs[1] as u32).try_into()?,
699                entry: EntryPoint::Entry32 {
700                    entry_point_address: regs[2] as u32,
701                    context_id: regs[3] as u32,
702                },
703            },
704            FunctionId::CpuOn64 => Self::CpuOn {
705                target_cpu: regs[1].try_into()?,
706                entry: EntryPoint::Entry64 {
707                    entry_point_address: regs[2],
708                    context_id: regs[3],
709                },
710            },
711            FunctionId::AffinityInfo32 => {
712                let lowest_affinity_level = regs[2] as u32;
713                if lowest_affinity_level > 2 {
714                    return Err(Error::InvalidLowerAffinityLevel(lowest_affinity_level));
715                }
716                Self::AffinityInfo {
717                    mpidr: (regs[1] as u32).try_into()?,
718                    lowest_affinity_level,
719                }
720            }
721            FunctionId::AffinityInfo64 => {
722                let lowest_affinity_level = regs[2] as u32;
723                if lowest_affinity_level > 3 {
724                    return Err(Error::InvalidLowerAffinityLevel(lowest_affinity_level));
725                }
726                Self::AffinityInfo {
727                    mpidr: regs[1].try_into()?,
728                    lowest_affinity_level,
729                }
730            }
731            FunctionId::Migrate32 => Self::Migrate {
732                target_affinity: (regs[1] as u32).try_into()?,
733            },
734            FunctionId::Migrate64 => Self::Migrate {
735                target_affinity: regs[1].try_into()?,
736            },
737            FunctionId::MigrateInfoType => Self::MigrateInfoType,
738            FunctionId::MigrateInfoUpCpu32 => Self::MigrateInfoUpCpu { is_32bit: true },
739            FunctionId::MigrateInfoUpCpu64 => Self::MigrateInfoUpCpu { is_32bit: false },
740            FunctionId::SystemOff => Self::SystemOff,
741            FunctionId::SystemOff232 => Self::SystemOff2 {
742                off_type: SystemOff2Type::try_from_primitive(regs[1] as u32)?,
743                cookie: Cookie::Cookie32(regs[2] as u32),
744            },
745            FunctionId::SystemOff264 => Self::SystemOff2 {
746                off_type: SystemOff2Type::try_from_primitive(regs[1] as u32)?,
747                cookie: Cookie::Cookie64(regs[2]),
748            },
749            FunctionId::SystemReset => Self::SystemReset,
750            FunctionId::SystemReset232 => Self::SystemReset2 {
751                reset_type: (regs[1] as u32).try_into()?,
752                cookie: Cookie::Cookie32(regs[2] as u32),
753            },
754            FunctionId::SystemReset264 => Self::SystemReset2 {
755                reset_type: (regs[1] as u32).try_into()?,
756                cookie: Cookie::Cookie64(regs[2]),
757            },
758            FunctionId::MemProtect => Self::MemProtect {
759                enabled: regs[1] != 0,
760            },
761            FunctionId::MemProtectCheckRange32 => Self::MemProtectCheckRange {
762                range: MemProtectRange::Range32 {
763                    base: regs[1] as u32,
764                    length: regs[2] as u32,
765                },
766            },
767            FunctionId::MemProtectCheckRange64 => Self::MemProtectCheckRange {
768                range: MemProtectRange::Range64 {
769                    base: regs[1],
770                    length: regs[2],
771                },
772            },
773            FunctionId::PsciFeatures => Self::Features {
774                psci_func_id: (regs[1] as u32).try_into()?,
775            },
776            FunctionId::CpuFreeze => Self::CpuFreeze,
777            FunctionId::CpuDefaultSuspend32 => Self::CpuDefaultSuspend {
778                entry: EntryPoint::Entry32 {
779                    entry_point_address: regs[1] as u32,
780                    context_id: regs[2] as u32,
781                },
782            },
783            FunctionId::CpuDefaultSuspend64 => Self::CpuDefaultSuspend {
784                entry: EntryPoint::Entry64 {
785                    entry_point_address: regs[1],
786                    context_id: regs[2],
787                },
788            },
789            FunctionId::NodeHwState32 => Self::NodeHwState {
790                target_cpu: (regs[1] as u32).try_into()?,
791                power_level: regs[2] as u32,
792            },
793            FunctionId::NodeHwState64 => Self::NodeHwState {
794                target_cpu: regs[1].try_into()?,
795                power_level: regs[2] as u32,
796            },
797            FunctionId::SystemSuspend32 => Self::SystemSuspend {
798                entry: EntryPoint::Entry32 {
799                    entry_point_address: regs[1] as u32,
800                    context_id: regs[2] as u32,
801                },
802            },
803            FunctionId::SystemSuspend64 => Self::SystemSuspend {
804                entry: EntryPoint::Entry64 {
805                    entry_point_address: regs[1],
806                    context_id: regs[2],
807                },
808            },
809            FunctionId::PsciSetSuspendMode => Self::SetSuspendMode {
810                mode: SuspendMode::try_from_primitive(regs[1] as u32)?,
811            },
812            FunctionId::PsciStatResidency32 => Self::StatResidency {
813                target_cpu: (regs[1] as u32).try_into()?,
814                power_state: PowerState::try_from(regs[2] as u32)?,
815            },
816            FunctionId::PsciStatResidency64 => Self::StatResidency {
817                target_cpu: regs[1].try_into()?,
818                power_state: PowerState::try_from(regs[2] as u32)?,
819            },
820            FunctionId::PsciStatCount32 => Self::StatCount {
821                target_cpu: (regs[1] as u32).try_into()?,
822                power_state: PowerState::try_from(regs[2] as u32)?,
823            },
824            FunctionId::PsciStatCount64 => Self::StatCount {
825                target_cpu: regs[1].try_into()?,
826                power_state: PowerState::try_from(regs[2] as u32)?,
827            },
828        };
829
830        Ok(msg)
831    }
832}
833
834impl Function {
835    /// Returns the function ID for the call.
836    pub fn function_id(&self) -> FunctionId {
837        match self {
838            Function::Version => FunctionId::PsciVersion,
839            Function::CpuSuspend {
840                entry: EntryPoint::Entry32 { .. },
841                ..
842            } => FunctionId::CpuSuspend32,
843            Function::CpuSuspend {
844                entry: EntryPoint::Entry64 { .. },
845                ..
846            } => FunctionId::CpuSuspend64,
847            Function::CpuOff => FunctionId::CpuOff,
848            Function::CpuOn {
849                target_cpu: Mpidr { aff3: None, .. },
850                entry: EntryPoint::Entry32 { .. },
851            } => FunctionId::CpuOn32,
852            Function::CpuOn {
853                target_cpu: Mpidr { aff3: Some(_), .. },
854                entry: EntryPoint::Entry64 { .. },
855            } => FunctionId::CpuOn64,
856            Function::CpuOn { .. } => panic!("Mixed 32 bit and 64 bit CpuOn arguments"),
857            Function::AffinityInfo {
858                mpidr: Mpidr { aff3: None, .. },
859                ..
860            } => FunctionId::AffinityInfo32,
861            Function::AffinityInfo {
862                mpidr: Mpidr { aff3: Some(_), .. },
863                ..
864            } => FunctionId::AffinityInfo64,
865            Function::Migrate {
866                target_affinity: Mpidr { aff3: None, .. },
867            } => FunctionId::Migrate32,
868            Function::Migrate {
869                target_affinity: Mpidr { aff3: Some(_), .. },
870            } => FunctionId::Migrate64,
871            Function::MigrateInfoType => FunctionId::MigrateInfoType,
872            Function::MigrateInfoUpCpu { is_32bit: true } => FunctionId::MigrateInfoUpCpu32,
873            Function::MigrateInfoUpCpu { is_32bit: false } => FunctionId::MigrateInfoUpCpu64,
874            Function::SystemOff => FunctionId::SystemOff,
875            Function::SystemOff2 {
876                cookie: Cookie::Cookie32(_),
877                ..
878            } => FunctionId::SystemOff232,
879            Function::SystemOff2 {
880                cookie: Cookie::Cookie64(_),
881                ..
882            } => FunctionId::SystemOff264,
883            Function::SystemReset => FunctionId::SystemReset,
884            Function::SystemReset2 {
885                cookie: Cookie::Cookie32(_),
886                ..
887            } => FunctionId::SystemReset232,
888            Function::SystemReset2 {
889                cookie: Cookie::Cookie64(_),
890                ..
891            } => FunctionId::SystemReset264,
892            Function::MemProtect { .. } => FunctionId::MemProtect,
893            Function::MemProtectCheckRange {
894                range: MemProtectRange::Range32 { .. },
895            } => FunctionId::MemProtectCheckRange32,
896            Function::MemProtectCheckRange {
897                range: MemProtectRange::Range64 { .. },
898            } => FunctionId::MemProtectCheckRange64,
899            Function::Features { .. } => FunctionId::PsciFeatures,
900            Function::CpuFreeze => FunctionId::CpuFreeze,
901            Function::CpuDefaultSuspend {
902                entry: EntryPoint::Entry32 { .. },
903            } => FunctionId::CpuDefaultSuspend32,
904            Function::CpuDefaultSuspend {
905                entry: EntryPoint::Entry64 { .. },
906            } => FunctionId::CpuDefaultSuspend64,
907            Function::NodeHwState {
908                target_cpu: Mpidr { aff3: None, .. },
909                ..
910            } => FunctionId::NodeHwState32,
911            Function::NodeHwState {
912                target_cpu: Mpidr { aff3: Some(_), .. },
913                ..
914            } => FunctionId::NodeHwState64,
915            Function::SystemSuspend {
916                entry: EntryPoint::Entry32 { .. },
917            } => FunctionId::SystemSuspend32,
918            Function::SystemSuspend {
919                entry: EntryPoint::Entry64 { .. },
920            } => FunctionId::SystemSuspend64,
921            Function::SetSuspendMode { .. } => FunctionId::PsciSetSuspendMode,
922            Function::StatResidency {
923                target_cpu: Mpidr { aff3: None, .. },
924                ..
925            } => FunctionId::PsciStatResidency32,
926            Function::StatResidency {
927                target_cpu: Mpidr { aff3: Some(_), .. },
928                ..
929            } => FunctionId::PsciStatResidency64,
930            Function::StatCount {
931                target_cpu: Mpidr { aff3: None, .. },
932                ..
933            } => FunctionId::PsciStatCount32,
934            Function::StatCount {
935                target_cpu: Mpidr { aff3: Some(_), .. },
936                ..
937            } => FunctionId::PsciStatCount64,
938        }
939    }
940
941    pub fn copy_to_array(&self, a: &mut [u64; 4]) {
942        a.fill(0);
943        a[0] = u32::from(self.function_id()).into();
944
945        match *self {
946            Function::Version
947            | Function::CpuOff
948            | Function::MigrateInfoType
949            | Function::MigrateInfoUpCpu { .. }
950            | Function::SystemOff
951            | Function::SystemReset
952            | Function::CpuFreeze => {}
953            Function::CpuSuspend { state, entry } => {
954                a[1] = u32::from(state).into();
955                (a[2], a[3]) = match entry {
956                    EntryPoint::Entry32 {
957                        entry_point_address,
958                        context_id,
959                    } => (entry_point_address.into(), context_id.into()),
960                    EntryPoint::Entry64 {
961                        entry_point_address,
962                        context_id,
963                    } => (entry_point_address, context_id),
964                }
965            }
966            Function::CpuOn { target_cpu, entry } => {
967                a[1] = target_cpu.into();
968                (a[2], a[3]) = match entry {
969                    EntryPoint::Entry32 {
970                        entry_point_address,
971                        context_id,
972                    } => (entry_point_address.into(), context_id.into()),
973                    EntryPoint::Entry64 {
974                        entry_point_address,
975                        context_id,
976                    } => (entry_point_address, context_id),
977                }
978            }
979            Function::AffinityInfo {
980                mpidr,
981                lowest_affinity_level,
982            } => {
983                a[1] = mpidr.into();
984                a[2] = lowest_affinity_level.into();
985            }
986            Function::Migrate { target_affinity } => {
987                a[1] = target_affinity.into();
988            }
989            Function::SystemOff2 {
990                off_type: hibernate_type,
991                cookie,
992            } => {
993                a[1] = u32::from(hibernate_type).into();
994                a[2] = match cookie {
995                    Cookie::Cookie32(value) => value.into(),
996                    Cookie::Cookie64(value) => value,
997                };
998            }
999            Function::SystemReset2 { reset_type, cookie } => {
1000                a[1] = u32::from(reset_type).into();
1001                a[2] = match cookie {
1002                    Cookie::Cookie32(value) => value.into(),
1003                    Cookie::Cookie64(value) => value,
1004                };
1005            }
1006            Function::MemProtect { enabled } => {
1007                a[1] = if enabled { 0x0000_0001 } else { 0x0000_0000 };
1008            }
1009            Function::MemProtectCheckRange { range } => {
1010                (a[1], a[2]) = match range {
1011                    MemProtectRange::Range32 { base, length } => (base.into(), length.into()),
1012                    MemProtectRange::Range64 { base, length } => (base, length),
1013                }
1014            }
1015            Function::Features { psci_func_id } => {
1016                a[1] = u32::from(psci_func_id).into();
1017            }
1018            Function::CpuDefaultSuspend { entry } => {
1019                (a[1], a[2]) = match entry {
1020                    EntryPoint::Entry32 {
1021                        entry_point_address,
1022                        context_id,
1023                    } => (entry_point_address.into(), context_id.into()),
1024                    EntryPoint::Entry64 {
1025                        entry_point_address,
1026                        context_id,
1027                    } => (entry_point_address, context_id),
1028                }
1029            }
1030            Function::NodeHwState {
1031                target_cpu,
1032                power_level,
1033            } => {
1034                a[1] = target_cpu.into();
1035                a[2] = power_level.into();
1036            }
1037            Function::SystemSuspend { entry } => {
1038                (a[1], a[2]) = match entry {
1039                    EntryPoint::Entry32 {
1040                        entry_point_address,
1041                        context_id,
1042                    } => (entry_point_address.into(), context_id.into()),
1043                    EntryPoint::Entry64 {
1044                        entry_point_address,
1045                        context_id,
1046                    } => (entry_point_address, context_id),
1047                }
1048            }
1049            Function::SetSuspendMode { mode } => {
1050                a[1] = u32::from(mode).into();
1051            }
1052            Function::StatResidency {
1053                target_cpu,
1054                power_state,
1055            } => {
1056                a[1] = target_cpu.into();
1057                a[2] = u32::from(power_state).into();
1058            }
1059            Function::StatCount {
1060                target_cpu,
1061                power_state,
1062            } => {
1063                a[1] = target_cpu.into();
1064                a[2] = u32::from(power_state).into();
1065            }
1066        }
1067    }
1068}
1069
1070#[cfg(test)]
1071mod tests {
1072    use super::*;
1073
1074    const MPIDR32: Mpidr = Mpidr {
1075        aff0: 0x11,
1076        aff1: 0x22,
1077        aff2: 0x33,
1078        aff3: None,
1079    };
1080    const MPIDR32_VALUE: u32 = 0x00332211;
1081
1082    const MPIDR64: Mpidr = Mpidr {
1083        aff0: 0x11,
1084        aff1: 0x22,
1085        aff2: 0x33,
1086        aff3: Some(0x44),
1087    };
1088
1089    const MPIDR64_VALUE: u64 = 0x00000044_00332211;
1090
1091    const POWER_STATE: PowerState = PowerState::PowerDown(0xf12_3456);
1092
1093    const POWER_STATE_VALUE: u32 = 0x4f12_3456;
1094
1095    macro_rules! validate_function {
1096        ( $regs:expr, $function:expr, $function_id:expr ) => {
1097            assert_eq!(Ok($function), Function::try_from($regs));
1098            assert_eq!($function_id, ($function).function_id());
1099
1100            let mut built_regs = [0; 4];
1101            $function.copy_to_array(&mut built_regs);
1102            assert_eq!($regs, built_regs);
1103        };
1104    }
1105
1106    #[test]
1107    fn test_error() {
1108        assert_eq!(
1109            ErrorCode::NotSupported,
1110            ErrorCode::from(Error::UnrecognisedFunctionId(0))
1111        );
1112
1113        assert_eq!(
1114            ErrorCode::InvalidParameters,
1115            ErrorCode::from(Error::InvalidVersion(0))
1116        );
1117    }
1118
1119    #[test]
1120    fn test_return_code() {
1121        assert_eq!(
1122            Err(Error::UnrecognisedErrorCode(-100)),
1123            ReturnCode::try_from(-100)
1124        );
1125
1126        assert_eq!(Ok(ReturnCode::Success), ReturnCode::try_from(0));
1127        assert_eq!(
1128            Ok(ReturnCode::Error(ErrorCode::NotSupported)),
1129            ReturnCode::try_from(-1)
1130        );
1131
1132        assert_eq!(0, i32::from(ReturnCode::Success));
1133        assert_eq!(-1, i32::from(ReturnCode::Error(ErrorCode::NotSupported)));
1134    }
1135
1136    #[test]
1137    fn test_error_code() {
1138        assert_eq!(-1, i32::from(ErrorCode::NotSupported));
1139        assert_eq!(-2, i32::from(ErrorCode::InvalidParameters));
1140        assert_eq!(-3, i32::from(ErrorCode::Denied));
1141        assert_eq!(-4, i32::from(ErrorCode::AlreadyOn));
1142        assert_eq!(-5, i32::from(ErrorCode::OnPending));
1143        assert_eq!(-6, i32::from(ErrorCode::InternalFailure));
1144        assert_eq!(-7, i32::from(ErrorCode::NotPresent));
1145        assert_eq!(-8, i32::from(ErrorCode::Disabled));
1146        assert_eq!(-9, i32::from(ErrorCode::InvalidAddress));
1147
1148        assert_eq!(
1149            Err(Error::UnrecognisedErrorCode(0)),
1150            ErrorCode::try_from(0u32)
1151        );
1152
1153        assert_eq!(
1154            Ok(ErrorCode::NotSupported),
1155            ErrorCode::try_from(0xffff_ffffu32)
1156        );
1157
1158        assert_eq!(
1159            Err(Error::UnrecognisedErrorCode(0)),
1160            ErrorCode::try_from(0u64)
1161        );
1162
1163        assert_eq!(
1164            Err(Error::OverflowingErrorCode(0x7fff_ffff_ffff_ffff)),
1165            ErrorCode::try_from(0x7fff_ffff_ffff_ffffu64)
1166        );
1167
1168        assert_eq!(
1169            Ok(ErrorCode::NotSupported),
1170            ErrorCode::try_from(0xffff_ffff_ffff_ffffu64)
1171        );
1172
1173        assert_eq!(0xffff_fffe, u32::from(ErrorCode::InvalidParameters));
1174        assert_eq!(
1175            0xffff_ffff_ffff_fffe,
1176            u64::from(ErrorCode::InvalidParameters)
1177        );
1178    }
1179
1180    #[test]
1181    fn test_version() {
1182        assert_eq!(
1183            Err(Error::InvalidVersion(0xffff_ffff)),
1184            Version::try_from(0xffff_ffff)
1185        );
1186
1187        assert_eq!(
1188            Ok(Version {
1189                major: 0x7123,
1190                minor: 0x4567
1191            }),
1192            Version::try_from(0x7123_4567)
1193        );
1194
1195        assert_eq!(
1196            0x789a_bcde,
1197            u32::from(Version {
1198                major: 0x789a,
1199                minor: 0xbcde
1200            })
1201        );
1202    }
1203
1204    #[test]
1205    fn test_power_state() {
1206        assert_eq!(
1207            Err(Error::InvalidPowerState(0x8000_0000)),
1208            PowerState::try_from(0x8000_0000)
1209        );
1210
1211        assert_eq!(
1212            Err(Error::InvalidPowerState(0x3000_0000)),
1213            PowerState::try_from(0x3000_0000)
1214        );
1215
1216        assert_eq!(
1217            Ok(PowerState::StandbyOrRetention(0xfff_ffff),),
1218            PowerState::try_from(0x0fff_ffff)
1219        );
1220
1221        assert_eq!(
1222            Ok(PowerState::PowerDown(0x123_ffff)),
1223            PowerState::try_from(0x4123_ffff)
1224        );
1225
1226        assert_eq!(
1227            0x0123_4567,
1228            u32::from(PowerState::StandbyOrRetention(0x123_4567))
1229        );
1230
1231        assert_eq!(0x4123_4567, u32::from(PowerState::PowerDown(0x123_4567)));
1232    }
1233
1234    #[test]
1235    fn test_entry_point() {
1236        let entry = EntryPoint::Entry32 {
1237            entry_point_address: 0x1234_5678,
1238            context_id: 0x9abc_def0,
1239        };
1240
1241        assert_eq!(0x1234_5678, entry.entry_point_address());
1242        assert_eq!(0x9abc_def0, entry.context_id());
1243
1244        let entry = EntryPoint::Entry64 {
1245            entry_point_address: 0x1234_5678_9abc_def0,
1246            context_id: 0x9abc_def0_1234_5678,
1247        };
1248
1249        assert_eq!(0x1234_5678_9abc_def0, entry.entry_point_address());
1250        assert_eq!(0x9abc_def0_1234_5678, entry.context_id());
1251    }
1252
1253    #[test]
1254    fn test_mpidr() {
1255        assert_eq!(
1256            Err(Error::InvalidMpidr32(0xff00_0000)),
1257            Mpidr::try_from(0xff00_0000u32)
1258        );
1259
1260        assert_eq!(
1261            Ok(Mpidr::from_aff210(0x11, 0x22, 0x33)),
1262            Mpidr::try_from(0x0011_2233u32)
1263        );
1264
1265        assert_eq!(
1266            Err(Error::InvalidMpidr64(0xff00_0000_0000_0000)),
1267            Mpidr::try_from(0xff00_0000_0000_0000u64)
1268        );
1269
1270        assert_eq!(
1271            Ok(Mpidr::from_aff3210(0x44, 0x11, 0x22, 0x33)),
1272            Mpidr::try_from(0x0000_0044_0011_2233u64)
1273        );
1274
1275        assert_eq!(
1276            Ok(0x0011_2233u32),
1277            Mpidr::from_aff210(0x11, 0x22, 0x33).try_into()
1278        );
1279
1280        assert_eq!(
1281            0x0000_0044_0011_2233u64,
1282            Mpidr::from_aff3210(0x44, 0x11, 0x22, 0x33).into()
1283        );
1284
1285        assert_eq!(
1286            Err(Error::IgnoredNonZeroAff3(0x44)),
1287            u32::try_from(Mpidr::from_aff3210(0x44, 0x11, 0x22, 0x33))
1288        );
1289
1290        assert_eq!(
1291            0x0011_2233u64,
1292            u64::from(Mpidr::from_aff210(0x11, 0x22, 0x33))
1293        );
1294
1295        assert_eq!(
1296            Mpidr::from_aff3210(0x44, 0x11, 0x22, 0x33),
1297            Mpidr::from_register_value(0x0000_0044_4111_2233u64)
1298        );
1299    }
1300
1301    #[test]
1302    fn test_affinity_info_value() {
1303        assert_eq!(0, u32::from(AffinityInfo::On));
1304        assert_eq!(1, u32::from(AffinityInfo::Off));
1305        assert_eq!(2, u32::from(AffinityInfo::OnPending));
1306    }
1307
1308    #[test]
1309    fn test_migration_info_type() {
1310        assert_eq!(0, u32::from(MigrateInfoType::MigrateCapable));
1311        assert_eq!(1, u32::from(MigrateInfoType::NotMigrateCapable));
1312        assert_eq!(2, u32::from(MigrateInfoType::MigrationNotRequired));
1313    }
1314
1315    #[test]
1316    fn test_reset_type() {
1317        assert_eq!(
1318            Err(Error::UnrecognisedSystemReset2Type(0x1234_5678)),
1319            ResetType::try_from(0x1234_5678)
1320        );
1321
1322        assert_eq!(
1323            Ok(ResetType::Architectural(
1324                ArchitecturalResetType::SystemWarmReset
1325            )),
1326            ResetType::try_from(0x0000_0000)
1327        );
1328
1329        assert_eq!(
1330            Ok(ResetType::VendorSpecific(0x0000_0001)),
1331            ResetType::try_from(0x8000_0001)
1332        );
1333
1334        assert_eq!(
1335            0x0000_0000u32,
1336            ResetType::Architectural(ArchitecturalResetType::SystemWarmReset).into()
1337        );
1338        assert_eq!(
1339            0x8000_0001u32,
1340            ResetType::VendorSpecific(0x0000_0001).into()
1341        );
1342    }
1343
1344    #[test]
1345    fn test_psci_feature() {
1346        assert_eq!(
1347            Err(Error::UnrecognisedFunctionId(0x1234_5678)),
1348            PsciFeature::try_from(0x1234_5678)
1349        );
1350
1351        assert_eq!(
1352            Ok(PsciFeature::SmcccVersion),
1353            PsciFeature::try_from(0x8000_0000)
1354        );
1355
1356        assert_eq!(
1357            Ok(PsciFeature::PsciFunction(FunctionId::PsciVersion)),
1358            PsciFeature::try_from(0x8400_0000)
1359        );
1360
1361        assert_eq!(0x8000_0000u32, PsciFeature::SmcccVersion.into());
1362        assert_eq!(
1363            0x8400_0000u32,
1364            PsciFeature::PsciFunction(FunctionId::PsciVersion).into()
1365        );
1366    }
1367
1368    #[test]
1369    fn test_feature_flags_suspend() {
1370        assert_eq!(
1371            Err(Error::UnrecognisedPsciFeaturesFlags(0x0000_0004)),
1372            FeatureFlagsCpuSuspend::try_from(0x0000_0004)
1373        );
1374
1375        assert_eq!(
1376            Ok(FeatureFlagsCpuSuspend::empty()),
1377            FeatureFlagsCpuSuspend::try_from(0x0000_0000)
1378        );
1379
1380        assert_eq!(
1381            Ok(FeatureFlagsCpuSuspend::OS_INITIATED_MODE),
1382            FeatureFlagsCpuSuspend::try_from(0x0000_0001)
1383        );
1384
1385        assert_eq!(
1386            Ok(FeatureFlagsCpuSuspend::EXTENDED_POWER_STATE),
1387            FeatureFlagsCpuSuspend::try_from(0x0000_0002)
1388        );
1389
1390        assert_eq!(
1391            Ok(FeatureFlagsCpuSuspend::OS_INITIATED_MODE
1392                | FeatureFlagsCpuSuspend::EXTENDED_POWER_STATE),
1393            FeatureFlagsCpuSuspend::try_from(0x0000_0003)
1394        );
1395
1396        assert_eq!(
1397            0x0000_0003,
1398            u32::from(
1399                FeatureFlagsCpuSuspend::OS_INITIATED_MODE
1400                    | FeatureFlagsCpuSuspend::EXTENDED_POWER_STATE
1401            )
1402        );
1403    }
1404
1405    #[test]
1406    fn test_feature_flags_system_off2() {
1407        assert_eq!(
1408            Err(Error::UnrecognisedPsciFeaturesFlags(0x0000_0002)),
1409            FeatureFlagsSystemOff2::try_from(0x0000_0002)
1410        );
1411
1412        assert_eq!(
1413            Ok(FeatureFlagsSystemOff2::empty()),
1414            FeatureFlagsSystemOff2::try_from(0x0000_0000)
1415        );
1416
1417        assert_eq!(
1418            Ok(FeatureFlagsSystemOff2::HIBERNATE_OFF),
1419            FeatureFlagsSystemOff2::try_from(0x0000_0001)
1420        );
1421
1422        assert_eq!(0x0000_0000u32, FeatureFlagsSystemOff2::empty().into());
1423
1424        assert_eq!(0x0000_0001u32, FeatureFlagsSystemOff2::HIBERNATE_OFF.into());
1425    }
1426
1427    #[test]
1428    fn test_hw_state() {
1429        assert_eq!(0, u32::from(HwState::On));
1430        assert_eq!(1, u32::from(HwState::Off));
1431        assert_eq!(2, u32::from(HwState::Standby));
1432    }
1433
1434    #[test]
1435    fn test_function_version() {
1436        validate_function!(
1437            [0x8400_0000, 0, 0, 0],
1438            Function::Version,
1439            FunctionId::PsciVersion
1440        );
1441    }
1442
1443    #[test]
1444    fn test_cpu_suspend() {
1445        validate_function!(
1446            [
1447                0x8400_0001,
1448                POWER_STATE_VALUE.into(),
1449                0xabcdef01,
1450                0x23456789
1451            ],
1452            Function::CpuSuspend {
1453                state: POWER_STATE,
1454                entry: EntryPoint::Entry32 {
1455                    entry_point_address: 0xabcdef01,
1456                    context_id: 0x23456789
1457                }
1458            },
1459            FunctionId::CpuSuspend32
1460        );
1461
1462        validate_function!(
1463            [
1464                0xc400_0001,
1465                POWER_STATE_VALUE.into(),
1466                0xabcdef01_23456789,
1467                0x23456789_abcdef01
1468            ],
1469            Function::CpuSuspend {
1470                state: POWER_STATE,
1471                entry: EntryPoint::Entry64 {
1472                    entry_point_address: 0xabcdef01_23456789,
1473                    context_id: 0x23456789_abcdef01
1474                }
1475            },
1476            FunctionId::CpuSuspend64
1477        );
1478    }
1479
1480    #[test]
1481    fn test_function_cpu_off() {
1482        validate_function!([0x8400_0002, 0, 0, 0], Function::CpuOff, FunctionId::CpuOff);
1483    }
1484
1485    #[test]
1486    fn test_function_cpu_on() {
1487        validate_function!(
1488            [0x8400_0003, MPIDR32_VALUE.into(), 0xabcdef01, 0x23456789],
1489            Function::CpuOn {
1490                target_cpu: MPIDR32,
1491                entry: EntryPoint::Entry32 {
1492                    entry_point_address: 0xabcdef01,
1493                    context_id: 0x23456789,
1494                },
1495            },
1496            FunctionId::CpuOn32
1497        );
1498
1499        validate_function!(
1500            [
1501                0xc400_0003,
1502                MPIDR64_VALUE,
1503                0x01234567_89abcdef,
1504                0x89abcdef_01234567,
1505            ],
1506            Function::CpuOn {
1507                target_cpu: MPIDR64,
1508                entry: EntryPoint::Entry64 {
1509                    entry_point_address: 0x01234567_89abcdef,
1510                    context_id: 0x89abcdef_01234567,
1511                },
1512            },
1513            FunctionId::CpuOn64
1514        );
1515    }
1516
1517    #[test]
1518    #[should_panic]
1519    fn test_function_cpu_on_mixed_32_64() {
1520        let mut regs = [0u64; 4];
1521        Function::CpuOn {
1522            target_cpu: MPIDR64,
1523            entry: EntryPoint::Entry32 {
1524                entry_point_address: 1,
1525                context_id: 2,
1526            },
1527        }
1528        .copy_to_array(&mut regs);
1529    }
1530
1531    #[test]
1532    fn test_function_affinity_info() {
1533        validate_function!(
1534            [0x8400_0004, MPIDR32_VALUE.into(), 2, 0],
1535            Function::AffinityInfo {
1536                mpidr: MPIDR32,
1537                lowest_affinity_level: 2,
1538            },
1539            FunctionId::AffinityInfo32
1540        );
1541
1542        validate_function!(
1543            [0xc400_0004, MPIDR64_VALUE, 2, 0],
1544            Function::AffinityInfo {
1545                mpidr: MPIDR64,
1546                lowest_affinity_level: 2,
1547            },
1548            FunctionId::AffinityInfo64
1549        );
1550
1551        assert_eq!(
1552            Err(Error::InvalidLowerAffinityLevel(3)),
1553            Function::try_from([0x8400_0004, MPIDR32_VALUE.into(), 3, 0])
1554        );
1555
1556        assert_eq!(
1557            Err(Error::InvalidLowerAffinityLevel(4)),
1558            Function::try_from([0xc400_0004, MPIDR64_VALUE, 4, 0])
1559        );
1560    }
1561
1562    #[test]
1563    fn test_function_migrate() {
1564        validate_function!(
1565            [0x8400_0005, MPIDR32_VALUE.into(), 0, 0],
1566            Function::Migrate {
1567                target_affinity: MPIDR32,
1568            },
1569            FunctionId::Migrate32
1570        );
1571
1572        validate_function!(
1573            [0xc400_0005, MPIDR64_VALUE, 0, 0],
1574            Function::Migrate {
1575                target_affinity: MPIDR64,
1576            },
1577            FunctionId::Migrate64
1578        );
1579    }
1580
1581    #[test]
1582    fn test_function_migrate_info_type() {
1583        validate_function!(
1584            [0x8400_0006, 0, 0, 0],
1585            Function::MigrateInfoType,
1586            FunctionId::MigrateInfoType
1587        );
1588    }
1589
1590    #[test]
1591    fn test_function_migrate_info_up_cpu() {
1592        validate_function!(
1593            [0x8400_0007, 0, 0, 0],
1594            Function::MigrateInfoUpCpu { is_32bit: true },
1595            FunctionId::MigrateInfoUpCpu32
1596        );
1597
1598        validate_function!(
1599            [0xc400_0007, 0, 0, 0],
1600            Function::MigrateInfoUpCpu { is_32bit: false },
1601            FunctionId::MigrateInfoUpCpu64
1602        );
1603    }
1604
1605    #[test]
1606    fn test_function_system_off() {
1607        validate_function!(
1608            [0x8400_0008, 0, 0, 0],
1609            Function::SystemOff,
1610            FunctionId::SystemOff
1611        );
1612    }
1613
1614    #[test]
1615    fn test_function_system_off2() {
1616        validate_function!(
1617            [0x8400_0015, 0x0000_0001, 0xabcdef01, 0],
1618            Function::SystemOff2 {
1619                off_type: SystemOff2Type::HibernateOff,
1620                cookie: Cookie::Cookie32(0xabcdef01),
1621            },
1622            FunctionId::SystemOff232
1623        );
1624
1625        validate_function!(
1626            [0xc400_0015, 0x0000_0001, 0xabcdef01_23456789, 0],
1627            Function::SystemOff2 {
1628                off_type: SystemOff2Type::HibernateOff,
1629                cookie: Cookie::Cookie64(0xabcdef01_23456789),
1630            },
1631            FunctionId::SystemOff264
1632        );
1633    }
1634
1635    #[test]
1636    fn test_function_system_reset() {
1637        validate_function!(
1638            [0x8400_0009, 0, 0, 0],
1639            Function::SystemReset,
1640            FunctionId::SystemReset
1641        );
1642    }
1643
1644    #[test]
1645    fn test_function_system_reset2() {
1646        validate_function!(
1647            [0x8400_0012, 0, 0xabcdef01, 0],
1648            Function::SystemReset2 {
1649                reset_type: ResetType::Architectural(ArchitecturalResetType::SystemWarmReset),
1650                cookie: Cookie::Cookie32(0xabcdef01),
1651            },
1652            FunctionId::SystemReset232
1653        );
1654
1655        validate_function!(
1656            [0xc400_0012, 0, 0xabcdef01_23456789, 0],
1657            Function::SystemReset2 {
1658                reset_type: ResetType::Architectural(ArchitecturalResetType::SystemWarmReset),
1659                cookie: Cookie::Cookie64(0xabcdef01_23456789),
1660            },
1661            FunctionId::SystemReset264
1662        );
1663    }
1664
1665    #[test]
1666    fn test_function_mem_protect() {
1667        validate_function!(
1668            [0x8400_0013, 0x0000_0001, 0, 0],
1669            Function::MemProtect { enabled: true },
1670            FunctionId::MemProtect
1671        );
1672
1673        validate_function!(
1674            [0x8400_0013, 0x0000_0000, 0, 0],
1675            Function::MemProtect { enabled: false },
1676            FunctionId::MemProtect
1677        );
1678    }
1679
1680    #[test]
1681    fn test_function_mem_protect_check_range() {
1682        validate_function!(
1683            [0x8400_0014, 0xabcdef01, 0x23456789, 0],
1684            Function::MemProtectCheckRange {
1685                range: MemProtectRange::Range32 {
1686                    base: 0xabcdef01,
1687                    length: 0x23456789,
1688                },
1689            },
1690            FunctionId::MemProtectCheckRange32
1691        );
1692
1693        validate_function!(
1694            [0xc400_0014, 0xabcdef01_23456789, 0x23456789_abcdef01, 0],
1695            Function::MemProtectCheckRange {
1696                range: MemProtectRange::Range64 {
1697                    base: 0xabcdef01_23456789,
1698                    length: 0x23456789_abcdef01,
1699                },
1700            },
1701            FunctionId::MemProtectCheckRange64
1702        );
1703    }
1704
1705    #[test]
1706    fn test_function_features() {
1707        validate_function!(
1708            [0x8400_000a, 0x8000_0000, 0, 0],
1709            Function::Features {
1710                psci_func_id: PsciFeature::SmcccVersion,
1711            },
1712            FunctionId::PsciFeatures
1713        );
1714
1715        validate_function!(
1716            [0x8400_000a, 0x8400_0001, 0, 0],
1717            Function::Features {
1718                psci_func_id: PsciFeature::PsciFunction(FunctionId::CpuSuspend32),
1719            },
1720            FunctionId::PsciFeatures
1721        );
1722    }
1723
1724    #[test]
1725    fn test_function_cpu_freeze() {
1726        validate_function!(
1727            [0x8400_000b, 0, 0, 0],
1728            Function::CpuFreeze,
1729            FunctionId::CpuFreeze
1730        );
1731    }
1732
1733    #[test]
1734    fn test_function_cpu_default_suspend() {
1735        validate_function!(
1736            [0x8400_000c, 0xabcdef01, 0x23456789, 0],
1737            Function::CpuDefaultSuspend {
1738                entry: EntryPoint::Entry32 {
1739                    entry_point_address: 0xabcdef01,
1740                    context_id: 0x23456789,
1741                },
1742            },
1743            FunctionId::CpuDefaultSuspend32
1744        );
1745
1746        validate_function!(
1747            [0xc400_000c, 0xabcdef01_23456789, 0x23456789_abcdef01, 0],
1748            Function::CpuDefaultSuspend {
1749                entry: EntryPoint::Entry64 {
1750                    entry_point_address: 0xabcdef01_23456789,
1751                    context_id: 0x23456789_abcdef01,
1752                },
1753            },
1754            FunctionId::CpuDefaultSuspend64
1755        );
1756    }
1757
1758    #[test]
1759    fn test_function_node_hw_state() {
1760        validate_function!(
1761            [0x8400_000d, MPIDR32_VALUE.into(), 0xabcdef01, 0],
1762            Function::NodeHwState {
1763                target_cpu: MPIDR32,
1764                power_level: 0xabcdef01,
1765            },
1766            FunctionId::NodeHwState32
1767        );
1768
1769        validate_function!(
1770            [0xc400_000d, MPIDR64_VALUE, 0xabcdef01, 0],
1771            Function::NodeHwState {
1772                target_cpu: MPIDR64,
1773                power_level: 0xabcdef01,
1774            },
1775            FunctionId::NodeHwState64
1776        );
1777    }
1778
1779    #[test]
1780    fn test_function_system_suspend() {
1781        validate_function!(
1782            [0x8400_000e, 0xabcdef01, 0x23456789, 0],
1783            Function::SystemSuspend {
1784                entry: EntryPoint::Entry32 {
1785                    entry_point_address: 0xabcdef01,
1786                    context_id: 0x23456789,
1787                },
1788            },
1789            FunctionId::SystemSuspend32
1790        );
1791
1792        validate_function!(
1793            [0xc400_000e, 0xabcdef01_23456789, 0x23456789_abcdef01, 0],
1794            Function::SystemSuspend {
1795                entry: EntryPoint::Entry64 {
1796                    entry_point_address: 0xabcdef01_23456789,
1797                    context_id: 0x23456789_abcdef01,
1798                },
1799            },
1800            FunctionId::SystemSuspend64
1801        );
1802    }
1803
1804    #[test]
1805    fn test_function_set_suspend_mode() {
1806        validate_function!(
1807            [0x8400_000f, 0x0000_0001, 0, 0],
1808            Function::SetSuspendMode {
1809                mode: SuspendMode::OsInitiated,
1810            },
1811            FunctionId::PsciSetSuspendMode
1812        );
1813    }
1814
1815    #[test]
1816    fn test_function_stat_residency() {
1817        validate_function!(
1818            [
1819                0x8400_0010,
1820                MPIDR32_VALUE.into(),
1821                POWER_STATE_VALUE.into(),
1822                0,
1823            ],
1824            Function::StatResidency {
1825                target_cpu: MPIDR32,
1826                power_state: POWER_STATE,
1827            },
1828            FunctionId::PsciStatResidency32
1829        );
1830
1831        validate_function!(
1832            [0xc400_0010, MPIDR64_VALUE, POWER_STATE_VALUE.into(), 0],
1833            Function::StatResidency {
1834                target_cpu: MPIDR64,
1835                power_state: POWER_STATE,
1836            },
1837            FunctionId::PsciStatResidency64
1838        );
1839    }
1840
1841    #[test]
1842    fn test_function_stat_count() {
1843        validate_function!(
1844            [
1845                0x8400_0011,
1846                MPIDR32_VALUE.into(),
1847                POWER_STATE_VALUE.into(),
1848                0,
1849            ],
1850            Function::StatCount {
1851                target_cpu: MPIDR32,
1852                power_state: POWER_STATE,
1853            },
1854            FunctionId::PsciStatCount32
1855        );
1856
1857        validate_function!(
1858            [0xc400_0011, MPIDR64_VALUE, POWER_STATE_VALUE.into(), 0],
1859            Function::StatCount {
1860                target_cpu: MPIDR64,
1861                power_state: POWER_STATE,
1862            },
1863            FunctionId::PsciStatCount64
1864        );
1865    }
1866}