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