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