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