blob: f198cc8011f7f9635191c0b1eff62445bee0bde8 [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.
Tomás González0a058bc2025-03-11 11:20:55 +000026#[derive(Debug, Error, PartialEq)]
Balint Dobszay3aad9572025-01-17 16:54:11 +010027pub 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),
Tomás González0a058bc2025-03-11 11:20:55 +000044 #[error("Invalid Information Tag {0}")]
45 InvalidInformationTag(u16),
Balint Dobszay3aad9572025-01-17 16:54:11 +010046}
47
48impl From<Error> for FfaError {
49 fn from(value: Error) -> Self {
50 match value {
51 Error::UnrecognisedFunctionId(_) | Error::UnrecognisedFeatureId(_) => {
52 Self::NotSupported
53 }
Tomás González0a058bc2025-03-11 11:20:55 +000054 Error::InvalidInformationTag(_) => Self::Retry,
Tomás González4d5b0ba2025-03-03 17:15:55 +000055 Error::UnrecognisedErrorCode(_)
56 | Error::UnrecognisedFwkMsg(_)
57 | Error::InvalidVersion(_)
Tomás González092202a2025-03-05 11:56:45 +000058 | Error::InvalidMsgWaitFlag(_)
Tomás González4d5b0ba2025-03-03 17:15:55 +000059 | Error::UnrecognisedVmAvailabilityStatus(_)
60 | Error::UnrecognisedWarmBootType(_) => Self::InvalidParameters,
Balint Dobszay3aad9572025-01-17 16:54:11 +010061 }
62 }
63}
Balint Dobszay5bf492f2024-07-29 17:21:32 +020064
Balint Dobszaya5846852025-02-26 15:38:53 +010065/// 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 +020066#[derive(PartialEq, Clone, Copy)]
67pub enum Instance {
Balint Dobszaya5846852025-02-26 15:38:53 +010068 /// The instance between the SPMC and SPMD.
Balint Dobszay5bf492f2024-07-29 17:21:32 +020069 SecurePhysical,
Balint Dobszaya5846852025-02-26 15:38:53 +010070 /// The instance between the SPMC and a physical SP (contains the SP's endpoint ID).
Balint Dobszay5bf492f2024-07-29 17:21:32 +020071 SecureVirtual(u16),
72}
73
Balint Dobszaya5846852025-02-26 15:38:53 +010074/// Function IDs of the various FF-A interfaces.
Andrew Walbran969b9252024-11-25 15:35:42 +000075#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
Balint Dobszay3aad9572025-01-17 16:54:11 +010076#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedFunctionId))]
Balint Dobszay5bf492f2024-07-29 17:21:32 +020077#[repr(u32)]
78pub enum FuncId {
79 Error = 0x84000060,
80 Success32 = 0x84000061,
81 Success64 = 0xc4000061,
82 Interrupt = 0x84000062,
83 Version = 0x84000063,
84 Features = 0x84000064,
85 RxAcquire = 0x84000084,
86 RxRelease = 0x84000065,
87 RxTxMap32 = 0x84000066,
88 RxTxMap64 = 0xc4000066,
89 RxTxUnmap = 0x84000067,
90 PartitionInfoGet = 0x84000068,
Balint Dobszaye6aa4862025-02-28 16:37:56 +010091 PartitionInfoGetRegs = 0xc400008b,
Balint Dobszay5bf492f2024-07-29 17:21:32 +020092 IdGet = 0x84000069,
93 SpmIdGet = 0x84000085,
Balint Dobszaye6aa4862025-02-28 16:37:56 +010094 ConsoleLog32 = 0x8400008a,
95 ConsoleLog64 = 0xc400008a,
Balint Dobszay5bf492f2024-07-29 17:21:32 +020096 MsgWait = 0x8400006b,
97 Yield = 0x8400006c,
98 Run = 0x8400006d,
99 NormalWorldResume = 0x8400007c,
100 MsgSend2 = 0x84000086,
101 MsgSendDirectReq32 = 0x8400006f,
102 MsgSendDirectReq64 = 0xc400006f,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100103 MsgSendDirectReq64_2 = 0xc400008d,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200104 MsgSendDirectResp32 = 0x84000070,
105 MsgSendDirectResp64 = 0xc4000070,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100106 MsgSendDirectResp64_2 = 0xc400008e,
Balint Dobszaye6aa4862025-02-28 16:37:56 +0100107 NotificationBitmapCreate = 0x8400007d,
108 NotificationBitmapDestroy = 0x8400007e,
109 NotificationBind = 0x8400007f,
110 NotificationUnbind = 0x84000080,
111 NotificationSet = 0x84000081,
112 NotificationGet = 0x84000082,
113 NotificationInfoGet32 = 0x84000083,
114 NotificationInfoGet64 = 0xc4000083,
115 El3IntrHandle = 0x8400008c,
Tomás González17b92442025-03-10 16:45:04 +0000116 SecondaryEpRegister32 = 0x84000087,
117 SecondaryEpRegister64 = 0xc4000087,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200118 MemDonate32 = 0x84000071,
119 MemDonate64 = 0xc4000071,
120 MemLend32 = 0x84000072,
121 MemLend64 = 0xc4000072,
122 MemShare32 = 0x84000073,
123 MemShare64 = 0xc4000073,
124 MemRetrieveReq32 = 0x84000074,
125 MemRetrieveReq64 = 0xc4000074,
126 MemRetrieveResp = 0x84000075,
127 MemRelinquish = 0x84000076,
128 MemReclaim = 0x84000077,
129 MemPermGet32 = 0x84000088,
130 MemPermGet64 = 0xc4000088,
131 MemPermSet32 = 0x84000089,
132 MemPermSet64 = 0xc4000089,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200133}
134
Balint Dobszayde0dc802025-02-28 14:16:52 +0100135impl FuncId {
136 /// Returns true if this is a 32-bit call, or false if it is a 64-bit call.
137 pub fn is_32bit(&self) -> bool {
138 u32::from(*self) & (1 << 30) != 0
139 }
140}
141
Balint Dobszaya5846852025-02-26 15:38:53 +0100142/// Error status codes used by the `FFA_ERROR` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100143#[derive(Clone, Copy, Debug, Eq, Error, IntoPrimitive, PartialEq, TryFromPrimitive)]
144#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedErrorCode))]
145#[repr(i32)]
146pub enum FfaError {
147 #[error("Not supported")]
148 NotSupported = -1,
149 #[error("Invalid parameters")]
150 InvalidParameters = -2,
151 #[error("No memory")]
152 NoMemory = -3,
153 #[error("Busy")]
154 Busy = -4,
155 #[error("Interrupted")]
156 Interrupted = -5,
157 #[error("Denied")]
158 Denied = -6,
159 #[error("Retry")]
160 Retry = -7,
161 #[error("Aborted")]
162 Aborted = -8,
163 #[error("No data")]
164 NoData = -9,
165}
166
Balint Dobszaya5846852025-02-26 15:38:53 +0100167/// Endpoint ID and vCPU ID pair, used by `FFA_ERROR`, `FFA_INTERRUPT` and `FFA_RUN` interfaces.
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200168#[derive(Debug, Eq, PartialEq, Clone, Copy)]
Balint Dobszay3aad9572025-01-17 16:54:11 +0100169pub struct TargetInfo {
170 pub endpoint_id: u16,
171 pub vcpu_id: u16,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200172}
173
Balint Dobszay3aad9572025-01-17 16:54:11 +0100174impl From<u32> for TargetInfo {
175 fn from(value: u32) -> Self {
176 Self {
177 endpoint_id: (value >> 16) as u16,
178 vcpu_id: value as u16,
179 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200180 }
181}
182
Balint Dobszay3aad9572025-01-17 16:54:11 +0100183impl From<TargetInfo> for u32 {
184 fn from(value: TargetInfo) -> Self {
Balint Dobszaye9a3e762025-02-26 17:29:57 +0100185 ((value.endpoint_id as u32) << 16) | value.vcpu_id as u32
Andrew Walbran0d315812024-11-25 15:36:36 +0000186 }
Balint Dobszay3aad9572025-01-17 16:54:11 +0100187}
Andrew Walbran0d315812024-11-25 15:36:36 +0000188
Balint Dobszaya5846852025-02-26 15:38:53 +0100189/// Arguments for the `FFA_SUCCESS` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100190#[derive(Debug, Eq, PartialEq, Clone, Copy)]
191pub enum SuccessArgs {
192 Result32([u32; 6]),
193 Result64([u64; 6]),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100194 Result64_2([u64; 16]),
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200195}
196
Tomás González17b92442025-03-10 16:45:04 +0000197/// Entrypoint address argument for `FFA_SECONDARY_EP_REGISTER` interface.
198#[derive(Debug, Eq, PartialEq, Clone, Copy)]
199pub enum SecondaryEpRegisterAddr {
200 Addr32(u32),
201 Addr64(u64),
202}
203
Balint Dobszaya5846852025-02-26 15:38:53 +0100204/// Version number of the FF-A implementation, `.0` is the major, `.1` is minor the version.
Balint Dobszayde0dc802025-02-28 14:16:52 +0100205#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord)]
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200206pub struct Version(pub u16, pub u16);
207
Tomás González1f794352025-03-03 16:47:06 +0000208impl Version {
Tomás González83146af2025-03-04 11:32:41 +0000209 // The FF-A spec mandates that bit[31] of a version number must be 0
210 const MBZ_BITS: u32 = 1 << 31;
211
Tomás González1f794352025-03-03 16:47:06 +0000212 /// Returns whether the caller's version (self) is compatible with the callee's version (input
213 /// parameter)
214 pub fn is_compatible_to(&self, callee_version: &Version) -> bool {
215 self.0 == callee_version.0 && self.1 <= callee_version.1
216 }
217}
218
Tomás González83146af2025-03-04 11:32:41 +0000219impl TryFrom<u32> for Version {
220 type Error = Error;
221
222 fn try_from(val: u32) -> Result<Self, Self::Error> {
223 if (val & Self::MBZ_BITS) != 0 {
224 Err(Error::InvalidVersion(val))
225 } else {
226 Ok(Self((val >> 16) as u16, val as u16))
227 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200228 }
229}
230
231impl From<Version> for u32 {
232 fn from(v: Version) -> Self {
Tomás González83146af2025-03-04 11:32:41 +0000233 let v_u32 = ((v.0 as u32) << 16) | v.1 as u32;
234 assert!(v_u32 & Version::MBZ_BITS == 0);
235 v_u32
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200236 }
237}
238
Andrew Walbran19970ba2024-11-25 15:35:00 +0000239impl Display for Version {
240 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
241 write!(f, "{}.{}", self.0, self.1)
242 }
243}
244
245impl Debug for Version {
246 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
247 Display::fmt(self, f)
248 }
249}
250
Balint Dobszaya5846852025-02-26 15:38:53 +0100251/// Feature IDs used by the `FFA_FEATURES` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100252#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
253#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedFeatureId))]
254#[repr(u8)]
255pub enum FeatureId {
256 NotificationPendingInterrupt = 0x1,
257 ScheduleReceiverInterrupt = 0x2,
258 ManagedExitInterrupt = 0x3,
259}
Balint Dobszayc8802492025-01-15 18:11:39 +0100260
Balint Dobszaya5846852025-02-26 15:38:53 +0100261/// Arguments for the `FFA_FEATURES` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100262#[derive(Debug, Eq, PartialEq, Clone, Copy)]
263pub enum Feature {
264 FuncId(FuncId),
265 FeatureId(FeatureId),
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100266 Unknown(u32),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100267}
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200268
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100269impl From<u32> for Feature {
270 fn from(value: u32) -> Self {
271 // Bit[31] is set for all valid FF-A function IDs so we don't have to check it separately
272 if let Ok(func_id) = value.try_into() {
273 Self::FuncId(func_id)
274 } else if let Ok(feat_id) = (value as u8).try_into() {
275 Self::FeatureId(feat_id)
Balint Dobszay3aad9572025-01-17 16:54:11 +0100276 } else {
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100277 Self::Unknown(value)
278 }
Balint Dobszay3aad9572025-01-17 16:54:11 +0100279 }
280}
281
282impl From<Feature> for u32 {
283 fn from(value: Feature) -> Self {
284 match value {
285 Feature::FuncId(func_id) => (1 << 31) | func_id as u32,
286 Feature::FeatureId(feature_id) => feature_id as u32,
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100287 Feature::Unknown(id) => panic!("Unknown feature or function ID {:#x?}", id),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100288 }
289 }
290}
291
Balint Dobszaya5846852025-02-26 15:38:53 +0100292/// RXTX buffer descriptor, used by `FFA_RXTX_MAP`.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100293#[derive(Debug, Eq, PartialEq, Clone, Copy)]
294pub enum RxTxAddr {
295 Addr32 { rx: u32, tx: u32 },
296 Addr64 { rx: u64, tx: u64 },
297}
298
Tomás González4d5b0ba2025-03-03 17:15:55 +0000299/// Composite type for capturing success and error return codes for the VM availability messages.
300///
301/// Error codes are handled by the `FfaError` type. Having a separate type for errors helps using
302/// `Result<(), FfaError>`. If a single type would include both success and error values,
303/// then `Err(FfaError::Success)` would be incomprehensible.
304#[derive(Debug, Eq, PartialEq, Clone, Copy)]
305pub enum VmAvailabilityStatus {
306 Success,
307 Error(FfaError),
308}
309
310impl TryFrom<i32> for VmAvailabilityStatus {
311 type Error = Error;
312 fn try_from(value: i32) -> Result<Self, <Self as TryFrom<i32>>::Error> {
313 Ok(match value {
314 0 => Self::Success,
315 error_code => Self::Error(FfaError::try_from(error_code)?),
316 })
317 }
318}
319
320impl From<VmAvailabilityStatus> for i32 {
321 fn from(value: VmAvailabilityStatus) -> Self {
322 match value {
323 VmAvailabilityStatus::Success => 0,
324 VmAvailabilityStatus::Error(error_code) => error_code.into(),
325 }
326 }
327}
328
329/// Arguments for the Power Warm Boot `FFA_MSG_SEND_DIRECT_REQ` interface.
330#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
331#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedWarmBootType))]
332#[repr(u32)]
333pub enum WarmBootType {
334 ExitFromSuspend = 0,
335 ExitFromLowPower = 1,
336}
337
Balint Dobszaya5846852025-02-26 15:38:53 +0100338/// Arguments for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}` interfaces.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100339#[derive(Debug, Eq, PartialEq, Clone, Copy)]
340pub enum DirectMsgArgs {
341 Args32([u32; 5]),
342 Args64([u64; 5]),
Tomás González4d5b0ba2025-03-03 17:15:55 +0000343 /// Message for forwarding FFA_VERSION call from Normal world to the SPMC
344 VersionReq {
345 version: Version,
346 },
347 /// Response message to forwarded FFA_VERSION call from the Normal world
348 /// Contains the version returned by the SPMC or None
349 VersionResp {
350 version: Option<Version>,
351 },
352 /// Message for a power management operation initiated by a PSCI function
353 PowerPsciReq32 {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000354 // params[i]: Input parameter in w[i] in PSCI function invocation at EL3.
Tomás González67f92c72025-03-20 16:50:42 +0000355 // params[0]: Function ID.
356 params: [u32; 4],
Tomás González4d5b0ba2025-03-03 17:15:55 +0000357 },
358 /// Message for a power management operation initiated by a PSCI function
359 PowerPsciReq64 {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000360 // params[i]: Input parameter in x[i] in PSCI function invocation at EL3.
Tomás González67f92c72025-03-20 16:50:42 +0000361 // params[0]: Function ID.
362 params: [u64; 4],
Tomás González4d5b0ba2025-03-03 17:15:55 +0000363 },
364 /// Message for a warm boot
365 PowerWarmBootReq {
366 boot_type: WarmBootType,
367 },
368 /// Response message to indicate return status of the last power management request message
369 /// Return error code SUCCESS or DENIED as defined in PSCI spec. Caller is left to do the
370 /// parsing of the return status.
371 PowerPsciResp {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000372 psci_status: i32,
373 },
374 /// Message to signal creation of a VM
375 VmCreated {
376 // Globally unique Handle to identify a memory region that contains IMPLEMENTATION DEFINED
377 // information associated with the created VM.
378 // The invalid memory region handle must be specified by the Hypervisor if this field is not
379 // used.
380 handle: memory_management::Handle,
381 vm_id: u16,
382 },
383 /// Message to acknowledge creation of a VM
384 VmCreatedAck {
385 sp_status: VmAvailabilityStatus,
386 },
387 /// Message to signal destruction of a VM
388 VmDestructed {
389 // Globally unique Handle to identify a memory region that contains IMPLEMENTATION DEFINED
390 // information associated with the created VM.
391 // The invalid memory region handle must be specified by the Hypervisor if this field is not
392 // used.
393 handle: memory_management::Handle,
394 vm_id: u16,
395 },
396 /// Message to acknowledge destruction of a VM
397 VmDestructedAck {
398 sp_status: VmAvailabilityStatus,
399 },
400}
401
402impl DirectMsgArgs {
403 // Flags for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}` interfaces.
404
405 const FWK_MSG_BITS: u32 = 1 << 31;
406 const VERSION_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b1000;
407 const VERSION_RESP: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b1001;
408 const POWER_PSCI_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS;
409 const POWER_WARM_BOOT_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0001;
410 const POWER_PSCI_RESP: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0010;
411 const VM_CREATED: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0100;
412 const VM_CREATED_ACK: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0101;
413 const VM_DESTRUCTED: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0110;
414 const VM_DESTRUCTED_ACK: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0111;
Balint Dobszay3aad9572025-01-17 16:54:11 +0100415}
416
Balint Dobszayde0dc802025-02-28 14:16:52 +0100417/// Arguments for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}2` interfaces.
418#[derive(Debug, Eq, PartialEq, Clone, Copy)]
419pub struct DirectMsg2Args([u64; 14]);
420
Tomás González092202a2025-03-05 11:56:45 +0000421#[derive(Debug, Eq, PartialEq, Clone, Copy)]
422pub struct MsgWaitFlags {
423 retain_rx_buffer: bool,
424}
425
426impl MsgWaitFlags {
427 const RETAIN_RX_BUFFER: u32 = 0x01;
428 const MBZ_BITS: u32 = 0xfffe;
429}
430
431impl TryFrom<u32> for MsgWaitFlags {
432 type Error = Error;
433
434 fn try_from(val: u32) -> Result<Self, Self::Error> {
435 if (val & Self::MBZ_BITS) != 0 {
436 Err(Error::InvalidMsgWaitFlag(val))
437 } else {
438 Ok(MsgWaitFlags {
439 retain_rx_buffer: val & Self::RETAIN_RX_BUFFER != 0,
440 })
441 }
442 }
443}
444
445impl From<MsgWaitFlags> for u32 {
446 fn from(flags: MsgWaitFlags) -> Self {
447 let mut bits: u32 = 0;
448 if flags.retain_rx_buffer {
449 bits |= MsgWaitFlags::RETAIN_RX_BUFFER;
450 }
451 bits
452 }
453}
454
Balint Dobszaya5846852025-02-26 15:38:53 +0100455/// Descriptor for a dynamically allocated memory buffer that contains the memory transaction
Tomás Gonzálezf268e322025-03-05 11:18:11 +0000456/// descriptor.
457///
458/// Used by `FFA_MEM_{DONATE,LEND,SHARE,RETRIEVE_REQ}` interfaces, only when the TX buffer is not
459/// used to transmit the transaction descriptor.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100460#[derive(Debug, Eq, PartialEq, Clone, Copy)]
461pub enum MemOpBuf {
462 Buf32 { addr: u32, page_cnt: u32 },
463 Buf64 { addr: u64, page_cnt: u32 },
464}
465
Balint Dobszaya5846852025-02-26 15:38:53 +0100466/// Memory address argument for `FFA_MEM_PERM_{GET,SET}` interfaces.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100467#[derive(Debug, Eq, PartialEq, Clone, Copy)]
468pub enum MemAddr {
469 Addr32(u32),
470 Addr64(u64),
471}
472
Balint Dobszayde0dc802025-02-28 14:16:52 +0100473/// Argument for the `FFA_CONSOLE_LOG` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100474#[derive(Debug, Eq, PartialEq, Clone, Copy)]
475pub enum ConsoleLogChars {
476 Reg32([u32; 6]),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100477 Reg64([u64; 16]),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100478}
479
Tomás Gonzálezf268e322025-03-05 11:18:11 +0000480/// FF-A "message types", the terminology used by the spec is "interfaces".
481///
482/// The interfaces are used by FF-A components for communication at an FF-A instance. The spec also
483/// describes the valid FF-A instances and conduits for each interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100484#[derive(Debug, Eq, PartialEq, Clone, Copy)]
485pub enum Interface {
486 Error {
487 target_info: TargetInfo,
488 error_code: FfaError,
489 },
490 Success {
491 target_info: u32,
492 args: SuccessArgs,
493 },
494 Interrupt {
495 target_info: TargetInfo,
496 interrupt_id: u32,
497 },
498 Version {
499 input_version: Version,
500 },
501 VersionOut {
502 output_version: Version,
503 },
504 Features {
505 feat_id: Feature,
506 input_properties: u32,
507 },
508 RxAcquire {
509 vm_id: u16,
510 },
511 RxRelease {
512 vm_id: u16,
513 },
514 RxTxMap {
515 addr: RxTxAddr,
516 page_cnt: u32,
517 },
518 RxTxUnmap {
519 id: u16,
520 },
521 PartitionInfoGet {
522 uuid: Uuid,
523 flags: u32,
524 },
Tomás González0a058bc2025-03-11 11:20:55 +0000525 PartitionInfoGetRegs {
526 uuid: Uuid,
527 start_index: u16,
528 info_tag: u16,
529 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100530 IdGet,
531 SpmIdGet,
Tomás González092202a2025-03-05 11:56:45 +0000532 MsgWait {
533 flags: Option<MsgWaitFlags>,
534 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100535 Yield,
536 Run {
537 target_info: TargetInfo,
538 },
539 NormalWorldResume,
Tomás González17b92442025-03-10 16:45:04 +0000540 SecondaryEpRegister {
541 entrypoint: SecondaryEpRegisterAddr,
542 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100543 MsgSend2 {
544 sender_vm_id: u16,
545 flags: u32,
546 },
547 MsgSendDirectReq {
548 src_id: u16,
549 dst_id: u16,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100550 args: DirectMsgArgs,
551 },
552 MsgSendDirectResp {
553 src_id: u16,
554 dst_id: u16,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100555 args: DirectMsgArgs,
556 },
Balint Dobszayde0dc802025-02-28 14:16:52 +0100557 MsgSendDirectReq2 {
558 src_id: u16,
559 dst_id: u16,
560 uuid: Uuid,
561 args: DirectMsg2Args,
562 },
563 MsgSendDirectResp2 {
564 src_id: u16,
565 dst_id: u16,
566 args: DirectMsg2Args,
567 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100568 MemDonate {
569 total_len: u32,
570 frag_len: u32,
571 buf: Option<MemOpBuf>,
572 },
573 MemLend {
574 total_len: u32,
575 frag_len: u32,
576 buf: Option<MemOpBuf>,
577 },
578 MemShare {
579 total_len: u32,
580 frag_len: u32,
581 buf: Option<MemOpBuf>,
582 },
583 MemRetrieveReq {
584 total_len: u32,
585 frag_len: u32,
586 buf: Option<MemOpBuf>,
587 },
588 MemRetrieveResp {
589 total_len: u32,
590 frag_len: u32,
591 },
592 MemRelinquish,
593 MemReclaim {
594 handle: memory_management::Handle,
595 flags: u32,
596 },
597 MemPermGet {
598 addr: MemAddr,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100599 page_cnt: Option<u32>,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100600 },
601 MemPermSet {
602 addr: MemAddr,
603 page_cnt: u32,
604 mem_perm: u32,
605 },
606 ConsoleLog {
607 char_cnt: u8,
608 char_lists: ConsoleLogChars,
609 },
610}
611
Balint Dobszayde0dc802025-02-28 14:16:52 +0100612impl Interface {
613 /// Returns the function ID for the call, if it has one.
614 pub fn function_id(&self) -> Option<FuncId> {
615 match self {
616 Interface::Error { .. } => Some(FuncId::Error),
617 Interface::Success { args, .. } => match args {
618 SuccessArgs::Result32(..) => Some(FuncId::Success32),
619 SuccessArgs::Result64(..) | SuccessArgs::Result64_2(..) => Some(FuncId::Success64),
620 },
621 Interface::Interrupt { .. } => Some(FuncId::Interrupt),
622 Interface::Version { .. } => Some(FuncId::Version),
623 Interface::VersionOut { .. } => None,
624 Interface::Features { .. } => Some(FuncId::Features),
625 Interface::RxAcquire { .. } => Some(FuncId::RxAcquire),
626 Interface::RxRelease { .. } => Some(FuncId::RxRelease),
627 Interface::RxTxMap { addr, .. } => match addr {
628 RxTxAddr::Addr32 { .. } => Some(FuncId::RxTxMap32),
629 RxTxAddr::Addr64 { .. } => Some(FuncId::RxTxMap64),
630 },
631 Interface::RxTxUnmap { .. } => Some(FuncId::RxTxUnmap),
632 Interface::PartitionInfoGet { .. } => Some(FuncId::PartitionInfoGet),
Tomás González0a058bc2025-03-11 11:20:55 +0000633 Interface::PartitionInfoGetRegs { .. } => Some(FuncId::PartitionInfoGetRegs),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100634 Interface::IdGet => Some(FuncId::IdGet),
635 Interface::SpmIdGet => Some(FuncId::SpmIdGet),
Tomás González092202a2025-03-05 11:56:45 +0000636 Interface::MsgWait { .. } => Some(FuncId::MsgWait),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100637 Interface::Yield => Some(FuncId::Yield),
638 Interface::Run { .. } => Some(FuncId::Run),
639 Interface::NormalWorldResume => Some(FuncId::NormalWorldResume),
Tomás González17b92442025-03-10 16:45:04 +0000640 Interface::SecondaryEpRegister { entrypoint } => match entrypoint {
641 SecondaryEpRegisterAddr::Addr32 { .. } => Some(FuncId::SecondaryEpRegister32),
642 SecondaryEpRegisterAddr::Addr64 { .. } => Some(FuncId::SecondaryEpRegister64),
643 },
Balint Dobszayde0dc802025-02-28 14:16:52 +0100644 Interface::MsgSend2 { .. } => Some(FuncId::MsgSend2),
645 Interface::MsgSendDirectReq { args, .. } => match args {
646 DirectMsgArgs::Args32(_) => Some(FuncId::MsgSendDirectReq32),
647 DirectMsgArgs::Args64(_) => Some(FuncId::MsgSendDirectReq64),
Tomás González4d5b0ba2025-03-03 17:15:55 +0000648 DirectMsgArgs::VersionReq { .. } => Some(FuncId::MsgSendDirectReq32),
649 DirectMsgArgs::PowerPsciReq32 { .. } => Some(FuncId::MsgSendDirectReq32),
650 DirectMsgArgs::PowerPsciReq64 { .. } => Some(FuncId::MsgSendDirectReq64),
651 DirectMsgArgs::PowerWarmBootReq { .. } => Some(FuncId::MsgSendDirectReq32),
652 DirectMsgArgs::VmCreated { .. } => Some(FuncId::MsgSendDirectReq32),
653 DirectMsgArgs::VmDestructed { .. } => Some(FuncId::MsgSendDirectReq32),
654 _ => None,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100655 },
656 Interface::MsgSendDirectResp { args, .. } => match args {
657 DirectMsgArgs::Args32(_) => Some(FuncId::MsgSendDirectResp32),
658 DirectMsgArgs::Args64(_) => Some(FuncId::MsgSendDirectResp64),
Tomás González4d5b0ba2025-03-03 17:15:55 +0000659 DirectMsgArgs::VersionResp { .. } => Some(FuncId::MsgSendDirectResp32),
660 DirectMsgArgs::PowerPsciResp { .. } => Some(FuncId::MsgSendDirectResp32),
661 DirectMsgArgs::VmCreatedAck { .. } => Some(FuncId::MsgSendDirectResp32),
662 DirectMsgArgs::VmDestructedAck { .. } => Some(FuncId::MsgSendDirectResp32),
663 _ => None,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100664 },
665 Interface::MsgSendDirectReq2 { .. } => Some(FuncId::MsgSendDirectReq64_2),
666 Interface::MsgSendDirectResp2 { .. } => Some(FuncId::MsgSendDirectResp64_2),
667 Interface::MemDonate { buf, .. } => match buf {
668 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemDonate64),
669 _ => Some(FuncId::MemDonate32),
670 },
671 Interface::MemLend { buf, .. } => match buf {
672 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemLend64),
673 _ => Some(FuncId::MemLend32),
674 },
675 Interface::MemShare { buf, .. } => match buf {
676 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemShare64),
677 _ => Some(FuncId::MemShare32),
678 },
679 Interface::MemRetrieveReq { buf, .. } => match buf {
680 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemRetrieveReq64),
681 _ => Some(FuncId::MemRetrieveReq32),
682 },
683 Interface::MemRetrieveResp { .. } => Some(FuncId::MemRetrieveResp),
684 Interface::MemRelinquish => Some(FuncId::MemRelinquish),
685 Interface::MemReclaim { .. } => Some(FuncId::MemReclaim),
686 Interface::MemPermGet { addr, .. } => match addr {
687 MemAddr::Addr32(_) => Some(FuncId::MemPermGet32),
688 MemAddr::Addr64(_) => Some(FuncId::MemPermGet64),
689 },
690 Interface::MemPermSet { addr, .. } => match addr {
691 MemAddr::Addr32(_) => Some(FuncId::MemPermSet32),
692 MemAddr::Addr64(_) => Some(FuncId::MemPermSet64),
693 },
694 Interface::ConsoleLog { char_lists, .. } => match char_lists {
695 ConsoleLogChars::Reg32(_) => Some(FuncId::ConsoleLog32),
696 ConsoleLogChars::Reg64(_) => Some(FuncId::ConsoleLog64),
697 },
698 }
699 }
Balint Dobszay3aad9572025-01-17 16:54:11 +0100700
Balint Dobszayde0dc802025-02-28 14:16:52 +0100701 /// Returns true if this is a 32-bit call, or false if it is a 64-bit call.
702 pub fn is_32bit(&self) -> bool {
703 // TODO: self should always have a function ID?
704 self.function_id().unwrap().is_32bit()
705 }
706
707 /// Parse interface from register contents. The caller must ensure that the `regs` argument has
708 /// the correct length: 8 registers for FF-A v1.1 and lower, 18 registers for v1.2 and higher.
709 pub fn from_regs(version: Version, regs: &[u64]) -> Result<Self, Error> {
710 let reg_cnt = regs.len();
711
712 let msg = match reg_cnt {
713 8 => {
714 assert!(version <= Version(1, 1));
715 Interface::unpack_regs8(version, regs.try_into().unwrap())?
716 }
717 18 => {
718 assert!(version >= Version(1, 2));
719 match FuncId::try_from(regs[0] as u32)? {
720 FuncId::ConsoleLog64
721 | FuncId::Success64
722 | FuncId::MsgSendDirectReq64_2
Tomás González0a058bc2025-03-11 11:20:55 +0000723 | FuncId::MsgSendDirectResp64_2
724 | FuncId::PartitionInfoGetRegs => {
Balint Dobszayde0dc802025-02-28 14:16:52 +0100725 Interface::unpack_regs18(version, regs.try_into().unwrap())?
726 }
727 _ => Interface::unpack_regs8(version, regs[..8].try_into().unwrap())?,
728 }
729 }
730 _ => panic!(
731 "Invalid number of registers ({}) for FF-A version {}",
732 reg_cnt, version
733 ),
734 };
735
736 Ok(msg)
737 }
738
739 fn unpack_regs8(version: Version, regs: &[u64; 8]) -> Result<Self, Error> {
Balint Dobszay3aad9572025-01-17 16:54:11 +0100740 let fid = FuncId::try_from(regs[0] as u32)?;
741
742 let msg = match fid {
743 FuncId::Error => Self::Error {
744 target_info: (regs[1] as u32).into(),
745 error_code: FfaError::try_from(regs[2] as i32)?,
746 },
747 FuncId::Success32 => Self::Success {
748 target_info: regs[1] as u32,
749 args: SuccessArgs::Result32([
750 regs[2] as u32,
751 regs[3] as u32,
752 regs[4] as u32,
753 regs[5] as u32,
754 regs[6] as u32,
755 regs[7] as u32,
756 ]),
757 },
758 FuncId::Success64 => Self::Success {
759 target_info: regs[1] as u32,
760 args: SuccessArgs::Result64([regs[2], regs[3], regs[4], regs[5], regs[6], regs[7]]),
761 },
762 FuncId::Interrupt => Self::Interrupt {
763 target_info: (regs[1] as u32).into(),
764 interrupt_id: regs[2] as u32,
765 },
766 FuncId::Version => Self::Version {
Tomás González83146af2025-03-04 11:32:41 +0000767 input_version: (regs[1] as u32).try_into()?,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100768 },
769 FuncId::Features => Self::Features {
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100770 feat_id: (regs[1] as u32).into(),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100771 input_properties: regs[2] as u32,
772 },
773 FuncId::RxAcquire => Self::RxAcquire {
774 vm_id: regs[1] as u16,
775 },
776 FuncId::RxRelease => Self::RxRelease {
777 vm_id: regs[1] as u16,
778 },
779 FuncId::RxTxMap32 => {
780 let addr = RxTxAddr::Addr32 {
781 rx: regs[2] as u32,
782 tx: regs[1] as u32,
783 };
784 let page_cnt = regs[3] as u32;
785
786 Self::RxTxMap { addr, page_cnt }
787 }
788 FuncId::RxTxMap64 => {
789 let addr = RxTxAddr::Addr64 {
790 rx: regs[2],
791 tx: regs[1],
792 };
793 let page_cnt = regs[3] as u32;
794
795 Self::RxTxMap { addr, page_cnt }
796 }
797 FuncId::RxTxUnmap => Self::RxTxUnmap { id: regs[1] as u16 },
798 FuncId::PartitionInfoGet => {
799 let uuid_words = [
800 regs[1] as u32,
801 regs[2] as u32,
802 regs[3] as u32,
803 regs[4] as u32,
804 ];
805 let mut bytes: [u8; 16] = [0; 16];
806 for (i, b) in uuid_words.iter().flat_map(|w| w.to_le_bytes()).enumerate() {
807 bytes[i] = b;
808 }
809 Self::PartitionInfoGet {
810 uuid: Uuid::from_bytes(bytes),
811 flags: regs[5] as u32,
812 }
813 }
814 FuncId::IdGet => Self::IdGet,
815 FuncId::SpmIdGet => Self::SpmIdGet,
Tomás González092202a2025-03-05 11:56:45 +0000816 FuncId::MsgWait => Self::MsgWait {
817 flags: if version >= Version(1, 2) {
818 Some(MsgWaitFlags::try_from(regs[2] as u32)?)
819 } else {
820 None
821 },
822 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100823 FuncId::Yield => Self::Yield,
824 FuncId::Run => Self::Run {
825 target_info: (regs[1] as u32).into(),
826 },
827 FuncId::NormalWorldResume => Self::NormalWorldResume,
Tomás González17b92442025-03-10 16:45:04 +0000828 FuncId::SecondaryEpRegister32 => Self::SecondaryEpRegister {
829 entrypoint: SecondaryEpRegisterAddr::Addr32(regs[1] as u32),
830 },
831 FuncId::SecondaryEpRegister64 => Self::SecondaryEpRegister {
832 entrypoint: SecondaryEpRegisterAddr::Addr64(regs[1]),
833 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100834 FuncId::MsgSend2 => Self::MsgSend2 {
835 sender_vm_id: regs[1] as u16,
836 flags: regs[2] as u32,
837 },
838 FuncId::MsgSendDirectReq32 => Self::MsgSendDirectReq {
839 src_id: (regs[1] >> 16) as u16,
840 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +0000841 args: if (regs[2] as u32 & DirectMsgArgs::FWK_MSG_BITS) != 0 {
842 match regs[2] as u32 {
843 DirectMsgArgs::VERSION_REQ => DirectMsgArgs::VersionReq {
844 version: Version::try_from(regs[3] as u32)?,
845 },
846 DirectMsgArgs::POWER_PSCI_REQ => DirectMsgArgs::PowerPsciReq32 {
Tomás González67f92c72025-03-20 16:50:42 +0000847 params: [
848 regs[3] as u32,
849 regs[4] as u32,
850 regs[5] as u32,
851 regs[6] as u32,
852 ],
Tomás González4d5b0ba2025-03-03 17:15:55 +0000853 },
854 DirectMsgArgs::POWER_WARM_BOOT_REQ => DirectMsgArgs::PowerWarmBootReq {
855 boot_type: WarmBootType::try_from(regs[3] as u32)?,
856 },
857 DirectMsgArgs::VM_CREATED => DirectMsgArgs::VmCreated {
858 handle: memory_management::Handle::from([
859 regs[3] as u32,
860 regs[4] as u32,
861 ]),
862 vm_id: regs[5] as u16,
863 },
864 DirectMsgArgs::VM_DESTRUCTED => DirectMsgArgs::VmDestructed {
865 handle: memory_management::Handle::from([
866 regs[3] as u32,
867 regs[4] as u32,
868 ]),
869 vm_id: regs[5] as u16,
870 },
871 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
872 }
873 } else {
874 DirectMsgArgs::Args32([
875 regs[3] as u32,
876 regs[4] as u32,
877 regs[5] as u32,
878 regs[6] as u32,
879 regs[7] as u32,
880 ])
881 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100882 },
883 FuncId::MsgSendDirectReq64 => Self::MsgSendDirectReq {
884 src_id: (regs[1] >> 16) as u16,
885 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +0000886 args: if (regs[2] & DirectMsgArgs::FWK_MSG_BITS as u64) != 0 {
887 match regs[2] as u32 {
888 DirectMsgArgs::POWER_PSCI_REQ => DirectMsgArgs::PowerPsciReq64 {
Tomás González67f92c72025-03-20 16:50:42 +0000889 params: [regs[3], regs[4], regs[5], regs[6]],
Tomás González4d5b0ba2025-03-03 17:15:55 +0000890 },
891 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
892 }
893 } else {
894 DirectMsgArgs::Args64([regs[3], regs[4], regs[5], regs[6], regs[7]])
895 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100896 },
897 FuncId::MsgSendDirectResp32 => Self::MsgSendDirectResp {
898 src_id: (regs[1] >> 16) as u16,
899 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +0000900 args: if (regs[2] as u32 & DirectMsgArgs::FWK_MSG_BITS) != 0 {
901 match regs[2] as u32 {
902 DirectMsgArgs::VERSION_RESP => {
903 if regs[3] as i32 == FfaError::NotSupported.into() {
904 DirectMsgArgs::VersionResp { version: None }
905 } else {
906 DirectMsgArgs::VersionResp {
907 version: Some(Version::try_from(regs[3] as u32)?),
908 }
909 }
910 }
911 DirectMsgArgs::POWER_PSCI_RESP => DirectMsgArgs::PowerPsciResp {
912 psci_status: regs[3] as i32,
913 },
914 DirectMsgArgs::VM_CREATED_ACK => DirectMsgArgs::VmCreatedAck {
915 sp_status: (regs[3] as i32).try_into()?,
916 },
917 DirectMsgArgs::VM_DESTRUCTED_ACK => DirectMsgArgs::VmDestructedAck {
918 sp_status: (regs[3] as i32).try_into()?,
919 },
920 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
921 }
922 } else {
923 DirectMsgArgs::Args32([
924 regs[3] as u32,
925 regs[4] as u32,
926 regs[5] as u32,
927 regs[6] as u32,
928 regs[7] as u32,
929 ])
930 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100931 },
932 FuncId::MsgSendDirectResp64 => Self::MsgSendDirectResp {
933 src_id: (regs[1] >> 16) as u16,
934 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +0000935 args: if (regs[2] & DirectMsgArgs::FWK_MSG_BITS as u64) != 0 {
936 return Err(Error::UnrecognisedFwkMsg(regs[2] as u32));
937 } else {
938 DirectMsgArgs::Args64([regs[3], regs[4], regs[5], regs[6], regs[7]])
939 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100940 },
941 FuncId::MemDonate32 => Self::MemDonate {
942 total_len: regs[1] as u32,
943 frag_len: regs[2] as u32,
944 buf: if regs[3] != 0 && regs[4] != 0 {
945 Some(MemOpBuf::Buf32 {
946 addr: regs[3] as u32,
947 page_cnt: regs[4] as u32,
948 })
949 } else {
950 None
951 },
952 },
953 FuncId::MemDonate64 => Self::MemDonate {
954 total_len: regs[1] as u32,
955 frag_len: regs[2] as u32,
956 buf: if regs[3] != 0 && regs[4] != 0 {
957 Some(MemOpBuf::Buf64 {
958 addr: regs[3],
959 page_cnt: regs[4] as u32,
960 })
961 } else {
962 None
963 },
964 },
965 FuncId::MemLend32 => Self::MemLend {
966 total_len: regs[1] as u32,
967 frag_len: regs[2] as u32,
968 buf: if regs[3] != 0 && regs[4] != 0 {
969 Some(MemOpBuf::Buf32 {
970 addr: regs[3] as u32,
971 page_cnt: regs[4] as u32,
972 })
973 } else {
974 None
975 },
976 },
977 FuncId::MemLend64 => Self::MemLend {
978 total_len: regs[1] as u32,
979 frag_len: regs[2] as u32,
980 buf: if regs[3] != 0 && regs[4] != 0 {
981 Some(MemOpBuf::Buf64 {
982 addr: regs[3],
983 page_cnt: regs[4] as u32,
984 })
985 } else {
986 None
987 },
988 },
989 FuncId::MemShare32 => Self::MemShare {
990 total_len: regs[1] as u32,
991 frag_len: regs[2] as u32,
992 buf: if regs[3] != 0 && regs[4] != 0 {
993 Some(MemOpBuf::Buf32 {
994 addr: regs[3] as u32,
995 page_cnt: regs[4] as u32,
996 })
997 } else {
998 None
999 },
1000 },
1001 FuncId::MemShare64 => Self::MemShare {
1002 total_len: regs[1] as u32,
1003 frag_len: regs[2] as u32,
1004 buf: if regs[3] != 0 && regs[4] != 0 {
1005 Some(MemOpBuf::Buf64 {
1006 addr: regs[3],
1007 page_cnt: regs[4] as u32,
1008 })
1009 } else {
1010 None
1011 },
1012 },
1013 FuncId::MemRetrieveReq32 => Self::MemRetrieveReq {
1014 total_len: regs[1] as u32,
1015 frag_len: regs[2] as u32,
1016 buf: if regs[3] != 0 && regs[4] != 0 {
1017 Some(MemOpBuf::Buf32 {
1018 addr: regs[3] as u32,
1019 page_cnt: regs[4] as u32,
1020 })
1021 } else {
1022 None
1023 },
1024 },
1025 FuncId::MemRetrieveReq64 => Self::MemRetrieveReq {
1026 total_len: regs[1] as u32,
1027 frag_len: regs[2] as u32,
1028 buf: if regs[3] != 0 && regs[4] != 0 {
1029 Some(MemOpBuf::Buf64 {
1030 addr: regs[3],
1031 page_cnt: regs[4] as u32,
1032 })
1033 } else {
1034 None
1035 },
1036 },
1037 FuncId::MemRetrieveResp => Self::MemRetrieveResp {
1038 total_len: regs[1] as u32,
1039 frag_len: regs[2] as u32,
1040 },
1041 FuncId::MemRelinquish => Self::MemRelinquish,
1042 FuncId::MemReclaim => Self::MemReclaim {
1043 handle: memory_management::Handle::from([regs[1] as u32, regs[2] as u32]),
1044 flags: regs[3] as u32,
1045 },
1046 FuncId::MemPermGet32 => Self::MemPermGet {
1047 addr: MemAddr::Addr32(regs[1] as u32),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001048 page_cnt: if version >= Version(1, 3) {
1049 Some(regs[2] as u32)
1050 } else {
1051 None
1052 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001053 },
1054 FuncId::MemPermGet64 => Self::MemPermGet {
1055 addr: MemAddr::Addr64(regs[1]),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001056 page_cnt: if version >= Version(1, 3) {
1057 Some(regs[2] as u32)
1058 } else {
1059 None
1060 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001061 },
1062 FuncId::MemPermSet32 => Self::MemPermSet {
1063 addr: MemAddr::Addr32(regs[1] as u32),
1064 page_cnt: regs[2] as u32,
1065 mem_perm: regs[3] as u32,
1066 },
1067 FuncId::MemPermSet64 => Self::MemPermSet {
1068 addr: MemAddr::Addr64(regs[1]),
1069 page_cnt: regs[2] as u32,
1070 mem_perm: regs[3] as u32,
1071 },
1072 FuncId::ConsoleLog32 => Self::ConsoleLog {
1073 char_cnt: regs[1] as u8,
1074 char_lists: ConsoleLogChars::Reg32([
1075 regs[2] as u32,
1076 regs[3] as u32,
1077 regs[4] as u32,
1078 regs[5] as u32,
1079 regs[6] as u32,
1080 regs[7] as u32,
1081 ]),
1082 },
Balint Dobszayde0dc802025-02-28 14:16:52 +01001083 _ => panic!("Invalid number of registers (8) for function {:#x?}", fid),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001084 };
1085
1086 Ok(msg)
1087 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01001088
Balint Dobszayde0dc802025-02-28 14:16:52 +01001089 fn unpack_regs18(version: Version, regs: &[u64; 18]) -> Result<Self, Error> {
1090 assert!(version >= Version(1, 2));
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001091
Balint Dobszayde0dc802025-02-28 14:16:52 +01001092 let fid = FuncId::try_from(regs[0] as u32)?;
1093
1094 let msg = match fid {
1095 FuncId::Success64 => Self::Success {
1096 target_info: regs[1] as u32,
1097 args: SuccessArgs::Result64_2(regs[2..18].try_into().unwrap()),
1098 },
1099 FuncId::MsgSendDirectReq64_2 => Self::MsgSendDirectReq2 {
1100 src_id: (regs[1] >> 16) as u16,
1101 dst_id: regs[1] as u16,
1102 uuid: Uuid::from_u64_pair(regs[2], regs[3]),
1103 args: DirectMsg2Args(regs[4..18].try_into().unwrap()),
1104 },
1105 FuncId::MsgSendDirectResp64_2 => Self::MsgSendDirectResp2 {
1106 src_id: (regs[1] >> 16) as u16,
1107 dst_id: regs[1] as u16,
1108 args: DirectMsg2Args(regs[4..18].try_into().unwrap()),
1109 },
1110 FuncId::ConsoleLog64 => Self::ConsoleLog {
1111 char_cnt: regs[1] as u8,
1112 char_lists: ConsoleLogChars::Reg64(regs[2..18].try_into().unwrap()),
1113 },
Tomás González0a058bc2025-03-11 11:20:55 +00001114 FuncId::PartitionInfoGetRegs => {
1115 // Bits[15:0]: Start index
1116 let start_index = (regs[3] & 0xffff) as u16;
1117 let info_tag = ((regs[3] >> 16) & 0xffff) as u16;
1118 Self::PartitionInfoGetRegs {
1119 uuid: Uuid::from_u64_pair(regs[1].swap_bytes(), regs[2].swap_bytes()),
1120 start_index,
1121 info_tag: if start_index == 0 && info_tag != 0 {
1122 return Err(Error::InvalidInformationTag(info_tag));
1123 } else {
1124 info_tag
1125 },
1126 }
1127 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001128 _ => panic!("Invalid number of registers (18) for function {:#x?}", fid),
1129 };
1130
1131 Ok(msg)
Balint Dobszay3aad9572025-01-17 16:54:11 +01001132 }
1133
Balint Dobszaya5846852025-02-26 15:38:53 +01001134 /// Create register contents for an interface.
Balint Dobszayde0dc802025-02-28 14:16:52 +01001135 pub fn to_regs(&self, version: Version, regs: &mut [u64]) {
1136 let reg_cnt = regs.len();
1137
1138 match reg_cnt {
1139 8 => {
1140 assert!(version <= Version(1, 1));
1141 self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
1142 }
1143 18 => {
1144 assert!(version >= Version(1, 2));
1145
1146 match self {
1147 Interface::ConsoleLog {
1148 char_lists: ConsoleLogChars::Reg64(_),
1149 ..
1150 }
1151 | Interface::Success {
1152 args: SuccessArgs::Result64_2(_),
1153 ..
1154 }
1155 | Interface::MsgSendDirectReq2 { .. }
Tomás González0a058bc2025-03-11 11:20:55 +00001156 | Interface::MsgSendDirectResp2 { .. }
1157 | Interface::PartitionInfoGetRegs { .. } => {
Balint Dobszayde0dc802025-02-28 14:16:52 +01001158 self.pack_regs18(version, regs.try_into().unwrap());
1159 }
1160 _ => {
1161 self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
1162 }
1163 }
1164 }
1165 _ => panic!("Invalid number of registers {}", reg_cnt),
1166 }
1167 }
1168
1169 fn pack_regs8(&self, version: Version, a: &mut [u64; 8]) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001170 a.fill(0);
1171 if let Some(function_id) = self.function_id() {
1172 a[0] = function_id as u64;
1173 }
1174
1175 match *self {
1176 Interface::Error {
1177 target_info,
1178 error_code,
1179 } => {
1180 a[1] = u32::from(target_info).into();
1181 a[2] = (error_code as u32).into();
1182 }
1183 Interface::Success { target_info, args } => {
1184 a[1] = target_info.into();
1185 match args {
1186 SuccessArgs::Result32(regs) => {
1187 a[2] = regs[0].into();
1188 a[3] = regs[1].into();
1189 a[4] = regs[2].into();
1190 a[5] = regs[3].into();
1191 a[6] = regs[4].into();
1192 a[7] = regs[5].into();
1193 }
1194 SuccessArgs::Result64(regs) => {
1195 a[2] = regs[0];
1196 a[3] = regs[1];
1197 a[4] = regs[2];
1198 a[5] = regs[3];
1199 a[6] = regs[4];
1200 a[7] = regs[5];
1201 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001202 _ => panic!("{:#x?} requires 18 registers", args),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001203 }
1204 }
1205 Interface::Interrupt {
1206 target_info,
1207 interrupt_id,
1208 } => {
1209 a[1] = u32::from(target_info).into();
1210 a[2] = interrupt_id.into();
1211 }
1212 Interface::Version { input_version } => {
1213 a[1] = u32::from(input_version).into();
1214 }
1215 Interface::VersionOut { output_version } => {
1216 a[0] = u32::from(output_version).into();
1217 }
1218 Interface::Features {
1219 feat_id,
1220 input_properties,
1221 } => {
1222 a[1] = u32::from(feat_id).into();
1223 a[2] = input_properties.into();
1224 }
1225 Interface::RxAcquire { vm_id } => {
1226 a[1] = vm_id.into();
1227 }
1228 Interface::RxRelease { vm_id } => {
1229 a[1] = vm_id.into();
1230 }
1231 Interface::RxTxMap { addr, page_cnt } => {
1232 match addr {
1233 RxTxAddr::Addr32 { rx, tx } => {
1234 a[1] = tx.into();
1235 a[2] = rx.into();
1236 }
1237 RxTxAddr::Addr64 { rx, tx } => {
1238 a[1] = tx;
1239 a[2] = rx;
1240 }
1241 }
1242 a[3] = page_cnt.into();
1243 }
1244 Interface::RxTxUnmap { id } => {
1245 a[1] = id.into();
1246 }
1247 Interface::PartitionInfoGet { uuid, flags } => {
1248 let bytes = uuid.into_bytes();
1249 a[1] = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]).into();
1250 a[2] = u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]).into();
1251 a[3] = u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]).into();
1252 a[4] = u32::from_le_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]).into();
1253 a[5] = flags.into();
1254 }
Tomás González092202a2025-03-05 11:56:45 +00001255 Interface::MsgWait { flags } => {
1256 if version >= Version(1, 2) {
1257 if let Some(flags) = flags {
1258 a[2] = u32::from(flags).into();
1259 }
1260 }
1261 }
1262 Interface::IdGet | Interface::SpmIdGet | Interface::Yield => {}
Balint Dobszay3aad9572025-01-17 16:54:11 +01001263 Interface::Run { target_info } => {
1264 a[1] = u32::from(target_info).into();
1265 }
1266 Interface::NormalWorldResume => {}
Tomás González17b92442025-03-10 16:45:04 +00001267 Interface::SecondaryEpRegister { entrypoint } => match entrypoint {
1268 SecondaryEpRegisterAddr::Addr32(addr) => a[1] = addr as u64,
1269 SecondaryEpRegisterAddr::Addr64(addr) => a[1] = addr,
1270 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001271 Interface::MsgSend2 {
1272 sender_vm_id,
1273 flags,
1274 } => {
1275 a[1] = sender_vm_id.into();
1276 a[2] = flags.into();
1277 }
1278 Interface::MsgSendDirectReq {
1279 src_id,
1280 dst_id,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001281 args,
1282 } => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +01001283 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Balint Dobszay3aad9572025-01-17 16:54:11 +01001284 match args {
1285 DirectMsgArgs::Args32(args) => {
1286 a[3] = args[0].into();
1287 a[4] = args[1].into();
1288 a[5] = args[2].into();
1289 a[6] = args[3].into();
1290 a[7] = args[4].into();
1291 }
1292 DirectMsgArgs::Args64(args) => {
1293 a[3] = args[0];
1294 a[4] = args[1];
1295 a[5] = args[2];
1296 a[6] = args[3];
1297 a[7] = args[4];
1298 }
Tomás González4d5b0ba2025-03-03 17:15:55 +00001299 DirectMsgArgs::VersionReq { version } => {
1300 a[2] = DirectMsgArgs::VERSION_REQ.into();
1301 a[3] = u32::from(version).into();
1302 }
Tomás González67f92c72025-03-20 16:50:42 +00001303 DirectMsgArgs::PowerPsciReq32 { params } => {
Tomás González4d5b0ba2025-03-03 17:15:55 +00001304 a[2] = DirectMsgArgs::POWER_PSCI_REQ.into();
Tomás González67f92c72025-03-20 16:50:42 +00001305 a[3] = params[0].into();
1306 a[4] = params[1].into();
1307 a[5] = params[2].into();
1308 a[6] = params[3].into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00001309 }
Tomás González67f92c72025-03-20 16:50:42 +00001310 DirectMsgArgs::PowerPsciReq64 { params } => {
Tomás González4d5b0ba2025-03-03 17:15:55 +00001311 a[2] = DirectMsgArgs::POWER_PSCI_REQ.into();
Tomás González67f92c72025-03-20 16:50:42 +00001312 a[3] = params[0];
1313 a[4] = params[1];
1314 a[5] = params[2];
1315 a[6] = params[3];
Tomás González4d5b0ba2025-03-03 17:15:55 +00001316 }
1317 DirectMsgArgs::PowerWarmBootReq { boot_type } => {
1318 a[2] = DirectMsgArgs::POWER_WARM_BOOT_REQ.into();
1319 a[3] = u32::from(boot_type).into();
1320 }
1321 DirectMsgArgs::VmCreated { handle, vm_id } => {
1322 a[2] = DirectMsgArgs::VM_CREATED.into();
1323 let handle_regs: [u32; 2] = handle.into();
1324 a[3] = handle_regs[0].into();
1325 a[4] = handle_regs[1].into();
1326 a[5] = vm_id.into();
1327 }
1328 DirectMsgArgs::VmDestructed { handle, vm_id } => {
1329 a[2] = DirectMsgArgs::VM_DESTRUCTED.into();
1330 let handle_regs: [u32; 2] = handle.into();
1331 a[3] = handle_regs[0].into();
1332 a[4] = handle_regs[1].into();
1333 a[5] = vm_id.into();
1334 }
1335 _ => panic!("Malformed MsgSendDirectReq interface"),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001336 }
1337 }
1338 Interface::MsgSendDirectResp {
1339 src_id,
1340 dst_id,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001341 args,
1342 } => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +01001343 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Balint Dobszay3aad9572025-01-17 16:54:11 +01001344 match args {
1345 DirectMsgArgs::Args32(args) => {
1346 a[3] = args[0].into();
1347 a[4] = args[1].into();
1348 a[5] = args[2].into();
1349 a[6] = args[3].into();
1350 a[7] = args[4].into();
1351 }
1352 DirectMsgArgs::Args64(args) => {
1353 a[3] = args[0];
1354 a[4] = args[1];
1355 a[5] = args[2];
1356 a[6] = args[3];
1357 a[7] = args[4];
1358 }
Tomás González4d5b0ba2025-03-03 17:15:55 +00001359 DirectMsgArgs::VersionResp { version } => {
1360 a[2] = DirectMsgArgs::VERSION_RESP.into();
1361 match version {
Tomás González67f92c72025-03-20 16:50:42 +00001362 None => a[3] = (i32::from(FfaError::NotSupported) as u32).into(),
Tomás González4d5b0ba2025-03-03 17:15:55 +00001363 Some(ver) => a[3] = u32::from(ver).into(),
1364 }
1365 }
1366 DirectMsgArgs::PowerPsciResp { psci_status } => {
1367 a[2] = DirectMsgArgs::POWER_PSCI_RESP.into();
1368 a[3] = psci_status as u64;
1369 }
1370 DirectMsgArgs::VmCreatedAck { sp_status } => {
1371 a[2] = DirectMsgArgs::VM_CREATED_ACK.into();
Tomás González67f92c72025-03-20 16:50:42 +00001372 a[3] = (i32::from(sp_status) as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00001373 }
1374 DirectMsgArgs::VmDestructedAck { sp_status } => {
1375 a[2] = DirectMsgArgs::VM_DESTRUCTED_ACK.into();
Tomás González67f92c72025-03-20 16:50:42 +00001376 a[3] = (i32::from(sp_status) as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00001377 }
1378 _ => panic!("Malformed MsgSendDirectResp interface"),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001379 }
1380 }
1381 Interface::MemDonate {
1382 total_len,
1383 frag_len,
1384 buf,
1385 } => {
1386 a[1] = total_len.into();
1387 a[2] = frag_len.into();
1388 (a[3], a[4]) = match buf {
1389 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1390 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1391 None => (0, 0),
1392 };
1393 }
1394 Interface::MemLend {
1395 total_len,
1396 frag_len,
1397 buf,
1398 } => {
1399 a[1] = total_len.into();
1400 a[2] = frag_len.into();
1401 (a[3], a[4]) = match buf {
1402 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1403 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1404 None => (0, 0),
1405 };
1406 }
1407 Interface::MemShare {
1408 total_len,
1409 frag_len,
1410 buf,
1411 } => {
1412 a[1] = total_len.into();
1413 a[2] = frag_len.into();
1414 (a[3], a[4]) = match buf {
1415 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1416 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1417 None => (0, 0),
1418 };
1419 }
1420 Interface::MemRetrieveReq {
1421 total_len,
1422 frag_len,
1423 buf,
1424 } => {
1425 a[1] = total_len.into();
1426 a[2] = frag_len.into();
1427 (a[3], a[4]) = match buf {
1428 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1429 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1430 None => (0, 0),
1431 };
1432 }
1433 Interface::MemRetrieveResp {
1434 total_len,
1435 frag_len,
1436 } => {
1437 a[1] = total_len.into();
1438 a[2] = frag_len.into();
1439 }
1440 Interface::MemRelinquish => {}
1441 Interface::MemReclaim { handle, flags } => {
1442 let handle_regs: [u32; 2] = handle.into();
1443 a[1] = handle_regs[0].into();
1444 a[2] = handle_regs[1].into();
1445 a[3] = flags.into();
1446 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001447 Interface::MemPermGet { addr, page_cnt } => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001448 a[1] = match addr {
1449 MemAddr::Addr32(addr) => addr.into(),
1450 MemAddr::Addr64(addr) => addr,
1451 };
Balint Dobszayde0dc802025-02-28 14:16:52 +01001452 a[2] = if version >= Version(1, 3) {
1453 page_cnt.unwrap().into()
1454 } else {
1455 assert!(page_cnt.is_none());
1456 0
1457 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01001458 }
1459 Interface::MemPermSet {
1460 addr,
1461 page_cnt,
1462 mem_perm,
1463 } => {
1464 a[1] = match addr {
1465 MemAddr::Addr32(addr) => addr.into(),
1466 MemAddr::Addr64(addr) => addr,
1467 };
1468 a[2] = page_cnt.into();
1469 a[3] = mem_perm.into();
1470 }
1471 Interface::ConsoleLog {
1472 char_cnt,
1473 char_lists,
1474 } => {
1475 a[1] = char_cnt.into();
1476 match char_lists {
1477 ConsoleLogChars::Reg32(regs) => {
1478 a[2] = regs[0].into();
1479 a[3] = regs[1].into();
1480 a[4] = regs[2].into();
1481 a[5] = regs[3].into();
1482 a[6] = regs[4].into();
1483 a[7] = regs[5].into();
1484 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001485 _ => panic!("{:#x?} requires 18 registers", char_lists),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001486 }
1487 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001488 _ => panic!("{:#x?} requires 18 registers", self),
1489 }
1490 }
1491
1492 fn pack_regs18(&self, version: Version, a: &mut [u64; 18]) {
1493 assert!(version >= Version(1, 2));
1494
1495 a.fill(0);
1496 if let Some(function_id) = self.function_id() {
1497 a[0] = function_id as u64;
1498 }
1499
1500 match *self {
1501 Interface::Success { target_info, args } => {
1502 a[1] = target_info.into();
1503 match args {
1504 SuccessArgs::Result64_2(regs) => a[2..18].copy_from_slice(&regs[..16]),
1505 _ => panic!("{:#x?} requires 8 registers", args),
1506 }
1507 }
1508 Interface::MsgSendDirectReq2 {
1509 src_id,
1510 dst_id,
1511 uuid,
1512 args,
1513 } => {
1514 a[1] = ((src_id as u64) << 16) | dst_id as u64;
1515 (a[2], a[3]) = uuid.as_u64_pair();
1516 a[4..18].copy_from_slice(&args.0[..14]);
1517 }
1518 Interface::MsgSendDirectResp2 {
1519 src_id,
1520 dst_id,
1521 args,
1522 } => {
1523 a[1] = ((src_id as u64) << 16) | dst_id as u64;
1524 a[2] = 0;
1525 a[3] = 0;
1526 a[4..18].copy_from_slice(&args.0[..14]);
1527 }
1528 Interface::ConsoleLog {
1529 char_cnt,
1530 char_lists,
1531 } => {
1532 a[1] = char_cnt.into();
1533 match char_lists {
1534 ConsoleLogChars::Reg64(regs) => a[2..18].copy_from_slice(&regs[..16]),
1535 _ => panic!("{:#x?} requires 8 registers", char_lists),
1536 }
1537 }
Tomás González0a058bc2025-03-11 11:20:55 +00001538 Interface::PartitionInfoGetRegs {
1539 uuid,
1540 start_index,
1541 info_tag,
1542 } => {
1543 if start_index == 0 && info_tag != 0 {
1544 panic!("Information Tag MBZ if start index is 0: {:#x?}", self);
1545 }
1546 let (uuid_msb, uuid_lsb) = uuid.as_u64_pair();
1547 (a[1], a[2]) = (uuid_msb.swap_bytes(), uuid_lsb.swap_bytes());
1548 a[3] = (u64::from(info_tag) << 16) | u64::from(start_index);
1549 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001550 _ => panic!("{:#x?} requires 8 registers", self),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001551 }
1552 }
1553
Balint Dobszaya5846852025-02-26 15:38:53 +01001554 /// Helper function to create an `FFA_SUCCESS` interface without any arguments.
Balint Dobszay3aad9572025-01-17 16:54:11 +01001555 pub fn success32_noargs() -> Self {
1556 Self::Success {
1557 target_info: 0,
1558 args: SuccessArgs::Result32([0, 0, 0, 0, 0, 0]),
1559 }
1560 }
1561
Tomás González4c8c7d22025-03-10 17:14:57 +00001562 /// Helper function to create an `FFA_SUCCESS` interface without any arguments.
1563 pub fn success64_noargs() -> Self {
1564 Self::Success {
1565 target_info: 0,
1566 args: SuccessArgs::Result64([0, 0, 0, 0, 0, 0]),
1567 }
1568 }
1569
Balint Dobszaya5846852025-02-26 15:38:53 +01001570 /// Helper function to create an `FFA_ERROR` interface with an error code.
Balint Dobszay3aad9572025-01-17 16:54:11 +01001571 pub fn error(error_code: FfaError) -> Self {
1572 Self::Error {
1573 target_info: TargetInfo {
1574 endpoint_id: 0,
1575 vcpu_id: 0,
1576 },
1577 error_code,
1578 }
1579 }
1580}
1581
Balint Dobszaya5846852025-02-26 15:38:53 +01001582/// Maximum number of characters transmitted in a single `FFA_CONSOLE_LOG32` message.
1583pub const CONSOLE_LOG_32_MAX_CHAR_CNT: u8 = 24;
Balint Dobszayde0dc802025-02-28 14:16:52 +01001584/// Maximum number of characters transmitted in a single `FFA_CONSOLE_LOG64` message.
1585pub const CONSOLE_LOG_64_MAX_CHAR_CNT: u8 = 128;
Balint Dobszay3aad9572025-01-17 16:54:11 +01001586
Balint Dobszaya5846852025-02-26 15:38:53 +01001587/// Helper function to convert the "Tightly packed list of characters" format used by the
1588/// `FFA_CONSOLE_LOG` interface into a byte slice.
Balint Dobszay3aad9572025-01-17 16:54:11 +01001589pub fn parse_console_log(
1590 char_cnt: u8,
1591 char_lists: &ConsoleLogChars,
1592 log_bytes: &mut [u8],
1593) -> Result<(), FfaError> {
1594 match char_lists {
1595 ConsoleLogChars::Reg32(regs) => {
Balint Dobszaya5846852025-02-26 15:38:53 +01001596 if !(1..=CONSOLE_LOG_32_MAX_CHAR_CNT).contains(&char_cnt) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001597 return Err(FfaError::InvalidParameters);
1598 }
1599 for (i, reg) in regs.iter().enumerate() {
1600 log_bytes[4 * i..4 * (i + 1)].copy_from_slice(&reg.to_le_bytes());
1601 }
1602 }
1603 ConsoleLogChars::Reg64(regs) => {
Balint Dobszaya5846852025-02-26 15:38:53 +01001604 if !(1..=CONSOLE_LOG_64_MAX_CHAR_CNT).contains(&char_cnt) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001605 return Err(FfaError::InvalidParameters);
1606 }
1607 for (i, reg) in regs.iter().enumerate() {
1608 log_bytes[8 * i..8 * (i + 1)].copy_from_slice(&reg.to_le_bytes());
1609 }
1610 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001611 }
1612
Balint Dobszayc8802492025-01-15 18:11:39 +01001613 Ok(())
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001614}
Tomás González0a058bc2025-03-11 11:20:55 +00001615
1616#[cfg(test)]
1617mod tests {
1618 use super::*;
1619
1620 #[test]
1621 fn part_info_get_regs() {
1622 let uuid = Uuid::parse_str("a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8").unwrap();
1623 let uuid_bytes = uuid.as_bytes();
1624 let test_info_tag = 0b1101_1101;
1625 let test_start_index = 0b1101;
1626 let start_index_and_tag = (test_info_tag << 16) | test_start_index;
1627 let version = Version(1, 2);
1628
1629 // From spec:
1630 // Bytes[0...7] of UUID with byte 0 in the low-order bits.
1631 let reg_x1 = (uuid_bytes[7] as u64) << 56
1632 | (uuid_bytes[6] as u64) << 48
1633 | (uuid_bytes[5] as u64) << 40
1634 | (uuid_bytes[4] as u64) << 32
1635 | (uuid_bytes[3] as u64) << 24
1636 | (uuid_bytes[2] as u64) << 16
1637 | (uuid_bytes[1] as u64) << 8
1638 | (uuid_bytes[0] as u64);
1639
1640 // From spec:
1641 // Bytes[8...15] of UUID with byte 8 in the low-order bits.
1642 let reg_x2 = (uuid_bytes[15] as u64) << 56
1643 | (uuid_bytes[14] as u64) << 48
1644 | (uuid_bytes[13] as u64) << 40
1645 | (uuid_bytes[12] as u64) << 32
1646 | (uuid_bytes[11] as u64) << 24
1647 | (uuid_bytes[10] as u64) << 16
1648 | (uuid_bytes[9] as u64) << 8
1649 | (uuid_bytes[8] as u64);
1650
1651 // First, test for wrong tag:
1652 {
1653 let mut regs = [0u64; 18];
1654 regs[0] = FuncId::PartitionInfoGetRegs as u64;
1655 regs[1] = reg_x1;
1656 regs[2] = reg_x2;
1657 regs[3] = test_info_tag << 16;
1658
1659 assert!(Interface::from_regs(version, &regs).is_err_and(
1660 |e| e == Error::InvalidInformationTag(test_info_tag.try_into().unwrap())
1661 ));
1662 }
1663
1664 // Test for regs -> Interface -> regs
1665 {
1666 let mut orig_regs = [0u64; 18];
1667 orig_regs[0] = FuncId::PartitionInfoGetRegs as u64;
1668 orig_regs[1] = reg_x1;
1669 orig_regs[2] = reg_x2;
1670 orig_regs[3] = start_index_and_tag;
1671
1672 let mut test_regs = orig_regs.clone();
1673 let interface = Interface::from_regs(version, &mut test_regs).unwrap();
1674 match &interface {
1675 Interface::PartitionInfoGetRegs {
1676 info_tag,
1677 start_index,
1678 uuid: int_uuid,
1679 } => {
1680 assert_eq!(u64::from(*info_tag), test_info_tag);
1681 assert_eq!(u64::from(*start_index), test_start_index);
1682 assert_eq!(*int_uuid, uuid);
1683 }
1684 _ => panic!("Expecting Interface::PartitionInfoGetRegs!"),
1685 }
1686 test_regs.fill(0);
1687 interface.to_regs(version, &mut test_regs);
1688 assert_eq!(orig_regs, test_regs);
1689 }
1690
1691 // Test for Interface -> regs -> Interface
1692 {
1693 let interface = Interface::PartitionInfoGetRegs {
1694 info_tag: test_info_tag.try_into().unwrap(),
1695 start_index: test_start_index.try_into().unwrap(),
1696 uuid,
1697 };
1698
1699 let mut regs: [u64; 18] = [0; 18];
1700 interface.to_regs(version, &mut regs);
1701
1702 assert_eq!(Some(FuncId::PartitionInfoGetRegs), interface.function_id());
1703 assert_eq!(regs[0], interface.function_id().unwrap() as u64);
1704 assert_eq!(regs[1], reg_x1);
1705 assert_eq!(regs[2], reg_x2);
1706 assert_eq!(regs[3], (test_info_tag << 16) | test_start_index);
1707
1708 assert_eq!(Interface::from_regs(version, &regs).unwrap(), interface);
1709 }
1710 }
1711}