blob: ae66d123b661ab7381ebefd4f8f062da1f06c846 [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 {
206 /// Returns whether the caller's version (self) is compatible with the callee's version (input
207 /// parameter)
208 pub fn is_compatible_to(&self, callee_version: &Version) -> bool {
209 self.0 == callee_version.0 && self.1 <= callee_version.1
210 }
211}
212
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200213impl From<u32> for Version {
214 fn from(val: u32) -> Self {
215 Self((val >> 16) as u16, val as u16)
216 }
217}
218
219impl From<Version> for u32 {
220 fn from(v: Version) -> Self {
Balint Dobszaye9a3e762025-02-26 17:29:57 +0100221 ((v.0 as u32) << 16) | v.1 as u32
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200222 }
223}
224
Andrew Walbran19970ba2024-11-25 15:35:00 +0000225impl Display for Version {
226 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
227 write!(f, "{}.{}", self.0, self.1)
228 }
229}
230
231impl Debug for Version {
232 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
233 Display::fmt(self, f)
234 }
235}
236
Balint Dobszaya5846852025-02-26 15:38:53 +0100237/// Feature IDs used by the `FFA_FEATURES` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100238#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
239#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedFeatureId))]
240#[repr(u8)]
241pub enum FeatureId {
242 NotificationPendingInterrupt = 0x1,
243 ScheduleReceiverInterrupt = 0x2,
244 ManagedExitInterrupt = 0x3,
245}
Balint Dobszayc8802492025-01-15 18:11:39 +0100246
Balint Dobszaya5846852025-02-26 15:38:53 +0100247/// Arguments for the `FFA_FEATURES` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100248#[derive(Debug, Eq, PartialEq, Clone, Copy)]
249pub enum Feature {
250 FuncId(FuncId),
251 FeatureId(FeatureId),
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100252 Unknown(u32),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100253}
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200254
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100255impl From<u32> for Feature {
256 fn from(value: u32) -> Self {
257 // Bit[31] is set for all valid FF-A function IDs so we don't have to check it separately
258 if let Ok(func_id) = value.try_into() {
259 Self::FuncId(func_id)
260 } else if let Ok(feat_id) = (value as u8).try_into() {
261 Self::FeatureId(feat_id)
Balint Dobszay3aad9572025-01-17 16:54:11 +0100262 } else {
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100263 Self::Unknown(value)
264 }
Balint Dobszay3aad9572025-01-17 16:54:11 +0100265 }
266}
267
268impl From<Feature> for u32 {
269 fn from(value: Feature) -> Self {
270 match value {
271 Feature::FuncId(func_id) => (1 << 31) | func_id as u32,
272 Feature::FeatureId(feature_id) => feature_id as u32,
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100273 Feature::Unknown(id) => panic!("Unknown feature or function ID {:#x?}", id),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100274 }
275 }
276}
277
Balint Dobszaya5846852025-02-26 15:38:53 +0100278/// RXTX buffer descriptor, used by `FFA_RXTX_MAP`.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100279#[derive(Debug, Eq, PartialEq, Clone, Copy)]
280pub enum RxTxAddr {
281 Addr32 { rx: u32, tx: u32 },
282 Addr64 { rx: u64, tx: u64 },
283}
284
Tomás González4d5b0ba2025-03-03 17:15:55 +0000285/// Composite type for capturing success and error return codes for the VM availability messages.
286///
287/// Error codes are handled by the `FfaError` type. Having a separate type for errors helps using
288/// `Result<(), FfaError>`. If a single type would include both success and error values,
289/// then `Err(FfaError::Success)` would be incomprehensible.
290#[derive(Debug, Eq, PartialEq, Clone, Copy)]
291pub enum VmAvailabilityStatus {
292 Success,
293 Error(FfaError),
294}
295
296impl TryFrom<i32> for VmAvailabilityStatus {
297 type Error = Error;
298 fn try_from(value: i32) -> Result<Self, <Self as TryFrom<i32>>::Error> {
299 Ok(match value {
300 0 => Self::Success,
301 error_code => Self::Error(FfaError::try_from(error_code)?),
302 })
303 }
304}
305
306impl From<VmAvailabilityStatus> for i32 {
307 fn from(value: VmAvailabilityStatus) -> Self {
308 match value {
309 VmAvailabilityStatus::Success => 0,
310 VmAvailabilityStatus::Error(error_code) => error_code.into(),
311 }
312 }
313}
314
315/// Arguments for the Power Warm Boot `FFA_MSG_SEND_DIRECT_REQ` interface.
316#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
317#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedWarmBootType))]
318#[repr(u32)]
319pub enum WarmBootType {
320 ExitFromSuspend = 0,
321 ExitFromLowPower = 1,
322}
323
Balint Dobszaya5846852025-02-26 15:38:53 +0100324/// Arguments for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}` interfaces.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100325#[derive(Debug, Eq, PartialEq, Clone, Copy)]
326pub enum DirectMsgArgs {
327 Args32([u32; 5]),
328 Args64([u64; 5]),
Tomás González4d5b0ba2025-03-03 17:15:55 +0000329 /// Message for forwarding FFA_VERSION call from Normal world to the SPMC
330 VersionReq {
331 version: Version,
332 },
333 /// Response message to forwarded FFA_VERSION call from the Normal world
334 /// Contains the version returned by the SPMC or None
335 VersionResp {
336 version: Option<Version>,
337 },
338 /// Message for a power management operation initiated by a PSCI function
339 PowerPsciReq32 {
340 function_id: u32,
341 // params[i]: Input parameter in w[i] in PSCI function invocation at EL3.
342 params: [u32; 3],
343 },
344 /// Message for a power management operation initiated by a PSCI function
345 PowerPsciReq64 {
346 function_id: u32,
347 // params[i]: Input parameter in x[i] in PSCI function invocation at EL3.
348 params: [u64; 3],
349 },
350 /// Message for a warm boot
351 PowerWarmBootReq {
352 boot_type: WarmBootType,
353 },
354 /// Response message to indicate return status of the last power management request message
355 /// Return error code SUCCESS or DENIED as defined in PSCI spec. Caller is left to do the
356 /// parsing of the return status.
357 PowerPsciResp {
358 // TODO: Use arm-psci crate's return status here.
359 psci_status: i32,
360 },
361 /// Message to signal creation of a VM
362 VmCreated {
363 // Globally unique Handle to identify a memory region that contains IMPLEMENTATION DEFINED
364 // information associated with the created VM.
365 // The invalid memory region handle must be specified by the Hypervisor if this field is not
366 // used.
367 handle: memory_management::Handle,
368 vm_id: u16,
369 },
370 /// Message to acknowledge creation of a VM
371 VmCreatedAck {
372 sp_status: VmAvailabilityStatus,
373 },
374 /// Message to signal destruction of a VM
375 VmDestructed {
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 destruction of a VM
384 VmDestructedAck {
385 sp_status: VmAvailabilityStatus,
386 },
387}
388
389impl DirectMsgArgs {
390 // Flags for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}` interfaces.
391
392 const FWK_MSG_BITS: u32 = 1 << 31;
393 const VERSION_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b1000;
394 const VERSION_RESP: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b1001;
395 const POWER_PSCI_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS;
396 const POWER_WARM_BOOT_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0001;
397 const POWER_PSCI_RESP: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0010;
398 const VM_CREATED: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0100;
399 const VM_CREATED_ACK: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0101;
400 const VM_DESTRUCTED: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0110;
401 const VM_DESTRUCTED_ACK: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0111;
Balint Dobszay3aad9572025-01-17 16:54:11 +0100402}
403
Balint Dobszayde0dc802025-02-28 14:16:52 +0100404/// Arguments for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}2` interfaces.
405#[derive(Debug, Eq, PartialEq, Clone, Copy)]
406pub struct DirectMsg2Args([u64; 14]);
407
Balint Dobszaya5846852025-02-26 15:38:53 +0100408/// Descriptor for a dynamically allocated memory buffer that contains the memory transaction
409/// descriptor. Used by `FFA_MEM_{DONATE,LEND,SHARE,RETRIEVE_REQ}` interfaces, only when the TX
410/// buffer is not used to transmit the transaction descriptor.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100411#[derive(Debug, Eq, PartialEq, Clone, Copy)]
412pub enum MemOpBuf {
413 Buf32 { addr: u32, page_cnt: u32 },
414 Buf64 { addr: u64, page_cnt: u32 },
415}
416
Balint Dobszaya5846852025-02-26 15:38:53 +0100417/// Memory address argument for `FFA_MEM_PERM_{GET,SET}` interfaces.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100418#[derive(Debug, Eq, PartialEq, Clone, Copy)]
419pub enum MemAddr {
420 Addr32(u32),
421 Addr64(u64),
422}
423
Balint Dobszayde0dc802025-02-28 14:16:52 +0100424/// Argument for the `FFA_CONSOLE_LOG` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100425#[derive(Debug, Eq, PartialEq, Clone, Copy)]
426pub enum ConsoleLogChars {
427 Reg32([u32; 6]),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100428 Reg64([u64; 16]),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100429}
430
Balint Dobszaya5846852025-02-26 15:38:53 +0100431/// FF-A "message types", the terminology used by the spec is "interfaces". The interfaces are used
432/// by FF-A components for communication at an FF-A instance. The spec also describes the valid FF-A
433/// instances and conduits for each interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100434#[derive(Debug, Eq, PartialEq, Clone, Copy)]
435pub enum Interface {
436 Error {
437 target_info: TargetInfo,
438 error_code: FfaError,
439 },
440 Success {
441 target_info: u32,
442 args: SuccessArgs,
443 },
444 Interrupt {
445 target_info: TargetInfo,
446 interrupt_id: u32,
447 },
448 Version {
449 input_version: Version,
450 },
451 VersionOut {
452 output_version: Version,
453 },
454 Features {
455 feat_id: Feature,
456 input_properties: u32,
457 },
458 RxAcquire {
459 vm_id: u16,
460 },
461 RxRelease {
462 vm_id: u16,
463 },
464 RxTxMap {
465 addr: RxTxAddr,
466 page_cnt: u32,
467 },
468 RxTxUnmap {
469 id: u16,
470 },
471 PartitionInfoGet {
472 uuid: Uuid,
473 flags: u32,
474 },
475 IdGet,
476 SpmIdGet,
477 MsgWait,
478 Yield,
479 Run {
480 target_info: TargetInfo,
481 },
482 NormalWorldResume,
Tomás González17b92442025-03-10 16:45:04 +0000483 SecondaryEpRegister {
484 entrypoint: SecondaryEpRegisterAddr,
485 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100486 MsgSend2 {
487 sender_vm_id: u16,
488 flags: u32,
489 },
490 MsgSendDirectReq {
491 src_id: u16,
492 dst_id: u16,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100493 args: DirectMsgArgs,
494 },
495 MsgSendDirectResp {
496 src_id: u16,
497 dst_id: u16,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100498 args: DirectMsgArgs,
499 },
Balint Dobszayde0dc802025-02-28 14:16:52 +0100500 MsgSendDirectReq2 {
501 src_id: u16,
502 dst_id: u16,
503 uuid: Uuid,
504 args: DirectMsg2Args,
505 },
506 MsgSendDirectResp2 {
507 src_id: u16,
508 dst_id: u16,
509 args: DirectMsg2Args,
510 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100511 MemDonate {
512 total_len: u32,
513 frag_len: u32,
514 buf: Option<MemOpBuf>,
515 },
516 MemLend {
517 total_len: u32,
518 frag_len: u32,
519 buf: Option<MemOpBuf>,
520 },
521 MemShare {
522 total_len: u32,
523 frag_len: u32,
524 buf: Option<MemOpBuf>,
525 },
526 MemRetrieveReq {
527 total_len: u32,
528 frag_len: u32,
529 buf: Option<MemOpBuf>,
530 },
531 MemRetrieveResp {
532 total_len: u32,
533 frag_len: u32,
534 },
535 MemRelinquish,
536 MemReclaim {
537 handle: memory_management::Handle,
538 flags: u32,
539 },
540 MemPermGet {
541 addr: MemAddr,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100542 page_cnt: Option<u32>,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100543 },
544 MemPermSet {
545 addr: MemAddr,
546 page_cnt: u32,
547 mem_perm: u32,
548 },
549 ConsoleLog {
550 char_cnt: u8,
551 char_lists: ConsoleLogChars,
552 },
553}
554
Balint Dobszayde0dc802025-02-28 14:16:52 +0100555impl Interface {
556 /// Returns the function ID for the call, if it has one.
557 pub fn function_id(&self) -> Option<FuncId> {
558 match self {
559 Interface::Error { .. } => Some(FuncId::Error),
560 Interface::Success { args, .. } => match args {
561 SuccessArgs::Result32(..) => Some(FuncId::Success32),
562 SuccessArgs::Result64(..) | SuccessArgs::Result64_2(..) => Some(FuncId::Success64),
563 },
564 Interface::Interrupt { .. } => Some(FuncId::Interrupt),
565 Interface::Version { .. } => Some(FuncId::Version),
566 Interface::VersionOut { .. } => None,
567 Interface::Features { .. } => Some(FuncId::Features),
568 Interface::RxAcquire { .. } => Some(FuncId::RxAcquire),
569 Interface::RxRelease { .. } => Some(FuncId::RxRelease),
570 Interface::RxTxMap { addr, .. } => match addr {
571 RxTxAddr::Addr32 { .. } => Some(FuncId::RxTxMap32),
572 RxTxAddr::Addr64 { .. } => Some(FuncId::RxTxMap64),
573 },
574 Interface::RxTxUnmap { .. } => Some(FuncId::RxTxUnmap),
575 Interface::PartitionInfoGet { .. } => Some(FuncId::PartitionInfoGet),
576 Interface::IdGet => Some(FuncId::IdGet),
577 Interface::SpmIdGet => Some(FuncId::SpmIdGet),
578 Interface::MsgWait => Some(FuncId::MsgWait),
579 Interface::Yield => Some(FuncId::Yield),
580 Interface::Run { .. } => Some(FuncId::Run),
581 Interface::NormalWorldResume => Some(FuncId::NormalWorldResume),
Tomás González17b92442025-03-10 16:45:04 +0000582 Interface::SecondaryEpRegister { entrypoint } => match entrypoint {
583 SecondaryEpRegisterAddr::Addr32 { .. } => Some(FuncId::SecondaryEpRegister32),
584 SecondaryEpRegisterAddr::Addr64 { .. } => Some(FuncId::SecondaryEpRegister64),
585 },
Balint Dobszayde0dc802025-02-28 14:16:52 +0100586 Interface::MsgSend2 { .. } => Some(FuncId::MsgSend2),
587 Interface::MsgSendDirectReq { args, .. } => match args {
588 DirectMsgArgs::Args32(_) => Some(FuncId::MsgSendDirectReq32),
589 DirectMsgArgs::Args64(_) => Some(FuncId::MsgSendDirectReq64),
Tomás González4d5b0ba2025-03-03 17:15:55 +0000590 DirectMsgArgs::VersionReq { .. } => Some(FuncId::MsgSendDirectReq32),
591 DirectMsgArgs::PowerPsciReq32 { .. } => Some(FuncId::MsgSendDirectReq32),
592 DirectMsgArgs::PowerPsciReq64 { .. } => Some(FuncId::MsgSendDirectReq64),
593 DirectMsgArgs::PowerWarmBootReq { .. } => Some(FuncId::MsgSendDirectReq32),
594 DirectMsgArgs::VmCreated { .. } => Some(FuncId::MsgSendDirectReq32),
595 DirectMsgArgs::VmDestructed { .. } => Some(FuncId::MsgSendDirectReq32),
596 _ => None,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100597 },
598 Interface::MsgSendDirectResp { args, .. } => match args {
599 DirectMsgArgs::Args32(_) => Some(FuncId::MsgSendDirectResp32),
600 DirectMsgArgs::Args64(_) => Some(FuncId::MsgSendDirectResp64),
Tomás González4d5b0ba2025-03-03 17:15:55 +0000601 DirectMsgArgs::VersionResp { .. } => Some(FuncId::MsgSendDirectResp32),
602 DirectMsgArgs::PowerPsciResp { .. } => Some(FuncId::MsgSendDirectResp32),
603 DirectMsgArgs::VmCreatedAck { .. } => Some(FuncId::MsgSendDirectResp32),
604 DirectMsgArgs::VmDestructedAck { .. } => Some(FuncId::MsgSendDirectResp32),
605 _ => None,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100606 },
607 Interface::MsgSendDirectReq2 { .. } => Some(FuncId::MsgSendDirectReq64_2),
608 Interface::MsgSendDirectResp2 { .. } => Some(FuncId::MsgSendDirectResp64_2),
609 Interface::MemDonate { buf, .. } => match buf {
610 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemDonate64),
611 _ => Some(FuncId::MemDonate32),
612 },
613 Interface::MemLend { buf, .. } => match buf {
614 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemLend64),
615 _ => Some(FuncId::MemLend32),
616 },
617 Interface::MemShare { buf, .. } => match buf {
618 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemShare64),
619 _ => Some(FuncId::MemShare32),
620 },
621 Interface::MemRetrieveReq { buf, .. } => match buf {
622 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemRetrieveReq64),
623 _ => Some(FuncId::MemRetrieveReq32),
624 },
625 Interface::MemRetrieveResp { .. } => Some(FuncId::MemRetrieveResp),
626 Interface::MemRelinquish => Some(FuncId::MemRelinquish),
627 Interface::MemReclaim { .. } => Some(FuncId::MemReclaim),
628 Interface::MemPermGet { addr, .. } => match addr {
629 MemAddr::Addr32(_) => Some(FuncId::MemPermGet32),
630 MemAddr::Addr64(_) => Some(FuncId::MemPermGet64),
631 },
632 Interface::MemPermSet { addr, .. } => match addr {
633 MemAddr::Addr32(_) => Some(FuncId::MemPermSet32),
634 MemAddr::Addr64(_) => Some(FuncId::MemPermSet64),
635 },
636 Interface::ConsoleLog { char_lists, .. } => match char_lists {
637 ConsoleLogChars::Reg32(_) => Some(FuncId::ConsoleLog32),
638 ConsoleLogChars::Reg64(_) => Some(FuncId::ConsoleLog64),
639 },
640 }
641 }
Balint Dobszay3aad9572025-01-17 16:54:11 +0100642
Balint Dobszayde0dc802025-02-28 14:16:52 +0100643 /// Returns true if this is a 32-bit call, or false if it is a 64-bit call.
644 pub fn is_32bit(&self) -> bool {
645 // TODO: self should always have a function ID?
646 self.function_id().unwrap().is_32bit()
647 }
648
649 /// Parse interface from register contents. The caller must ensure that the `regs` argument has
650 /// the correct length: 8 registers for FF-A v1.1 and lower, 18 registers for v1.2 and higher.
651 pub fn from_regs(version: Version, regs: &[u64]) -> Result<Self, Error> {
652 let reg_cnt = regs.len();
653
654 let msg = match reg_cnt {
655 8 => {
656 assert!(version <= Version(1, 1));
657 Interface::unpack_regs8(version, regs.try_into().unwrap())?
658 }
659 18 => {
660 assert!(version >= Version(1, 2));
661 match FuncId::try_from(regs[0] as u32)? {
662 FuncId::ConsoleLog64
663 | FuncId::Success64
664 | FuncId::MsgSendDirectReq64_2
665 | FuncId::MsgSendDirectResp64_2 => {
666 Interface::unpack_regs18(version, regs.try_into().unwrap())?
667 }
668 _ => Interface::unpack_regs8(version, regs[..8].try_into().unwrap())?,
669 }
670 }
671 _ => panic!(
672 "Invalid number of registers ({}) for FF-A version {}",
673 reg_cnt, version
674 ),
675 };
676
677 Ok(msg)
678 }
679
680 fn unpack_regs8(version: Version, regs: &[u64; 8]) -> Result<Self, Error> {
Balint Dobszay3aad9572025-01-17 16:54:11 +0100681 let fid = FuncId::try_from(regs[0] as u32)?;
682
683 let msg = match fid {
684 FuncId::Error => Self::Error {
685 target_info: (regs[1] as u32).into(),
686 error_code: FfaError::try_from(regs[2] as i32)?,
687 },
688 FuncId::Success32 => Self::Success {
689 target_info: regs[1] as u32,
690 args: SuccessArgs::Result32([
691 regs[2] as u32,
692 regs[3] as u32,
693 regs[4] as u32,
694 regs[5] as u32,
695 regs[6] as u32,
696 regs[7] as u32,
697 ]),
698 },
699 FuncId::Success64 => Self::Success {
700 target_info: regs[1] as u32,
701 args: SuccessArgs::Result64([regs[2], regs[3], regs[4], regs[5], regs[6], regs[7]]),
702 },
703 FuncId::Interrupt => Self::Interrupt {
704 target_info: (regs[1] as u32).into(),
705 interrupt_id: regs[2] as u32,
706 },
707 FuncId::Version => Self::Version {
708 input_version: (regs[1] as u32).into(),
709 },
710 FuncId::Features => Self::Features {
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100711 feat_id: (regs[1] as u32).into(),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100712 input_properties: regs[2] as u32,
713 },
714 FuncId::RxAcquire => Self::RxAcquire {
715 vm_id: regs[1] as u16,
716 },
717 FuncId::RxRelease => Self::RxRelease {
718 vm_id: regs[1] as u16,
719 },
720 FuncId::RxTxMap32 => {
721 let addr = RxTxAddr::Addr32 {
722 rx: regs[2] as u32,
723 tx: regs[1] as u32,
724 };
725 let page_cnt = regs[3] as u32;
726
727 Self::RxTxMap { addr, page_cnt }
728 }
729 FuncId::RxTxMap64 => {
730 let addr = RxTxAddr::Addr64 {
731 rx: regs[2],
732 tx: regs[1],
733 };
734 let page_cnt = regs[3] as u32;
735
736 Self::RxTxMap { addr, page_cnt }
737 }
738 FuncId::RxTxUnmap => Self::RxTxUnmap { id: regs[1] as u16 },
739 FuncId::PartitionInfoGet => {
740 let uuid_words = [
741 regs[1] as u32,
742 regs[2] as u32,
743 regs[3] as u32,
744 regs[4] as u32,
745 ];
746 let mut bytes: [u8; 16] = [0; 16];
747 for (i, b) in uuid_words.iter().flat_map(|w| w.to_le_bytes()).enumerate() {
748 bytes[i] = b;
749 }
750 Self::PartitionInfoGet {
751 uuid: Uuid::from_bytes(bytes),
752 flags: regs[5] as u32,
753 }
754 }
755 FuncId::IdGet => Self::IdGet,
756 FuncId::SpmIdGet => Self::SpmIdGet,
757 FuncId::MsgWait => Self::MsgWait,
758 FuncId::Yield => Self::Yield,
759 FuncId::Run => Self::Run {
760 target_info: (regs[1] as u32).into(),
761 },
762 FuncId::NormalWorldResume => Self::NormalWorldResume,
Tomás González17b92442025-03-10 16:45:04 +0000763 FuncId::SecondaryEpRegister32 => Self::SecondaryEpRegister {
764 entrypoint: SecondaryEpRegisterAddr::Addr32(regs[1] as u32),
765 },
766 FuncId::SecondaryEpRegister64 => Self::SecondaryEpRegister {
767 entrypoint: SecondaryEpRegisterAddr::Addr64(regs[1]),
768 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100769 FuncId::MsgSend2 => Self::MsgSend2 {
770 sender_vm_id: regs[1] as u16,
771 flags: regs[2] as u32,
772 },
773 FuncId::MsgSendDirectReq32 => Self::MsgSendDirectReq {
774 src_id: (regs[1] >> 16) as u16,
775 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +0000776 args: if (regs[2] as u32 & DirectMsgArgs::FWK_MSG_BITS) != 0 {
777 match regs[2] as u32 {
778 DirectMsgArgs::VERSION_REQ => DirectMsgArgs::VersionReq {
779 version: Version::try_from(regs[3] as u32)?,
780 },
781 DirectMsgArgs::POWER_PSCI_REQ => DirectMsgArgs::PowerPsciReq32 {
782 function_id: regs[3] as u32,
783 params: [regs[4] as u32, regs[5] as u32, regs[6] as u32],
784 },
785 DirectMsgArgs::POWER_WARM_BOOT_REQ => DirectMsgArgs::PowerWarmBootReq {
786 boot_type: WarmBootType::try_from(regs[3] as u32)?,
787 },
788 DirectMsgArgs::VM_CREATED => DirectMsgArgs::VmCreated {
789 handle: memory_management::Handle::from([
790 regs[3] as u32,
791 regs[4] as u32,
792 ]),
793 vm_id: regs[5] as u16,
794 },
795 DirectMsgArgs::VM_DESTRUCTED => DirectMsgArgs::VmDestructed {
796 handle: memory_management::Handle::from([
797 regs[3] as u32,
798 regs[4] as u32,
799 ]),
800 vm_id: regs[5] as u16,
801 },
802 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
803 }
804 } else {
805 DirectMsgArgs::Args32([
806 regs[3] as u32,
807 regs[4] as u32,
808 regs[5] as u32,
809 regs[6] as u32,
810 regs[7] as u32,
811 ])
812 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100813 },
814 FuncId::MsgSendDirectReq64 => Self::MsgSendDirectReq {
815 src_id: (regs[1] >> 16) as u16,
816 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +0000817 args: if (regs[2] & DirectMsgArgs::FWK_MSG_BITS as u64) != 0 {
818 match regs[2] as u32 {
819 DirectMsgArgs::POWER_PSCI_REQ => DirectMsgArgs::PowerPsciReq64 {
820 function_id: regs[3] as u32,
821 params: [regs[4], regs[5], regs[6]],
822 },
823 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
824 }
825 } else {
826 DirectMsgArgs::Args64([regs[3], regs[4], regs[5], regs[6], regs[7]])
827 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100828 },
829 FuncId::MsgSendDirectResp32 => Self::MsgSendDirectResp {
830 src_id: (regs[1] >> 16) as u16,
831 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +0000832 args: if (regs[2] as u32 & DirectMsgArgs::FWK_MSG_BITS) != 0 {
833 match regs[2] as u32 {
834 DirectMsgArgs::VERSION_RESP => {
835 if regs[3] as i32 == FfaError::NotSupported.into() {
836 DirectMsgArgs::VersionResp { version: None }
837 } else {
838 DirectMsgArgs::VersionResp {
839 version: Some(Version::try_from(regs[3] as u32)?),
840 }
841 }
842 }
843 DirectMsgArgs::POWER_PSCI_RESP => DirectMsgArgs::PowerPsciResp {
844 psci_status: regs[3] as i32,
845 },
846 DirectMsgArgs::VM_CREATED_ACK => DirectMsgArgs::VmCreatedAck {
847 sp_status: (regs[3] as i32).try_into()?,
848 },
849 DirectMsgArgs::VM_DESTRUCTED_ACK => DirectMsgArgs::VmDestructedAck {
850 sp_status: (regs[3] as i32).try_into()?,
851 },
852 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
853 }
854 } else {
855 DirectMsgArgs::Args32([
856 regs[3] as u32,
857 regs[4] as u32,
858 regs[5] as u32,
859 regs[6] as u32,
860 regs[7] as u32,
861 ])
862 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100863 },
864 FuncId::MsgSendDirectResp64 => Self::MsgSendDirectResp {
865 src_id: (regs[1] >> 16) as u16,
866 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +0000867 args: if (regs[2] & DirectMsgArgs::FWK_MSG_BITS as u64) != 0 {
868 return Err(Error::UnrecognisedFwkMsg(regs[2] as u32));
869 } else {
870 DirectMsgArgs::Args64([regs[3], regs[4], regs[5], regs[6], regs[7]])
871 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100872 },
873 FuncId::MemDonate32 => Self::MemDonate {
874 total_len: regs[1] as u32,
875 frag_len: regs[2] as u32,
876 buf: if regs[3] != 0 && regs[4] != 0 {
877 Some(MemOpBuf::Buf32 {
878 addr: regs[3] as u32,
879 page_cnt: regs[4] as u32,
880 })
881 } else {
882 None
883 },
884 },
885 FuncId::MemDonate64 => Self::MemDonate {
886 total_len: regs[1] as u32,
887 frag_len: regs[2] as u32,
888 buf: if regs[3] != 0 && regs[4] != 0 {
889 Some(MemOpBuf::Buf64 {
890 addr: regs[3],
891 page_cnt: regs[4] as u32,
892 })
893 } else {
894 None
895 },
896 },
897 FuncId::MemLend32 => Self::MemLend {
898 total_len: regs[1] as u32,
899 frag_len: regs[2] as u32,
900 buf: if regs[3] != 0 && regs[4] != 0 {
901 Some(MemOpBuf::Buf32 {
902 addr: regs[3] as u32,
903 page_cnt: regs[4] as u32,
904 })
905 } else {
906 None
907 },
908 },
909 FuncId::MemLend64 => Self::MemLend {
910 total_len: regs[1] as u32,
911 frag_len: regs[2] as u32,
912 buf: if regs[3] != 0 && regs[4] != 0 {
913 Some(MemOpBuf::Buf64 {
914 addr: regs[3],
915 page_cnt: regs[4] as u32,
916 })
917 } else {
918 None
919 },
920 },
921 FuncId::MemShare32 => Self::MemShare {
922 total_len: regs[1] as u32,
923 frag_len: regs[2] as u32,
924 buf: if regs[3] != 0 && regs[4] != 0 {
925 Some(MemOpBuf::Buf32 {
926 addr: regs[3] as u32,
927 page_cnt: regs[4] as u32,
928 })
929 } else {
930 None
931 },
932 },
933 FuncId::MemShare64 => Self::MemShare {
934 total_len: regs[1] as u32,
935 frag_len: regs[2] as u32,
936 buf: if regs[3] != 0 && regs[4] != 0 {
937 Some(MemOpBuf::Buf64 {
938 addr: regs[3],
939 page_cnt: regs[4] as u32,
940 })
941 } else {
942 None
943 },
944 },
945 FuncId::MemRetrieveReq32 => Self::MemRetrieveReq {
946 total_len: regs[1] as u32,
947 frag_len: regs[2] as u32,
948 buf: if regs[3] != 0 && regs[4] != 0 {
949 Some(MemOpBuf::Buf32 {
950 addr: regs[3] as u32,
951 page_cnt: regs[4] as u32,
952 })
953 } else {
954 None
955 },
956 },
957 FuncId::MemRetrieveReq64 => Self::MemRetrieveReq {
958 total_len: regs[1] as u32,
959 frag_len: regs[2] as u32,
960 buf: if regs[3] != 0 && regs[4] != 0 {
961 Some(MemOpBuf::Buf64 {
962 addr: regs[3],
963 page_cnt: regs[4] as u32,
964 })
965 } else {
966 None
967 },
968 },
969 FuncId::MemRetrieveResp => Self::MemRetrieveResp {
970 total_len: regs[1] as u32,
971 frag_len: regs[2] as u32,
972 },
973 FuncId::MemRelinquish => Self::MemRelinquish,
974 FuncId::MemReclaim => Self::MemReclaim {
975 handle: memory_management::Handle::from([regs[1] as u32, regs[2] as u32]),
976 flags: regs[3] as u32,
977 },
978 FuncId::MemPermGet32 => Self::MemPermGet {
979 addr: MemAddr::Addr32(regs[1] as u32),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100980 page_cnt: if version >= Version(1, 3) {
981 Some(regs[2] as u32)
982 } else {
983 None
984 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100985 },
986 FuncId::MemPermGet64 => Self::MemPermGet {
987 addr: MemAddr::Addr64(regs[1]),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100988 page_cnt: if version >= Version(1, 3) {
989 Some(regs[2] as u32)
990 } else {
991 None
992 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100993 },
994 FuncId::MemPermSet32 => Self::MemPermSet {
995 addr: MemAddr::Addr32(regs[1] as u32),
996 page_cnt: regs[2] as u32,
997 mem_perm: regs[3] as u32,
998 },
999 FuncId::MemPermSet64 => Self::MemPermSet {
1000 addr: MemAddr::Addr64(regs[1]),
1001 page_cnt: regs[2] as u32,
1002 mem_perm: regs[3] as u32,
1003 },
1004 FuncId::ConsoleLog32 => Self::ConsoleLog {
1005 char_cnt: regs[1] as u8,
1006 char_lists: ConsoleLogChars::Reg32([
1007 regs[2] as u32,
1008 regs[3] as u32,
1009 regs[4] as u32,
1010 regs[5] as u32,
1011 regs[6] as u32,
1012 regs[7] as u32,
1013 ]),
1014 },
Balint Dobszayde0dc802025-02-28 14:16:52 +01001015 _ => panic!("Invalid number of registers (8) for function {:#x?}", fid),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001016 };
1017
1018 Ok(msg)
1019 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01001020
Balint Dobszayde0dc802025-02-28 14:16:52 +01001021 fn unpack_regs18(version: Version, regs: &[u64; 18]) -> Result<Self, Error> {
1022 assert!(version >= Version(1, 2));
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001023
Balint Dobszayde0dc802025-02-28 14:16:52 +01001024 let fid = FuncId::try_from(regs[0] as u32)?;
1025
1026 let msg = match fid {
1027 FuncId::Success64 => Self::Success {
1028 target_info: regs[1] as u32,
1029 args: SuccessArgs::Result64_2(regs[2..18].try_into().unwrap()),
1030 },
1031 FuncId::MsgSendDirectReq64_2 => Self::MsgSendDirectReq2 {
1032 src_id: (regs[1] >> 16) as u16,
1033 dst_id: regs[1] as u16,
1034 uuid: Uuid::from_u64_pair(regs[2], regs[3]),
1035 args: DirectMsg2Args(regs[4..18].try_into().unwrap()),
1036 },
1037 FuncId::MsgSendDirectResp64_2 => Self::MsgSendDirectResp2 {
1038 src_id: (regs[1] >> 16) as u16,
1039 dst_id: regs[1] as u16,
1040 args: DirectMsg2Args(regs[4..18].try_into().unwrap()),
1041 },
1042 FuncId::ConsoleLog64 => Self::ConsoleLog {
1043 char_cnt: regs[1] as u8,
1044 char_lists: ConsoleLogChars::Reg64(regs[2..18].try_into().unwrap()),
1045 },
1046 _ => panic!("Invalid number of registers (18) for function {:#x?}", fid),
1047 };
1048
1049 Ok(msg)
Balint Dobszay3aad9572025-01-17 16:54:11 +01001050 }
1051
Balint Dobszaya5846852025-02-26 15:38:53 +01001052 /// Create register contents for an interface.
Balint Dobszayde0dc802025-02-28 14:16:52 +01001053 pub fn to_regs(&self, version: Version, regs: &mut [u64]) {
1054 let reg_cnt = regs.len();
1055
1056 match reg_cnt {
1057 8 => {
1058 assert!(version <= Version(1, 1));
1059 self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
1060 }
1061 18 => {
1062 assert!(version >= Version(1, 2));
1063
1064 match self {
1065 Interface::ConsoleLog {
1066 char_lists: ConsoleLogChars::Reg64(_),
1067 ..
1068 }
1069 | Interface::Success {
1070 args: SuccessArgs::Result64_2(_),
1071 ..
1072 }
1073 | Interface::MsgSendDirectReq2 { .. }
1074 | Interface::MsgSendDirectResp2 { .. } => {
1075 self.pack_regs18(version, regs.try_into().unwrap());
1076 }
1077 _ => {
1078 self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
1079 }
1080 }
1081 }
1082 _ => panic!("Invalid number of registers {}", reg_cnt),
1083 }
1084 }
1085
1086 fn pack_regs8(&self, version: Version, a: &mut [u64; 8]) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001087 a.fill(0);
1088 if let Some(function_id) = self.function_id() {
1089 a[0] = function_id as u64;
1090 }
1091
1092 match *self {
1093 Interface::Error {
1094 target_info,
1095 error_code,
1096 } => {
1097 a[1] = u32::from(target_info).into();
1098 a[2] = (error_code as u32).into();
1099 }
1100 Interface::Success { target_info, args } => {
1101 a[1] = target_info.into();
1102 match args {
1103 SuccessArgs::Result32(regs) => {
1104 a[2] = regs[0].into();
1105 a[3] = regs[1].into();
1106 a[4] = regs[2].into();
1107 a[5] = regs[3].into();
1108 a[6] = regs[4].into();
1109 a[7] = regs[5].into();
1110 }
1111 SuccessArgs::Result64(regs) => {
1112 a[2] = regs[0];
1113 a[3] = regs[1];
1114 a[4] = regs[2];
1115 a[5] = regs[3];
1116 a[6] = regs[4];
1117 a[7] = regs[5];
1118 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001119 _ => panic!("{:#x?} requires 18 registers", args),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001120 }
1121 }
1122 Interface::Interrupt {
1123 target_info,
1124 interrupt_id,
1125 } => {
1126 a[1] = u32::from(target_info).into();
1127 a[2] = interrupt_id.into();
1128 }
1129 Interface::Version { input_version } => {
1130 a[1] = u32::from(input_version).into();
1131 }
1132 Interface::VersionOut { output_version } => {
1133 a[0] = u32::from(output_version).into();
1134 }
1135 Interface::Features {
1136 feat_id,
1137 input_properties,
1138 } => {
1139 a[1] = u32::from(feat_id).into();
1140 a[2] = input_properties.into();
1141 }
1142 Interface::RxAcquire { vm_id } => {
1143 a[1] = vm_id.into();
1144 }
1145 Interface::RxRelease { vm_id } => {
1146 a[1] = vm_id.into();
1147 }
1148 Interface::RxTxMap { addr, page_cnt } => {
1149 match addr {
1150 RxTxAddr::Addr32 { rx, tx } => {
1151 a[1] = tx.into();
1152 a[2] = rx.into();
1153 }
1154 RxTxAddr::Addr64 { rx, tx } => {
1155 a[1] = tx;
1156 a[2] = rx;
1157 }
1158 }
1159 a[3] = page_cnt.into();
1160 }
1161 Interface::RxTxUnmap { id } => {
1162 a[1] = id.into();
1163 }
1164 Interface::PartitionInfoGet { uuid, flags } => {
1165 let bytes = uuid.into_bytes();
1166 a[1] = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]).into();
1167 a[2] = u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]).into();
1168 a[3] = u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]).into();
1169 a[4] = u32::from_le_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]).into();
1170 a[5] = flags.into();
1171 }
1172 Interface::IdGet | Interface::SpmIdGet | Interface::MsgWait | Interface::Yield => {}
1173 Interface::Run { target_info } => {
1174 a[1] = u32::from(target_info).into();
1175 }
1176 Interface::NormalWorldResume => {}
Tomás González17b92442025-03-10 16:45:04 +00001177 Interface::SecondaryEpRegister { entrypoint } => match entrypoint {
1178 SecondaryEpRegisterAddr::Addr32(addr) => a[1] = addr as u64,
1179 SecondaryEpRegisterAddr::Addr64(addr) => a[1] = addr,
1180 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001181 Interface::MsgSend2 {
1182 sender_vm_id,
1183 flags,
1184 } => {
1185 a[1] = sender_vm_id.into();
1186 a[2] = flags.into();
1187 }
1188 Interface::MsgSendDirectReq {
1189 src_id,
1190 dst_id,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001191 args,
1192 } => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +01001193 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Balint Dobszay3aad9572025-01-17 16:54:11 +01001194 match args {
1195 DirectMsgArgs::Args32(args) => {
1196 a[3] = args[0].into();
1197 a[4] = args[1].into();
1198 a[5] = args[2].into();
1199 a[6] = args[3].into();
1200 a[7] = args[4].into();
1201 }
1202 DirectMsgArgs::Args64(args) => {
1203 a[3] = args[0];
1204 a[4] = args[1];
1205 a[5] = args[2];
1206 a[6] = args[3];
1207 a[7] = args[4];
1208 }
Tomás González4d5b0ba2025-03-03 17:15:55 +00001209 DirectMsgArgs::VersionReq { version } => {
1210 a[2] = DirectMsgArgs::VERSION_REQ.into();
1211 a[3] = u32::from(version).into();
1212 }
1213 DirectMsgArgs::PowerPsciReq32 {
1214 function_id,
1215 params,
1216 } => {
1217 a[2] = DirectMsgArgs::POWER_PSCI_REQ.into();
1218 a[3] = function_id.into();
1219 a[4] = params[0].into();
1220 a[5] = params[1].into();
1221 a[6] = params[2].into();
1222 }
1223 DirectMsgArgs::PowerPsciReq64 {
1224 function_id,
1225 params,
1226 } => {
1227 a[2] = DirectMsgArgs::POWER_PSCI_REQ.into();
1228 a[3] = function_id.into();
1229 a[4] = params[0];
1230 a[5] = params[1];
1231 a[6] = params[2];
1232 }
1233 DirectMsgArgs::PowerWarmBootReq { boot_type } => {
1234 a[2] = DirectMsgArgs::POWER_WARM_BOOT_REQ.into();
1235 a[3] = u32::from(boot_type).into();
1236 }
1237 DirectMsgArgs::VmCreated { handle, vm_id } => {
1238 a[2] = DirectMsgArgs::VM_CREATED.into();
1239 let handle_regs: [u32; 2] = handle.into();
1240 a[3] = handle_regs[0].into();
1241 a[4] = handle_regs[1].into();
1242 a[5] = vm_id.into();
1243 }
1244 DirectMsgArgs::VmDestructed { handle, vm_id } => {
1245 a[2] = DirectMsgArgs::VM_DESTRUCTED.into();
1246 let handle_regs: [u32; 2] = handle.into();
1247 a[3] = handle_regs[0].into();
1248 a[4] = handle_regs[1].into();
1249 a[5] = vm_id.into();
1250 }
1251 _ => panic!("Malformed MsgSendDirectReq interface"),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001252 }
1253 }
1254 Interface::MsgSendDirectResp {
1255 src_id,
1256 dst_id,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001257 args,
1258 } => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +01001259 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Balint Dobszay3aad9572025-01-17 16:54:11 +01001260 match args {
1261 DirectMsgArgs::Args32(args) => {
1262 a[3] = args[0].into();
1263 a[4] = args[1].into();
1264 a[5] = args[2].into();
1265 a[6] = args[3].into();
1266 a[7] = args[4].into();
1267 }
1268 DirectMsgArgs::Args64(args) => {
1269 a[3] = args[0];
1270 a[4] = args[1];
1271 a[5] = args[2];
1272 a[6] = args[3];
1273 a[7] = args[4];
1274 }
Tomás González4d5b0ba2025-03-03 17:15:55 +00001275 DirectMsgArgs::VersionResp { version } => {
1276 a[2] = DirectMsgArgs::VERSION_RESP.into();
1277 match version {
1278 None => a[3] = i32::from(FfaError::NotSupported) as u64,
1279 Some(ver) => a[3] = u32::from(ver).into(),
1280 }
1281 }
1282 DirectMsgArgs::PowerPsciResp { psci_status } => {
1283 a[2] = DirectMsgArgs::POWER_PSCI_RESP.into();
1284 a[3] = psci_status as u64;
1285 }
1286 DirectMsgArgs::VmCreatedAck { sp_status } => {
1287 a[2] = DirectMsgArgs::VM_CREATED_ACK.into();
1288 a[4] = i32::from(sp_status) as u64;
1289 }
1290 DirectMsgArgs::VmDestructedAck { sp_status } => {
1291 a[2] = DirectMsgArgs::VM_DESTRUCTED_ACK.into();
1292 a[3] = i32::from(sp_status) as u64;
1293 }
1294 _ => panic!("Malformed MsgSendDirectResp interface"),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001295 }
1296 }
1297 Interface::MemDonate {
1298 total_len,
1299 frag_len,
1300 buf,
1301 } => {
1302 a[1] = total_len.into();
1303 a[2] = frag_len.into();
1304 (a[3], a[4]) = match buf {
1305 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1306 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1307 None => (0, 0),
1308 };
1309 }
1310 Interface::MemLend {
1311 total_len,
1312 frag_len,
1313 buf,
1314 } => {
1315 a[1] = total_len.into();
1316 a[2] = frag_len.into();
1317 (a[3], a[4]) = match buf {
1318 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1319 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1320 None => (0, 0),
1321 };
1322 }
1323 Interface::MemShare {
1324 total_len,
1325 frag_len,
1326 buf,
1327 } => {
1328 a[1] = total_len.into();
1329 a[2] = frag_len.into();
1330 (a[3], a[4]) = match buf {
1331 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1332 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1333 None => (0, 0),
1334 };
1335 }
1336 Interface::MemRetrieveReq {
1337 total_len,
1338 frag_len,
1339 buf,
1340 } => {
1341 a[1] = total_len.into();
1342 a[2] = frag_len.into();
1343 (a[3], a[4]) = match buf {
1344 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1345 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1346 None => (0, 0),
1347 };
1348 }
1349 Interface::MemRetrieveResp {
1350 total_len,
1351 frag_len,
1352 } => {
1353 a[1] = total_len.into();
1354 a[2] = frag_len.into();
1355 }
1356 Interface::MemRelinquish => {}
1357 Interface::MemReclaim { handle, flags } => {
1358 let handle_regs: [u32; 2] = handle.into();
1359 a[1] = handle_regs[0].into();
1360 a[2] = handle_regs[1].into();
1361 a[3] = flags.into();
1362 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001363 Interface::MemPermGet { addr, page_cnt } => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001364 a[1] = match addr {
1365 MemAddr::Addr32(addr) => addr.into(),
1366 MemAddr::Addr64(addr) => addr,
1367 };
Balint Dobszayde0dc802025-02-28 14:16:52 +01001368 a[2] = if version >= Version(1, 3) {
1369 page_cnt.unwrap().into()
1370 } else {
1371 assert!(page_cnt.is_none());
1372 0
1373 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01001374 }
1375 Interface::MemPermSet {
1376 addr,
1377 page_cnt,
1378 mem_perm,
1379 } => {
1380 a[1] = match addr {
1381 MemAddr::Addr32(addr) => addr.into(),
1382 MemAddr::Addr64(addr) => addr,
1383 };
1384 a[2] = page_cnt.into();
1385 a[3] = mem_perm.into();
1386 }
1387 Interface::ConsoleLog {
1388 char_cnt,
1389 char_lists,
1390 } => {
1391 a[1] = char_cnt.into();
1392 match char_lists {
1393 ConsoleLogChars::Reg32(regs) => {
1394 a[2] = regs[0].into();
1395 a[3] = regs[1].into();
1396 a[4] = regs[2].into();
1397 a[5] = regs[3].into();
1398 a[6] = regs[4].into();
1399 a[7] = regs[5].into();
1400 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001401 _ => panic!("{:#x?} requires 18 registers", char_lists),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001402 }
1403 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001404 _ => panic!("{:#x?} requires 18 registers", self),
1405 }
1406 }
1407
1408 fn pack_regs18(&self, version: Version, a: &mut [u64; 18]) {
1409 assert!(version >= Version(1, 2));
1410
1411 a.fill(0);
1412 if let Some(function_id) = self.function_id() {
1413 a[0] = function_id as u64;
1414 }
1415
1416 match *self {
1417 Interface::Success { target_info, args } => {
1418 a[1] = target_info.into();
1419 match args {
1420 SuccessArgs::Result64_2(regs) => a[2..18].copy_from_slice(&regs[..16]),
1421 _ => panic!("{:#x?} requires 8 registers", args),
1422 }
1423 }
1424 Interface::MsgSendDirectReq2 {
1425 src_id,
1426 dst_id,
1427 uuid,
1428 args,
1429 } => {
1430 a[1] = ((src_id as u64) << 16) | dst_id as u64;
1431 (a[2], a[3]) = uuid.as_u64_pair();
1432 a[4..18].copy_from_slice(&args.0[..14]);
1433 }
1434 Interface::MsgSendDirectResp2 {
1435 src_id,
1436 dst_id,
1437 args,
1438 } => {
1439 a[1] = ((src_id as u64) << 16) | dst_id as u64;
1440 a[2] = 0;
1441 a[3] = 0;
1442 a[4..18].copy_from_slice(&args.0[..14]);
1443 }
1444 Interface::ConsoleLog {
1445 char_cnt,
1446 char_lists,
1447 } => {
1448 a[1] = char_cnt.into();
1449 match char_lists {
1450 ConsoleLogChars::Reg64(regs) => a[2..18].copy_from_slice(&regs[..16]),
1451 _ => panic!("{:#x?} requires 8 registers", char_lists),
1452 }
1453 }
1454 _ => panic!("{:#x?} requires 8 registers", self),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001455 }
1456 }
1457
Balint Dobszaya5846852025-02-26 15:38:53 +01001458 /// Helper function to create an `FFA_SUCCESS` interface without any arguments.
Balint Dobszay3aad9572025-01-17 16:54:11 +01001459 pub fn success32_noargs() -> Self {
1460 Self::Success {
1461 target_info: 0,
1462 args: SuccessArgs::Result32([0, 0, 0, 0, 0, 0]),
1463 }
1464 }
1465
Tomás González4c8c7d22025-03-10 17:14:57 +00001466 /// Helper function to create an `FFA_SUCCESS` interface without any arguments.
1467 pub fn success64_noargs() -> Self {
1468 Self::Success {
1469 target_info: 0,
1470 args: SuccessArgs::Result64([0, 0, 0, 0, 0, 0]),
1471 }
1472 }
1473
Balint Dobszaya5846852025-02-26 15:38:53 +01001474 /// Helper function to create an `FFA_ERROR` interface with an error code.
Balint Dobszay3aad9572025-01-17 16:54:11 +01001475 pub fn error(error_code: FfaError) -> Self {
1476 Self::Error {
1477 target_info: TargetInfo {
1478 endpoint_id: 0,
1479 vcpu_id: 0,
1480 },
1481 error_code,
1482 }
1483 }
1484}
1485
Balint Dobszaya5846852025-02-26 15:38:53 +01001486/// Maximum number of characters transmitted in a single `FFA_CONSOLE_LOG32` message.
1487pub const CONSOLE_LOG_32_MAX_CHAR_CNT: u8 = 24;
Balint Dobszayde0dc802025-02-28 14:16:52 +01001488/// Maximum number of characters transmitted in a single `FFA_CONSOLE_LOG64` message.
1489pub const CONSOLE_LOG_64_MAX_CHAR_CNT: u8 = 128;
Balint Dobszay3aad9572025-01-17 16:54:11 +01001490
Balint Dobszaya5846852025-02-26 15:38:53 +01001491/// Helper function to convert the "Tightly packed list of characters" format used by the
1492/// `FFA_CONSOLE_LOG` interface into a byte slice.
Balint Dobszay3aad9572025-01-17 16:54:11 +01001493pub fn parse_console_log(
1494 char_cnt: u8,
1495 char_lists: &ConsoleLogChars,
1496 log_bytes: &mut [u8],
1497) -> Result<(), FfaError> {
1498 match char_lists {
1499 ConsoleLogChars::Reg32(regs) => {
Balint Dobszaya5846852025-02-26 15:38:53 +01001500 if !(1..=CONSOLE_LOG_32_MAX_CHAR_CNT).contains(&char_cnt) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001501 return Err(FfaError::InvalidParameters);
1502 }
1503 for (i, reg) in regs.iter().enumerate() {
1504 log_bytes[4 * i..4 * (i + 1)].copy_from_slice(&reg.to_le_bytes());
1505 }
1506 }
1507 ConsoleLogChars::Reg64(regs) => {
Balint Dobszaya5846852025-02-26 15:38:53 +01001508 if !(1..=CONSOLE_LOG_64_MAX_CHAR_CNT).contains(&char_cnt) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001509 return Err(FfaError::InvalidParameters);
1510 }
1511 for (i, reg) in regs.iter().enumerate() {
1512 log_bytes[8 * i..8 * (i + 1)].copy_from_slice(&reg.to_le_bytes());
1513 }
1514 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001515 }
1516
Balint Dobszayc8802492025-01-15 18:11:39 +01001517 Ok(())
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001518}