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