blob: 0328a82dad0be05789cca2a124f888c292949476 [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),
36 #[error("Unrecognised FF-A Msg Wait Flag {0}")]
37 UnrecognisedMsgWaitFlag(u32),
38 #[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(_)
55 | Error::UnrecognisedMsgWaitFlag(_)
56 | 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 {
351 function_id: u32,
352 // params[i]: Input parameter in w[i] in PSCI function invocation at EL3.
353 params: [u32; 3],
354 },
355 /// Message for a power management operation initiated by a PSCI function
356 PowerPsciReq64 {
357 function_id: u32,
358 // params[i]: Input parameter in x[i] in PSCI function invocation at EL3.
359 params: [u64; 3],
360 },
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 {
369 // TODO: Use arm-psci crate's return status here.
370 psci_status: i32,
371 },
372 /// Message to signal creation of a VM
373 VmCreated {
374 // Globally unique Handle to identify a memory region that contains IMPLEMENTATION DEFINED
375 // information associated with the created VM.
376 // The invalid memory region handle must be specified by the Hypervisor if this field is not
377 // used.
378 handle: memory_management::Handle,
379 vm_id: u16,
380 },
381 /// Message to acknowledge creation of a VM
382 VmCreatedAck {
383 sp_status: VmAvailabilityStatus,
384 },
385 /// Message to signal destruction of a VM
386 VmDestructed {
387 // Globally unique Handle to identify a memory region that contains IMPLEMENTATION DEFINED
388 // information associated with the created VM.
389 // The invalid memory region handle must be specified by the Hypervisor if this field is not
390 // used.
391 handle: memory_management::Handle,
392 vm_id: u16,
393 },
394 /// Message to acknowledge destruction of a VM
395 VmDestructedAck {
396 sp_status: VmAvailabilityStatus,
397 },
398}
399
400impl DirectMsgArgs {
401 // Flags for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}` interfaces.
402
403 const FWK_MSG_BITS: u32 = 1 << 31;
404 const VERSION_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b1000;
405 const VERSION_RESP: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b1001;
406 const POWER_PSCI_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS;
407 const POWER_WARM_BOOT_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0001;
408 const POWER_PSCI_RESP: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0010;
409 const VM_CREATED: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0100;
410 const VM_CREATED_ACK: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0101;
411 const VM_DESTRUCTED: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0110;
412 const VM_DESTRUCTED_ACK: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0111;
Balint Dobszay3aad9572025-01-17 16:54:11 +0100413}
414
Balint Dobszayde0dc802025-02-28 14:16:52 +0100415/// Arguments for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}2` interfaces.
416#[derive(Debug, Eq, PartialEq, Clone, Copy)]
417pub struct DirectMsg2Args([u64; 14]);
418
Balint Dobszaya5846852025-02-26 15:38:53 +0100419/// Descriptor for a dynamically allocated memory buffer that contains the memory transaction
Tomás Gonzálezf268e322025-03-05 11:18:11 +0000420/// descriptor.
421///
422/// Used by `FFA_MEM_{DONATE,LEND,SHARE,RETRIEVE_REQ}` interfaces, only when the TX buffer is not
423/// used to transmit the transaction descriptor.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100424#[derive(Debug, Eq, PartialEq, Clone, Copy)]
425pub enum MemOpBuf {
426 Buf32 { addr: u32, page_cnt: u32 },
427 Buf64 { addr: u64, page_cnt: u32 },
428}
429
Balint Dobszaya5846852025-02-26 15:38:53 +0100430/// Memory address argument for `FFA_MEM_PERM_{GET,SET}` interfaces.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100431#[derive(Debug, Eq, PartialEq, Clone, Copy)]
432pub enum MemAddr {
433 Addr32(u32),
434 Addr64(u64),
435}
436
Balint Dobszayde0dc802025-02-28 14:16:52 +0100437/// Argument for the `FFA_CONSOLE_LOG` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100438#[derive(Debug, Eq, PartialEq, Clone, Copy)]
439pub enum ConsoleLogChars {
440 Reg32([u32; 6]),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100441 Reg64([u64; 16]),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100442}
443
Tomás Gonzálezf268e322025-03-05 11:18:11 +0000444/// FF-A "message types", the terminology used by the spec is "interfaces".
445///
446/// The interfaces are used by FF-A components for communication at an FF-A instance. The spec also
447/// describes the valid FF-A instances and conduits for each interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100448#[derive(Debug, Eq, PartialEq, Clone, Copy)]
449pub enum Interface {
450 Error {
451 target_info: TargetInfo,
452 error_code: FfaError,
453 },
454 Success {
455 target_info: u32,
456 args: SuccessArgs,
457 },
458 Interrupt {
459 target_info: TargetInfo,
460 interrupt_id: u32,
461 },
462 Version {
463 input_version: Version,
464 },
465 VersionOut {
466 output_version: Version,
467 },
468 Features {
469 feat_id: Feature,
470 input_properties: u32,
471 },
472 RxAcquire {
473 vm_id: u16,
474 },
475 RxRelease {
476 vm_id: u16,
477 },
478 RxTxMap {
479 addr: RxTxAddr,
480 page_cnt: u32,
481 },
482 RxTxUnmap {
483 id: u16,
484 },
485 PartitionInfoGet {
486 uuid: Uuid,
487 flags: u32,
488 },
489 IdGet,
490 SpmIdGet,
491 MsgWait,
492 Yield,
493 Run {
494 target_info: TargetInfo,
495 },
496 NormalWorldResume,
Tomás González17b92442025-03-10 16:45:04 +0000497 SecondaryEpRegister {
498 entrypoint: SecondaryEpRegisterAddr,
499 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100500 MsgSend2 {
501 sender_vm_id: u16,
502 flags: u32,
503 },
504 MsgSendDirectReq {
505 src_id: u16,
506 dst_id: u16,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100507 args: DirectMsgArgs,
508 },
509 MsgSendDirectResp {
510 src_id: u16,
511 dst_id: u16,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100512 args: DirectMsgArgs,
513 },
Balint Dobszayde0dc802025-02-28 14:16:52 +0100514 MsgSendDirectReq2 {
515 src_id: u16,
516 dst_id: u16,
517 uuid: Uuid,
518 args: DirectMsg2Args,
519 },
520 MsgSendDirectResp2 {
521 src_id: u16,
522 dst_id: u16,
523 args: DirectMsg2Args,
524 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100525 MemDonate {
526 total_len: u32,
527 frag_len: u32,
528 buf: Option<MemOpBuf>,
529 },
530 MemLend {
531 total_len: u32,
532 frag_len: u32,
533 buf: Option<MemOpBuf>,
534 },
535 MemShare {
536 total_len: u32,
537 frag_len: u32,
538 buf: Option<MemOpBuf>,
539 },
540 MemRetrieveReq {
541 total_len: u32,
542 frag_len: u32,
543 buf: Option<MemOpBuf>,
544 },
545 MemRetrieveResp {
546 total_len: u32,
547 frag_len: u32,
548 },
549 MemRelinquish,
550 MemReclaim {
551 handle: memory_management::Handle,
552 flags: u32,
553 },
554 MemPermGet {
555 addr: MemAddr,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100556 page_cnt: Option<u32>,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100557 },
558 MemPermSet {
559 addr: MemAddr,
560 page_cnt: u32,
561 mem_perm: u32,
562 },
563 ConsoleLog {
564 char_cnt: u8,
565 char_lists: ConsoleLogChars,
566 },
567}
568
Balint Dobszayde0dc802025-02-28 14:16:52 +0100569impl Interface {
570 /// Returns the function ID for the call, if it has one.
571 pub fn function_id(&self) -> Option<FuncId> {
572 match self {
573 Interface::Error { .. } => Some(FuncId::Error),
574 Interface::Success { args, .. } => match args {
575 SuccessArgs::Result32(..) => Some(FuncId::Success32),
576 SuccessArgs::Result64(..) | SuccessArgs::Result64_2(..) => Some(FuncId::Success64),
577 },
578 Interface::Interrupt { .. } => Some(FuncId::Interrupt),
579 Interface::Version { .. } => Some(FuncId::Version),
580 Interface::VersionOut { .. } => None,
581 Interface::Features { .. } => Some(FuncId::Features),
582 Interface::RxAcquire { .. } => Some(FuncId::RxAcquire),
583 Interface::RxRelease { .. } => Some(FuncId::RxRelease),
584 Interface::RxTxMap { addr, .. } => match addr {
585 RxTxAddr::Addr32 { .. } => Some(FuncId::RxTxMap32),
586 RxTxAddr::Addr64 { .. } => Some(FuncId::RxTxMap64),
587 },
588 Interface::RxTxUnmap { .. } => Some(FuncId::RxTxUnmap),
589 Interface::PartitionInfoGet { .. } => Some(FuncId::PartitionInfoGet),
590 Interface::IdGet => Some(FuncId::IdGet),
591 Interface::SpmIdGet => Some(FuncId::SpmIdGet),
592 Interface::MsgWait => Some(FuncId::MsgWait),
593 Interface::Yield => Some(FuncId::Yield),
594 Interface::Run { .. } => Some(FuncId::Run),
595 Interface::NormalWorldResume => Some(FuncId::NormalWorldResume),
Tomás González17b92442025-03-10 16:45:04 +0000596 Interface::SecondaryEpRegister { entrypoint } => match entrypoint {
597 SecondaryEpRegisterAddr::Addr32 { .. } => Some(FuncId::SecondaryEpRegister32),
598 SecondaryEpRegisterAddr::Addr64 { .. } => Some(FuncId::SecondaryEpRegister64),
599 },
Balint Dobszayde0dc802025-02-28 14:16:52 +0100600 Interface::MsgSend2 { .. } => Some(FuncId::MsgSend2),
601 Interface::MsgSendDirectReq { args, .. } => match args {
602 DirectMsgArgs::Args32(_) => Some(FuncId::MsgSendDirectReq32),
603 DirectMsgArgs::Args64(_) => Some(FuncId::MsgSendDirectReq64),
Tomás González4d5b0ba2025-03-03 17:15:55 +0000604 DirectMsgArgs::VersionReq { .. } => Some(FuncId::MsgSendDirectReq32),
605 DirectMsgArgs::PowerPsciReq32 { .. } => Some(FuncId::MsgSendDirectReq32),
606 DirectMsgArgs::PowerPsciReq64 { .. } => Some(FuncId::MsgSendDirectReq64),
607 DirectMsgArgs::PowerWarmBootReq { .. } => Some(FuncId::MsgSendDirectReq32),
608 DirectMsgArgs::VmCreated { .. } => Some(FuncId::MsgSendDirectReq32),
609 DirectMsgArgs::VmDestructed { .. } => Some(FuncId::MsgSendDirectReq32),
610 _ => None,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100611 },
612 Interface::MsgSendDirectResp { args, .. } => match args {
613 DirectMsgArgs::Args32(_) => Some(FuncId::MsgSendDirectResp32),
614 DirectMsgArgs::Args64(_) => Some(FuncId::MsgSendDirectResp64),
Tomás González4d5b0ba2025-03-03 17:15:55 +0000615 DirectMsgArgs::VersionResp { .. } => Some(FuncId::MsgSendDirectResp32),
616 DirectMsgArgs::PowerPsciResp { .. } => Some(FuncId::MsgSendDirectResp32),
617 DirectMsgArgs::VmCreatedAck { .. } => Some(FuncId::MsgSendDirectResp32),
618 DirectMsgArgs::VmDestructedAck { .. } => Some(FuncId::MsgSendDirectResp32),
619 _ => None,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100620 },
621 Interface::MsgSendDirectReq2 { .. } => Some(FuncId::MsgSendDirectReq64_2),
622 Interface::MsgSendDirectResp2 { .. } => Some(FuncId::MsgSendDirectResp64_2),
623 Interface::MemDonate { buf, .. } => match buf {
624 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemDonate64),
625 _ => Some(FuncId::MemDonate32),
626 },
627 Interface::MemLend { buf, .. } => match buf {
628 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemLend64),
629 _ => Some(FuncId::MemLend32),
630 },
631 Interface::MemShare { buf, .. } => match buf {
632 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemShare64),
633 _ => Some(FuncId::MemShare32),
634 },
635 Interface::MemRetrieveReq { buf, .. } => match buf {
636 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemRetrieveReq64),
637 _ => Some(FuncId::MemRetrieveReq32),
638 },
639 Interface::MemRetrieveResp { .. } => Some(FuncId::MemRetrieveResp),
640 Interface::MemRelinquish => Some(FuncId::MemRelinquish),
641 Interface::MemReclaim { .. } => Some(FuncId::MemReclaim),
642 Interface::MemPermGet { addr, .. } => match addr {
643 MemAddr::Addr32(_) => Some(FuncId::MemPermGet32),
644 MemAddr::Addr64(_) => Some(FuncId::MemPermGet64),
645 },
646 Interface::MemPermSet { addr, .. } => match addr {
647 MemAddr::Addr32(_) => Some(FuncId::MemPermSet32),
648 MemAddr::Addr64(_) => Some(FuncId::MemPermSet64),
649 },
650 Interface::ConsoleLog { char_lists, .. } => match char_lists {
651 ConsoleLogChars::Reg32(_) => Some(FuncId::ConsoleLog32),
652 ConsoleLogChars::Reg64(_) => Some(FuncId::ConsoleLog64),
653 },
654 }
655 }
Balint Dobszay3aad9572025-01-17 16:54:11 +0100656
Balint Dobszayde0dc802025-02-28 14:16:52 +0100657 /// Returns true if this is a 32-bit call, or false if it is a 64-bit call.
658 pub fn is_32bit(&self) -> bool {
659 // TODO: self should always have a function ID?
660 self.function_id().unwrap().is_32bit()
661 }
662
663 /// Parse interface from register contents. The caller must ensure that the `regs` argument has
664 /// the correct length: 8 registers for FF-A v1.1 and lower, 18 registers for v1.2 and higher.
665 pub fn from_regs(version: Version, regs: &[u64]) -> Result<Self, Error> {
666 let reg_cnt = regs.len();
667
668 let msg = match reg_cnt {
669 8 => {
670 assert!(version <= Version(1, 1));
671 Interface::unpack_regs8(version, regs.try_into().unwrap())?
672 }
673 18 => {
674 assert!(version >= Version(1, 2));
675 match FuncId::try_from(regs[0] as u32)? {
676 FuncId::ConsoleLog64
677 | FuncId::Success64
678 | FuncId::MsgSendDirectReq64_2
679 | FuncId::MsgSendDirectResp64_2 => {
680 Interface::unpack_regs18(version, regs.try_into().unwrap())?
681 }
682 _ => Interface::unpack_regs8(version, regs[..8].try_into().unwrap())?,
683 }
684 }
685 _ => panic!(
686 "Invalid number of registers ({}) for FF-A version {}",
687 reg_cnt, version
688 ),
689 };
690
691 Ok(msg)
692 }
693
694 fn unpack_regs8(version: Version, regs: &[u64; 8]) -> Result<Self, Error> {
Balint Dobszay3aad9572025-01-17 16:54:11 +0100695 let fid = FuncId::try_from(regs[0] as u32)?;
696
697 let msg = match fid {
698 FuncId::Error => Self::Error {
699 target_info: (regs[1] as u32).into(),
700 error_code: FfaError::try_from(regs[2] as i32)?,
701 },
702 FuncId::Success32 => Self::Success {
703 target_info: regs[1] as u32,
704 args: SuccessArgs::Result32([
705 regs[2] as u32,
706 regs[3] as u32,
707 regs[4] as u32,
708 regs[5] as u32,
709 regs[6] as u32,
710 regs[7] as u32,
711 ]),
712 },
713 FuncId::Success64 => Self::Success {
714 target_info: regs[1] as u32,
715 args: SuccessArgs::Result64([regs[2], regs[3], regs[4], regs[5], regs[6], regs[7]]),
716 },
717 FuncId::Interrupt => Self::Interrupt {
718 target_info: (regs[1] as u32).into(),
719 interrupt_id: regs[2] as u32,
720 },
721 FuncId::Version => Self::Version {
Tomás González83146af2025-03-04 11:32:41 +0000722 input_version: (regs[1] as u32).try_into()?,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100723 },
724 FuncId::Features => Self::Features {
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100725 feat_id: (regs[1] as u32).into(),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100726 input_properties: regs[2] as u32,
727 },
728 FuncId::RxAcquire => Self::RxAcquire {
729 vm_id: regs[1] as u16,
730 },
731 FuncId::RxRelease => Self::RxRelease {
732 vm_id: regs[1] as u16,
733 },
734 FuncId::RxTxMap32 => {
735 let addr = RxTxAddr::Addr32 {
736 rx: regs[2] as u32,
737 tx: regs[1] as u32,
738 };
739 let page_cnt = regs[3] as u32;
740
741 Self::RxTxMap { addr, page_cnt }
742 }
743 FuncId::RxTxMap64 => {
744 let addr = RxTxAddr::Addr64 {
745 rx: regs[2],
746 tx: regs[1],
747 };
748 let page_cnt = regs[3] as u32;
749
750 Self::RxTxMap { addr, page_cnt }
751 }
752 FuncId::RxTxUnmap => Self::RxTxUnmap { id: regs[1] as u16 },
753 FuncId::PartitionInfoGet => {
754 let uuid_words = [
755 regs[1] as u32,
756 regs[2] as u32,
757 regs[3] as u32,
758 regs[4] as u32,
759 ];
760 let mut bytes: [u8; 16] = [0; 16];
761 for (i, b) in uuid_words.iter().flat_map(|w| w.to_le_bytes()).enumerate() {
762 bytes[i] = b;
763 }
764 Self::PartitionInfoGet {
765 uuid: Uuid::from_bytes(bytes),
766 flags: regs[5] as u32,
767 }
768 }
769 FuncId::IdGet => Self::IdGet,
770 FuncId::SpmIdGet => Self::SpmIdGet,
771 FuncId::MsgWait => Self::MsgWait,
772 FuncId::Yield => Self::Yield,
773 FuncId::Run => Self::Run {
774 target_info: (regs[1] as u32).into(),
775 },
776 FuncId::NormalWorldResume => Self::NormalWorldResume,
Tomás González17b92442025-03-10 16:45:04 +0000777 FuncId::SecondaryEpRegister32 => Self::SecondaryEpRegister {
778 entrypoint: SecondaryEpRegisterAddr::Addr32(regs[1] as u32),
779 },
780 FuncId::SecondaryEpRegister64 => Self::SecondaryEpRegister {
781 entrypoint: SecondaryEpRegisterAddr::Addr64(regs[1]),
782 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100783 FuncId::MsgSend2 => Self::MsgSend2 {
784 sender_vm_id: regs[1] as u16,
785 flags: regs[2] as u32,
786 },
787 FuncId::MsgSendDirectReq32 => Self::MsgSendDirectReq {
788 src_id: (regs[1] >> 16) as u16,
789 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +0000790 args: if (regs[2] as u32 & DirectMsgArgs::FWK_MSG_BITS) != 0 {
791 match regs[2] as u32 {
792 DirectMsgArgs::VERSION_REQ => DirectMsgArgs::VersionReq {
793 version: Version::try_from(regs[3] as u32)?,
794 },
795 DirectMsgArgs::POWER_PSCI_REQ => DirectMsgArgs::PowerPsciReq32 {
796 function_id: regs[3] as u32,
797 params: [regs[4] as u32, regs[5] as u32, regs[6] as u32],
798 },
799 DirectMsgArgs::POWER_WARM_BOOT_REQ => DirectMsgArgs::PowerWarmBootReq {
800 boot_type: WarmBootType::try_from(regs[3] as u32)?,
801 },
802 DirectMsgArgs::VM_CREATED => DirectMsgArgs::VmCreated {
803 handle: memory_management::Handle::from([
804 regs[3] as u32,
805 regs[4] as u32,
806 ]),
807 vm_id: regs[5] as u16,
808 },
809 DirectMsgArgs::VM_DESTRUCTED => DirectMsgArgs::VmDestructed {
810 handle: memory_management::Handle::from([
811 regs[3] as u32,
812 regs[4] as u32,
813 ]),
814 vm_id: regs[5] as u16,
815 },
816 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
817 }
818 } else {
819 DirectMsgArgs::Args32([
820 regs[3] as u32,
821 regs[4] as u32,
822 regs[5] as u32,
823 regs[6] as u32,
824 regs[7] as u32,
825 ])
826 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100827 },
828 FuncId::MsgSendDirectReq64 => 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] & DirectMsgArgs::FWK_MSG_BITS as u64) != 0 {
832 match regs[2] as u32 {
833 DirectMsgArgs::POWER_PSCI_REQ => DirectMsgArgs::PowerPsciReq64 {
834 function_id: regs[3] as u32,
835 params: [regs[4], regs[5], regs[6]],
836 },
837 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
838 }
839 } else {
840 DirectMsgArgs::Args64([regs[3], regs[4], regs[5], regs[6], regs[7]])
841 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100842 },
843 FuncId::MsgSendDirectResp32 => Self::MsgSendDirectResp {
844 src_id: (regs[1] >> 16) as u16,
845 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +0000846 args: if (regs[2] as u32 & DirectMsgArgs::FWK_MSG_BITS) != 0 {
847 match regs[2] as u32 {
848 DirectMsgArgs::VERSION_RESP => {
849 if regs[3] as i32 == FfaError::NotSupported.into() {
850 DirectMsgArgs::VersionResp { version: None }
851 } else {
852 DirectMsgArgs::VersionResp {
853 version: Some(Version::try_from(regs[3] as u32)?),
854 }
855 }
856 }
857 DirectMsgArgs::POWER_PSCI_RESP => DirectMsgArgs::PowerPsciResp {
858 psci_status: regs[3] as i32,
859 },
860 DirectMsgArgs::VM_CREATED_ACK => DirectMsgArgs::VmCreatedAck {
861 sp_status: (regs[3] as i32).try_into()?,
862 },
863 DirectMsgArgs::VM_DESTRUCTED_ACK => DirectMsgArgs::VmDestructedAck {
864 sp_status: (regs[3] as i32).try_into()?,
865 },
866 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
867 }
868 } else {
869 DirectMsgArgs::Args32([
870 regs[3] as u32,
871 regs[4] as u32,
872 regs[5] as u32,
873 regs[6] as u32,
874 regs[7] as u32,
875 ])
876 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100877 },
878 FuncId::MsgSendDirectResp64 => Self::MsgSendDirectResp {
879 src_id: (regs[1] >> 16) as u16,
880 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +0000881 args: if (regs[2] & DirectMsgArgs::FWK_MSG_BITS as u64) != 0 {
882 return Err(Error::UnrecognisedFwkMsg(regs[2] as u32));
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::MemDonate32 => Self::MemDonate {
888 total_len: regs[1] as u32,
889 frag_len: regs[2] as u32,
890 buf: if regs[3] != 0 && regs[4] != 0 {
891 Some(MemOpBuf::Buf32 {
892 addr: regs[3] as u32,
893 page_cnt: regs[4] as u32,
894 })
895 } else {
896 None
897 },
898 },
899 FuncId::MemDonate64 => Self::MemDonate {
900 total_len: regs[1] as u32,
901 frag_len: regs[2] as u32,
902 buf: if regs[3] != 0 && regs[4] != 0 {
903 Some(MemOpBuf::Buf64 {
904 addr: regs[3],
905 page_cnt: regs[4] as u32,
906 })
907 } else {
908 None
909 },
910 },
911 FuncId::MemLend32 => Self::MemLend {
912 total_len: regs[1] as u32,
913 frag_len: regs[2] as u32,
914 buf: if regs[3] != 0 && regs[4] != 0 {
915 Some(MemOpBuf::Buf32 {
916 addr: regs[3] as u32,
917 page_cnt: regs[4] as u32,
918 })
919 } else {
920 None
921 },
922 },
923 FuncId::MemLend64 => Self::MemLend {
924 total_len: regs[1] as u32,
925 frag_len: regs[2] as u32,
926 buf: if regs[3] != 0 && regs[4] != 0 {
927 Some(MemOpBuf::Buf64 {
928 addr: regs[3],
929 page_cnt: regs[4] as u32,
930 })
931 } else {
932 None
933 },
934 },
935 FuncId::MemShare32 => Self::MemShare {
936 total_len: regs[1] as u32,
937 frag_len: regs[2] as u32,
938 buf: if regs[3] != 0 && regs[4] != 0 {
939 Some(MemOpBuf::Buf32 {
940 addr: regs[3] as u32,
941 page_cnt: regs[4] as u32,
942 })
943 } else {
944 None
945 },
946 },
947 FuncId::MemShare64 => Self::MemShare {
948 total_len: regs[1] as u32,
949 frag_len: regs[2] as u32,
950 buf: if regs[3] != 0 && regs[4] != 0 {
951 Some(MemOpBuf::Buf64 {
952 addr: regs[3],
953 page_cnt: regs[4] as u32,
954 })
955 } else {
956 None
957 },
958 },
959 FuncId::MemRetrieveReq32 => Self::MemRetrieveReq {
960 total_len: regs[1] as u32,
961 frag_len: regs[2] as u32,
962 buf: if regs[3] != 0 && regs[4] != 0 {
963 Some(MemOpBuf::Buf32 {
964 addr: regs[3] as u32,
965 page_cnt: regs[4] as u32,
966 })
967 } else {
968 None
969 },
970 },
971 FuncId::MemRetrieveReq64 => Self::MemRetrieveReq {
972 total_len: regs[1] as u32,
973 frag_len: regs[2] as u32,
974 buf: if regs[3] != 0 && regs[4] != 0 {
975 Some(MemOpBuf::Buf64 {
976 addr: regs[3],
977 page_cnt: regs[4] as u32,
978 })
979 } else {
980 None
981 },
982 },
983 FuncId::MemRetrieveResp => Self::MemRetrieveResp {
984 total_len: regs[1] as u32,
985 frag_len: regs[2] as u32,
986 },
987 FuncId::MemRelinquish => Self::MemRelinquish,
988 FuncId::MemReclaim => Self::MemReclaim {
989 handle: memory_management::Handle::from([regs[1] as u32, regs[2] as u32]),
990 flags: regs[3] as u32,
991 },
992 FuncId::MemPermGet32 => Self::MemPermGet {
993 addr: MemAddr::Addr32(regs[1] as u32),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100994 page_cnt: if version >= Version(1, 3) {
995 Some(regs[2] as u32)
996 } else {
997 None
998 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100999 },
1000 FuncId::MemPermGet64 => Self::MemPermGet {
1001 addr: MemAddr::Addr64(regs[1]),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001002 page_cnt: if version >= Version(1, 3) {
1003 Some(regs[2] as u32)
1004 } else {
1005 None
1006 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001007 },
1008 FuncId::MemPermSet32 => Self::MemPermSet {
1009 addr: MemAddr::Addr32(regs[1] as u32),
1010 page_cnt: regs[2] as u32,
1011 mem_perm: regs[3] as u32,
1012 },
1013 FuncId::MemPermSet64 => Self::MemPermSet {
1014 addr: MemAddr::Addr64(regs[1]),
1015 page_cnt: regs[2] as u32,
1016 mem_perm: regs[3] as u32,
1017 },
1018 FuncId::ConsoleLog32 => Self::ConsoleLog {
1019 char_cnt: regs[1] as u8,
1020 char_lists: ConsoleLogChars::Reg32([
1021 regs[2] as u32,
1022 regs[3] as u32,
1023 regs[4] as u32,
1024 regs[5] as u32,
1025 regs[6] as u32,
1026 regs[7] as u32,
1027 ]),
1028 },
Balint Dobszayde0dc802025-02-28 14:16:52 +01001029 _ => panic!("Invalid number of registers (8) for function {:#x?}", fid),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001030 };
1031
1032 Ok(msg)
1033 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01001034
Balint Dobszayde0dc802025-02-28 14:16:52 +01001035 fn unpack_regs18(version: Version, regs: &[u64; 18]) -> Result<Self, Error> {
1036 assert!(version >= Version(1, 2));
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001037
Balint Dobszayde0dc802025-02-28 14:16:52 +01001038 let fid = FuncId::try_from(regs[0] as u32)?;
1039
1040 let msg = match fid {
1041 FuncId::Success64 => Self::Success {
1042 target_info: regs[1] as u32,
1043 args: SuccessArgs::Result64_2(regs[2..18].try_into().unwrap()),
1044 },
1045 FuncId::MsgSendDirectReq64_2 => Self::MsgSendDirectReq2 {
1046 src_id: (regs[1] >> 16) as u16,
1047 dst_id: regs[1] as u16,
1048 uuid: Uuid::from_u64_pair(regs[2], regs[3]),
1049 args: DirectMsg2Args(regs[4..18].try_into().unwrap()),
1050 },
1051 FuncId::MsgSendDirectResp64_2 => Self::MsgSendDirectResp2 {
1052 src_id: (regs[1] >> 16) as u16,
1053 dst_id: regs[1] as u16,
1054 args: DirectMsg2Args(regs[4..18].try_into().unwrap()),
1055 },
1056 FuncId::ConsoleLog64 => Self::ConsoleLog {
1057 char_cnt: regs[1] as u8,
1058 char_lists: ConsoleLogChars::Reg64(regs[2..18].try_into().unwrap()),
1059 },
1060 _ => panic!("Invalid number of registers (18) for function {:#x?}", fid),
1061 };
1062
1063 Ok(msg)
Balint Dobszay3aad9572025-01-17 16:54:11 +01001064 }
1065
Balint Dobszaya5846852025-02-26 15:38:53 +01001066 /// Create register contents for an interface.
Balint Dobszayde0dc802025-02-28 14:16:52 +01001067 pub fn to_regs(&self, version: Version, regs: &mut [u64]) {
1068 let reg_cnt = regs.len();
1069
1070 match reg_cnt {
1071 8 => {
1072 assert!(version <= Version(1, 1));
1073 self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
1074 }
1075 18 => {
1076 assert!(version >= Version(1, 2));
1077
1078 match self {
1079 Interface::ConsoleLog {
1080 char_lists: ConsoleLogChars::Reg64(_),
1081 ..
1082 }
1083 | Interface::Success {
1084 args: SuccessArgs::Result64_2(_),
1085 ..
1086 }
1087 | Interface::MsgSendDirectReq2 { .. }
1088 | Interface::MsgSendDirectResp2 { .. } => {
1089 self.pack_regs18(version, regs.try_into().unwrap());
1090 }
1091 _ => {
1092 self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
1093 }
1094 }
1095 }
1096 _ => panic!("Invalid number of registers {}", reg_cnt),
1097 }
1098 }
1099
1100 fn pack_regs8(&self, version: Version, a: &mut [u64; 8]) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001101 a.fill(0);
1102 if let Some(function_id) = self.function_id() {
1103 a[0] = function_id as u64;
1104 }
1105
1106 match *self {
1107 Interface::Error {
1108 target_info,
1109 error_code,
1110 } => {
1111 a[1] = u32::from(target_info).into();
1112 a[2] = (error_code as u32).into();
1113 }
1114 Interface::Success { target_info, args } => {
1115 a[1] = target_info.into();
1116 match args {
1117 SuccessArgs::Result32(regs) => {
1118 a[2] = regs[0].into();
1119 a[3] = regs[1].into();
1120 a[4] = regs[2].into();
1121 a[5] = regs[3].into();
1122 a[6] = regs[4].into();
1123 a[7] = regs[5].into();
1124 }
1125 SuccessArgs::Result64(regs) => {
1126 a[2] = regs[0];
1127 a[3] = regs[1];
1128 a[4] = regs[2];
1129 a[5] = regs[3];
1130 a[6] = regs[4];
1131 a[7] = regs[5];
1132 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001133 _ => panic!("{:#x?} requires 18 registers", args),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001134 }
1135 }
1136 Interface::Interrupt {
1137 target_info,
1138 interrupt_id,
1139 } => {
1140 a[1] = u32::from(target_info).into();
1141 a[2] = interrupt_id.into();
1142 }
1143 Interface::Version { input_version } => {
1144 a[1] = u32::from(input_version).into();
1145 }
1146 Interface::VersionOut { output_version } => {
1147 a[0] = u32::from(output_version).into();
1148 }
1149 Interface::Features {
1150 feat_id,
1151 input_properties,
1152 } => {
1153 a[1] = u32::from(feat_id).into();
1154 a[2] = input_properties.into();
1155 }
1156 Interface::RxAcquire { vm_id } => {
1157 a[1] = vm_id.into();
1158 }
1159 Interface::RxRelease { vm_id } => {
1160 a[1] = vm_id.into();
1161 }
1162 Interface::RxTxMap { addr, page_cnt } => {
1163 match addr {
1164 RxTxAddr::Addr32 { rx, tx } => {
1165 a[1] = tx.into();
1166 a[2] = rx.into();
1167 }
1168 RxTxAddr::Addr64 { rx, tx } => {
1169 a[1] = tx;
1170 a[2] = rx;
1171 }
1172 }
1173 a[3] = page_cnt.into();
1174 }
1175 Interface::RxTxUnmap { id } => {
1176 a[1] = id.into();
1177 }
1178 Interface::PartitionInfoGet { uuid, flags } => {
1179 let bytes = uuid.into_bytes();
1180 a[1] = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]).into();
1181 a[2] = u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]).into();
1182 a[3] = u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]).into();
1183 a[4] = u32::from_le_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]).into();
1184 a[5] = flags.into();
1185 }
1186 Interface::IdGet | Interface::SpmIdGet | Interface::MsgWait | Interface::Yield => {}
1187 Interface::Run { target_info } => {
1188 a[1] = u32::from(target_info).into();
1189 }
1190 Interface::NormalWorldResume => {}
Tomás González17b92442025-03-10 16:45:04 +00001191 Interface::SecondaryEpRegister { entrypoint } => match entrypoint {
1192 SecondaryEpRegisterAddr::Addr32(addr) => a[1] = addr as u64,
1193 SecondaryEpRegisterAddr::Addr64(addr) => a[1] = addr,
1194 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001195 Interface::MsgSend2 {
1196 sender_vm_id,
1197 flags,
1198 } => {
1199 a[1] = sender_vm_id.into();
1200 a[2] = flags.into();
1201 }
1202 Interface::MsgSendDirectReq {
1203 src_id,
1204 dst_id,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001205 args,
1206 } => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +01001207 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Balint Dobszay3aad9572025-01-17 16:54:11 +01001208 match args {
1209 DirectMsgArgs::Args32(args) => {
1210 a[3] = args[0].into();
1211 a[4] = args[1].into();
1212 a[5] = args[2].into();
1213 a[6] = args[3].into();
1214 a[7] = args[4].into();
1215 }
1216 DirectMsgArgs::Args64(args) => {
1217 a[3] = args[0];
1218 a[4] = args[1];
1219 a[5] = args[2];
1220 a[6] = args[3];
1221 a[7] = args[4];
1222 }
Tomás González4d5b0ba2025-03-03 17:15:55 +00001223 DirectMsgArgs::VersionReq { version } => {
1224 a[2] = DirectMsgArgs::VERSION_REQ.into();
1225 a[3] = u32::from(version).into();
1226 }
1227 DirectMsgArgs::PowerPsciReq32 {
1228 function_id,
1229 params,
1230 } => {
1231 a[2] = DirectMsgArgs::POWER_PSCI_REQ.into();
1232 a[3] = function_id.into();
1233 a[4] = params[0].into();
1234 a[5] = params[1].into();
1235 a[6] = params[2].into();
1236 }
1237 DirectMsgArgs::PowerPsciReq64 {
1238 function_id,
1239 params,
1240 } => {
1241 a[2] = DirectMsgArgs::POWER_PSCI_REQ.into();
1242 a[3] = function_id.into();
1243 a[4] = params[0];
1244 a[5] = params[1];
1245 a[6] = params[2];
1246 }
1247 DirectMsgArgs::PowerWarmBootReq { boot_type } => {
1248 a[2] = DirectMsgArgs::POWER_WARM_BOOT_REQ.into();
1249 a[3] = u32::from(boot_type).into();
1250 }
1251 DirectMsgArgs::VmCreated { handle, vm_id } => {
1252 a[2] = DirectMsgArgs::VM_CREATED.into();
1253 let handle_regs: [u32; 2] = handle.into();
1254 a[3] = handle_regs[0].into();
1255 a[4] = handle_regs[1].into();
1256 a[5] = vm_id.into();
1257 }
1258 DirectMsgArgs::VmDestructed { handle, vm_id } => {
1259 a[2] = DirectMsgArgs::VM_DESTRUCTED.into();
1260 let handle_regs: [u32; 2] = handle.into();
1261 a[3] = handle_regs[0].into();
1262 a[4] = handle_regs[1].into();
1263 a[5] = vm_id.into();
1264 }
1265 _ => panic!("Malformed MsgSendDirectReq interface"),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001266 }
1267 }
1268 Interface::MsgSendDirectResp {
1269 src_id,
1270 dst_id,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001271 args,
1272 } => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +01001273 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Balint Dobszay3aad9572025-01-17 16:54:11 +01001274 match args {
1275 DirectMsgArgs::Args32(args) => {
1276 a[3] = args[0].into();
1277 a[4] = args[1].into();
1278 a[5] = args[2].into();
1279 a[6] = args[3].into();
1280 a[7] = args[4].into();
1281 }
1282 DirectMsgArgs::Args64(args) => {
1283 a[3] = args[0];
1284 a[4] = args[1];
1285 a[5] = args[2];
1286 a[6] = args[3];
1287 a[7] = args[4];
1288 }
Tomás González4d5b0ba2025-03-03 17:15:55 +00001289 DirectMsgArgs::VersionResp { version } => {
1290 a[2] = DirectMsgArgs::VERSION_RESP.into();
1291 match version {
1292 None => a[3] = i32::from(FfaError::NotSupported) as u64,
1293 Some(ver) => a[3] = u32::from(ver).into(),
1294 }
1295 }
1296 DirectMsgArgs::PowerPsciResp { psci_status } => {
1297 a[2] = DirectMsgArgs::POWER_PSCI_RESP.into();
1298 a[3] = psci_status as u64;
1299 }
1300 DirectMsgArgs::VmCreatedAck { sp_status } => {
1301 a[2] = DirectMsgArgs::VM_CREATED_ACK.into();
1302 a[4] = i32::from(sp_status) as u64;
1303 }
1304 DirectMsgArgs::VmDestructedAck { sp_status } => {
1305 a[2] = DirectMsgArgs::VM_DESTRUCTED_ACK.into();
1306 a[3] = i32::from(sp_status) as u64;
1307 }
1308 _ => panic!("Malformed MsgSendDirectResp interface"),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001309 }
1310 }
1311 Interface::MemDonate {
1312 total_len,
1313 frag_len,
1314 buf,
1315 } => {
1316 a[1] = total_len.into();
1317 a[2] = frag_len.into();
1318 (a[3], a[4]) = match buf {
1319 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1320 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1321 None => (0, 0),
1322 };
1323 }
1324 Interface::MemLend {
1325 total_len,
1326 frag_len,
1327 buf,
1328 } => {
1329 a[1] = total_len.into();
1330 a[2] = frag_len.into();
1331 (a[3], a[4]) = match buf {
1332 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1333 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1334 None => (0, 0),
1335 };
1336 }
1337 Interface::MemShare {
1338 total_len,
1339 frag_len,
1340 buf,
1341 } => {
1342 a[1] = total_len.into();
1343 a[2] = frag_len.into();
1344 (a[3], a[4]) = match buf {
1345 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1346 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1347 None => (0, 0),
1348 };
1349 }
1350 Interface::MemRetrieveReq {
1351 total_len,
1352 frag_len,
1353 buf,
1354 } => {
1355 a[1] = total_len.into();
1356 a[2] = frag_len.into();
1357 (a[3], a[4]) = match buf {
1358 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1359 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1360 None => (0, 0),
1361 };
1362 }
1363 Interface::MemRetrieveResp {
1364 total_len,
1365 frag_len,
1366 } => {
1367 a[1] = total_len.into();
1368 a[2] = frag_len.into();
1369 }
1370 Interface::MemRelinquish => {}
1371 Interface::MemReclaim { handle, flags } => {
1372 let handle_regs: [u32; 2] = handle.into();
1373 a[1] = handle_regs[0].into();
1374 a[2] = handle_regs[1].into();
1375 a[3] = flags.into();
1376 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001377 Interface::MemPermGet { addr, page_cnt } => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001378 a[1] = match addr {
1379 MemAddr::Addr32(addr) => addr.into(),
1380 MemAddr::Addr64(addr) => addr,
1381 };
Balint Dobszayde0dc802025-02-28 14:16:52 +01001382 a[2] = if version >= Version(1, 3) {
1383 page_cnt.unwrap().into()
1384 } else {
1385 assert!(page_cnt.is_none());
1386 0
1387 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01001388 }
1389 Interface::MemPermSet {
1390 addr,
1391 page_cnt,
1392 mem_perm,
1393 } => {
1394 a[1] = match addr {
1395 MemAddr::Addr32(addr) => addr.into(),
1396 MemAddr::Addr64(addr) => addr,
1397 };
1398 a[2] = page_cnt.into();
1399 a[3] = mem_perm.into();
1400 }
1401 Interface::ConsoleLog {
1402 char_cnt,
1403 char_lists,
1404 } => {
1405 a[1] = char_cnt.into();
1406 match char_lists {
1407 ConsoleLogChars::Reg32(regs) => {
1408 a[2] = regs[0].into();
1409 a[3] = regs[1].into();
1410 a[4] = regs[2].into();
1411 a[5] = regs[3].into();
1412 a[6] = regs[4].into();
1413 a[7] = regs[5].into();
1414 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001415 _ => panic!("{:#x?} requires 18 registers", char_lists),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001416 }
1417 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001418 _ => panic!("{:#x?} requires 18 registers", self),
1419 }
1420 }
1421
1422 fn pack_regs18(&self, version: Version, a: &mut [u64; 18]) {
1423 assert!(version >= Version(1, 2));
1424
1425 a.fill(0);
1426 if let Some(function_id) = self.function_id() {
1427 a[0] = function_id as u64;
1428 }
1429
1430 match *self {
1431 Interface::Success { target_info, args } => {
1432 a[1] = target_info.into();
1433 match args {
1434 SuccessArgs::Result64_2(regs) => a[2..18].copy_from_slice(&regs[..16]),
1435 _ => panic!("{:#x?} requires 8 registers", args),
1436 }
1437 }
1438 Interface::MsgSendDirectReq2 {
1439 src_id,
1440 dst_id,
1441 uuid,
1442 args,
1443 } => {
1444 a[1] = ((src_id as u64) << 16) | dst_id as u64;
1445 (a[2], a[3]) = uuid.as_u64_pair();
1446 a[4..18].copy_from_slice(&args.0[..14]);
1447 }
1448 Interface::MsgSendDirectResp2 {
1449 src_id,
1450 dst_id,
1451 args,
1452 } => {
1453 a[1] = ((src_id as u64) << 16) | dst_id as u64;
1454 a[2] = 0;
1455 a[3] = 0;
1456 a[4..18].copy_from_slice(&args.0[..14]);
1457 }
1458 Interface::ConsoleLog {
1459 char_cnt,
1460 char_lists,
1461 } => {
1462 a[1] = char_cnt.into();
1463 match char_lists {
1464 ConsoleLogChars::Reg64(regs) => a[2..18].copy_from_slice(&regs[..16]),
1465 _ => panic!("{:#x?} requires 8 registers", char_lists),
1466 }
1467 }
1468 _ => panic!("{:#x?} requires 8 registers", self),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001469 }
1470 }
1471
Balint Dobszaya5846852025-02-26 15:38:53 +01001472 /// Helper function to create an `FFA_SUCCESS` interface without any arguments.
Balint Dobszay3aad9572025-01-17 16:54:11 +01001473 pub fn success32_noargs() -> Self {
1474 Self::Success {
1475 target_info: 0,
1476 args: SuccessArgs::Result32([0, 0, 0, 0, 0, 0]),
1477 }
1478 }
1479
Tomás González4c8c7d22025-03-10 17:14:57 +00001480 /// Helper function to create an `FFA_SUCCESS` interface without any arguments.
1481 pub fn success64_noargs() -> Self {
1482 Self::Success {
1483 target_info: 0,
1484 args: SuccessArgs::Result64([0, 0, 0, 0, 0, 0]),
1485 }
1486 }
1487
Balint Dobszaya5846852025-02-26 15:38:53 +01001488 /// Helper function to create an `FFA_ERROR` interface with an error code.
Balint Dobszay3aad9572025-01-17 16:54:11 +01001489 pub fn error(error_code: FfaError) -> Self {
1490 Self::Error {
1491 target_info: TargetInfo {
1492 endpoint_id: 0,
1493 vcpu_id: 0,
1494 },
1495 error_code,
1496 }
1497 }
1498}
1499
Balint Dobszaya5846852025-02-26 15:38:53 +01001500/// Maximum number of characters transmitted in a single `FFA_CONSOLE_LOG32` message.
1501pub const CONSOLE_LOG_32_MAX_CHAR_CNT: u8 = 24;
Balint Dobszayde0dc802025-02-28 14:16:52 +01001502/// Maximum number of characters transmitted in a single `FFA_CONSOLE_LOG64` message.
1503pub const CONSOLE_LOG_64_MAX_CHAR_CNT: u8 = 128;
Balint Dobszay3aad9572025-01-17 16:54:11 +01001504
Balint Dobszaya5846852025-02-26 15:38:53 +01001505/// Helper function to convert the "Tightly packed list of characters" format used by the
1506/// `FFA_CONSOLE_LOG` interface into a byte slice.
Balint Dobszay3aad9572025-01-17 16:54:11 +01001507pub fn parse_console_log(
1508 char_cnt: u8,
1509 char_lists: &ConsoleLogChars,
1510 log_bytes: &mut [u8],
1511) -> Result<(), FfaError> {
1512 match char_lists {
1513 ConsoleLogChars::Reg32(regs) => {
Balint Dobszaya5846852025-02-26 15:38:53 +01001514 if !(1..=CONSOLE_LOG_32_MAX_CHAR_CNT).contains(&char_cnt) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001515 return Err(FfaError::InvalidParameters);
1516 }
1517 for (i, reg) in regs.iter().enumerate() {
1518 log_bytes[4 * i..4 * (i + 1)].copy_from_slice(&reg.to_le_bytes());
1519 }
1520 }
1521 ConsoleLogChars::Reg64(regs) => {
Balint Dobszaya5846852025-02-26 15:38:53 +01001522 if !(1..=CONSOLE_LOG_64_MAX_CHAR_CNT).contains(&char_cnt) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001523 return Err(FfaError::InvalidParameters);
1524 }
1525 for (i, reg) in regs.iter().enumerate() {
1526 log_bytes[8 * i..8 * (i + 1)].copy_from_slice(&reg.to_le_bytes());
1527 }
1528 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001529 }
1530
Balint Dobszayc8802492025-01-15 18:11:39 +01001531 Ok(())
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001532}