blob: e834c25723cae984af98faac86519555e1dda744 [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}