blob: 2695d8d37507d5be5477be9ae7b10095179a8875 [file] [log] [blame]
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001// SPDX-FileCopyrightText: Copyright 2023 Arm Limited and/or its affiliates <open-source-office@arm.com>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4#![cfg_attr(not(test), no_std)]
Balint Dobszaya5846852025-02-26 15:38:53 +01005#![deny(clippy::undocumented_unsafe_blocks)]
6#![deny(unsafe_op_in_unsafe_fn)]
7#![doc = include_str!("../README.md")]
Balint Dobszay5bf492f2024-07-29 17:21:32 +02008
Andrew Walbran19970ba2024-11-25 15:35:00 +00009use core::fmt::{self, Debug, Display, Formatter};
Andrew Walbran44029a02024-11-25 15:34:31 +000010use num_enum::{IntoPrimitive, TryFromPrimitive};
11use thiserror::Error;
Balint Dobszay5bf492f2024-07-29 17:21:32 +020012use uuid::Uuid;
13
14pub mod boot_info;
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +010015mod ffa_v1_1;
Balint Dobszayde0dc802025-02-28 14:16:52 +010016mod ffa_v1_2;
Balint Dobszay5bf492f2024-07-29 17:21:32 +020017pub mod memory_management;
18pub mod partition_info;
19
Balint Dobszaya5846852025-02-26 15:38:53 +010020/// Constant for 4K page size. On many occasions the FF-A spec defines memory size as count of 4K
21/// pages, regardless of the current translation granule.
Balint Dobszay3aad9572025-01-17 16:54:11 +010022pub const FFA_PAGE_SIZE_4K: usize = 4096;
23
Balint Dobszaya5846852025-02-26 15:38:53 +010024/// Rich error types returned by this module. Should be converted to [`crate::FfaError`] when used
25/// with the `FFA_ERROR` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +010026#[derive(Debug, Error)]
27pub enum Error {
28 #[error("Unrecognised FF-A function ID {0}")]
29 UnrecognisedFunctionId(u32),
30 #[error("Unrecognised FF-A feature ID {0}")]
31 UnrecognisedFeatureId(u8),
32 #[error("Unrecognised FF-A error code {0}")]
33 UnrecognisedErrorCode(i32),
Tomás González4d5b0ba2025-03-03 17:15:55 +000034 #[error("Unrecognised FF-A Framework Message {0}")]
35 UnrecognisedFwkMsg(u32),
Tomás González092202a2025-03-05 11:56:45 +000036 #[error("Invalid FF-A Msg Wait Flag {0}")]
37 InvalidMsgWaitFlag(u32),
Tomás González4d5b0ba2025-03-03 17:15:55 +000038 #[error("Unrecognised VM availability status {0}")]
39 UnrecognisedVmAvailabilityStatus(i32),
40 #[error("Unrecognised FF-A Warm Boot Type {0}")]
41 UnrecognisedWarmBootType(u32),
42 #[error("Invalid version {0}")]
43 InvalidVersion(u32),
Balint Dobszay3aad9572025-01-17 16:54:11 +010044}
45
46impl From<Error> for FfaError {
47 fn from(value: Error) -> Self {
48 match value {
49 Error::UnrecognisedFunctionId(_) | Error::UnrecognisedFeatureId(_) => {
50 Self::NotSupported
51 }
Tomás González4d5b0ba2025-03-03 17:15:55 +000052 Error::UnrecognisedErrorCode(_)
53 | Error::UnrecognisedFwkMsg(_)
54 | Error::InvalidVersion(_)
Tomás González092202a2025-03-05 11:56:45 +000055 | Error::InvalidMsgWaitFlag(_)
Tomás González4d5b0ba2025-03-03 17:15:55 +000056 | Error::UnrecognisedVmAvailabilityStatus(_)
57 | Error::UnrecognisedWarmBootType(_) => Self::InvalidParameters,
Balint Dobszay3aad9572025-01-17 16:54:11 +010058 }
59 }
60}
Balint Dobszay5bf492f2024-07-29 17:21:32 +020061
Balint Dobszaya5846852025-02-26 15:38:53 +010062/// An FF-A instance is a valid combination of two FF-A components at an exception level boundary.
Balint Dobszay5bf492f2024-07-29 17:21:32 +020063#[derive(PartialEq, Clone, Copy)]
64pub enum Instance {
Balint Dobszaya5846852025-02-26 15:38:53 +010065 /// The instance between the SPMC and SPMD.
Balint Dobszay5bf492f2024-07-29 17:21:32 +020066 SecurePhysical,
Balint Dobszaya5846852025-02-26 15:38:53 +010067 /// The instance between the SPMC and a physical SP (contains the SP's endpoint ID).
Balint Dobszay5bf492f2024-07-29 17:21:32 +020068 SecureVirtual(u16),
69}
70
Balint Dobszaya5846852025-02-26 15:38:53 +010071/// Function IDs of the various FF-A interfaces.
Andrew Walbran969b9252024-11-25 15:35:42 +000072#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
Balint Dobszay3aad9572025-01-17 16:54:11 +010073#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedFunctionId))]
Balint Dobszay5bf492f2024-07-29 17:21:32 +020074#[repr(u32)]
75pub enum FuncId {
76 Error = 0x84000060,
77 Success32 = 0x84000061,
78 Success64 = 0xc4000061,
79 Interrupt = 0x84000062,
80 Version = 0x84000063,
81 Features = 0x84000064,
82 RxAcquire = 0x84000084,
83 RxRelease = 0x84000065,
84 RxTxMap32 = 0x84000066,
85 RxTxMap64 = 0xc4000066,
86 RxTxUnmap = 0x84000067,
87 PartitionInfoGet = 0x84000068,
Balint Dobszaye6aa4862025-02-28 16:37:56 +010088 PartitionInfoGetRegs = 0xc400008b,
Balint Dobszay5bf492f2024-07-29 17:21:32 +020089 IdGet = 0x84000069,
90 SpmIdGet = 0x84000085,
Balint Dobszaye6aa4862025-02-28 16:37:56 +010091 ConsoleLog32 = 0x8400008a,
92 ConsoleLog64 = 0xc400008a,
Balint Dobszay5bf492f2024-07-29 17:21:32 +020093 MsgWait = 0x8400006b,
94 Yield = 0x8400006c,
95 Run = 0x8400006d,
96 NormalWorldResume = 0x8400007c,
97 MsgSend2 = 0x84000086,
98 MsgSendDirectReq32 = 0x8400006f,
99 MsgSendDirectReq64 = 0xc400006f,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100100 MsgSendDirectReq64_2 = 0xc400008d,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200101 MsgSendDirectResp32 = 0x84000070,
102 MsgSendDirectResp64 = 0xc4000070,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100103 MsgSendDirectResp64_2 = 0xc400008e,
Balint Dobszaye6aa4862025-02-28 16:37:56 +0100104 NotificationBitmapCreate = 0x8400007d,
105 NotificationBitmapDestroy = 0x8400007e,
106 NotificationBind = 0x8400007f,
107 NotificationUnbind = 0x84000080,
108 NotificationSet = 0x84000081,
109 NotificationGet = 0x84000082,
110 NotificationInfoGet32 = 0x84000083,
111 NotificationInfoGet64 = 0xc4000083,
112 El3IntrHandle = 0x8400008c,
Tomás González17b92442025-03-10 16:45:04 +0000113 SecondaryEpRegister32 = 0x84000087,
114 SecondaryEpRegister64 = 0xc4000087,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200115 MemDonate32 = 0x84000071,
116 MemDonate64 = 0xc4000071,
117 MemLend32 = 0x84000072,
118 MemLend64 = 0xc4000072,
119 MemShare32 = 0x84000073,
120 MemShare64 = 0xc4000073,
121 MemRetrieveReq32 = 0x84000074,
122 MemRetrieveReq64 = 0xc4000074,
123 MemRetrieveResp = 0x84000075,
124 MemRelinquish = 0x84000076,
125 MemReclaim = 0x84000077,
126 MemPermGet32 = 0x84000088,
127 MemPermGet64 = 0xc4000088,
128 MemPermSet32 = 0x84000089,
129 MemPermSet64 = 0xc4000089,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200130}
131
Balint Dobszayde0dc802025-02-28 14:16:52 +0100132impl FuncId {
133 /// Returns true if this is a 32-bit call, or false if it is a 64-bit call.
134 pub fn is_32bit(&self) -> bool {
135 u32::from(*self) & (1 << 30) != 0
136 }
137}
138
Balint Dobszaya5846852025-02-26 15:38:53 +0100139/// Error status codes used by the `FFA_ERROR` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100140#[derive(Clone, Copy, Debug, Eq, Error, IntoPrimitive, PartialEq, TryFromPrimitive)]
141#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedErrorCode))]
142#[repr(i32)]
143pub enum FfaError {
144 #[error("Not supported")]
145 NotSupported = -1,
146 #[error("Invalid parameters")]
147 InvalidParameters = -2,
148 #[error("No memory")]
149 NoMemory = -3,
150 #[error("Busy")]
151 Busy = -4,
152 #[error("Interrupted")]
153 Interrupted = -5,
154 #[error("Denied")]
155 Denied = -6,
156 #[error("Retry")]
157 Retry = -7,
158 #[error("Aborted")]
159 Aborted = -8,
160 #[error("No data")]
161 NoData = -9,
162}
163
Balint Dobszaya5846852025-02-26 15:38:53 +0100164/// Endpoint ID and vCPU ID pair, used by `FFA_ERROR`, `FFA_INTERRUPT` and `FFA_RUN` interfaces.
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200165#[derive(Debug, Eq, PartialEq, Clone, Copy)]
Balint Dobszay3aad9572025-01-17 16:54:11 +0100166pub struct TargetInfo {
167 pub endpoint_id: u16,
168 pub vcpu_id: u16,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200169}
170
Balint Dobszay3aad9572025-01-17 16:54:11 +0100171impl From<u32> for TargetInfo {
172 fn from(value: u32) -> Self {
173 Self {
174 endpoint_id: (value >> 16) as u16,
175 vcpu_id: value as u16,
176 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200177 }
178}
179
Balint Dobszay3aad9572025-01-17 16:54:11 +0100180impl From<TargetInfo> for u32 {
181 fn from(value: TargetInfo) -> Self {
Balint Dobszaye9a3e762025-02-26 17:29:57 +0100182 ((value.endpoint_id as u32) << 16) | value.vcpu_id as u32
Andrew Walbran0d315812024-11-25 15:36:36 +0000183 }
Balint Dobszay3aad9572025-01-17 16:54:11 +0100184}
Andrew Walbran0d315812024-11-25 15:36:36 +0000185
Balint Dobszaya5846852025-02-26 15:38:53 +0100186/// Arguments for the `FFA_SUCCESS` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100187#[derive(Debug, Eq, PartialEq, Clone, Copy)]
188pub enum SuccessArgs {
189 Result32([u32; 6]),
190 Result64([u64; 6]),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100191 Result64_2([u64; 16]),
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200192}
193
Tomás González17b92442025-03-10 16:45:04 +0000194/// Entrypoint address argument for `FFA_SECONDARY_EP_REGISTER` interface.
195#[derive(Debug, Eq, PartialEq, Clone, Copy)]
196pub enum SecondaryEpRegisterAddr {
197 Addr32(u32),
198 Addr64(u64),
199}
200
Balint Dobszaya5846852025-02-26 15:38:53 +0100201/// Version number of the FF-A implementation, `.0` is the major, `.1` is minor the version.
Balint Dobszayde0dc802025-02-28 14:16:52 +0100202#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord)]
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200203pub struct Version(pub u16, pub u16);
204
Tomás González1f794352025-03-03 16:47:06 +0000205impl Version {
Tomás González83146af2025-03-04 11:32:41 +0000206 // The FF-A spec mandates that bit[31] of a version number must be 0
207 const MBZ_BITS: u32 = 1 << 31;
208
Tomás González1f794352025-03-03 16:47:06 +0000209 /// Returns whether the caller's version (self) is compatible with the callee's version (input
210 /// parameter)
211 pub fn is_compatible_to(&self, callee_version: &Version) -> bool {
212 self.0 == callee_version.0 && self.1 <= callee_version.1
213 }
214}
215
Tomás González83146af2025-03-04 11:32:41 +0000216impl TryFrom<u32> for Version {
217 type Error = Error;
218
219 fn try_from(val: u32) -> Result<Self, Self::Error> {
220 if (val & Self::MBZ_BITS) != 0 {
221 Err(Error::InvalidVersion(val))
222 } else {
223 Ok(Self((val >> 16) as u16, val as u16))
224 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200225 }
226}
227
228impl From<Version> for u32 {
229 fn from(v: Version) -> Self {
Tomás González83146af2025-03-04 11:32:41 +0000230 let v_u32 = ((v.0 as u32) << 16) | v.1 as u32;
231 assert!(v_u32 & Version::MBZ_BITS == 0);
232 v_u32
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200233 }
234}
235
Andrew Walbran19970ba2024-11-25 15:35:00 +0000236impl Display for Version {
237 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
238 write!(f, "{}.{}", self.0, self.1)
239 }
240}
241
242impl Debug for Version {
243 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
244 Display::fmt(self, f)
245 }
246}
247
Balint Dobszaya5846852025-02-26 15:38:53 +0100248/// Feature IDs used by the `FFA_FEATURES` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100249#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
250#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedFeatureId))]
251#[repr(u8)]
252pub enum FeatureId {
253 NotificationPendingInterrupt = 0x1,
254 ScheduleReceiverInterrupt = 0x2,
255 ManagedExitInterrupt = 0x3,
256}
Balint Dobszayc8802492025-01-15 18:11:39 +0100257
Balint Dobszaya5846852025-02-26 15:38:53 +0100258/// Arguments for the `FFA_FEATURES` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100259#[derive(Debug, Eq, PartialEq, Clone, Copy)]
260pub enum Feature {
261 FuncId(FuncId),
262 FeatureId(FeatureId),
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100263 Unknown(u32),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100264}
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200265
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100266impl From<u32> for Feature {
267 fn from(value: u32) -> Self {
268 // Bit[31] is set for all valid FF-A function IDs so we don't have to check it separately
269 if let Ok(func_id) = value.try_into() {
270 Self::FuncId(func_id)
271 } else if let Ok(feat_id) = (value as u8).try_into() {
272 Self::FeatureId(feat_id)
Balint Dobszay3aad9572025-01-17 16:54:11 +0100273 } else {
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100274 Self::Unknown(value)
275 }
Balint Dobszay3aad9572025-01-17 16:54:11 +0100276 }
277}
278
279impl From<Feature> for u32 {
280 fn from(value: Feature) -> Self {
281 match value {
282 Feature::FuncId(func_id) => (1 << 31) | func_id as u32,
283 Feature::FeatureId(feature_id) => feature_id as u32,
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100284 Feature::Unknown(id) => panic!("Unknown feature or function ID {:#x?}", id),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100285 }
286 }
287}
288
Balint Dobszaya5846852025-02-26 15:38:53 +0100289/// RXTX buffer descriptor, used by `FFA_RXTX_MAP`.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100290#[derive(Debug, Eq, PartialEq, Clone, Copy)]
291pub enum RxTxAddr {
292 Addr32 { rx: u32, tx: u32 },
293 Addr64 { rx: u64, tx: u64 },
294}
295
Tomás González4d5b0ba2025-03-03 17:15:55 +0000296/// Composite type for capturing success and error return codes for the VM availability messages.
297///
298/// Error codes are handled by the `FfaError` type. Having a separate type for errors helps using
299/// `Result<(), FfaError>`. If a single type would include both success and error values,
300/// then `Err(FfaError::Success)` would be incomprehensible.
301#[derive(Debug, Eq, PartialEq, Clone, Copy)]
302pub enum VmAvailabilityStatus {
303 Success,
304 Error(FfaError),
305}
306
307impl TryFrom<i32> for VmAvailabilityStatus {
308 type Error = Error;
309 fn try_from(value: i32) -> Result<Self, <Self as TryFrom<i32>>::Error> {
310 Ok(match value {
311 0 => Self::Success,
312 error_code => Self::Error(FfaError::try_from(error_code)?),
313 })
314 }
315}
316
317impl From<VmAvailabilityStatus> for i32 {
318 fn from(value: VmAvailabilityStatus) -> Self {
319 match value {
320 VmAvailabilityStatus::Success => 0,
321 VmAvailabilityStatus::Error(error_code) => error_code.into(),
322 }
323 }
324}
325
326/// Arguments for the Power Warm Boot `FFA_MSG_SEND_DIRECT_REQ` interface.
327#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
328#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedWarmBootType))]
329#[repr(u32)]
330pub enum WarmBootType {
331 ExitFromSuspend = 0,
332 ExitFromLowPower = 1,
333}
334
Balint Dobszaya5846852025-02-26 15:38:53 +0100335/// Arguments for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}` interfaces.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100336#[derive(Debug, Eq, PartialEq, Clone, Copy)]
337pub enum DirectMsgArgs {
338 Args32([u32; 5]),
339 Args64([u64; 5]),
Tomás González4d5b0ba2025-03-03 17:15:55 +0000340 /// Message for forwarding FFA_VERSION call from Normal world to the SPMC
341 VersionReq {
342 version: Version,
343 },
344 /// Response message to forwarded FFA_VERSION call from the Normal world
345 /// Contains the version returned by the SPMC or None
346 VersionResp {
347 version: Option<Version>,
348 },
349 /// Message for a power management operation initiated by a PSCI function
350 PowerPsciReq32 {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000351 // params[i]: Input parameter in w[i] in PSCI function invocation at EL3.
Tomás González67f92c72025-03-20 16:50:42 +0000352 // params[0]: Function ID.
353 params: [u32; 4],
Tomás González4d5b0ba2025-03-03 17:15:55 +0000354 },
355 /// Message for a power management operation initiated by a PSCI function
356 PowerPsciReq64 {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000357 // params[i]: Input parameter in x[i] in PSCI function invocation at EL3.
Tomás González67f92c72025-03-20 16:50:42 +0000358 // params[0]: Function ID.
359 params: [u64; 4],
Tomás González4d5b0ba2025-03-03 17:15:55 +0000360 },
361 /// Message for a warm boot
362 PowerWarmBootReq {
363 boot_type: WarmBootType,
364 },
365 /// Response message to indicate return status of the last power management request message
366 /// Return error code SUCCESS or DENIED as defined in PSCI spec. Caller is left to do the
367 /// parsing of the return status.
368 PowerPsciResp {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000369 psci_status: i32,
370 },
371 /// Message to signal creation of a VM
372 VmCreated {
373 // Globally unique Handle to identify a memory region that contains IMPLEMENTATION DEFINED
374 // information associated with the created VM.
375 // The invalid memory region handle must be specified by the Hypervisor if this field is not
376 // used.
377 handle: memory_management::Handle,
378 vm_id: u16,
379 },
380 /// Message to acknowledge creation of a VM
381 VmCreatedAck {
382 sp_status: VmAvailabilityStatus,
383 },
384 /// Message to signal destruction of a VM
385 VmDestructed {
386 // Globally unique Handle to identify a memory region that contains IMPLEMENTATION DEFINED
387 // information associated with the created VM.
388 // The invalid memory region handle must be specified by the Hypervisor if this field is not
389 // used.
390 handle: memory_management::Handle,
391 vm_id: u16,
392 },
393 /// Message to acknowledge destruction of a VM
394 VmDestructedAck {
395 sp_status: VmAvailabilityStatus,
396 },
397}
398
399impl DirectMsgArgs {
400 // Flags for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}` interfaces.
401
402 const FWK_MSG_BITS: u32 = 1 << 31;
403 const VERSION_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b1000;
404 const VERSION_RESP: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b1001;
405 const POWER_PSCI_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS;
406 const POWER_WARM_BOOT_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0001;
407 const POWER_PSCI_RESP: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0010;
408 const VM_CREATED: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0100;
409 const VM_CREATED_ACK: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0101;
410 const VM_DESTRUCTED: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0110;
411 const VM_DESTRUCTED_ACK: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0111;
Balint Dobszay3aad9572025-01-17 16:54:11 +0100412}
413
Balint Dobszayde0dc802025-02-28 14:16:52 +0100414/// Arguments for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}2` interfaces.
415#[derive(Debug, Eq, PartialEq, Clone, Copy)]
416pub struct DirectMsg2Args([u64; 14]);
417
Tomás González092202a2025-03-05 11:56:45 +0000418#[derive(Debug, Eq, PartialEq, Clone, Copy)]
419pub struct MsgWaitFlags {
420 retain_rx_buffer: bool,
421}
422
423impl MsgWaitFlags {
424 const RETAIN_RX_BUFFER: u32 = 0x01;
425 const MBZ_BITS: u32 = 0xfffe;
426}
427
428impl TryFrom<u32> for MsgWaitFlags {
429 type Error = Error;
430
431 fn try_from(val: u32) -> Result<Self, Self::Error> {
432 if (val & Self::MBZ_BITS) != 0 {
433 Err(Error::InvalidMsgWaitFlag(val))
434 } else {
435 Ok(MsgWaitFlags {
436 retain_rx_buffer: val & Self::RETAIN_RX_BUFFER != 0,
437 })
438 }
439 }
440}
441
442impl From<MsgWaitFlags> for u32 {
443 fn from(flags: MsgWaitFlags) -> Self {
444 let mut bits: u32 = 0;
445 if flags.retain_rx_buffer {
446 bits |= MsgWaitFlags::RETAIN_RX_BUFFER;
447 }
448 bits
449 }
450}
451
Balint Dobszaya5846852025-02-26 15:38:53 +0100452/// Descriptor for a dynamically allocated memory buffer that contains the memory transaction
Tomás Gonzálezf268e322025-03-05 11:18:11 +0000453/// descriptor.
454///
455/// Used by `FFA_MEM_{DONATE,LEND,SHARE,RETRIEVE_REQ}` interfaces, only when the TX buffer is not
456/// used to transmit the transaction descriptor.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100457#[derive(Debug, Eq, PartialEq, Clone, Copy)]
458pub enum MemOpBuf {
459 Buf32 { addr: u32, page_cnt: u32 },
460 Buf64 { addr: u64, page_cnt: u32 },
461}
462
Balint Dobszaya5846852025-02-26 15:38:53 +0100463/// Memory address argument for `FFA_MEM_PERM_{GET,SET}` interfaces.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100464#[derive(Debug, Eq, PartialEq, Clone, Copy)]
465pub enum MemAddr {
466 Addr32(u32),
467 Addr64(u64),
468}
469
Balint Dobszayde0dc802025-02-28 14:16:52 +0100470/// Argument for the `FFA_CONSOLE_LOG` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100471#[derive(Debug, Eq, PartialEq, Clone, Copy)]
472pub enum ConsoleLogChars {
473 Reg32([u32; 6]),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100474 Reg64([u64; 16]),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100475}
476
Tomás Gonzálezf268e322025-03-05 11:18:11 +0000477/// FF-A "message types", the terminology used by the spec is "interfaces".
478///
479/// The interfaces are used by FF-A components for communication at an FF-A instance. The spec also
480/// describes the valid FF-A instances and conduits for each interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100481#[derive(Debug, Eq, PartialEq, Clone, Copy)]
482pub enum Interface {
483 Error {
484 target_info: TargetInfo,
485 error_code: FfaError,
486 },
487 Success {
488 target_info: u32,
489 args: SuccessArgs,
490 },
491 Interrupt {
492 target_info: TargetInfo,
493 interrupt_id: u32,
494 },
495 Version {
496 input_version: Version,
497 },
498 VersionOut {
499 output_version: Version,
500 },
501 Features {
502 feat_id: Feature,
503 input_properties: u32,
504 },
505 RxAcquire {
506 vm_id: u16,
507 },
508 RxRelease {
509 vm_id: u16,
510 },
511 RxTxMap {
512 addr: RxTxAddr,
513 page_cnt: u32,
514 },
515 RxTxUnmap {
516 id: u16,
517 },
518 PartitionInfoGet {
519 uuid: Uuid,
520 flags: u32,
521 },
522 IdGet,
523 SpmIdGet,
Tomás González092202a2025-03-05 11:56:45 +0000524 MsgWait {
525 flags: Option<MsgWaitFlags>,
526 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100527 Yield,
528 Run {
529 target_info: TargetInfo,
530 },
531 NormalWorldResume,
Tomás González17b92442025-03-10 16:45:04 +0000532 SecondaryEpRegister {
533 entrypoint: SecondaryEpRegisterAddr,
534 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100535 MsgSend2 {
536 sender_vm_id: u16,
537 flags: u32,
538 },
539 MsgSendDirectReq {
540 src_id: u16,
541 dst_id: u16,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100542 args: DirectMsgArgs,
543 },
544 MsgSendDirectResp {
545 src_id: u16,
546 dst_id: u16,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100547 args: DirectMsgArgs,
548 },
Balint Dobszayde0dc802025-02-28 14:16:52 +0100549 MsgSendDirectReq2 {
550 src_id: u16,
551 dst_id: u16,
552 uuid: Uuid,
553 args: DirectMsg2Args,
554 },
555 MsgSendDirectResp2 {
556 src_id: u16,
557 dst_id: u16,
558 args: DirectMsg2Args,
559 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100560 MemDonate {
561 total_len: u32,
562 frag_len: u32,
563 buf: Option<MemOpBuf>,
564 },
565 MemLend {
566 total_len: u32,
567 frag_len: u32,
568 buf: Option<MemOpBuf>,
569 },
570 MemShare {
571 total_len: u32,
572 frag_len: u32,
573 buf: Option<MemOpBuf>,
574 },
575 MemRetrieveReq {
576 total_len: u32,
577 frag_len: u32,
578 buf: Option<MemOpBuf>,
579 },
580 MemRetrieveResp {
581 total_len: u32,
582 frag_len: u32,
583 },
584 MemRelinquish,
585 MemReclaim {
586 handle: memory_management::Handle,
587 flags: u32,
588 },
589 MemPermGet {
590 addr: MemAddr,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100591 page_cnt: Option<u32>,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100592 },
593 MemPermSet {
594 addr: MemAddr,
595 page_cnt: u32,
596 mem_perm: u32,
597 },
598 ConsoleLog {
599 char_cnt: u8,
600 char_lists: ConsoleLogChars,
601 },
602}
603
Balint Dobszayde0dc802025-02-28 14:16:52 +0100604impl Interface {
605 /// Returns the function ID for the call, if it has one.
606 pub fn function_id(&self) -> Option<FuncId> {
607 match self {
608 Interface::Error { .. } => Some(FuncId::Error),
609 Interface::Success { args, .. } => match args {
610 SuccessArgs::Result32(..) => Some(FuncId::Success32),
611 SuccessArgs::Result64(..) | SuccessArgs::Result64_2(..) => Some(FuncId::Success64),
612 },
613 Interface::Interrupt { .. } => Some(FuncId::Interrupt),
614 Interface::Version { .. } => Some(FuncId::Version),
615 Interface::VersionOut { .. } => None,
616 Interface::Features { .. } => Some(FuncId::Features),
617 Interface::RxAcquire { .. } => Some(FuncId::RxAcquire),
618 Interface::RxRelease { .. } => Some(FuncId::RxRelease),
619 Interface::RxTxMap { addr, .. } => match addr {
620 RxTxAddr::Addr32 { .. } => Some(FuncId::RxTxMap32),
621 RxTxAddr::Addr64 { .. } => Some(FuncId::RxTxMap64),
622 },
623 Interface::RxTxUnmap { .. } => Some(FuncId::RxTxUnmap),
624 Interface::PartitionInfoGet { .. } => Some(FuncId::PartitionInfoGet),
625 Interface::IdGet => Some(FuncId::IdGet),
626 Interface::SpmIdGet => Some(FuncId::SpmIdGet),
Tomás González092202a2025-03-05 11:56:45 +0000627 Interface::MsgWait { .. } => Some(FuncId::MsgWait),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100628 Interface::Yield => Some(FuncId::Yield),
629 Interface::Run { .. } => Some(FuncId::Run),
630 Interface::NormalWorldResume => Some(FuncId::NormalWorldResume),
Tomás González17b92442025-03-10 16:45:04 +0000631 Interface::SecondaryEpRegister { entrypoint } => match entrypoint {
632 SecondaryEpRegisterAddr::Addr32 { .. } => Some(FuncId::SecondaryEpRegister32),
633 SecondaryEpRegisterAddr::Addr64 { .. } => Some(FuncId::SecondaryEpRegister64),
634 },
Balint Dobszayde0dc802025-02-28 14:16:52 +0100635 Interface::MsgSend2 { .. } => Some(FuncId::MsgSend2),
636 Interface::MsgSendDirectReq { args, .. } => match args {
637 DirectMsgArgs::Args32(_) => Some(FuncId::MsgSendDirectReq32),
638 DirectMsgArgs::Args64(_) => Some(FuncId::MsgSendDirectReq64),
Tomás González4d5b0ba2025-03-03 17:15:55 +0000639 DirectMsgArgs::VersionReq { .. } => Some(FuncId::MsgSendDirectReq32),
640 DirectMsgArgs::PowerPsciReq32 { .. } => Some(FuncId::MsgSendDirectReq32),
641 DirectMsgArgs::PowerPsciReq64 { .. } => Some(FuncId::MsgSendDirectReq64),
642 DirectMsgArgs::PowerWarmBootReq { .. } => Some(FuncId::MsgSendDirectReq32),
643 DirectMsgArgs::VmCreated { .. } => Some(FuncId::MsgSendDirectReq32),
644 DirectMsgArgs::VmDestructed { .. } => Some(FuncId::MsgSendDirectReq32),
645 _ => None,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100646 },
647 Interface::MsgSendDirectResp { args, .. } => match args {
648 DirectMsgArgs::Args32(_) => Some(FuncId::MsgSendDirectResp32),
649 DirectMsgArgs::Args64(_) => Some(FuncId::MsgSendDirectResp64),
Tomás González4d5b0ba2025-03-03 17:15:55 +0000650 DirectMsgArgs::VersionResp { .. } => Some(FuncId::MsgSendDirectResp32),
651 DirectMsgArgs::PowerPsciResp { .. } => Some(FuncId::MsgSendDirectResp32),
652 DirectMsgArgs::VmCreatedAck { .. } => Some(FuncId::MsgSendDirectResp32),
653 DirectMsgArgs::VmDestructedAck { .. } => Some(FuncId::MsgSendDirectResp32),
654 _ => None,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100655 },
656 Interface::MsgSendDirectReq2 { .. } => Some(FuncId::MsgSendDirectReq64_2),
657 Interface::MsgSendDirectResp2 { .. } => Some(FuncId::MsgSendDirectResp64_2),
658 Interface::MemDonate { buf, .. } => match buf {
659 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemDonate64),
660 _ => Some(FuncId::MemDonate32),
661 },
662 Interface::MemLend { buf, .. } => match buf {
663 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemLend64),
664 _ => Some(FuncId::MemLend32),
665 },
666 Interface::MemShare { buf, .. } => match buf {
667 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemShare64),
668 _ => Some(FuncId::MemShare32),
669 },
670 Interface::MemRetrieveReq { buf, .. } => match buf {
671 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemRetrieveReq64),
672 _ => Some(FuncId::MemRetrieveReq32),
673 },
674 Interface::MemRetrieveResp { .. } => Some(FuncId::MemRetrieveResp),
675 Interface::MemRelinquish => Some(FuncId::MemRelinquish),
676 Interface::MemReclaim { .. } => Some(FuncId::MemReclaim),
677 Interface::MemPermGet { addr, .. } => match addr {
678 MemAddr::Addr32(_) => Some(FuncId::MemPermGet32),
679 MemAddr::Addr64(_) => Some(FuncId::MemPermGet64),
680 },
681 Interface::MemPermSet { addr, .. } => match addr {
682 MemAddr::Addr32(_) => Some(FuncId::MemPermSet32),
683 MemAddr::Addr64(_) => Some(FuncId::MemPermSet64),
684 },
685 Interface::ConsoleLog { char_lists, .. } => match char_lists {
686 ConsoleLogChars::Reg32(_) => Some(FuncId::ConsoleLog32),
687 ConsoleLogChars::Reg64(_) => Some(FuncId::ConsoleLog64),
688 },
689 }
690 }
Balint Dobszay3aad9572025-01-17 16:54:11 +0100691
Balint Dobszayde0dc802025-02-28 14:16:52 +0100692 /// Returns true if this is a 32-bit call, or false if it is a 64-bit call.
693 pub fn is_32bit(&self) -> bool {
694 // TODO: self should always have a function ID?
695 self.function_id().unwrap().is_32bit()
696 }
697
698 /// Parse interface from register contents. The caller must ensure that the `regs` argument has
699 /// the correct length: 8 registers for FF-A v1.1 and lower, 18 registers for v1.2 and higher.
700 pub fn from_regs(version: Version, regs: &[u64]) -> Result<Self, Error> {
701 let reg_cnt = regs.len();
702
703 let msg = match reg_cnt {
704 8 => {
705 assert!(version <= Version(1, 1));
706 Interface::unpack_regs8(version, regs.try_into().unwrap())?
707 }
708 18 => {
709 assert!(version >= Version(1, 2));
710 match FuncId::try_from(regs[0] as u32)? {
711 FuncId::ConsoleLog64
712 | FuncId::Success64
713 | FuncId::MsgSendDirectReq64_2
714 | FuncId::MsgSendDirectResp64_2 => {
715 Interface::unpack_regs18(version, regs.try_into().unwrap())?
716 }
717 _ => Interface::unpack_regs8(version, regs[..8].try_into().unwrap())?,
718 }
719 }
720 _ => panic!(
721 "Invalid number of registers ({}) for FF-A version {}",
722 reg_cnt, version
723 ),
724 };
725
726 Ok(msg)
727 }
728
729 fn unpack_regs8(version: Version, regs: &[u64; 8]) -> Result<Self, Error> {
Balint Dobszay3aad9572025-01-17 16:54:11 +0100730 let fid = FuncId::try_from(regs[0] as u32)?;
731
732 let msg = match fid {
733 FuncId::Error => Self::Error {
734 target_info: (regs[1] as u32).into(),
735 error_code: FfaError::try_from(regs[2] as i32)?,
736 },
737 FuncId::Success32 => Self::Success {
738 target_info: regs[1] as u32,
739 args: SuccessArgs::Result32([
740 regs[2] as u32,
741 regs[3] as u32,
742 regs[4] as u32,
743 regs[5] as u32,
744 regs[6] as u32,
745 regs[7] as u32,
746 ]),
747 },
748 FuncId::Success64 => Self::Success {
749 target_info: regs[1] as u32,
750 args: SuccessArgs::Result64([regs[2], regs[3], regs[4], regs[5], regs[6], regs[7]]),
751 },
752 FuncId::Interrupt => Self::Interrupt {
753 target_info: (regs[1] as u32).into(),
754 interrupt_id: regs[2] as u32,
755 },
756 FuncId::Version => Self::Version {
Tomás González83146af2025-03-04 11:32:41 +0000757 input_version: (regs[1] as u32).try_into()?,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100758 },
759 FuncId::Features => Self::Features {
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100760 feat_id: (regs[1] as u32).into(),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100761 input_properties: regs[2] as u32,
762 },
763 FuncId::RxAcquire => Self::RxAcquire {
764 vm_id: regs[1] as u16,
765 },
766 FuncId::RxRelease => Self::RxRelease {
767 vm_id: regs[1] as u16,
768 },
769 FuncId::RxTxMap32 => {
770 let addr = RxTxAddr::Addr32 {
771 rx: regs[2] as u32,
772 tx: regs[1] as u32,
773 };
774 let page_cnt = regs[3] as u32;
775
776 Self::RxTxMap { addr, page_cnt }
777 }
778 FuncId::RxTxMap64 => {
779 let addr = RxTxAddr::Addr64 {
780 rx: regs[2],
781 tx: regs[1],
782 };
783 let page_cnt = regs[3] as u32;
784
785 Self::RxTxMap { addr, page_cnt }
786 }
787 FuncId::RxTxUnmap => Self::RxTxUnmap { id: regs[1] as u16 },
788 FuncId::PartitionInfoGet => {
789 let uuid_words = [
790 regs[1] as u32,
791 regs[2] as u32,
792 regs[3] as u32,
793 regs[4] as u32,
794 ];
795 let mut bytes: [u8; 16] = [0; 16];
796 for (i, b) in uuid_words.iter().flat_map(|w| w.to_le_bytes()).enumerate() {
797 bytes[i] = b;
798 }
799 Self::PartitionInfoGet {
800 uuid: Uuid::from_bytes(bytes),
801 flags: regs[5] as u32,
802 }
803 }
804 FuncId::IdGet => Self::IdGet,
805 FuncId::SpmIdGet => Self::SpmIdGet,
Tomás González092202a2025-03-05 11:56:45 +0000806 FuncId::MsgWait => Self::MsgWait {
807 flags: if version >= Version(1, 2) {
808 Some(MsgWaitFlags::try_from(regs[2] as u32)?)
809 } else {
810 None
811 },
812 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100813 FuncId::Yield => Self::Yield,
814 FuncId::Run => Self::Run {
815 target_info: (regs[1] as u32).into(),
816 },
817 FuncId::NormalWorldResume => Self::NormalWorldResume,
Tomás González17b92442025-03-10 16:45:04 +0000818 FuncId::SecondaryEpRegister32 => Self::SecondaryEpRegister {
819 entrypoint: SecondaryEpRegisterAddr::Addr32(regs[1] as u32),
820 },
821 FuncId::SecondaryEpRegister64 => Self::SecondaryEpRegister {
822 entrypoint: SecondaryEpRegisterAddr::Addr64(regs[1]),
823 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100824 FuncId::MsgSend2 => Self::MsgSend2 {
825 sender_vm_id: regs[1] as u16,
826 flags: regs[2] as u32,
827 },
828 FuncId::MsgSendDirectReq32 => Self::MsgSendDirectReq {
829 src_id: (regs[1] >> 16) as u16,
830 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +0000831 args: if (regs[2] as u32 & DirectMsgArgs::FWK_MSG_BITS) != 0 {
832 match regs[2] as u32 {
833 DirectMsgArgs::VERSION_REQ => DirectMsgArgs::VersionReq {
834 version: Version::try_from(regs[3] as u32)?,
835 },
836 DirectMsgArgs::POWER_PSCI_REQ => DirectMsgArgs::PowerPsciReq32 {
Tomás González67f92c72025-03-20 16:50:42 +0000837 params: [
838 regs[3] as u32,
839 regs[4] as u32,
840 regs[5] as u32,
841 regs[6] as u32,
842 ],
Tomás González4d5b0ba2025-03-03 17:15:55 +0000843 },
844 DirectMsgArgs::POWER_WARM_BOOT_REQ => DirectMsgArgs::PowerWarmBootReq {
845 boot_type: WarmBootType::try_from(regs[3] as u32)?,
846 },
847 DirectMsgArgs::VM_CREATED => DirectMsgArgs::VmCreated {
848 handle: memory_management::Handle::from([
849 regs[3] as u32,
850 regs[4] as u32,
851 ]),
852 vm_id: regs[5] as u16,
853 },
854 DirectMsgArgs::VM_DESTRUCTED => DirectMsgArgs::VmDestructed {
855 handle: memory_management::Handle::from([
856 regs[3] as u32,
857 regs[4] as u32,
858 ]),
859 vm_id: regs[5] as u16,
860 },
861 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
862 }
863 } else {
864 DirectMsgArgs::Args32([
865 regs[3] as u32,
866 regs[4] as u32,
867 regs[5] as u32,
868 regs[6] as u32,
869 regs[7] as u32,
870 ])
871 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100872 },
873 FuncId::MsgSendDirectReq64 => Self::MsgSendDirectReq {
874 src_id: (regs[1] >> 16) as u16,
875 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +0000876 args: if (regs[2] & DirectMsgArgs::FWK_MSG_BITS as u64) != 0 {
877 match regs[2] as u32 {
878 DirectMsgArgs::POWER_PSCI_REQ => DirectMsgArgs::PowerPsciReq64 {
Tomás González67f92c72025-03-20 16:50:42 +0000879 params: [regs[3], regs[4], regs[5], regs[6]],
Tomás González4d5b0ba2025-03-03 17:15:55 +0000880 },
881 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
882 }
883 } else {
884 DirectMsgArgs::Args64([regs[3], regs[4], regs[5], regs[6], regs[7]])
885 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100886 },
887 FuncId::MsgSendDirectResp32 => Self::MsgSendDirectResp {
888 src_id: (regs[1] >> 16) as u16,
889 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +0000890 args: if (regs[2] as u32 & DirectMsgArgs::FWK_MSG_BITS) != 0 {
891 match regs[2] as u32 {
892 DirectMsgArgs::VERSION_RESP => {
893 if regs[3] as i32 == FfaError::NotSupported.into() {
894 DirectMsgArgs::VersionResp { version: None }
895 } else {
896 DirectMsgArgs::VersionResp {
897 version: Some(Version::try_from(regs[3] as u32)?),
898 }
899 }
900 }
901 DirectMsgArgs::POWER_PSCI_RESP => DirectMsgArgs::PowerPsciResp {
902 psci_status: regs[3] as i32,
903 },
904 DirectMsgArgs::VM_CREATED_ACK => DirectMsgArgs::VmCreatedAck {
905 sp_status: (regs[3] as i32).try_into()?,
906 },
907 DirectMsgArgs::VM_DESTRUCTED_ACK => DirectMsgArgs::VmDestructedAck {
908 sp_status: (regs[3] as i32).try_into()?,
909 },
910 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
911 }
912 } else {
913 DirectMsgArgs::Args32([
914 regs[3] as u32,
915 regs[4] as u32,
916 regs[5] as u32,
917 regs[6] as u32,
918 regs[7] as u32,
919 ])
920 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100921 },
922 FuncId::MsgSendDirectResp64 => Self::MsgSendDirectResp {
923 src_id: (regs[1] >> 16) as u16,
924 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +0000925 args: if (regs[2] & DirectMsgArgs::FWK_MSG_BITS as u64) != 0 {
926 return Err(Error::UnrecognisedFwkMsg(regs[2] as u32));
927 } else {
928 DirectMsgArgs::Args64([regs[3], regs[4], regs[5], regs[6], regs[7]])
929 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100930 },
931 FuncId::MemDonate32 => Self::MemDonate {
932 total_len: regs[1] as u32,
933 frag_len: regs[2] as u32,
934 buf: if regs[3] != 0 && regs[4] != 0 {
935 Some(MemOpBuf::Buf32 {
936 addr: regs[3] as u32,
937 page_cnt: regs[4] as u32,
938 })
939 } else {
940 None
941 },
942 },
943 FuncId::MemDonate64 => Self::MemDonate {
944 total_len: regs[1] as u32,
945 frag_len: regs[2] as u32,
946 buf: if regs[3] != 0 && regs[4] != 0 {
947 Some(MemOpBuf::Buf64 {
948 addr: regs[3],
949 page_cnt: regs[4] as u32,
950 })
951 } else {
952 None
953 },
954 },
955 FuncId::MemLend32 => Self::MemLend {
956 total_len: regs[1] as u32,
957 frag_len: regs[2] as u32,
958 buf: if regs[3] != 0 && regs[4] != 0 {
959 Some(MemOpBuf::Buf32 {
960 addr: regs[3] as u32,
961 page_cnt: regs[4] as u32,
962 })
963 } else {
964 None
965 },
966 },
967 FuncId::MemLend64 => Self::MemLend {
968 total_len: regs[1] as u32,
969 frag_len: regs[2] as u32,
970 buf: if regs[3] != 0 && regs[4] != 0 {
971 Some(MemOpBuf::Buf64 {
972 addr: regs[3],
973 page_cnt: regs[4] as u32,
974 })
975 } else {
976 None
977 },
978 },
979 FuncId::MemShare32 => Self::MemShare {
980 total_len: regs[1] as u32,
981 frag_len: regs[2] as u32,
982 buf: if regs[3] != 0 && regs[4] != 0 {
983 Some(MemOpBuf::Buf32 {
984 addr: regs[3] as u32,
985 page_cnt: regs[4] as u32,
986 })
987 } else {
988 None
989 },
990 },
991 FuncId::MemShare64 => Self::MemShare {
992 total_len: regs[1] as u32,
993 frag_len: regs[2] as u32,
994 buf: if regs[3] != 0 && regs[4] != 0 {
995 Some(MemOpBuf::Buf64 {
996 addr: regs[3],
997 page_cnt: regs[4] as u32,
998 })
999 } else {
1000 None
1001 },
1002 },
1003 FuncId::MemRetrieveReq32 => Self::MemRetrieveReq {
1004 total_len: regs[1] as u32,
1005 frag_len: regs[2] as u32,
1006 buf: if regs[3] != 0 && regs[4] != 0 {
1007 Some(MemOpBuf::Buf32 {
1008 addr: regs[3] as u32,
1009 page_cnt: regs[4] as u32,
1010 })
1011 } else {
1012 None
1013 },
1014 },
1015 FuncId::MemRetrieveReq64 => Self::MemRetrieveReq {
1016 total_len: regs[1] as u32,
1017 frag_len: regs[2] as u32,
1018 buf: if regs[3] != 0 && regs[4] != 0 {
1019 Some(MemOpBuf::Buf64 {
1020 addr: regs[3],
1021 page_cnt: regs[4] as u32,
1022 })
1023 } else {
1024 None
1025 },
1026 },
1027 FuncId::MemRetrieveResp => Self::MemRetrieveResp {
1028 total_len: regs[1] as u32,
1029 frag_len: regs[2] as u32,
1030 },
1031 FuncId::MemRelinquish => Self::MemRelinquish,
1032 FuncId::MemReclaim => Self::MemReclaim {
1033 handle: memory_management::Handle::from([regs[1] as u32, regs[2] as u32]),
1034 flags: regs[3] as u32,
1035 },
1036 FuncId::MemPermGet32 => Self::MemPermGet {
1037 addr: MemAddr::Addr32(regs[1] as u32),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001038 page_cnt: if version >= Version(1, 3) {
1039 Some(regs[2] as u32)
1040 } else {
1041 None
1042 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001043 },
1044 FuncId::MemPermGet64 => Self::MemPermGet {
1045 addr: MemAddr::Addr64(regs[1]),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001046 page_cnt: if version >= Version(1, 3) {
1047 Some(regs[2] as u32)
1048 } else {
1049 None
1050 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001051 },
1052 FuncId::MemPermSet32 => Self::MemPermSet {
1053 addr: MemAddr::Addr32(regs[1] as u32),
1054 page_cnt: regs[2] as u32,
1055 mem_perm: regs[3] as u32,
1056 },
1057 FuncId::MemPermSet64 => Self::MemPermSet {
1058 addr: MemAddr::Addr64(regs[1]),
1059 page_cnt: regs[2] as u32,
1060 mem_perm: regs[3] as u32,
1061 },
1062 FuncId::ConsoleLog32 => Self::ConsoleLog {
1063 char_cnt: regs[1] as u8,
1064 char_lists: ConsoleLogChars::Reg32([
1065 regs[2] as u32,
1066 regs[3] as u32,
1067 regs[4] as u32,
1068 regs[5] as u32,
1069 regs[6] as u32,
1070 regs[7] as u32,
1071 ]),
1072 },
Balint Dobszayde0dc802025-02-28 14:16:52 +01001073 _ => panic!("Invalid number of registers (8) for function {:#x?}", fid),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001074 };
1075
1076 Ok(msg)
1077 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01001078
Balint Dobszayde0dc802025-02-28 14:16:52 +01001079 fn unpack_regs18(version: Version, regs: &[u64; 18]) -> Result<Self, Error> {
1080 assert!(version >= Version(1, 2));
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001081
Balint Dobszayde0dc802025-02-28 14:16:52 +01001082 let fid = FuncId::try_from(regs[0] as u32)?;
1083
1084 let msg = match fid {
1085 FuncId::Success64 => Self::Success {
1086 target_info: regs[1] as u32,
1087 args: SuccessArgs::Result64_2(regs[2..18].try_into().unwrap()),
1088 },
1089 FuncId::MsgSendDirectReq64_2 => Self::MsgSendDirectReq2 {
1090 src_id: (regs[1] >> 16) as u16,
1091 dst_id: regs[1] as u16,
1092 uuid: Uuid::from_u64_pair(regs[2], regs[3]),
1093 args: DirectMsg2Args(regs[4..18].try_into().unwrap()),
1094 },
1095 FuncId::MsgSendDirectResp64_2 => Self::MsgSendDirectResp2 {
1096 src_id: (regs[1] >> 16) as u16,
1097 dst_id: regs[1] as u16,
1098 args: DirectMsg2Args(regs[4..18].try_into().unwrap()),
1099 },
1100 FuncId::ConsoleLog64 => Self::ConsoleLog {
1101 char_cnt: regs[1] as u8,
1102 char_lists: ConsoleLogChars::Reg64(regs[2..18].try_into().unwrap()),
1103 },
1104 _ => panic!("Invalid number of registers (18) for function {:#x?}", fid),
1105 };
1106
1107 Ok(msg)
Balint Dobszay3aad9572025-01-17 16:54:11 +01001108 }
1109
Balint Dobszaya5846852025-02-26 15:38:53 +01001110 /// Create register contents for an interface.
Balint Dobszayde0dc802025-02-28 14:16:52 +01001111 pub fn to_regs(&self, version: Version, regs: &mut [u64]) {
1112 let reg_cnt = regs.len();
1113
1114 match reg_cnt {
1115 8 => {
1116 assert!(version <= Version(1, 1));
1117 self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
1118 }
1119 18 => {
1120 assert!(version >= Version(1, 2));
1121
1122 match self {
1123 Interface::ConsoleLog {
1124 char_lists: ConsoleLogChars::Reg64(_),
1125 ..
1126 }
1127 | Interface::Success {
1128 args: SuccessArgs::Result64_2(_),
1129 ..
1130 }
1131 | Interface::MsgSendDirectReq2 { .. }
1132 | Interface::MsgSendDirectResp2 { .. } => {
1133 self.pack_regs18(version, regs.try_into().unwrap());
1134 }
1135 _ => {
1136 self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
1137 }
1138 }
1139 }
1140 _ => panic!("Invalid number of registers {}", reg_cnt),
1141 }
1142 }
1143
1144 fn pack_regs8(&self, version: Version, a: &mut [u64; 8]) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001145 a.fill(0);
1146 if let Some(function_id) = self.function_id() {
1147 a[0] = function_id as u64;
1148 }
1149
1150 match *self {
1151 Interface::Error {
1152 target_info,
1153 error_code,
1154 } => {
1155 a[1] = u32::from(target_info).into();
1156 a[2] = (error_code as u32).into();
1157 }
1158 Interface::Success { target_info, args } => {
1159 a[1] = target_info.into();
1160 match args {
1161 SuccessArgs::Result32(regs) => {
1162 a[2] = regs[0].into();
1163 a[3] = regs[1].into();
1164 a[4] = regs[2].into();
1165 a[5] = regs[3].into();
1166 a[6] = regs[4].into();
1167 a[7] = regs[5].into();
1168 }
1169 SuccessArgs::Result64(regs) => {
1170 a[2] = regs[0];
1171 a[3] = regs[1];
1172 a[4] = regs[2];
1173 a[5] = regs[3];
1174 a[6] = regs[4];
1175 a[7] = regs[5];
1176 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001177 _ => panic!("{:#x?} requires 18 registers", args),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001178 }
1179 }
1180 Interface::Interrupt {
1181 target_info,
1182 interrupt_id,
1183 } => {
1184 a[1] = u32::from(target_info).into();
1185 a[2] = interrupt_id.into();
1186 }
1187 Interface::Version { input_version } => {
1188 a[1] = u32::from(input_version).into();
1189 }
1190 Interface::VersionOut { output_version } => {
1191 a[0] = u32::from(output_version).into();
1192 }
1193 Interface::Features {
1194 feat_id,
1195 input_properties,
1196 } => {
1197 a[1] = u32::from(feat_id).into();
1198 a[2] = input_properties.into();
1199 }
1200 Interface::RxAcquire { vm_id } => {
1201 a[1] = vm_id.into();
1202 }
1203 Interface::RxRelease { vm_id } => {
1204 a[1] = vm_id.into();
1205 }
1206 Interface::RxTxMap { addr, page_cnt } => {
1207 match addr {
1208 RxTxAddr::Addr32 { rx, tx } => {
1209 a[1] = tx.into();
1210 a[2] = rx.into();
1211 }
1212 RxTxAddr::Addr64 { rx, tx } => {
1213 a[1] = tx;
1214 a[2] = rx;
1215 }
1216 }
1217 a[3] = page_cnt.into();
1218 }
1219 Interface::RxTxUnmap { id } => {
1220 a[1] = id.into();
1221 }
1222 Interface::PartitionInfoGet { uuid, flags } => {
1223 let bytes = uuid.into_bytes();
1224 a[1] = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]).into();
1225 a[2] = u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]).into();
1226 a[3] = u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]).into();
1227 a[4] = u32::from_le_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]).into();
1228 a[5] = flags.into();
1229 }
Tomás González092202a2025-03-05 11:56:45 +00001230 Interface::MsgWait { flags } => {
1231 if version >= Version(1, 2) {
1232 if let Some(flags) = flags {
1233 a[2] = u32::from(flags).into();
1234 }
1235 }
1236 }
1237 Interface::IdGet | Interface::SpmIdGet | Interface::Yield => {}
Balint Dobszay3aad9572025-01-17 16:54:11 +01001238 Interface::Run { target_info } => {
1239 a[1] = u32::from(target_info).into();
1240 }
1241 Interface::NormalWorldResume => {}
Tomás González17b92442025-03-10 16:45:04 +00001242 Interface::SecondaryEpRegister { entrypoint } => match entrypoint {
1243 SecondaryEpRegisterAddr::Addr32(addr) => a[1] = addr as u64,
1244 SecondaryEpRegisterAddr::Addr64(addr) => a[1] = addr,
1245 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001246 Interface::MsgSend2 {
1247 sender_vm_id,
1248 flags,
1249 } => {
1250 a[1] = sender_vm_id.into();
1251 a[2] = flags.into();
1252 }
1253 Interface::MsgSendDirectReq {
1254 src_id,
1255 dst_id,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001256 args,
1257 } => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +01001258 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Balint Dobszay3aad9572025-01-17 16:54:11 +01001259 match args {
1260 DirectMsgArgs::Args32(args) => {
1261 a[3] = args[0].into();
1262 a[4] = args[1].into();
1263 a[5] = args[2].into();
1264 a[6] = args[3].into();
1265 a[7] = args[4].into();
1266 }
1267 DirectMsgArgs::Args64(args) => {
1268 a[3] = args[0];
1269 a[4] = args[1];
1270 a[5] = args[2];
1271 a[6] = args[3];
1272 a[7] = args[4];
1273 }
Tomás González4d5b0ba2025-03-03 17:15:55 +00001274 DirectMsgArgs::VersionReq { version } => {
1275 a[2] = DirectMsgArgs::VERSION_REQ.into();
1276 a[3] = u32::from(version).into();
1277 }
Tomás González67f92c72025-03-20 16:50:42 +00001278 DirectMsgArgs::PowerPsciReq32 { params } => {
Tomás González4d5b0ba2025-03-03 17:15:55 +00001279 a[2] = DirectMsgArgs::POWER_PSCI_REQ.into();
Tomás González67f92c72025-03-20 16:50:42 +00001280 a[3] = params[0].into();
1281 a[4] = params[1].into();
1282 a[5] = params[2].into();
1283 a[6] = params[3].into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00001284 }
Tomás González67f92c72025-03-20 16:50:42 +00001285 DirectMsgArgs::PowerPsciReq64 { params } => {
Tomás González4d5b0ba2025-03-03 17:15:55 +00001286 a[2] = DirectMsgArgs::POWER_PSCI_REQ.into();
Tomás González67f92c72025-03-20 16:50:42 +00001287 a[3] = params[0];
1288 a[4] = params[1];
1289 a[5] = params[2];
1290 a[6] = params[3];
Tomás González4d5b0ba2025-03-03 17:15:55 +00001291 }
1292 DirectMsgArgs::PowerWarmBootReq { boot_type } => {
1293 a[2] = DirectMsgArgs::POWER_WARM_BOOT_REQ.into();
1294 a[3] = u32::from(boot_type).into();
1295 }
1296 DirectMsgArgs::VmCreated { handle, vm_id } => {
1297 a[2] = DirectMsgArgs::VM_CREATED.into();
1298 let handle_regs: [u32; 2] = handle.into();
1299 a[3] = handle_regs[0].into();
1300 a[4] = handle_regs[1].into();
1301 a[5] = vm_id.into();
1302 }
1303 DirectMsgArgs::VmDestructed { handle, vm_id } => {
1304 a[2] = DirectMsgArgs::VM_DESTRUCTED.into();
1305 let handle_regs: [u32; 2] = handle.into();
1306 a[3] = handle_regs[0].into();
1307 a[4] = handle_regs[1].into();
1308 a[5] = vm_id.into();
1309 }
1310 _ => panic!("Malformed MsgSendDirectReq interface"),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001311 }
1312 }
1313 Interface::MsgSendDirectResp {
1314 src_id,
1315 dst_id,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001316 args,
1317 } => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +01001318 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Balint Dobszay3aad9572025-01-17 16:54:11 +01001319 match args {
1320 DirectMsgArgs::Args32(args) => {
1321 a[3] = args[0].into();
1322 a[4] = args[1].into();
1323 a[5] = args[2].into();
1324 a[6] = args[3].into();
1325 a[7] = args[4].into();
1326 }
1327 DirectMsgArgs::Args64(args) => {
1328 a[3] = args[0];
1329 a[4] = args[1];
1330 a[5] = args[2];
1331 a[6] = args[3];
1332 a[7] = args[4];
1333 }
Tomás González4d5b0ba2025-03-03 17:15:55 +00001334 DirectMsgArgs::VersionResp { version } => {
1335 a[2] = DirectMsgArgs::VERSION_RESP.into();
1336 match version {
Tomás González67f92c72025-03-20 16:50:42 +00001337 None => a[3] = (i32::from(FfaError::NotSupported) as u32).into(),
Tomás González4d5b0ba2025-03-03 17:15:55 +00001338 Some(ver) => a[3] = u32::from(ver).into(),
1339 }
1340 }
1341 DirectMsgArgs::PowerPsciResp { psci_status } => {
1342 a[2] = DirectMsgArgs::POWER_PSCI_RESP.into();
1343 a[3] = psci_status as u64;
1344 }
1345 DirectMsgArgs::VmCreatedAck { sp_status } => {
1346 a[2] = DirectMsgArgs::VM_CREATED_ACK.into();
Tomás González67f92c72025-03-20 16:50:42 +00001347 a[3] = (i32::from(sp_status) as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00001348 }
1349 DirectMsgArgs::VmDestructedAck { sp_status } => {
1350 a[2] = DirectMsgArgs::VM_DESTRUCTED_ACK.into();
Tomás González67f92c72025-03-20 16:50:42 +00001351 a[3] = (i32::from(sp_status) as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00001352 }
1353 _ => panic!("Malformed MsgSendDirectResp interface"),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001354 }
1355 }
1356 Interface::MemDonate {
1357 total_len,
1358 frag_len,
1359 buf,
1360 } => {
1361 a[1] = total_len.into();
1362 a[2] = frag_len.into();
1363 (a[3], a[4]) = match buf {
1364 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1365 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1366 None => (0, 0),
1367 };
1368 }
1369 Interface::MemLend {
1370 total_len,
1371 frag_len,
1372 buf,
1373 } => {
1374 a[1] = total_len.into();
1375 a[2] = frag_len.into();
1376 (a[3], a[4]) = match buf {
1377 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1378 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1379 None => (0, 0),
1380 };
1381 }
1382 Interface::MemShare {
1383 total_len,
1384 frag_len,
1385 buf,
1386 } => {
1387 a[1] = total_len.into();
1388 a[2] = frag_len.into();
1389 (a[3], a[4]) = match buf {
1390 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1391 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1392 None => (0, 0),
1393 };
1394 }
1395 Interface::MemRetrieveReq {
1396 total_len,
1397 frag_len,
1398 buf,
1399 } => {
1400 a[1] = total_len.into();
1401 a[2] = frag_len.into();
1402 (a[3], a[4]) = match buf {
1403 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1404 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1405 None => (0, 0),
1406 };
1407 }
1408 Interface::MemRetrieveResp {
1409 total_len,
1410 frag_len,
1411 } => {
1412 a[1] = total_len.into();
1413 a[2] = frag_len.into();
1414 }
1415 Interface::MemRelinquish => {}
1416 Interface::MemReclaim { handle, flags } => {
1417 let handle_regs: [u32; 2] = handle.into();
1418 a[1] = handle_regs[0].into();
1419 a[2] = handle_regs[1].into();
1420 a[3] = flags.into();
1421 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001422 Interface::MemPermGet { addr, page_cnt } => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001423 a[1] = match addr {
1424 MemAddr::Addr32(addr) => addr.into(),
1425 MemAddr::Addr64(addr) => addr,
1426 };
Balint Dobszayde0dc802025-02-28 14:16:52 +01001427 a[2] = if version >= Version(1, 3) {
1428 page_cnt.unwrap().into()
1429 } else {
1430 assert!(page_cnt.is_none());
1431 0
1432 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01001433 }
1434 Interface::MemPermSet {
1435 addr,
1436 page_cnt,
1437 mem_perm,
1438 } => {
1439 a[1] = match addr {
1440 MemAddr::Addr32(addr) => addr.into(),
1441 MemAddr::Addr64(addr) => addr,
1442 };
1443 a[2] = page_cnt.into();
1444 a[3] = mem_perm.into();
1445 }
1446 Interface::ConsoleLog {
1447 char_cnt,
1448 char_lists,
1449 } => {
1450 a[1] = char_cnt.into();
1451 match char_lists {
1452 ConsoleLogChars::Reg32(regs) => {
1453 a[2] = regs[0].into();
1454 a[3] = regs[1].into();
1455 a[4] = regs[2].into();
1456 a[5] = regs[3].into();
1457 a[6] = regs[4].into();
1458 a[7] = regs[5].into();
1459 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001460 _ => panic!("{:#x?} requires 18 registers", char_lists),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001461 }
1462 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001463 _ => panic!("{:#x?} requires 18 registers", self),
1464 }
1465 }
1466
1467 fn pack_regs18(&self, version: Version, a: &mut [u64; 18]) {
1468 assert!(version >= Version(1, 2));
1469
1470 a.fill(0);
1471 if let Some(function_id) = self.function_id() {
1472 a[0] = function_id as u64;
1473 }
1474
1475 match *self {
1476 Interface::Success { target_info, args } => {
1477 a[1] = target_info.into();
1478 match args {
1479 SuccessArgs::Result64_2(regs) => a[2..18].copy_from_slice(&regs[..16]),
1480 _ => panic!("{:#x?} requires 8 registers", args),
1481 }
1482 }
1483 Interface::MsgSendDirectReq2 {
1484 src_id,
1485 dst_id,
1486 uuid,
1487 args,
1488 } => {
1489 a[1] = ((src_id as u64) << 16) | dst_id as u64;
1490 (a[2], a[3]) = uuid.as_u64_pair();
1491 a[4..18].copy_from_slice(&args.0[..14]);
1492 }
1493 Interface::MsgSendDirectResp2 {
1494 src_id,
1495 dst_id,
1496 args,
1497 } => {
1498 a[1] = ((src_id as u64) << 16) | dst_id as u64;
1499 a[2] = 0;
1500 a[3] = 0;
1501 a[4..18].copy_from_slice(&args.0[..14]);
1502 }
1503 Interface::ConsoleLog {
1504 char_cnt,
1505 char_lists,
1506 } => {
1507 a[1] = char_cnt.into();
1508 match char_lists {
1509 ConsoleLogChars::Reg64(regs) => a[2..18].copy_from_slice(&regs[..16]),
1510 _ => panic!("{:#x?} requires 8 registers", char_lists),
1511 }
1512 }
1513 _ => panic!("{:#x?} requires 8 registers", self),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001514 }
1515 }
1516
Balint Dobszaya5846852025-02-26 15:38:53 +01001517 /// Helper function to create an `FFA_SUCCESS` interface without any arguments.
Balint Dobszay3aad9572025-01-17 16:54:11 +01001518 pub fn success32_noargs() -> Self {
1519 Self::Success {
1520 target_info: 0,
1521 args: SuccessArgs::Result32([0, 0, 0, 0, 0, 0]),
1522 }
1523 }
1524
Tomás González4c8c7d22025-03-10 17:14:57 +00001525 /// Helper function to create an `FFA_SUCCESS` interface without any arguments.
1526 pub fn success64_noargs() -> Self {
1527 Self::Success {
1528 target_info: 0,
1529 args: SuccessArgs::Result64([0, 0, 0, 0, 0, 0]),
1530 }
1531 }
1532
Balint Dobszaya5846852025-02-26 15:38:53 +01001533 /// Helper function to create an `FFA_ERROR` interface with an error code.
Balint Dobszay3aad9572025-01-17 16:54:11 +01001534 pub fn error(error_code: FfaError) -> Self {
1535 Self::Error {
1536 target_info: TargetInfo {
1537 endpoint_id: 0,
1538 vcpu_id: 0,
1539 },
1540 error_code,
1541 }
1542 }
1543}
1544
Balint Dobszaya5846852025-02-26 15:38:53 +01001545/// Maximum number of characters transmitted in a single `FFA_CONSOLE_LOG32` message.
1546pub const CONSOLE_LOG_32_MAX_CHAR_CNT: u8 = 24;
Balint Dobszayde0dc802025-02-28 14:16:52 +01001547/// Maximum number of characters transmitted in a single `FFA_CONSOLE_LOG64` message.
1548pub const CONSOLE_LOG_64_MAX_CHAR_CNT: u8 = 128;
Balint Dobszay3aad9572025-01-17 16:54:11 +01001549
Balint Dobszaya5846852025-02-26 15:38:53 +01001550/// Helper function to convert the "Tightly packed list of characters" format used by the
1551/// `FFA_CONSOLE_LOG` interface into a byte slice.
Balint Dobszay3aad9572025-01-17 16:54:11 +01001552pub fn parse_console_log(
1553 char_cnt: u8,
1554 char_lists: &ConsoleLogChars,
1555 log_bytes: &mut [u8],
1556) -> Result<(), FfaError> {
1557 match char_lists {
1558 ConsoleLogChars::Reg32(regs) => {
Balint Dobszaya5846852025-02-26 15:38:53 +01001559 if !(1..=CONSOLE_LOG_32_MAX_CHAR_CNT).contains(&char_cnt) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001560 return Err(FfaError::InvalidParameters);
1561 }
1562 for (i, reg) in regs.iter().enumerate() {
1563 log_bytes[4 * i..4 * (i + 1)].copy_from_slice(&reg.to_le_bytes());
1564 }
1565 }
1566 ConsoleLogChars::Reg64(regs) => {
Balint Dobszaya5846852025-02-26 15:38:53 +01001567 if !(1..=CONSOLE_LOG_64_MAX_CHAR_CNT).contains(&char_cnt) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001568 return Err(FfaError::InvalidParameters);
1569 }
1570 for (i, reg) in regs.iter().enumerate() {
1571 log_bytes[8 * i..8 * (i + 1)].copy_from_slice(&reg.to_le_bytes());
1572 }
1573 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001574 }
1575
Balint Dobszayc8802492025-01-15 18:11:39 +01001576 Ok(())
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001577}