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