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