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