blob: 2e6a1198fa3353928217948dbc66ef55f383750f [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;
Imre Kisc0b062c2025-05-26 19:31:00 +020012pub use uuid::Uuid;
Imre Kis189f18c2025-05-26 19:33:05 +020013use zerocopy::{transmute, FromBytes, Immutable, IntoBytes};
Balint Dobszay5bf492f2024-07-29 17:21:32 +020014
15pub mod boot_info;
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +010016mod ffa_v1_1;
Balint Dobszayde0dc802025-02-28 14:16:52 +010017mod ffa_v1_2;
Balint Dobszay5bf492f2024-07-29 17:21:32 +020018pub mod memory_management;
19pub mod partition_info;
20
Balint Dobszaya5846852025-02-26 15:38:53 +010021/// Constant for 4K page size. On many occasions the FF-A spec defines memory size as count of 4K
22/// pages, regardless of the current translation granule.
Balint Dobszay3aad9572025-01-17 16:54:11 +010023pub const FFA_PAGE_SIZE_4K: usize = 4096;
24
Balint Dobszaya5846852025-02-26 15:38:53 +010025/// Rich error types returned by this module. Should be converted to [`crate::FfaError`] when used
26/// with the `FFA_ERROR` interface.
Tomás González0a058bc2025-03-11 11:20:55 +000027#[derive(Debug, Error, PartialEq)]
Balint Dobszay3aad9572025-01-17 16:54:11 +010028pub enum Error {
29 #[error("Unrecognised FF-A function ID {0}")]
30 UnrecognisedFunctionId(u32),
31 #[error("Unrecognised FF-A feature ID {0}")]
32 UnrecognisedFeatureId(u8),
33 #[error("Unrecognised FF-A error code {0}")]
34 UnrecognisedErrorCode(i32),
Tomás González4d5b0ba2025-03-03 17:15:55 +000035 #[error("Unrecognised FF-A Framework Message {0}")]
36 UnrecognisedFwkMsg(u32),
Tomás González092202a2025-03-05 11:56:45 +000037 #[error("Invalid FF-A Msg Wait Flag {0}")]
38 InvalidMsgWaitFlag(u32),
Imre Kisa2fd69b2025-06-13 13:39:47 +020039 #[error("Invalid FF-A Msg Send2 Flag {0}")]
40 InvalidMsgSend2Flag(u32),
Tomás González4d5b0ba2025-03-03 17:15:55 +000041 #[error("Unrecognised VM availability status {0}")]
42 UnrecognisedVmAvailabilityStatus(i32),
43 #[error("Unrecognised FF-A Warm Boot Type {0}")]
44 UnrecognisedWarmBootType(u32),
45 #[error("Invalid version {0}")]
46 InvalidVersion(u32),
Tomás González0a058bc2025-03-11 11:20:55 +000047 #[error("Invalid Information Tag {0}")]
48 InvalidInformationTag(u16),
Tomás González7ffb6132025-04-03 12:28:58 +010049 #[error("Invalid Flag for Notification Set")]
50 InvalidNotificationSetFlag(u32),
51 #[error("Invalid Vm ID")]
52 InvalidVmId(u32),
Imre Kise295adb2025-04-10 13:26:28 +020053 #[error("Invalid FF-A Partition Info Get Flag {0}")]
54 InvalidPartitionInfoGetFlag(u32),
Imre Kis839eaef2025-04-11 17:38:36 +020055 #[error("Invalid success argument variant")]
56 InvalidSuccessArgsVariant,
Imre Kis787c5002025-04-10 14:25:51 +020057 #[error("Invalid notification count")]
58 InvalidNotificationCount,
Imre Kis92b663e2025-04-10 14:15:05 +020059 #[error("Invalid Partition Info Get Regs response")]
60 InvalidPartitionInfoGetRegsResponse,
Balint Dobszay82c71dd2025-04-15 10:16:44 +020061 #[error("Invalid FF-A version {0} for function ID {1:?}")]
62 InvalidVersionForFunctionId(Version, FuncId),
Imre Kis189f18c2025-05-26 19:33:05 +020063 #[error("Invalid character count {0}")]
64 InvalidCharacterCount(u8),
Imre Kisdcb7df22025-06-06 15:24:40 +020065 #[error("Memory management error")]
66 MemoryManagementError(#[from] memory_management::Error),
Balint Dobszay3aad9572025-01-17 16:54:11 +010067}
68
69impl From<Error> for FfaError {
70 fn from(value: Error) -> Self {
71 match value {
Balint Dobszay82c71dd2025-04-15 10:16:44 +020072 Error::UnrecognisedFunctionId(_)
73 | Error::UnrecognisedFeatureId(_)
74 | Error::InvalidVersionForFunctionId(..) => Self::NotSupported,
Tomás González0a058bc2025-03-11 11:20:55 +000075 Error::InvalidInformationTag(_) => Self::Retry,
Tomás González4d5b0ba2025-03-03 17:15:55 +000076 Error::UnrecognisedErrorCode(_)
77 | Error::UnrecognisedFwkMsg(_)
78 | Error::InvalidVersion(_)
Tomás González092202a2025-03-05 11:56:45 +000079 | Error::InvalidMsgWaitFlag(_)
Imre Kisa2fd69b2025-06-13 13:39:47 +020080 | Error::InvalidMsgSend2Flag(_)
Tomás González4d5b0ba2025-03-03 17:15:55 +000081 | Error::UnrecognisedVmAvailabilityStatus(_)
Tomás González7ffb6132025-04-03 12:28:58 +010082 | Error::InvalidNotificationSetFlag(_)
83 | Error::InvalidVmId(_)
Imre Kise295adb2025-04-10 13:26:28 +020084 | Error::UnrecognisedWarmBootType(_)
Imre Kis839eaef2025-04-11 17:38:36 +020085 | Error::InvalidPartitionInfoGetFlag(_)
Imre Kis787c5002025-04-10 14:25:51 +020086 | Error::InvalidSuccessArgsVariant
Imre Kis92b663e2025-04-10 14:15:05 +020087 | Error::InvalidNotificationCount
Imre Kis189f18c2025-05-26 19:33:05 +020088 | Error::InvalidPartitionInfoGetRegsResponse
Imre Kisdcb7df22025-06-06 15:24:40 +020089 | Error::InvalidCharacterCount(_)
90 | Error::MemoryManagementError(_) => Self::InvalidParameters,
Balint Dobszay3aad9572025-01-17 16:54:11 +010091 }
92 }
93}
Balint Dobszay5bf492f2024-07-29 17:21:32 +020094
Balint Dobszaya5846852025-02-26 15:38:53 +010095/// 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 +020096#[derive(PartialEq, Clone, Copy)]
97pub enum Instance {
Balint Dobszaya5846852025-02-26 15:38:53 +010098 /// The instance between the SPMC and SPMD.
Balint Dobszay5bf492f2024-07-29 17:21:32 +020099 SecurePhysical,
Balint Dobszaya5846852025-02-26 15:38:53 +0100100 /// The instance between the SPMC and a physical SP (contains the SP's endpoint ID).
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200101 SecureVirtual(u16),
102}
103
Balint Dobszaya5846852025-02-26 15:38:53 +0100104/// Function IDs of the various FF-A interfaces.
Andrew Walbran969b9252024-11-25 15:35:42 +0000105#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
Balint Dobszay3aad9572025-01-17 16:54:11 +0100106#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedFunctionId))]
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200107#[repr(u32)]
108pub enum FuncId {
109 Error = 0x84000060,
110 Success32 = 0x84000061,
111 Success64 = 0xc4000061,
112 Interrupt = 0x84000062,
113 Version = 0x84000063,
114 Features = 0x84000064,
115 RxAcquire = 0x84000084,
116 RxRelease = 0x84000065,
117 RxTxMap32 = 0x84000066,
118 RxTxMap64 = 0xc4000066,
119 RxTxUnmap = 0x84000067,
120 PartitionInfoGet = 0x84000068,
Balint Dobszaye6aa4862025-02-28 16:37:56 +0100121 PartitionInfoGetRegs = 0xc400008b,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200122 IdGet = 0x84000069,
123 SpmIdGet = 0x84000085,
Balint Dobszaye6aa4862025-02-28 16:37:56 +0100124 ConsoleLog32 = 0x8400008a,
125 ConsoleLog64 = 0xc400008a,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200126 MsgWait = 0x8400006b,
127 Yield = 0x8400006c,
128 Run = 0x8400006d,
129 NormalWorldResume = 0x8400007c,
130 MsgSend2 = 0x84000086,
131 MsgSendDirectReq32 = 0x8400006f,
132 MsgSendDirectReq64 = 0xc400006f,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100133 MsgSendDirectReq64_2 = 0xc400008d,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200134 MsgSendDirectResp32 = 0x84000070,
135 MsgSendDirectResp64 = 0xc4000070,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100136 MsgSendDirectResp64_2 = 0xc400008e,
Balint Dobszaye6aa4862025-02-28 16:37:56 +0100137 NotificationBitmapCreate = 0x8400007d,
138 NotificationBitmapDestroy = 0x8400007e,
139 NotificationBind = 0x8400007f,
140 NotificationUnbind = 0x84000080,
141 NotificationSet = 0x84000081,
142 NotificationGet = 0x84000082,
143 NotificationInfoGet32 = 0x84000083,
144 NotificationInfoGet64 = 0xc4000083,
145 El3IntrHandle = 0x8400008c,
Tomás González17b92442025-03-10 16:45:04 +0000146 SecondaryEpRegister32 = 0x84000087,
147 SecondaryEpRegister64 = 0xc4000087,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200148 MemDonate32 = 0x84000071,
149 MemDonate64 = 0xc4000071,
150 MemLend32 = 0x84000072,
151 MemLend64 = 0xc4000072,
152 MemShare32 = 0x84000073,
153 MemShare64 = 0xc4000073,
154 MemRetrieveReq32 = 0x84000074,
155 MemRetrieveReq64 = 0xc4000074,
156 MemRetrieveResp = 0x84000075,
157 MemRelinquish = 0x84000076,
158 MemReclaim = 0x84000077,
159 MemPermGet32 = 0x84000088,
160 MemPermGet64 = 0xc4000088,
161 MemPermSet32 = 0x84000089,
162 MemPermSet64 = 0xc4000089,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200163}
164
Balint Dobszayde0dc802025-02-28 14:16:52 +0100165impl FuncId {
166 /// Returns true if this is a 32-bit call, or false if it is a 64-bit call.
167 pub fn is_32bit(&self) -> bool {
Tomás González6ccba0a2025-04-09 13:31:29 +0100168 u32::from(*self) & (1 << 30) == 0
Balint Dobszayde0dc802025-02-28 14:16:52 +0100169 }
Balint Dobszay82c71dd2025-04-15 10:16:44 +0200170
171 /// Returns the FF-A version that has introduced the function ID.
172 pub fn minimum_ffa_version(&self) -> Version {
173 match self {
174 FuncId::Error
175 | FuncId::Success32
176 | FuncId::Success64
177 | FuncId::Interrupt
178 | FuncId::Version
179 | FuncId::Features
180 | FuncId::RxRelease
181 | FuncId::RxTxMap32
182 | FuncId::RxTxMap64
183 | FuncId::RxTxUnmap
184 | FuncId::PartitionInfoGet
185 | FuncId::IdGet
186 | FuncId::MsgWait
187 | FuncId::Yield
188 | FuncId::Run
189 | FuncId::NormalWorldResume
190 | FuncId::MsgSendDirectReq32
191 | FuncId::MsgSendDirectReq64
192 | FuncId::MsgSendDirectResp32
193 | FuncId::MsgSendDirectResp64
194 | FuncId::MemDonate32
195 | FuncId::MemDonate64
196 | FuncId::MemLend32
197 | FuncId::MemLend64
198 | FuncId::MemShare32
199 | FuncId::MemShare64
200 | FuncId::MemRetrieveReq32
201 | FuncId::MemRetrieveReq64
202 | FuncId::MemRetrieveResp
203 | FuncId::MemRelinquish
204 | FuncId::MemReclaim => Version(1, 0),
205
206 FuncId::RxAcquire
207 | FuncId::SpmIdGet
208 | FuncId::MsgSend2
209 | FuncId::MemPermGet32
210 | FuncId::MemPermGet64
211 | FuncId::MemPermSet32
212 | FuncId::MemPermSet64
213 | FuncId::NotificationBitmapCreate
214 | FuncId::NotificationBitmapDestroy
215 | FuncId::NotificationBind
216 | FuncId::NotificationUnbind
217 | FuncId::NotificationSet
218 | FuncId::NotificationGet
219 | FuncId::NotificationInfoGet32
220 | FuncId::NotificationInfoGet64
221 | FuncId::SecondaryEpRegister32
222 | FuncId::SecondaryEpRegister64 => Version(1, 1),
223
224 FuncId::PartitionInfoGetRegs
225 | FuncId::ConsoleLog32
226 | FuncId::ConsoleLog64
227 | FuncId::MsgSendDirectReq64_2
228 | FuncId::MsgSendDirectResp64_2
229 | FuncId::El3IntrHandle => Version(1, 2),
230 }
231 }
Balint Dobszayde0dc802025-02-28 14:16:52 +0100232}
233
Balint Dobszaya5846852025-02-26 15:38:53 +0100234/// Error status codes used by the `FFA_ERROR` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100235#[derive(Clone, Copy, Debug, Eq, Error, IntoPrimitive, PartialEq, TryFromPrimitive)]
236#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedErrorCode))]
237#[repr(i32)]
238pub enum FfaError {
239 #[error("Not supported")]
240 NotSupported = -1,
241 #[error("Invalid parameters")]
242 InvalidParameters = -2,
243 #[error("No memory")]
244 NoMemory = -3,
245 #[error("Busy")]
246 Busy = -4,
247 #[error("Interrupted")]
248 Interrupted = -5,
249 #[error("Denied")]
250 Denied = -6,
251 #[error("Retry")]
252 Retry = -7,
253 #[error("Aborted")]
254 Aborted = -8,
255 #[error("No data")]
256 NoData = -9,
257}
258
Balint Dobszaya5846852025-02-26 15:38:53 +0100259/// Endpoint ID and vCPU ID pair, used by `FFA_ERROR`, `FFA_INTERRUPT` and `FFA_RUN` interfaces.
Imre Kise521a282025-06-13 13:29:24 +0200260#[derive(Debug, Default, Eq, PartialEq, Clone, Copy)]
Balint Dobszay3aad9572025-01-17 16:54:11 +0100261pub struct TargetInfo {
262 pub endpoint_id: u16,
263 pub vcpu_id: u16,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200264}
265
Balint Dobszay3aad9572025-01-17 16:54:11 +0100266impl From<u32> for TargetInfo {
267 fn from(value: u32) -> Self {
268 Self {
269 endpoint_id: (value >> 16) as u16,
270 vcpu_id: value as u16,
271 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200272 }
273}
274
Balint Dobszay3aad9572025-01-17 16:54:11 +0100275impl From<TargetInfo> for u32 {
276 fn from(value: TargetInfo) -> Self {
Balint Dobszaye9a3e762025-02-26 17:29:57 +0100277 ((value.endpoint_id as u32) << 16) | value.vcpu_id as u32
Andrew Walbran0d315812024-11-25 15:36:36 +0000278 }
Balint Dobszay3aad9572025-01-17 16:54:11 +0100279}
Andrew Walbran0d315812024-11-25 15:36:36 +0000280
Imre Kis839eaef2025-04-11 17:38:36 +0200281/// Generic arguments of the `FFA_SUCCESS` interface. The interpretation of the arguments depends on
282/// the interface that initiated the request. The application code has knowledge of the request, so
283/// it has to convert `SuccessArgs` into/from a specific success args structure that matches the
284/// request.
Imre Kis4e9d8bc2025-04-10 13:48:26 +0200285///
286/// The current specialized success arguments types are:
287/// * `FFA_FEATURES` - [`SuccessArgsFeatures`]
Imre Kisbbef2872025-04-10 14:11:29 +0200288/// * `FFA_ID_GET` - [`SuccessArgsIdGet`]
289/// * `FFA_SPM_ID_GET` - [`SuccessArgsSpmIdGet`]
Imre Kis61c34092025-04-10 14:14:38 +0200290/// * `FFA_PARTITION_INFO_GET` - [`partition_info::SuccessArgsPartitionInfoGet`]
Imre Kis92b663e2025-04-10 14:15:05 +0200291/// * `FFA_PARTITION_INFO_GET_REGS` - [`partition_info::SuccessArgsPartitionInfoGetRegs`]
Imre Kis9959e062025-04-10 14:16:10 +0200292/// * `FFA_NOTIFICATION_GET` - [`SuccessArgsNotificationGet`]
Imre Kis787c5002025-04-10 14:25:51 +0200293/// * `FFA_NOTIFICATION_INFO_GET_32` - [`SuccessArgsNotificationInfoGet32`]
294/// * `FFA_NOTIFICATION_INFO_GET_64` - [`SuccessArgsNotificationInfoGet64`]
Balint Dobszay3aad9572025-01-17 16:54:11 +0100295#[derive(Debug, Eq, PartialEq, Clone, Copy)]
296pub enum SuccessArgs {
Imre Kis54773b62025-04-10 13:47:39 +0200297 Args32([u32; 6]),
298 Args64([u64; 6]),
299 Args64_2([u64; 16]),
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200300}
301
Imre Kis839eaef2025-04-11 17:38:36 +0200302impl SuccessArgs {
303 fn try_get_args32(self) -> Result<[u32; 6], Error> {
304 match self {
305 SuccessArgs::Args32(args) => Ok(args),
306 SuccessArgs::Args64(_) | SuccessArgs::Args64_2(_) => {
307 Err(Error::InvalidSuccessArgsVariant)
308 }
309 }
310 }
311
312 fn try_get_args64(self) -> Result<[u64; 6], Error> {
313 match self {
314 SuccessArgs::Args64(args) => Ok(args),
315 SuccessArgs::Args32(_) | SuccessArgs::Args64_2(_) => {
316 Err(Error::InvalidSuccessArgsVariant)
317 }
318 }
319 }
320
321 fn try_get_args64_2(self) -> Result<[u64; 16], Error> {
322 match self {
323 SuccessArgs::Args64_2(args) => Ok(args),
324 SuccessArgs::Args32(_) | SuccessArgs::Args64(_) => {
325 Err(Error::InvalidSuccessArgsVariant)
326 }
327 }
328 }
329}
330
Tomás González17b92442025-03-10 16:45:04 +0000331/// Entrypoint address argument for `FFA_SECONDARY_EP_REGISTER` interface.
332#[derive(Debug, Eq, PartialEq, Clone, Copy)]
333pub enum SecondaryEpRegisterAddr {
334 Addr32(u32),
335 Addr64(u64),
336}
337
Balint Dobszaya5846852025-02-26 15:38:53 +0100338/// Version number of the FF-A implementation, `.0` is the major, `.1` is minor the version.
Balint Dobszayde0dc802025-02-28 14:16:52 +0100339#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord)]
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200340pub struct Version(pub u16, pub u16);
341
Tomás González1f794352025-03-03 16:47:06 +0000342impl Version {
Tomás González83146af2025-03-04 11:32:41 +0000343 // The FF-A spec mandates that bit[31] of a version number must be 0
344 const MBZ_BITS: u32 = 1 << 31;
345
Tomás González1f794352025-03-03 16:47:06 +0000346 /// Returns whether the caller's version (self) is compatible with the callee's version (input
347 /// parameter)
348 pub fn is_compatible_to(&self, callee_version: &Version) -> bool {
349 self.0 == callee_version.0 && self.1 <= callee_version.1
350 }
Balint Dobszay5ded5922025-06-13 12:06:53 +0200351
352 /// Returns true if the specified FF-A version uses 18 registers for calls, false if it uses 8.
353 pub fn needs_18_regs(&self) -> bool {
354 *self >= Version(1, 2)
355 }
Tomás González1f794352025-03-03 16:47:06 +0000356}
357
Tomás González83146af2025-03-04 11:32:41 +0000358impl TryFrom<u32> for Version {
359 type Error = Error;
360
361 fn try_from(val: u32) -> Result<Self, Self::Error> {
362 if (val & Self::MBZ_BITS) != 0 {
363 Err(Error::InvalidVersion(val))
364 } else {
365 Ok(Self((val >> 16) as u16, val as u16))
366 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200367 }
368}
369
370impl From<Version> for u32 {
371 fn from(v: Version) -> Self {
Tomás González83146af2025-03-04 11:32:41 +0000372 let v_u32 = ((v.0 as u32) << 16) | v.1 as u32;
373 assert!(v_u32 & Version::MBZ_BITS == 0);
374 v_u32
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200375 }
376}
377
Andrew Walbran19970ba2024-11-25 15:35:00 +0000378impl Display for Version {
379 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
380 write!(f, "{}.{}", self.0, self.1)
381 }
382}
383
384impl Debug for Version {
385 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
386 Display::fmt(self, f)
387 }
388}
389
Balint Dobszaya5846852025-02-26 15:38:53 +0100390/// Feature IDs used by the `FFA_FEATURES` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100391#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
392#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedFeatureId))]
393#[repr(u8)]
394pub enum FeatureId {
395 NotificationPendingInterrupt = 0x1,
396 ScheduleReceiverInterrupt = 0x2,
397 ManagedExitInterrupt = 0x3,
398}
Balint Dobszayc8802492025-01-15 18:11:39 +0100399
Balint Dobszaya5846852025-02-26 15:38:53 +0100400/// Arguments for the `FFA_FEATURES` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100401#[derive(Debug, Eq, PartialEq, Clone, Copy)]
402pub enum Feature {
403 FuncId(FuncId),
404 FeatureId(FeatureId),
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100405 Unknown(u32),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100406}
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200407
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100408impl From<u32> for Feature {
409 fn from(value: u32) -> Self {
410 // Bit[31] is set for all valid FF-A function IDs so we don't have to check it separately
411 if let Ok(func_id) = value.try_into() {
412 Self::FuncId(func_id)
413 } else if let Ok(feat_id) = (value as u8).try_into() {
414 Self::FeatureId(feat_id)
Balint Dobszay3aad9572025-01-17 16:54:11 +0100415 } else {
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100416 Self::Unknown(value)
417 }
Balint Dobszay3aad9572025-01-17 16:54:11 +0100418 }
419}
420
421impl From<Feature> for u32 {
422 fn from(value: Feature) -> Self {
423 match value {
424 Feature::FuncId(func_id) => (1 << 31) | func_id as u32,
425 Feature::FeatureId(feature_id) => feature_id as u32,
Imre Kis29c8ace2025-04-11 13:49:58 +0200426 Feature::Unknown(id) => id,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100427 }
428 }
429}
430
Imre Kis4e9d8bc2025-04-10 13:48:26 +0200431/// `FFA_FEATURES` specific success argument structure. This type needs further specialization based
432/// on 'FF-A function ID or Feature ID' field of the preceeding `FFA_FEATURES` request.
433#[derive(Debug, Eq, PartialEq, Clone, Copy)]
434pub struct SuccessArgsFeatures {
435 pub properties: [u32; 2],
436}
437
438impl From<SuccessArgsFeatures> for SuccessArgs {
439 fn from(value: SuccessArgsFeatures) -> Self {
440 Self::Args32([value.properties[0], value.properties[1], 0, 0, 0, 0])
441 }
442}
443
444impl TryFrom<SuccessArgs> for SuccessArgsFeatures {
445 type Error = Error;
446
447 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
448 let args = value.try_get_args32()?;
449
450 Ok(Self {
451 properties: [args[0], args[1]],
452 })
453 }
454}
455
Balint Dobszaya5846852025-02-26 15:38:53 +0100456/// RXTX buffer descriptor, used by `FFA_RXTX_MAP`.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100457#[derive(Debug, Eq, PartialEq, Clone, Copy)]
458pub enum RxTxAddr {
459 Addr32 { rx: u32, tx: u32 },
460 Addr64 { rx: u64, tx: u64 },
461}
462
Imre Kisbbef2872025-04-10 14:11:29 +0200463/// `FFA_ID_GET` specific success argument structure.
464#[derive(Debug, Eq, PartialEq, Clone, Copy)]
465pub struct SuccessArgsIdGet {
466 pub id: u16,
467}
468
469impl From<SuccessArgsIdGet> for SuccessArgs {
470 fn from(value: SuccessArgsIdGet) -> Self {
471 SuccessArgs::Args32([value.id as u32, 0, 0, 0, 0, 0])
472 }
473}
474
475impl TryFrom<SuccessArgs> for SuccessArgsIdGet {
476 type Error = Error;
477
478 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
479 let args = value.try_get_args32()?;
480 Ok(Self { id: args[0] as u16 })
481 }
482}
483
484/// `FFA_SPM_ID_GET` specific success argument structure.
485#[derive(Debug, Eq, PartialEq, Clone, Copy)]
486pub struct SuccessArgsSpmIdGet {
487 pub id: u16,
488}
489
490impl From<SuccessArgsSpmIdGet> for SuccessArgs {
491 fn from(value: SuccessArgsSpmIdGet) -> Self {
492 SuccessArgs::Args32([value.id as u32, 0, 0, 0, 0, 0])
493 }
494}
495
496impl TryFrom<SuccessArgs> for SuccessArgsSpmIdGet {
497 type Error = Error;
498
499 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
500 let args = value.try_get_args32()?;
501 Ok(Self { id: args[0] as u16 })
502 }
503}
504
Imre Kise295adb2025-04-10 13:26:28 +0200505/// Flags of the `FFA_PARTITION_INFO_GET` interface.
506#[derive(Debug, Eq, PartialEq, Clone, Copy)]
507pub struct PartitionInfoGetFlags {
508 pub count_only: bool,
509}
510
511impl PartitionInfoGetFlags {
512 const RETURN_INFORMATION_TYPE_FLAG: u32 = 1 << 0;
513 const MBZ_BITS: u32 = 0xffff_fffe;
514}
515
516impl TryFrom<u32> for PartitionInfoGetFlags {
517 type Error = Error;
518
519 fn try_from(val: u32) -> Result<Self, Self::Error> {
520 if (val & Self::MBZ_BITS) != 0 {
521 Err(Error::InvalidPartitionInfoGetFlag(val))
522 } else {
523 Ok(Self {
524 count_only: val & Self::RETURN_INFORMATION_TYPE_FLAG != 0,
525 })
526 }
527 }
528}
529
530impl From<PartitionInfoGetFlags> for u32 {
531 fn from(flags: PartitionInfoGetFlags) -> Self {
532 let mut bits: u32 = 0;
533 if flags.count_only {
534 bits |= PartitionInfoGetFlags::RETURN_INFORMATION_TYPE_FLAG;
535 }
536 bits
537 }
538}
539
Imre Kisa2fd69b2025-06-13 13:39:47 +0200540/// Flags field of the FFA_MSG_SEND2 interface.
541#[derive(Debug, Eq, PartialEq, Clone, Copy)]
542pub struct MsgSend2Flags {
543 pub delay_schedule_receiver: bool,
544}
545
546impl MsgSend2Flags {
547 const DELAY_SCHEDULE_RECEIVER: u32 = 1 << 1;
548 const MBZ_BITS: u32 = 0xffff_fffd;
549}
550
551impl TryFrom<u32> for MsgSend2Flags {
552 type Error = Error;
553
554 fn try_from(val: u32) -> Result<Self, Self::Error> {
555 if (val & Self::MBZ_BITS) != 0 {
556 Err(Error::InvalidMsgSend2Flag(val))
557 } else {
558 Ok(MsgSend2Flags {
559 delay_schedule_receiver: val & Self::DELAY_SCHEDULE_RECEIVER != 0,
560 })
561 }
562 }
563}
564
565impl From<MsgSend2Flags> for u32 {
566 fn from(flags: MsgSend2Flags) -> Self {
567 let mut bits: u32 = 0;
568 if flags.delay_schedule_receiver {
569 bits |= MsgSend2Flags::DELAY_SCHEDULE_RECEIVER;
570 }
571 bits
572 }
573}
574
Tomás González4d5b0ba2025-03-03 17:15:55 +0000575/// Composite type for capturing success and error return codes for the VM availability messages.
576///
577/// Error codes are handled by the `FfaError` type. Having a separate type for errors helps using
578/// `Result<(), FfaError>`. If a single type would include both success and error values,
579/// then `Err(FfaError::Success)` would be incomprehensible.
580#[derive(Debug, Eq, PartialEq, Clone, Copy)]
581pub enum VmAvailabilityStatus {
582 Success,
583 Error(FfaError),
584}
585
586impl TryFrom<i32> for VmAvailabilityStatus {
587 type Error = Error;
588 fn try_from(value: i32) -> Result<Self, <Self as TryFrom<i32>>::Error> {
589 Ok(match value {
590 0 => Self::Success,
591 error_code => Self::Error(FfaError::try_from(error_code)?),
592 })
593 }
594}
595
596impl From<VmAvailabilityStatus> for i32 {
597 fn from(value: VmAvailabilityStatus) -> Self {
598 match value {
599 VmAvailabilityStatus::Success => 0,
600 VmAvailabilityStatus::Error(error_code) => error_code.into(),
601 }
602 }
603}
604
605/// Arguments for the Power Warm Boot `FFA_MSG_SEND_DIRECT_REQ` interface.
606#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
607#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedWarmBootType))]
608#[repr(u32)]
609pub enum WarmBootType {
610 ExitFromSuspend = 0,
611 ExitFromLowPower = 1,
612}
613
Balint Dobszaya5846852025-02-26 15:38:53 +0100614/// Arguments for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}` interfaces.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100615#[derive(Debug, Eq, PartialEq, Clone, Copy)]
616pub enum DirectMsgArgs {
617 Args32([u32; 5]),
618 Args64([u64; 5]),
Tomás González4d5b0ba2025-03-03 17:15:55 +0000619 /// Message for forwarding FFA_VERSION call from Normal world to the SPMC
620 VersionReq {
621 version: Version,
622 },
623 /// Response message to forwarded FFA_VERSION call from the Normal world
624 /// Contains the version returned by the SPMC or None
625 VersionResp {
626 version: Option<Version>,
627 },
628 /// Message for a power management operation initiated by a PSCI function
629 PowerPsciReq32 {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000630 // params[i]: Input parameter in w[i] in PSCI function invocation at EL3.
Tomás González67f92c72025-03-20 16:50:42 +0000631 // params[0]: Function ID.
632 params: [u32; 4],
Tomás González4d5b0ba2025-03-03 17:15:55 +0000633 },
634 /// Message for a power management operation initiated by a PSCI function
635 PowerPsciReq64 {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000636 // params[i]: Input parameter in x[i] in PSCI function invocation at EL3.
Tomás González67f92c72025-03-20 16:50:42 +0000637 // params[0]: Function ID.
638 params: [u64; 4],
Tomás González4d5b0ba2025-03-03 17:15:55 +0000639 },
640 /// Message for a warm boot
641 PowerWarmBootReq {
642 boot_type: WarmBootType,
643 },
644 /// Response message to indicate return status of the last power management request message
645 /// Return error code SUCCESS or DENIED as defined in PSCI spec. Caller is left to do the
646 /// parsing of the return status.
647 PowerPsciResp {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000648 psci_status: i32,
649 },
650 /// Message to signal creation of a VM
651 VmCreated {
652 // Globally unique Handle to identify a memory region that contains IMPLEMENTATION DEFINED
653 // information associated with the created VM.
654 // The invalid memory region handle must be specified by the Hypervisor if this field is not
655 // used.
656 handle: memory_management::Handle,
657 vm_id: u16,
658 },
659 /// Message to acknowledge creation of a VM
660 VmCreatedAck {
661 sp_status: VmAvailabilityStatus,
662 },
663 /// Message to signal destruction of a VM
664 VmDestructed {
665 // Globally unique Handle to identify a memory region that contains IMPLEMENTATION DEFINED
666 // information associated with the created VM.
667 // The invalid memory region handle must be specified by the Hypervisor if this field is not
668 // used.
669 handle: memory_management::Handle,
670 vm_id: u16,
671 },
672 /// Message to acknowledge destruction of a VM
673 VmDestructedAck {
674 sp_status: VmAvailabilityStatus,
675 },
676}
677
678impl DirectMsgArgs {
679 // Flags for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}` interfaces.
680
681 const FWK_MSG_BITS: u32 = 1 << 31;
682 const VERSION_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b1000;
683 const VERSION_RESP: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b1001;
684 const POWER_PSCI_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS;
685 const POWER_WARM_BOOT_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0001;
686 const POWER_PSCI_RESP: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0010;
687 const VM_CREATED: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0100;
688 const VM_CREATED_ACK: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0101;
689 const VM_DESTRUCTED: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0110;
690 const VM_DESTRUCTED_ACK: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0111;
Balint Dobszay3aad9572025-01-17 16:54:11 +0100691}
692
Balint Dobszayde0dc802025-02-28 14:16:52 +0100693/// Arguments for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}2` interfaces.
694#[derive(Debug, Eq, PartialEq, Clone, Copy)]
Imre Kisc739e0e2025-05-30 11:49:25 +0200695pub struct DirectMsg2Args(pub [u64; 14]);
Balint Dobszayde0dc802025-02-28 14:16:52 +0100696
Tomás González092202a2025-03-05 11:56:45 +0000697#[derive(Debug, Eq, PartialEq, Clone, Copy)]
698pub struct MsgWaitFlags {
Imre Kisc739e0e2025-05-30 11:49:25 +0200699 pub retain_rx_buffer: bool,
Tomás González092202a2025-03-05 11:56:45 +0000700}
701
702impl MsgWaitFlags {
703 const RETAIN_RX_BUFFER: u32 = 0x01;
704 const MBZ_BITS: u32 = 0xfffe;
705}
706
707impl TryFrom<u32> for MsgWaitFlags {
708 type Error = Error;
709
710 fn try_from(val: u32) -> Result<Self, Self::Error> {
711 if (val & Self::MBZ_BITS) != 0 {
712 Err(Error::InvalidMsgWaitFlag(val))
713 } else {
714 Ok(MsgWaitFlags {
715 retain_rx_buffer: val & Self::RETAIN_RX_BUFFER != 0,
716 })
717 }
718 }
719}
720
721impl From<MsgWaitFlags> for u32 {
722 fn from(flags: MsgWaitFlags) -> Self {
723 let mut bits: u32 = 0;
724 if flags.retain_rx_buffer {
725 bits |= MsgWaitFlags::RETAIN_RX_BUFFER;
726 }
727 bits
728 }
729}
730
Balint Dobszaya5846852025-02-26 15:38:53 +0100731/// Descriptor for a dynamically allocated memory buffer that contains the memory transaction
Tomás Gonzálezf268e322025-03-05 11:18:11 +0000732/// descriptor.
733///
734/// Used by `FFA_MEM_{DONATE,LEND,SHARE,RETRIEVE_REQ}` interfaces, only when the TX buffer is not
735/// used to transmit the transaction descriptor.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100736#[derive(Debug, Eq, PartialEq, Clone, Copy)]
737pub enum MemOpBuf {
738 Buf32 { addr: u32, page_cnt: u32 },
739 Buf64 { addr: u64, page_cnt: u32 },
740}
741
Balint Dobszaya5846852025-02-26 15:38:53 +0100742/// Memory address argument for `FFA_MEM_PERM_{GET,SET}` interfaces.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100743#[derive(Debug, Eq, PartialEq, Clone, Copy)]
744pub enum MemAddr {
745 Addr32(u32),
746 Addr64(u64),
747}
748
Balint Dobszayde0dc802025-02-28 14:16:52 +0100749/// Argument for the `FFA_CONSOLE_LOG` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100750#[derive(Debug, Eq, PartialEq, Clone, Copy)]
751pub enum ConsoleLogChars {
Imre Kis189f18c2025-05-26 19:33:05 +0200752 Chars32(ConsoleLogChars32),
753 Chars64(ConsoleLogChars64),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100754}
755
Imre Kis189f18c2025-05-26 19:33:05 +0200756/// Generic type for storing `FFA_CONSOLE_LOG` character payload and its length in bytes.
757#[derive(Debug, Default, Eq, PartialEq, Clone, Copy)]
758pub struct LogChars<T>
759where
760 T: IntoBytes + FromBytes + Immutable,
761{
762 char_cnt: u8,
763 char_lists: T,
764}
765
766impl<T> LogChars<T>
767where
768 T: IntoBytes + FromBytes + Immutable,
769{
770 const MAX_LENGTH: u8 = core::mem::size_of::<T>() as u8;
771
772 /// Returns true if there are no characters in the structure.
773 pub fn empty(&self) -> bool {
774 self.char_cnt == 0
775 }
776
777 /// Returns true if the structure is full.
778 pub fn full(&self) -> bool {
779 self.char_cnt as usize >= core::mem::size_of::<T>()
780 }
781
782 /// Returns the payload bytes.
783 pub fn bytes(&self) -> &[u8] {
784 &self.char_lists.as_bytes()[..self.char_cnt as usize]
785 }
786
787 /// Append byte slice to the end of the characters.
788 pub fn push(&mut self, source: &[u8]) -> usize {
789 let empty_area = &mut self.char_lists.as_mut_bytes()[self.char_cnt.into()..];
790 let len = empty_area.len().min(source.len());
791
792 empty_area[..len].copy_from_slice(&source[..len]);
793 self.char_cnt += len as u8;
794
795 len
796 }
797}
798
799/// Specialized type for 32-bit `FFA_CONSOLE_LOG` payload.
800pub type ConsoleLogChars32 = LogChars<[u32; 6]>;
801
802/// Specialized type for 64-bit `FFA_CONSOLE_LOG` payload.
803pub type ConsoleLogChars64 = LogChars<[u64; 16]>;
804
Tomás González7ffb6132025-04-03 12:28:58 +0100805#[derive(Debug, Eq, PartialEq, Clone, Copy)]
806pub struct NotificationBindFlags {
Imre Kisc739e0e2025-05-30 11:49:25 +0200807 pub per_vcpu_notification: bool,
Tomás González7ffb6132025-04-03 12:28:58 +0100808}
809
810impl NotificationBindFlags {
811 const PER_VCPU_NOTIFICATION: u32 = 1;
812}
813
814impl From<NotificationBindFlags> for u32 {
815 fn from(flags: NotificationBindFlags) -> Self {
816 let mut bits: u32 = 0;
817 if flags.per_vcpu_notification {
818 bits |= NotificationBindFlags::PER_VCPU_NOTIFICATION;
819 }
820 bits
821 }
822}
823
824impl From<u32> for NotificationBindFlags {
825 fn from(flags: u32) -> Self {
826 Self {
827 per_vcpu_notification: flags & Self::PER_VCPU_NOTIFICATION != 0,
828 }
829 }
830}
831
832#[derive(Debug, Eq, PartialEq, Clone, Copy)]
833pub struct NotificationSetFlags {
Imre Kisc739e0e2025-05-30 11:49:25 +0200834 pub delay_schedule_receiver: bool,
835 pub vcpu_id: Option<u16>,
Tomás González7ffb6132025-04-03 12:28:58 +0100836}
837
838impl NotificationSetFlags {
839 const PER_VCP_NOTIFICATION: u32 = 1 << 0;
840 const DELAY_SCHEDULE_RECEIVER: u32 = 1 << 1;
841 const VCPU_ID_SHIFT: u32 = 16;
842
843 const MBZ_BITS: u32 = 0xfffc;
844}
845
846impl From<NotificationSetFlags> for u32 {
847 fn from(flags: NotificationSetFlags) -> Self {
848 let mut bits: u32 = 0;
849
850 if flags.delay_schedule_receiver {
851 bits |= NotificationSetFlags::DELAY_SCHEDULE_RECEIVER;
852 }
853 if let Some(vcpu_id) = flags.vcpu_id {
854 bits |= NotificationSetFlags::PER_VCP_NOTIFICATION;
855 bits |= u32::from(vcpu_id) << NotificationSetFlags::VCPU_ID_SHIFT;
856 }
857
858 bits
859 }
860}
861
862impl TryFrom<u32> for NotificationSetFlags {
863 type Error = Error;
864
865 fn try_from(flags: u32) -> Result<Self, Self::Error> {
866 if (flags & Self::MBZ_BITS) != 0 {
867 return Err(Error::InvalidNotificationSetFlag(flags));
868 }
869
870 let tentative_vcpu_id = (flags >> Self::VCPU_ID_SHIFT) as u16;
871
872 let vcpu_id = if (flags & Self::PER_VCP_NOTIFICATION) != 0 {
873 Some(tentative_vcpu_id)
874 } else {
875 if tentative_vcpu_id != 0 {
876 return Err(Error::InvalidNotificationSetFlag(flags));
877 }
878 None
879 };
880
881 Ok(Self {
882 delay_schedule_receiver: (flags & Self::DELAY_SCHEDULE_RECEIVER) != 0,
883 vcpu_id,
884 })
885 }
886}
887
888#[derive(Debug, Eq, PartialEq, Clone, Copy)]
889pub struct NotificationGetFlags {
Imre Kisc739e0e2025-05-30 11:49:25 +0200890 pub sp_bitmap_id: bool,
891 pub vm_bitmap_id: bool,
892 pub spm_bitmap_id: bool,
893 pub hyp_bitmap_id: bool,
Tomás González7ffb6132025-04-03 12:28:58 +0100894}
895
896impl NotificationGetFlags {
897 const SP_BITMAP_ID: u32 = 1;
898 const VM_BITMAP_ID: u32 = 1 << 1;
899 const SPM_BITMAP_ID: u32 = 1 << 2;
900 const HYP_BITMAP_ID: u32 = 1 << 3;
901}
902
903impl From<NotificationGetFlags> for u32 {
904 fn from(flags: NotificationGetFlags) -> Self {
905 let mut bits: u32 = 0;
906 if flags.sp_bitmap_id {
907 bits |= NotificationGetFlags::SP_BITMAP_ID;
908 }
909 if flags.vm_bitmap_id {
910 bits |= NotificationGetFlags::VM_BITMAP_ID;
911 }
912 if flags.spm_bitmap_id {
913 bits |= NotificationGetFlags::SPM_BITMAP_ID;
914 }
915 if flags.hyp_bitmap_id {
916 bits |= NotificationGetFlags::HYP_BITMAP_ID;
917 }
918 bits
919 }
920}
921
922impl From<u32> for NotificationGetFlags {
923 // This is a "from" instead of a "try_from" because Reserved Bits are SBZ, *not* MBZ.
924 fn from(flags: u32) -> Self {
925 Self {
926 sp_bitmap_id: (flags & Self::SP_BITMAP_ID) != 0,
927 vm_bitmap_id: (flags & Self::VM_BITMAP_ID) != 0,
928 spm_bitmap_id: (flags & Self::SPM_BITMAP_ID) != 0,
929 hyp_bitmap_id: (flags & Self::HYP_BITMAP_ID) != 0,
930 }
931 }
932}
933
Imre Kis9959e062025-04-10 14:16:10 +0200934/// `FFA_NOTIFICATION_GET` specific success argument structure.
935#[derive(Debug, Eq, PartialEq, Clone, Copy)]
936pub struct SuccessArgsNotificationGet {
937 pub sp_notifications: Option<u64>,
938 pub vm_notifications: Option<u64>,
939 pub spm_notifications: Option<u32>,
940 pub hypervisor_notifications: Option<u32>,
941}
942
943impl From<SuccessArgsNotificationGet> for SuccessArgs {
944 fn from(value: SuccessArgsNotificationGet) -> Self {
945 let mut args = [0; 6];
946
947 if let Some(bitmap) = value.sp_notifications {
948 args[0] = bitmap as u32;
949 args[1] = (bitmap >> 32) as u32;
950 }
951
952 if let Some(bitmap) = value.vm_notifications {
953 args[2] = bitmap as u32;
954 args[3] = (bitmap >> 32) as u32;
955 }
956
957 if let Some(bitmap) = value.spm_notifications {
958 args[4] = bitmap;
959 }
960
961 if let Some(bitmap) = value.hypervisor_notifications {
962 args[5] = bitmap;
963 }
964
965 Self::Args32(args)
966 }
967}
968
969impl TryFrom<(NotificationGetFlags, SuccessArgs)> for SuccessArgsNotificationGet {
970 type Error = Error;
971
972 fn try_from(value: (NotificationGetFlags, SuccessArgs)) -> Result<Self, Self::Error> {
973 let (flags, value) = value;
974 let args = value.try_get_args32()?;
975
976 let sp_notifications = if flags.sp_bitmap_id {
977 Some(u64::from(args[0]) | (u64::from(args[1]) << 32))
978 } else {
979 None
980 };
981
982 let vm_notifications = if flags.vm_bitmap_id {
983 Some(u64::from(args[2]) | (u64::from(args[3]) << 32))
984 } else {
985 None
986 };
987
988 let spm_notifications = if flags.spm_bitmap_id {
989 Some(args[4])
990 } else {
991 None
992 };
993
994 let hypervisor_notifications = if flags.hyp_bitmap_id {
995 Some(args[5])
996 } else {
997 None
998 };
999
1000 Ok(Self {
1001 sp_notifications,
1002 vm_notifications,
1003 spm_notifications,
1004 hypervisor_notifications,
1005 })
1006 }
1007}
Imre Kis787c5002025-04-10 14:25:51 +02001008
1009/// `FFA_NOTIFICATION_INFO_GET` specific success argument structure. The `MAX_COUNT` parameter
1010/// depends on the 32-bit or 64-bit packing.
1011#[derive(Debug, Eq, PartialEq, Clone, Copy)]
1012pub struct SuccessArgsNotificationInfoGet<const MAX_COUNT: usize> {
1013 pub more_pending_notifications: bool,
1014 list_count: usize,
1015 id_counts: [u8; MAX_COUNT],
1016 ids: [u16; MAX_COUNT],
1017}
1018
1019impl<const MAX_COUNT: usize> Default for SuccessArgsNotificationInfoGet<MAX_COUNT> {
1020 fn default() -> Self {
1021 Self {
1022 more_pending_notifications: false,
1023 list_count: 0,
1024 id_counts: [0; MAX_COUNT],
1025 ids: [0; MAX_COUNT],
1026 }
1027 }
1028}
1029
1030impl<const MAX_COUNT: usize> SuccessArgsNotificationInfoGet<MAX_COUNT> {
1031 const MORE_PENDING_NOTIFICATIONS_FLAG: u64 = 1 << 0;
1032 const LIST_COUNT_SHIFT: usize = 7;
1033 const LIST_COUNT_MASK: u64 = 0x1f;
1034 const ID_COUNT_SHIFT: usize = 12;
1035 const ID_COUNT_MASK: u64 = 0x03;
1036 const ID_COUNT_BITS: usize = 2;
1037
1038 pub fn add_list(&mut self, endpoint: u16, vcpu_ids: &[u16]) -> Result<(), Error> {
1039 if self.list_count >= MAX_COUNT || vcpu_ids.len() > Self::ID_COUNT_MASK as usize {
1040 return Err(Error::InvalidNotificationCount);
1041 }
1042
1043 // Each list contains at least one ID: the partition ID, followed by vCPU IDs. The number
1044 // of vCPU IDs is recorded in `id_counts`.
1045 let mut current_id_index = self.list_count + self.id_counts.iter().sum::<u8>() as usize;
1046 if current_id_index + 1 + vcpu_ids.len() > MAX_COUNT {
1047 // The new list does not fit into the available space for IDs.
1048 return Err(Error::InvalidNotificationCount);
1049 }
1050
1051 self.id_counts[self.list_count] = vcpu_ids.len() as u8;
1052 self.list_count += 1;
1053
1054 // The first ID is the endpoint ID.
1055 self.ids[current_id_index] = endpoint;
1056 current_id_index += 1;
1057
1058 // Insert the vCPU IDs.
1059 self.ids[current_id_index..current_id_index + vcpu_ids.len()].copy_from_slice(vcpu_ids);
1060
1061 Ok(())
1062 }
1063
1064 pub fn iter(&self) -> NotificationInfoGetIterator<'_> {
1065 NotificationInfoGetIterator {
1066 list_index: 0,
1067 id_index: 0,
1068 id_count: &self.id_counts[0..self.list_count],
1069 ids: &self.ids,
1070 }
1071 }
1072
1073 /// Pack flags field and IDs.
1074 fn pack(self) -> (u64, [u16; MAX_COUNT]) {
1075 let mut flags = if self.more_pending_notifications {
1076 Self::MORE_PENDING_NOTIFICATIONS_FLAG
1077 } else {
1078 0
1079 };
1080
1081 flags |= (self.list_count as u64) << Self::LIST_COUNT_SHIFT;
1082 for (count, shift) in self.id_counts.iter().take(self.list_count).zip(
1083 (Self::ID_COUNT_SHIFT..Self::ID_COUNT_SHIFT + Self::ID_COUNT_BITS * MAX_COUNT)
1084 .step_by(Self::ID_COUNT_BITS),
1085 ) {
1086 flags |= u64::from(*count) << shift;
1087 }
1088
1089 (flags, self.ids)
1090 }
1091
1092 /// Unpack flags field and IDs.
1093 fn unpack(flags: u64, ids: [u16; MAX_COUNT]) -> Result<Self, Error> {
1094 let count_of_lists = ((flags >> Self::LIST_COUNT_SHIFT) & Self::LIST_COUNT_MASK) as usize;
1095
1096 if count_of_lists > MAX_COUNT {
1097 return Err(Error::InvalidNotificationCount);
1098 }
1099
1100 let mut count_of_ids = [0; MAX_COUNT];
1101 let mut count_of_ids_bits = flags >> Self::ID_COUNT_SHIFT;
1102
1103 for id in count_of_ids.iter_mut().take(count_of_lists) {
1104 *id = (count_of_ids_bits & Self::ID_COUNT_MASK) as u8;
1105 count_of_ids_bits >>= Self::ID_COUNT_BITS;
1106 }
1107
Imre Kis7846c9f2025-04-15 09:45:00 +02001108 let id_field_count = count_of_lists + count_of_ids.iter().sum::<u8>() as usize;
1109 if id_field_count > MAX_COUNT {
1110 return Err(Error::InvalidNotificationCount);
1111 }
1112
Imre Kis787c5002025-04-10 14:25:51 +02001113 Ok(Self {
1114 more_pending_notifications: (flags & Self::MORE_PENDING_NOTIFICATIONS_FLAG) != 0,
1115 list_count: count_of_lists,
1116 id_counts: count_of_ids,
1117 ids,
1118 })
1119 }
1120}
1121
1122/// `FFA_NOTIFICATION_INFO_GET_32` specific success argument structure.
1123pub type SuccessArgsNotificationInfoGet32 = SuccessArgsNotificationInfoGet<10>;
1124
1125impl From<SuccessArgsNotificationInfoGet32> for SuccessArgs {
1126 fn from(value: SuccessArgsNotificationInfoGet32) -> Self {
1127 let (flags, ids) = value.pack();
1128 let id_regs: [u32; 5] = transmute!(ids);
1129
1130 let mut args = [0; 6];
1131 args[0] = flags as u32;
1132 args[1..6].copy_from_slice(&id_regs);
1133
1134 SuccessArgs::Args32(args)
1135 }
1136}
1137
1138impl TryFrom<SuccessArgs> for SuccessArgsNotificationInfoGet32 {
1139 type Error = Error;
1140
1141 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
1142 let args = value.try_get_args32()?;
1143 let flags = args[0].into();
1144 let id_regs: [u32; 5] = args[1..6].try_into().unwrap();
1145 Self::unpack(flags, transmute!(id_regs))
1146 }
1147}
1148
1149/// `FFA_NOTIFICATION_INFO_GET_64` specific success argument structure.
1150pub type SuccessArgsNotificationInfoGet64 = SuccessArgsNotificationInfoGet<20>;
1151
1152impl From<SuccessArgsNotificationInfoGet64> for SuccessArgs {
1153 fn from(value: SuccessArgsNotificationInfoGet64) -> Self {
1154 let (flags, ids) = value.pack();
1155 let id_regs: [u64; 5] = transmute!(ids);
1156
1157 let mut args = [0; 6];
1158 args[0] = flags;
1159 args[1..6].copy_from_slice(&id_regs);
1160
1161 SuccessArgs::Args64(args)
1162 }
1163}
1164
1165impl TryFrom<SuccessArgs> for SuccessArgsNotificationInfoGet64 {
1166 type Error = Error;
1167
1168 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
1169 let args = value.try_get_args64()?;
1170 let flags = args[0];
1171 let id_regs: [u64; 5] = args[1..6].try_into().unwrap();
1172 Self::unpack(flags, transmute!(id_regs))
1173 }
1174}
1175
1176pub struct NotificationInfoGetIterator<'a> {
1177 list_index: usize,
1178 id_index: usize,
1179 id_count: &'a [u8],
1180 ids: &'a [u16],
1181}
1182
1183impl<'a> Iterator for NotificationInfoGetIterator<'a> {
1184 type Item = (u16, &'a [u16]);
1185
1186 fn next(&mut self) -> Option<Self::Item> {
1187 if self.list_index < self.id_count.len() {
1188 let partition_id = self.ids[self.id_index];
1189 let id_range =
1190 (self.id_index + 1)..=(self.id_index + self.id_count[self.list_index] as usize);
1191
1192 self.id_index += 1 + self.id_count[self.list_index] as usize;
1193 self.list_index += 1;
1194
1195 Some((partition_id, &self.ids[id_range]))
1196 } else {
1197 None
1198 }
1199 }
1200}
1201
Tomás Gonzálezf268e322025-03-05 11:18:11 +00001202/// FF-A "message types", the terminology used by the spec is "interfaces".
1203///
1204/// The interfaces are used by FF-A components for communication at an FF-A instance. The spec also
1205/// describes the valid FF-A instances and conduits for each interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +01001206#[derive(Debug, Eq, PartialEq, Clone, Copy)]
1207pub enum Interface {
1208 Error {
1209 target_info: TargetInfo,
1210 error_code: FfaError,
Balint Dobszayb727aab2025-04-07 10:24:59 +02001211 error_arg: u32,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001212 },
1213 Success {
Imre Kise521a282025-06-13 13:29:24 +02001214 target_info: TargetInfo,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001215 args: SuccessArgs,
1216 },
1217 Interrupt {
1218 target_info: TargetInfo,
1219 interrupt_id: u32,
1220 },
1221 Version {
1222 input_version: Version,
1223 },
1224 VersionOut {
1225 output_version: Version,
1226 },
1227 Features {
1228 feat_id: Feature,
1229 input_properties: u32,
1230 },
1231 RxAcquire {
1232 vm_id: u16,
1233 },
1234 RxRelease {
1235 vm_id: u16,
1236 },
1237 RxTxMap {
1238 addr: RxTxAddr,
1239 page_cnt: u32,
1240 },
1241 RxTxUnmap {
1242 id: u16,
1243 },
1244 PartitionInfoGet {
1245 uuid: Uuid,
Imre Kise295adb2025-04-10 13:26:28 +02001246 flags: PartitionInfoGetFlags,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001247 },
Tomás González0a058bc2025-03-11 11:20:55 +00001248 PartitionInfoGetRegs {
1249 uuid: Uuid,
1250 start_index: u16,
1251 info_tag: u16,
1252 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001253 IdGet,
1254 SpmIdGet,
Tomás González092202a2025-03-05 11:56:45 +00001255 MsgWait {
1256 flags: Option<MsgWaitFlags>,
1257 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001258 Yield,
1259 Run {
1260 target_info: TargetInfo,
1261 },
1262 NormalWorldResume,
Tomás González17b92442025-03-10 16:45:04 +00001263 SecondaryEpRegister {
1264 entrypoint: SecondaryEpRegisterAddr,
1265 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001266 MsgSend2 {
1267 sender_vm_id: u16,
Imre Kisa2fd69b2025-06-13 13:39:47 +02001268 flags: MsgSend2Flags,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001269 },
1270 MsgSendDirectReq {
1271 src_id: u16,
1272 dst_id: u16,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001273 args: DirectMsgArgs,
1274 },
1275 MsgSendDirectResp {
1276 src_id: u16,
1277 dst_id: u16,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001278 args: DirectMsgArgs,
1279 },
Balint Dobszayde0dc802025-02-28 14:16:52 +01001280 MsgSendDirectReq2 {
1281 src_id: u16,
1282 dst_id: u16,
1283 uuid: Uuid,
1284 args: DirectMsg2Args,
1285 },
1286 MsgSendDirectResp2 {
1287 src_id: u16,
1288 dst_id: u16,
1289 args: DirectMsg2Args,
1290 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001291 MemDonate {
1292 total_len: u32,
1293 frag_len: u32,
1294 buf: Option<MemOpBuf>,
1295 },
1296 MemLend {
1297 total_len: u32,
1298 frag_len: u32,
1299 buf: Option<MemOpBuf>,
1300 },
1301 MemShare {
1302 total_len: u32,
1303 frag_len: u32,
1304 buf: Option<MemOpBuf>,
1305 },
1306 MemRetrieveReq {
1307 total_len: u32,
1308 frag_len: u32,
1309 buf: Option<MemOpBuf>,
1310 },
1311 MemRetrieveResp {
1312 total_len: u32,
1313 frag_len: u32,
1314 },
1315 MemRelinquish,
1316 MemReclaim {
1317 handle: memory_management::Handle,
1318 flags: u32,
1319 },
1320 MemPermGet {
1321 addr: MemAddr,
Balint Dobszayde0dc802025-02-28 14:16:52 +01001322 page_cnt: Option<u32>,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001323 },
1324 MemPermSet {
1325 addr: MemAddr,
1326 page_cnt: u32,
Imre Kisdcb7df22025-06-06 15:24:40 +02001327 mem_perm: memory_management::MemPermissionsGetSet,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001328 },
1329 ConsoleLog {
Imre Kis189f18c2025-05-26 19:33:05 +02001330 chars: ConsoleLogChars,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001331 },
Tomás González7ffb6132025-04-03 12:28:58 +01001332 NotificationBitmapCreate {
1333 vm_id: u16,
1334 vcpu_cnt: u32,
1335 },
1336 NotificationBitmapDestroy {
1337 vm_id: u16,
1338 },
1339 NotificationBind {
1340 sender_id: u16,
1341 receiver_id: u16,
1342 flags: NotificationBindFlags,
1343 bitmap: u64,
1344 },
Imre Kis3571f2c2025-05-26 19:29:23 +02001345 NotificationUnbind {
Tomás González7ffb6132025-04-03 12:28:58 +01001346 sender_id: u16,
1347 receiver_id: u16,
1348 bitmap: u64,
1349 },
1350 NotificationSet {
1351 sender_id: u16,
1352 receiver_id: u16,
1353 flags: NotificationSetFlags,
1354 bitmap: u64,
1355 },
1356 NotificationGet {
1357 vcpu_id: u16,
1358 endpoint_id: u16,
1359 flags: NotificationGetFlags,
1360 },
1361 NotificationInfoGet {
1362 is_32bit: bool,
1363 },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01001364 El3IntrHandle,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001365}
1366
Balint Dobszayde0dc802025-02-28 14:16:52 +01001367impl Interface {
1368 /// Returns the function ID for the call, if it has one.
1369 pub fn function_id(&self) -> Option<FuncId> {
1370 match self {
1371 Interface::Error { .. } => Some(FuncId::Error),
1372 Interface::Success { args, .. } => match args {
Imre Kis54773b62025-04-10 13:47:39 +02001373 SuccessArgs::Args32(..) => Some(FuncId::Success32),
1374 SuccessArgs::Args64(..) | SuccessArgs::Args64_2(..) => Some(FuncId::Success64),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001375 },
1376 Interface::Interrupt { .. } => Some(FuncId::Interrupt),
1377 Interface::Version { .. } => Some(FuncId::Version),
1378 Interface::VersionOut { .. } => None,
1379 Interface::Features { .. } => Some(FuncId::Features),
1380 Interface::RxAcquire { .. } => Some(FuncId::RxAcquire),
1381 Interface::RxRelease { .. } => Some(FuncId::RxRelease),
1382 Interface::RxTxMap { addr, .. } => match addr {
1383 RxTxAddr::Addr32 { .. } => Some(FuncId::RxTxMap32),
1384 RxTxAddr::Addr64 { .. } => Some(FuncId::RxTxMap64),
1385 },
1386 Interface::RxTxUnmap { .. } => Some(FuncId::RxTxUnmap),
1387 Interface::PartitionInfoGet { .. } => Some(FuncId::PartitionInfoGet),
Tomás González0a058bc2025-03-11 11:20:55 +00001388 Interface::PartitionInfoGetRegs { .. } => Some(FuncId::PartitionInfoGetRegs),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001389 Interface::IdGet => Some(FuncId::IdGet),
1390 Interface::SpmIdGet => Some(FuncId::SpmIdGet),
Tomás González092202a2025-03-05 11:56:45 +00001391 Interface::MsgWait { .. } => Some(FuncId::MsgWait),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001392 Interface::Yield => Some(FuncId::Yield),
1393 Interface::Run { .. } => Some(FuncId::Run),
1394 Interface::NormalWorldResume => Some(FuncId::NormalWorldResume),
Tomás González17b92442025-03-10 16:45:04 +00001395 Interface::SecondaryEpRegister { entrypoint } => match entrypoint {
1396 SecondaryEpRegisterAddr::Addr32 { .. } => Some(FuncId::SecondaryEpRegister32),
1397 SecondaryEpRegisterAddr::Addr64 { .. } => Some(FuncId::SecondaryEpRegister64),
1398 },
Balint Dobszayde0dc802025-02-28 14:16:52 +01001399 Interface::MsgSend2 { .. } => Some(FuncId::MsgSend2),
1400 Interface::MsgSendDirectReq { args, .. } => match args {
1401 DirectMsgArgs::Args32(_) => Some(FuncId::MsgSendDirectReq32),
1402 DirectMsgArgs::Args64(_) => Some(FuncId::MsgSendDirectReq64),
Tomás González4d5b0ba2025-03-03 17:15:55 +00001403 DirectMsgArgs::VersionReq { .. } => Some(FuncId::MsgSendDirectReq32),
1404 DirectMsgArgs::PowerPsciReq32 { .. } => Some(FuncId::MsgSendDirectReq32),
1405 DirectMsgArgs::PowerPsciReq64 { .. } => Some(FuncId::MsgSendDirectReq64),
1406 DirectMsgArgs::PowerWarmBootReq { .. } => Some(FuncId::MsgSendDirectReq32),
1407 DirectMsgArgs::VmCreated { .. } => Some(FuncId::MsgSendDirectReq32),
1408 DirectMsgArgs::VmDestructed { .. } => Some(FuncId::MsgSendDirectReq32),
Balint Dobszay3c1c89a2025-04-25 17:36:46 +02001409 _ => panic!("Invalid direct request arguments: {:#?}", args),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001410 },
1411 Interface::MsgSendDirectResp { args, .. } => match args {
1412 DirectMsgArgs::Args32(_) => Some(FuncId::MsgSendDirectResp32),
1413 DirectMsgArgs::Args64(_) => Some(FuncId::MsgSendDirectResp64),
Tomás González4d5b0ba2025-03-03 17:15:55 +00001414 DirectMsgArgs::VersionResp { .. } => Some(FuncId::MsgSendDirectResp32),
1415 DirectMsgArgs::PowerPsciResp { .. } => Some(FuncId::MsgSendDirectResp32),
1416 DirectMsgArgs::VmCreatedAck { .. } => Some(FuncId::MsgSendDirectResp32),
1417 DirectMsgArgs::VmDestructedAck { .. } => Some(FuncId::MsgSendDirectResp32),
Balint Dobszay3c1c89a2025-04-25 17:36:46 +02001418 _ => panic!("Invalid direct response arguments: {:#?}", args),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001419 },
1420 Interface::MsgSendDirectReq2 { .. } => Some(FuncId::MsgSendDirectReq64_2),
1421 Interface::MsgSendDirectResp2 { .. } => Some(FuncId::MsgSendDirectResp64_2),
1422 Interface::MemDonate { buf, .. } => match buf {
1423 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemDonate64),
1424 _ => Some(FuncId::MemDonate32),
1425 },
1426 Interface::MemLend { buf, .. } => match buf {
1427 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemLend64),
1428 _ => Some(FuncId::MemLend32),
1429 },
1430 Interface::MemShare { buf, .. } => match buf {
1431 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemShare64),
1432 _ => Some(FuncId::MemShare32),
1433 },
1434 Interface::MemRetrieveReq { buf, .. } => match buf {
1435 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemRetrieveReq64),
1436 _ => Some(FuncId::MemRetrieveReq32),
1437 },
1438 Interface::MemRetrieveResp { .. } => Some(FuncId::MemRetrieveResp),
1439 Interface::MemRelinquish => Some(FuncId::MemRelinquish),
1440 Interface::MemReclaim { .. } => Some(FuncId::MemReclaim),
1441 Interface::MemPermGet { addr, .. } => match addr {
1442 MemAddr::Addr32(_) => Some(FuncId::MemPermGet32),
1443 MemAddr::Addr64(_) => Some(FuncId::MemPermGet64),
1444 },
1445 Interface::MemPermSet { addr, .. } => match addr {
1446 MemAddr::Addr32(_) => Some(FuncId::MemPermSet32),
1447 MemAddr::Addr64(_) => Some(FuncId::MemPermSet64),
1448 },
Imre Kis189f18c2025-05-26 19:33:05 +02001449 Interface::ConsoleLog { chars, .. } => match chars {
1450 ConsoleLogChars::Chars32(_) => Some(FuncId::ConsoleLog32),
1451 ConsoleLogChars::Chars64(_) => Some(FuncId::ConsoleLog64),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001452 },
Tomás González7ffb6132025-04-03 12:28:58 +01001453 Interface::NotificationBitmapCreate { .. } => Some(FuncId::NotificationBitmapCreate),
1454 Interface::NotificationBitmapDestroy { .. } => Some(FuncId::NotificationBitmapDestroy),
1455 Interface::NotificationBind { .. } => Some(FuncId::NotificationBind),
Imre Kis3571f2c2025-05-26 19:29:23 +02001456 Interface::NotificationUnbind { .. } => Some(FuncId::NotificationUnbind),
Tomás González7ffb6132025-04-03 12:28:58 +01001457 Interface::NotificationSet { .. } => Some(FuncId::NotificationSet),
1458 Interface::NotificationGet { .. } => Some(FuncId::NotificationGet),
1459 Interface::NotificationInfoGet { is_32bit } => match is_32bit {
1460 true => Some(FuncId::NotificationInfoGet32),
1461 false => Some(FuncId::NotificationInfoGet64),
1462 },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01001463 Interface::El3IntrHandle => Some(FuncId::El3IntrHandle),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001464 }
1465 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01001466
Balint Dobszayde0dc802025-02-28 14:16:52 +01001467 /// Returns true if this is a 32-bit call, or false if it is a 64-bit call.
1468 pub fn is_32bit(&self) -> bool {
Balint Dobszay3c1c89a2025-04-25 17:36:46 +02001469 if matches!(self, Self::VersionOut { .. }) {
1470 return true;
1471 }
1472
Balint Dobszayde0dc802025-02-28 14:16:52 +01001473 self.function_id().unwrap().is_32bit()
1474 }
1475
Balint Dobszay82c71dd2025-04-15 10:16:44 +02001476 /// Returns the FF-A version that has introduced the function ID.
1477 pub fn minimum_ffa_version(&self) -> Version {
Balint Dobszay3c1c89a2025-04-25 17:36:46 +02001478 if matches!(self, Self::VersionOut { .. }) {
1479 return Version(1, 0);
1480 }
1481
Balint Dobszay82c71dd2025-04-15 10:16:44 +02001482 self.function_id().unwrap().minimum_ffa_version()
1483 }
1484
Balint Dobszayde0dc802025-02-28 14:16:52 +01001485 /// Parse interface from register contents. The caller must ensure that the `regs` argument has
1486 /// the correct length: 8 registers for FF-A v1.1 and lower, 18 registers for v1.2 and higher.
1487 pub fn from_regs(version: Version, regs: &[u64]) -> Result<Self, Error> {
Balint Dobszay82c71dd2025-04-15 10:16:44 +02001488 let func_id = FuncId::try_from(regs[0] as u32)?;
1489 if version < func_id.minimum_ffa_version() {
1490 return Err(Error::InvalidVersionForFunctionId(version, func_id));
1491 }
1492
Balint Dobszayde0dc802025-02-28 14:16:52 +01001493 let reg_cnt = regs.len();
1494
1495 let msg = match reg_cnt {
1496 8 => {
1497 assert!(version <= Version(1, 1));
1498 Interface::unpack_regs8(version, regs.try_into().unwrap())?
1499 }
1500 18 => {
1501 assert!(version >= Version(1, 2));
Balint Dobszay82c71dd2025-04-15 10:16:44 +02001502 match func_id {
Balint Dobszayde0dc802025-02-28 14:16:52 +01001503 FuncId::ConsoleLog64
1504 | FuncId::Success64
1505 | FuncId::MsgSendDirectReq64_2
Tomás González0a058bc2025-03-11 11:20:55 +00001506 | FuncId::MsgSendDirectResp64_2
1507 | FuncId::PartitionInfoGetRegs => {
Balint Dobszayde0dc802025-02-28 14:16:52 +01001508 Interface::unpack_regs18(version, regs.try_into().unwrap())?
1509 }
1510 _ => Interface::unpack_regs8(version, regs[..8].try_into().unwrap())?,
1511 }
1512 }
1513 _ => panic!(
1514 "Invalid number of registers ({}) for FF-A version {}",
1515 reg_cnt, version
1516 ),
1517 };
1518
1519 Ok(msg)
1520 }
1521
1522 fn unpack_regs8(version: Version, regs: &[u64; 8]) -> Result<Self, Error> {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001523 let fid = FuncId::try_from(regs[0] as u32)?;
1524
1525 let msg = match fid {
1526 FuncId::Error => Self::Error {
1527 target_info: (regs[1] as u32).into(),
1528 error_code: FfaError::try_from(regs[2] as i32)?,
Balint Dobszayb727aab2025-04-07 10:24:59 +02001529 error_arg: regs[3] as u32,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001530 },
1531 FuncId::Success32 => Self::Success {
Imre Kise521a282025-06-13 13:29:24 +02001532 target_info: (regs[1] as u32).into(),
Imre Kis54773b62025-04-10 13:47:39 +02001533 args: SuccessArgs::Args32([
Balint Dobszay3aad9572025-01-17 16:54:11 +01001534 regs[2] as u32,
1535 regs[3] as u32,
1536 regs[4] as u32,
1537 regs[5] as u32,
1538 regs[6] as u32,
1539 regs[7] as u32,
1540 ]),
1541 },
1542 FuncId::Success64 => Self::Success {
Imre Kise521a282025-06-13 13:29:24 +02001543 target_info: (regs[1] as u32).into(),
Imre Kis54773b62025-04-10 13:47:39 +02001544 args: SuccessArgs::Args64([regs[2], regs[3], regs[4], regs[5], regs[6], regs[7]]),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001545 },
1546 FuncId::Interrupt => Self::Interrupt {
1547 target_info: (regs[1] as u32).into(),
1548 interrupt_id: regs[2] as u32,
1549 },
1550 FuncId::Version => Self::Version {
Tomás González83146af2025-03-04 11:32:41 +00001551 input_version: (regs[1] as u32).try_into()?,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001552 },
1553 FuncId::Features => Self::Features {
Balint Dobszayc31e0b92025-03-03 20:16:56 +01001554 feat_id: (regs[1] as u32).into(),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001555 input_properties: regs[2] as u32,
1556 },
1557 FuncId::RxAcquire => Self::RxAcquire {
1558 vm_id: regs[1] as u16,
1559 },
1560 FuncId::RxRelease => Self::RxRelease {
1561 vm_id: regs[1] as u16,
1562 },
1563 FuncId::RxTxMap32 => {
1564 let addr = RxTxAddr::Addr32 {
1565 rx: regs[2] as u32,
1566 tx: regs[1] as u32,
1567 };
1568 let page_cnt = regs[3] as u32;
1569
1570 Self::RxTxMap { addr, page_cnt }
1571 }
1572 FuncId::RxTxMap64 => {
1573 let addr = RxTxAddr::Addr64 {
1574 rx: regs[2],
1575 tx: regs[1],
1576 };
1577 let page_cnt = regs[3] as u32;
1578
1579 Self::RxTxMap { addr, page_cnt }
1580 }
1581 FuncId::RxTxUnmap => Self::RxTxUnmap { id: regs[1] as u16 },
1582 FuncId::PartitionInfoGet => {
1583 let uuid_words = [
1584 regs[1] as u32,
1585 regs[2] as u32,
1586 regs[3] as u32,
1587 regs[4] as u32,
1588 ];
1589 let mut bytes: [u8; 16] = [0; 16];
1590 for (i, b) in uuid_words.iter().flat_map(|w| w.to_le_bytes()).enumerate() {
1591 bytes[i] = b;
1592 }
1593 Self::PartitionInfoGet {
1594 uuid: Uuid::from_bytes(bytes),
Imre Kise295adb2025-04-10 13:26:28 +02001595 flags: PartitionInfoGetFlags::try_from(regs[5] as u32)?,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001596 }
1597 }
1598 FuncId::IdGet => Self::IdGet,
1599 FuncId::SpmIdGet => Self::SpmIdGet,
Tomás González092202a2025-03-05 11:56:45 +00001600 FuncId::MsgWait => Self::MsgWait {
1601 flags: if version >= Version(1, 2) {
1602 Some(MsgWaitFlags::try_from(regs[2] as u32)?)
1603 } else {
1604 None
1605 },
1606 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001607 FuncId::Yield => Self::Yield,
1608 FuncId::Run => Self::Run {
1609 target_info: (regs[1] as u32).into(),
1610 },
1611 FuncId::NormalWorldResume => Self::NormalWorldResume,
Tomás González17b92442025-03-10 16:45:04 +00001612 FuncId::SecondaryEpRegister32 => Self::SecondaryEpRegister {
1613 entrypoint: SecondaryEpRegisterAddr::Addr32(regs[1] as u32),
1614 },
1615 FuncId::SecondaryEpRegister64 => Self::SecondaryEpRegister {
1616 entrypoint: SecondaryEpRegisterAddr::Addr64(regs[1]),
1617 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001618 FuncId::MsgSend2 => Self::MsgSend2 {
1619 sender_vm_id: regs[1] as u16,
Imre Kisa2fd69b2025-06-13 13:39:47 +02001620 flags: (regs[2] as u32).try_into()?,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001621 },
1622 FuncId::MsgSendDirectReq32 => Self::MsgSendDirectReq {
1623 src_id: (regs[1] >> 16) as u16,
1624 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001625 args: if (regs[2] as u32 & DirectMsgArgs::FWK_MSG_BITS) != 0 {
1626 match regs[2] as u32 {
1627 DirectMsgArgs::VERSION_REQ => DirectMsgArgs::VersionReq {
1628 version: Version::try_from(regs[3] as u32)?,
1629 },
1630 DirectMsgArgs::POWER_PSCI_REQ => DirectMsgArgs::PowerPsciReq32 {
Tomás González67f92c72025-03-20 16:50:42 +00001631 params: [
1632 regs[3] as u32,
1633 regs[4] as u32,
1634 regs[5] as u32,
1635 regs[6] as u32,
1636 ],
Tomás González4d5b0ba2025-03-03 17:15:55 +00001637 },
1638 DirectMsgArgs::POWER_WARM_BOOT_REQ => DirectMsgArgs::PowerWarmBootReq {
1639 boot_type: WarmBootType::try_from(regs[3] as u32)?,
1640 },
1641 DirectMsgArgs::VM_CREATED => DirectMsgArgs::VmCreated {
1642 handle: memory_management::Handle::from([
1643 regs[3] as u32,
1644 regs[4] as u32,
1645 ]),
1646 vm_id: regs[5] as u16,
1647 },
1648 DirectMsgArgs::VM_DESTRUCTED => DirectMsgArgs::VmDestructed {
1649 handle: memory_management::Handle::from([
1650 regs[3] as u32,
1651 regs[4] as u32,
1652 ]),
1653 vm_id: regs[5] as u16,
1654 },
1655 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1656 }
1657 } else {
1658 DirectMsgArgs::Args32([
1659 regs[3] as u32,
1660 regs[4] as u32,
1661 regs[5] as u32,
1662 regs[6] as u32,
1663 regs[7] as u32,
1664 ])
1665 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001666 },
1667 FuncId::MsgSendDirectReq64 => Self::MsgSendDirectReq {
1668 src_id: (regs[1] >> 16) as u16,
1669 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001670 args: if (regs[2] & DirectMsgArgs::FWK_MSG_BITS as u64) != 0 {
1671 match regs[2] as u32 {
1672 DirectMsgArgs::POWER_PSCI_REQ => DirectMsgArgs::PowerPsciReq64 {
Tomás González67f92c72025-03-20 16:50:42 +00001673 params: [regs[3], regs[4], regs[5], regs[6]],
Tomás González4d5b0ba2025-03-03 17:15:55 +00001674 },
1675 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1676 }
1677 } else {
1678 DirectMsgArgs::Args64([regs[3], regs[4], regs[5], regs[6], regs[7]])
1679 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001680 },
1681 FuncId::MsgSendDirectResp32 => Self::MsgSendDirectResp {
1682 src_id: (regs[1] >> 16) as u16,
1683 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001684 args: if (regs[2] as u32 & DirectMsgArgs::FWK_MSG_BITS) != 0 {
1685 match regs[2] as u32 {
1686 DirectMsgArgs::VERSION_RESP => {
1687 if regs[3] as i32 == FfaError::NotSupported.into() {
1688 DirectMsgArgs::VersionResp { version: None }
1689 } else {
1690 DirectMsgArgs::VersionResp {
1691 version: Some(Version::try_from(regs[3] as u32)?),
1692 }
1693 }
1694 }
1695 DirectMsgArgs::POWER_PSCI_RESP => DirectMsgArgs::PowerPsciResp {
1696 psci_status: regs[3] as i32,
1697 },
1698 DirectMsgArgs::VM_CREATED_ACK => DirectMsgArgs::VmCreatedAck {
1699 sp_status: (regs[3] as i32).try_into()?,
1700 },
1701 DirectMsgArgs::VM_DESTRUCTED_ACK => DirectMsgArgs::VmDestructedAck {
1702 sp_status: (regs[3] as i32).try_into()?,
1703 },
1704 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1705 }
1706 } else {
1707 DirectMsgArgs::Args32([
1708 regs[3] as u32,
1709 regs[4] as u32,
1710 regs[5] as u32,
1711 regs[6] as u32,
1712 regs[7] as u32,
1713 ])
1714 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001715 },
1716 FuncId::MsgSendDirectResp64 => Self::MsgSendDirectResp {
1717 src_id: (regs[1] >> 16) as u16,
1718 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001719 args: if (regs[2] & DirectMsgArgs::FWK_MSG_BITS as u64) != 0 {
1720 return Err(Error::UnrecognisedFwkMsg(regs[2] as u32));
1721 } else {
1722 DirectMsgArgs::Args64([regs[3], regs[4], regs[5], regs[6], regs[7]])
1723 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001724 },
1725 FuncId::MemDonate32 => Self::MemDonate {
1726 total_len: regs[1] as u32,
1727 frag_len: regs[2] as u32,
1728 buf: if regs[3] != 0 && regs[4] != 0 {
1729 Some(MemOpBuf::Buf32 {
1730 addr: regs[3] as u32,
1731 page_cnt: regs[4] as u32,
1732 })
1733 } else {
1734 None
1735 },
1736 },
1737 FuncId::MemDonate64 => Self::MemDonate {
1738 total_len: regs[1] as u32,
1739 frag_len: regs[2] as u32,
1740 buf: if regs[3] != 0 && regs[4] != 0 {
1741 Some(MemOpBuf::Buf64 {
1742 addr: regs[3],
1743 page_cnt: regs[4] as u32,
1744 })
1745 } else {
1746 None
1747 },
1748 },
1749 FuncId::MemLend32 => Self::MemLend {
1750 total_len: regs[1] as u32,
1751 frag_len: regs[2] as u32,
1752 buf: if regs[3] != 0 && regs[4] != 0 {
1753 Some(MemOpBuf::Buf32 {
1754 addr: regs[3] as u32,
1755 page_cnt: regs[4] as u32,
1756 })
1757 } else {
1758 None
1759 },
1760 },
1761 FuncId::MemLend64 => Self::MemLend {
1762 total_len: regs[1] as u32,
1763 frag_len: regs[2] as u32,
1764 buf: if regs[3] != 0 && regs[4] != 0 {
1765 Some(MemOpBuf::Buf64 {
1766 addr: regs[3],
1767 page_cnt: regs[4] as u32,
1768 })
1769 } else {
1770 None
1771 },
1772 },
1773 FuncId::MemShare32 => Self::MemShare {
1774 total_len: regs[1] as u32,
1775 frag_len: regs[2] as u32,
1776 buf: if regs[3] != 0 && regs[4] != 0 {
1777 Some(MemOpBuf::Buf32 {
1778 addr: regs[3] as u32,
1779 page_cnt: regs[4] as u32,
1780 })
1781 } else {
1782 None
1783 },
1784 },
1785 FuncId::MemShare64 => Self::MemShare {
1786 total_len: regs[1] as u32,
1787 frag_len: regs[2] as u32,
1788 buf: if regs[3] != 0 && regs[4] != 0 {
1789 Some(MemOpBuf::Buf64 {
1790 addr: regs[3],
1791 page_cnt: regs[4] as u32,
1792 })
1793 } else {
1794 None
1795 },
1796 },
1797 FuncId::MemRetrieveReq32 => Self::MemRetrieveReq {
1798 total_len: regs[1] as u32,
1799 frag_len: regs[2] as u32,
1800 buf: if regs[3] != 0 && regs[4] != 0 {
1801 Some(MemOpBuf::Buf32 {
1802 addr: regs[3] as u32,
1803 page_cnt: regs[4] as u32,
1804 })
1805 } else {
1806 None
1807 },
1808 },
1809 FuncId::MemRetrieveReq64 => Self::MemRetrieveReq {
1810 total_len: regs[1] as u32,
1811 frag_len: regs[2] as u32,
1812 buf: if regs[3] != 0 && regs[4] != 0 {
1813 Some(MemOpBuf::Buf64 {
1814 addr: regs[3],
1815 page_cnt: regs[4] as u32,
1816 })
1817 } else {
1818 None
1819 },
1820 },
1821 FuncId::MemRetrieveResp => Self::MemRetrieveResp {
1822 total_len: regs[1] as u32,
1823 frag_len: regs[2] as u32,
1824 },
1825 FuncId::MemRelinquish => Self::MemRelinquish,
1826 FuncId::MemReclaim => Self::MemReclaim {
1827 handle: memory_management::Handle::from([regs[1] as u32, regs[2] as u32]),
1828 flags: regs[3] as u32,
1829 },
1830 FuncId::MemPermGet32 => Self::MemPermGet {
1831 addr: MemAddr::Addr32(regs[1] as u32),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001832 page_cnt: if version >= Version(1, 3) {
1833 Some(regs[2] as u32)
1834 } else {
1835 None
1836 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001837 },
1838 FuncId::MemPermGet64 => Self::MemPermGet {
1839 addr: MemAddr::Addr64(regs[1]),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001840 page_cnt: if version >= Version(1, 3) {
1841 Some(regs[2] as u32)
1842 } else {
1843 None
1844 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001845 },
1846 FuncId::MemPermSet32 => Self::MemPermSet {
1847 addr: MemAddr::Addr32(regs[1] as u32),
1848 page_cnt: regs[2] as u32,
Imre Kisdcb7df22025-06-06 15:24:40 +02001849 mem_perm: (regs[3] as u32).try_into()?,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001850 },
1851 FuncId::MemPermSet64 => Self::MemPermSet {
1852 addr: MemAddr::Addr64(regs[1]),
1853 page_cnt: regs[2] as u32,
Imre Kisdcb7df22025-06-06 15:24:40 +02001854 mem_perm: (regs[3] as u32).try_into()?,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001855 },
Imre Kis189f18c2025-05-26 19:33:05 +02001856 FuncId::ConsoleLog32 => {
1857 let char_cnt = regs[1] as u8;
1858 if char_cnt > ConsoleLogChars32::MAX_LENGTH {
1859 return Err(Error::InvalidCharacterCount(char_cnt));
1860 }
1861
1862 Self::ConsoleLog {
1863 chars: ConsoleLogChars::Chars32(ConsoleLogChars32 {
1864 char_cnt,
1865 char_lists: [
1866 regs[2] as u32,
1867 regs[3] as u32,
1868 regs[4] as u32,
1869 regs[5] as u32,
1870 regs[6] as u32,
1871 regs[7] as u32,
1872 ],
1873 }),
1874 }
1875 }
Tomás González7ffb6132025-04-03 12:28:58 +01001876 FuncId::NotificationBitmapCreate => {
1877 let tentative_vm_id = regs[1] as u32;
1878 if (tentative_vm_id >> 16) != 0 {
1879 return Err(Error::InvalidVmId(tentative_vm_id));
1880 }
1881 Self::NotificationBitmapCreate {
1882 vm_id: tentative_vm_id as u16,
1883 vcpu_cnt: regs[2] as u32,
1884 }
1885 }
1886 FuncId::NotificationBitmapDestroy => {
1887 let tentative_vm_id = regs[1] as u32;
1888 if (tentative_vm_id >> 16) != 0 {
1889 return Err(Error::InvalidVmId(tentative_vm_id));
1890 }
1891 Self::NotificationBitmapDestroy {
1892 vm_id: tentative_vm_id as u16,
1893 }
1894 }
1895 FuncId::NotificationBind => Self::NotificationBind {
1896 sender_id: (regs[1] >> 16) as u16,
1897 receiver_id: regs[1] as u16,
1898 flags: (regs[2] as u32).into(),
1899 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1900 },
Imre Kis3571f2c2025-05-26 19:29:23 +02001901 FuncId::NotificationUnbind => Self::NotificationUnbind {
Tomás González7ffb6132025-04-03 12:28:58 +01001902 sender_id: (regs[1] >> 16) as u16,
1903 receiver_id: regs[1] as u16,
1904 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1905 },
1906 FuncId::NotificationSet => Self::NotificationSet {
1907 sender_id: (regs[1] >> 16) as u16,
1908 receiver_id: regs[1] as u16,
1909 flags: (regs[2] as u32).try_into()?,
1910 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1911 },
1912 FuncId::NotificationGet => Self::NotificationGet {
1913 vcpu_id: (regs[1] >> 16) as u16,
1914 endpoint_id: regs[1] as u16,
1915 flags: (regs[2] as u32).into(),
1916 },
1917 FuncId::NotificationInfoGet32 => Self::NotificationInfoGet { is_32bit: true },
1918 FuncId::NotificationInfoGet64 => Self::NotificationInfoGet { is_32bit: false },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01001919 FuncId::El3IntrHandle => Self::El3IntrHandle,
Balint Dobszayde0dc802025-02-28 14:16:52 +01001920 _ => panic!("Invalid number of registers (8) for function {:#x?}", fid),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001921 };
1922
1923 Ok(msg)
1924 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01001925
Balint Dobszayde0dc802025-02-28 14:16:52 +01001926 fn unpack_regs18(version: Version, regs: &[u64; 18]) -> Result<Self, Error> {
1927 assert!(version >= Version(1, 2));
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001928
Balint Dobszayde0dc802025-02-28 14:16:52 +01001929 let fid = FuncId::try_from(regs[0] as u32)?;
1930
1931 let msg = match fid {
1932 FuncId::Success64 => Self::Success {
Imre Kise521a282025-06-13 13:29:24 +02001933 target_info: (regs[1] as u32).into(),
Imre Kis54773b62025-04-10 13:47:39 +02001934 args: SuccessArgs::Args64_2(regs[2..18].try_into().unwrap()),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001935 },
1936 FuncId::MsgSendDirectReq64_2 => Self::MsgSendDirectReq2 {
1937 src_id: (regs[1] >> 16) as u16,
1938 dst_id: regs[1] as u16,
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00001939 uuid: Uuid::from_u64_pair(regs[2].swap_bytes(), regs[3].swap_bytes()),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001940 args: DirectMsg2Args(regs[4..18].try_into().unwrap()),
1941 },
1942 FuncId::MsgSendDirectResp64_2 => Self::MsgSendDirectResp2 {
1943 src_id: (regs[1] >> 16) as u16,
1944 dst_id: regs[1] as u16,
1945 args: DirectMsg2Args(regs[4..18].try_into().unwrap()),
1946 },
Imre Kis189f18c2025-05-26 19:33:05 +02001947 FuncId::ConsoleLog64 => {
1948 let char_cnt = regs[1] as u8;
1949 if char_cnt > ConsoleLogChars64::MAX_LENGTH {
1950 return Err(Error::InvalidCharacterCount(char_cnt));
1951 }
1952
1953 Self::ConsoleLog {
1954 chars: ConsoleLogChars::Chars64(ConsoleLogChars64 {
1955 char_cnt,
1956 char_lists: regs[2..18].try_into().unwrap(),
1957 }),
1958 }
1959 }
Tomás González0a058bc2025-03-11 11:20:55 +00001960 FuncId::PartitionInfoGetRegs => {
1961 // Bits[15:0]: Start index
1962 let start_index = (regs[3] & 0xffff) as u16;
1963 let info_tag = ((regs[3] >> 16) & 0xffff) as u16;
1964 Self::PartitionInfoGetRegs {
1965 uuid: Uuid::from_u64_pair(regs[1].swap_bytes(), regs[2].swap_bytes()),
1966 start_index,
1967 info_tag: if start_index == 0 && info_tag != 0 {
1968 return Err(Error::InvalidInformationTag(info_tag));
1969 } else {
1970 info_tag
1971 },
1972 }
1973 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001974 _ => panic!("Invalid number of registers (18) for function {:#x?}", fid),
1975 };
1976
1977 Ok(msg)
Balint Dobszay3aad9572025-01-17 16:54:11 +01001978 }
1979
Balint Dobszaya5846852025-02-26 15:38:53 +01001980 /// Create register contents for an interface.
Balint Dobszayde0dc802025-02-28 14:16:52 +01001981 pub fn to_regs(&self, version: Version, regs: &mut [u64]) {
Balint Dobszay82c71dd2025-04-15 10:16:44 +02001982 assert!(self.minimum_ffa_version() <= version);
1983
Balint Dobszayde0dc802025-02-28 14:16:52 +01001984 let reg_cnt = regs.len();
1985
1986 match reg_cnt {
1987 8 => {
1988 assert!(version <= Version(1, 1));
Balint Dobszay91bea9b2025-04-09 13:16:06 +02001989 regs.fill(0);
1990
Balint Dobszayde0dc802025-02-28 14:16:52 +01001991 self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
1992 }
1993 18 => {
1994 assert!(version >= Version(1, 2));
Balint Dobszay91bea9b2025-04-09 13:16:06 +02001995 regs.fill(0);
Balint Dobszayde0dc802025-02-28 14:16:52 +01001996
1997 match self {
1998 Interface::ConsoleLog {
Imre Kis189f18c2025-05-26 19:33:05 +02001999 chars: ConsoleLogChars::Chars64(_),
Balint Dobszayde0dc802025-02-28 14:16:52 +01002000 ..
2001 }
2002 | Interface::Success {
Imre Kis54773b62025-04-10 13:47:39 +02002003 args: SuccessArgs::Args64_2(_),
Balint Dobszayde0dc802025-02-28 14:16:52 +01002004 ..
2005 }
2006 | Interface::MsgSendDirectReq2 { .. }
Tomás González0a058bc2025-03-11 11:20:55 +00002007 | Interface::MsgSendDirectResp2 { .. }
2008 | Interface::PartitionInfoGetRegs { .. } => {
Balint Dobszayde0dc802025-02-28 14:16:52 +01002009 self.pack_regs18(version, regs.try_into().unwrap());
2010 }
2011 _ => {
2012 self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
2013 }
2014 }
2015 }
2016 _ => panic!("Invalid number of registers {}", reg_cnt),
2017 }
2018 }
2019
2020 fn pack_regs8(&self, version: Version, a: &mut [u64; 8]) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01002021 if let Some(function_id) = self.function_id() {
2022 a[0] = function_id as u64;
2023 }
2024
2025 match *self {
2026 Interface::Error {
2027 target_info,
2028 error_code,
Balint Dobszayb727aab2025-04-07 10:24:59 +02002029 error_arg,
Balint Dobszay3aad9572025-01-17 16:54:11 +01002030 } => {
2031 a[1] = u32::from(target_info).into();
2032 a[2] = (error_code as u32).into();
Balint Dobszayb727aab2025-04-07 10:24:59 +02002033 a[3] = error_arg.into();
Balint Dobszay3aad9572025-01-17 16:54:11 +01002034 }
2035 Interface::Success { target_info, args } => {
Imre Kise521a282025-06-13 13:29:24 +02002036 a[1] = u32::from(target_info).into();
Balint Dobszay3aad9572025-01-17 16:54:11 +01002037 match args {
Imre Kis54773b62025-04-10 13:47:39 +02002038 SuccessArgs::Args32(regs) => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01002039 a[2] = regs[0].into();
2040 a[3] = regs[1].into();
2041 a[4] = regs[2].into();
2042 a[5] = regs[3].into();
2043 a[6] = regs[4].into();
2044 a[7] = regs[5].into();
2045 }
Imre Kis54773b62025-04-10 13:47:39 +02002046 SuccessArgs::Args64(regs) => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01002047 a[2] = regs[0];
2048 a[3] = regs[1];
2049 a[4] = regs[2];
2050 a[5] = regs[3];
2051 a[6] = regs[4];
2052 a[7] = regs[5];
2053 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01002054 _ => panic!("{:#x?} requires 18 registers", args),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002055 }
2056 }
2057 Interface::Interrupt {
2058 target_info,
2059 interrupt_id,
2060 } => {
2061 a[1] = u32::from(target_info).into();
2062 a[2] = interrupt_id.into();
2063 }
2064 Interface::Version { input_version } => {
2065 a[1] = u32::from(input_version).into();
2066 }
2067 Interface::VersionOut { output_version } => {
2068 a[0] = u32::from(output_version).into();
2069 }
2070 Interface::Features {
2071 feat_id,
2072 input_properties,
2073 } => {
2074 a[1] = u32::from(feat_id).into();
2075 a[2] = input_properties.into();
2076 }
2077 Interface::RxAcquire { vm_id } => {
2078 a[1] = vm_id.into();
2079 }
2080 Interface::RxRelease { vm_id } => {
2081 a[1] = vm_id.into();
2082 }
2083 Interface::RxTxMap { addr, page_cnt } => {
2084 match addr {
2085 RxTxAddr::Addr32 { rx, tx } => {
2086 a[1] = tx.into();
2087 a[2] = rx.into();
2088 }
2089 RxTxAddr::Addr64 { rx, tx } => {
2090 a[1] = tx;
2091 a[2] = rx;
2092 }
2093 }
2094 a[3] = page_cnt.into();
2095 }
2096 Interface::RxTxUnmap { id } => {
2097 a[1] = id.into();
2098 }
2099 Interface::PartitionInfoGet { uuid, flags } => {
2100 let bytes = uuid.into_bytes();
2101 a[1] = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]).into();
2102 a[2] = u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]).into();
2103 a[3] = u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]).into();
2104 a[4] = u32::from_le_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]).into();
Imre Kise295adb2025-04-10 13:26:28 +02002105 a[5] = u32::from(flags).into();
Balint Dobszay3aad9572025-01-17 16:54:11 +01002106 }
Tomás González092202a2025-03-05 11:56:45 +00002107 Interface::MsgWait { flags } => {
2108 if version >= Version(1, 2) {
2109 if let Some(flags) = flags {
2110 a[2] = u32::from(flags).into();
2111 }
2112 }
2113 }
2114 Interface::IdGet | Interface::SpmIdGet | Interface::Yield => {}
Balint Dobszay3aad9572025-01-17 16:54:11 +01002115 Interface::Run { target_info } => {
2116 a[1] = u32::from(target_info).into();
2117 }
2118 Interface::NormalWorldResume => {}
Tomás González17b92442025-03-10 16:45:04 +00002119 Interface::SecondaryEpRegister { entrypoint } => match entrypoint {
2120 SecondaryEpRegisterAddr::Addr32(addr) => a[1] = addr as u64,
2121 SecondaryEpRegisterAddr::Addr64(addr) => a[1] = addr,
2122 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01002123 Interface::MsgSend2 {
2124 sender_vm_id,
2125 flags,
2126 } => {
2127 a[1] = sender_vm_id.into();
Imre Kisa2fd69b2025-06-13 13:39:47 +02002128 a[2] = u32::from(flags).into();
Balint Dobszay3aad9572025-01-17 16:54:11 +01002129 }
2130 Interface::MsgSendDirectReq {
2131 src_id,
2132 dst_id,
Balint Dobszay3aad9572025-01-17 16:54:11 +01002133 args,
2134 } => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +01002135 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Balint Dobszay3aad9572025-01-17 16:54:11 +01002136 match args {
2137 DirectMsgArgs::Args32(args) => {
2138 a[3] = args[0].into();
2139 a[4] = args[1].into();
2140 a[5] = args[2].into();
2141 a[6] = args[3].into();
2142 a[7] = args[4].into();
2143 }
2144 DirectMsgArgs::Args64(args) => {
2145 a[3] = args[0];
2146 a[4] = args[1];
2147 a[5] = args[2];
2148 a[6] = args[3];
2149 a[7] = args[4];
2150 }
Tomás González4d5b0ba2025-03-03 17:15:55 +00002151 DirectMsgArgs::VersionReq { version } => {
2152 a[2] = DirectMsgArgs::VERSION_REQ.into();
2153 a[3] = u32::from(version).into();
2154 }
Tomás González67f92c72025-03-20 16:50:42 +00002155 DirectMsgArgs::PowerPsciReq32 { params } => {
Tomás González4d5b0ba2025-03-03 17:15:55 +00002156 a[2] = DirectMsgArgs::POWER_PSCI_REQ.into();
Tomás González67f92c72025-03-20 16:50:42 +00002157 a[3] = params[0].into();
2158 a[4] = params[1].into();
2159 a[5] = params[2].into();
2160 a[6] = params[3].into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00002161 }
Tomás González67f92c72025-03-20 16:50:42 +00002162 DirectMsgArgs::PowerPsciReq64 { params } => {
Tomás González4d5b0ba2025-03-03 17:15:55 +00002163 a[2] = DirectMsgArgs::POWER_PSCI_REQ.into();
Tomás González67f92c72025-03-20 16:50:42 +00002164 a[3] = params[0];
2165 a[4] = params[1];
2166 a[5] = params[2];
2167 a[6] = params[3];
Tomás González4d5b0ba2025-03-03 17:15:55 +00002168 }
2169 DirectMsgArgs::PowerWarmBootReq { boot_type } => {
2170 a[2] = DirectMsgArgs::POWER_WARM_BOOT_REQ.into();
2171 a[3] = u32::from(boot_type).into();
2172 }
2173 DirectMsgArgs::VmCreated { handle, vm_id } => {
2174 a[2] = DirectMsgArgs::VM_CREATED.into();
2175 let handle_regs: [u32; 2] = handle.into();
2176 a[3] = handle_regs[0].into();
2177 a[4] = handle_regs[1].into();
2178 a[5] = vm_id.into();
2179 }
2180 DirectMsgArgs::VmDestructed { handle, vm_id } => {
2181 a[2] = DirectMsgArgs::VM_DESTRUCTED.into();
2182 let handle_regs: [u32; 2] = handle.into();
2183 a[3] = handle_regs[0].into();
2184 a[4] = handle_regs[1].into();
2185 a[5] = vm_id.into();
2186 }
2187 _ => panic!("Malformed MsgSendDirectReq interface"),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002188 }
2189 }
2190 Interface::MsgSendDirectResp {
2191 src_id,
2192 dst_id,
Balint Dobszay3aad9572025-01-17 16:54:11 +01002193 args,
2194 } => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +01002195 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Balint Dobszay3aad9572025-01-17 16:54:11 +01002196 match args {
2197 DirectMsgArgs::Args32(args) => {
2198 a[3] = args[0].into();
2199 a[4] = args[1].into();
2200 a[5] = args[2].into();
2201 a[6] = args[3].into();
2202 a[7] = args[4].into();
2203 }
2204 DirectMsgArgs::Args64(args) => {
2205 a[3] = args[0];
2206 a[4] = args[1];
2207 a[5] = args[2];
2208 a[6] = args[3];
2209 a[7] = args[4];
2210 }
Tomás González4d5b0ba2025-03-03 17:15:55 +00002211 DirectMsgArgs::VersionResp { version } => {
2212 a[2] = DirectMsgArgs::VERSION_RESP.into();
2213 match version {
Tomás González67f92c72025-03-20 16:50:42 +00002214 None => a[3] = (i32::from(FfaError::NotSupported) as u32).into(),
Tomás González4d5b0ba2025-03-03 17:15:55 +00002215 Some(ver) => a[3] = u32::from(ver).into(),
2216 }
2217 }
2218 DirectMsgArgs::PowerPsciResp { psci_status } => {
2219 a[2] = DirectMsgArgs::POWER_PSCI_RESP.into();
Imre Kisb2d3c882025-04-11 14:19:35 +02002220 a[3] = (psci_status as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00002221 }
2222 DirectMsgArgs::VmCreatedAck { sp_status } => {
2223 a[2] = DirectMsgArgs::VM_CREATED_ACK.into();
Tomás González67f92c72025-03-20 16:50:42 +00002224 a[3] = (i32::from(sp_status) as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00002225 }
2226 DirectMsgArgs::VmDestructedAck { sp_status } => {
2227 a[2] = DirectMsgArgs::VM_DESTRUCTED_ACK.into();
Tomás González67f92c72025-03-20 16:50:42 +00002228 a[3] = (i32::from(sp_status) as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00002229 }
2230 _ => panic!("Malformed MsgSendDirectResp interface"),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002231 }
2232 }
2233 Interface::MemDonate {
2234 total_len,
2235 frag_len,
2236 buf,
2237 } => {
2238 a[1] = total_len.into();
2239 a[2] = frag_len.into();
2240 (a[3], a[4]) = match buf {
2241 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
2242 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
2243 None => (0, 0),
2244 };
2245 }
2246 Interface::MemLend {
2247 total_len,
2248 frag_len,
2249 buf,
2250 } => {
2251 a[1] = total_len.into();
2252 a[2] = frag_len.into();
2253 (a[3], a[4]) = match buf {
2254 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
2255 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
2256 None => (0, 0),
2257 };
2258 }
2259 Interface::MemShare {
2260 total_len,
2261 frag_len,
2262 buf,
2263 } => {
2264 a[1] = total_len.into();
2265 a[2] = frag_len.into();
2266 (a[3], a[4]) = match buf {
2267 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
2268 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
2269 None => (0, 0),
2270 };
2271 }
2272 Interface::MemRetrieveReq {
2273 total_len,
2274 frag_len,
2275 buf,
2276 } => {
2277 a[1] = total_len.into();
2278 a[2] = frag_len.into();
2279 (a[3], a[4]) = match buf {
2280 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
2281 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
2282 None => (0, 0),
2283 };
2284 }
2285 Interface::MemRetrieveResp {
2286 total_len,
2287 frag_len,
2288 } => {
2289 a[1] = total_len.into();
2290 a[2] = frag_len.into();
2291 }
2292 Interface::MemRelinquish => {}
2293 Interface::MemReclaim { handle, flags } => {
2294 let handle_regs: [u32; 2] = handle.into();
2295 a[1] = handle_regs[0].into();
2296 a[2] = handle_regs[1].into();
2297 a[3] = flags.into();
2298 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01002299 Interface::MemPermGet { addr, page_cnt } => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01002300 a[1] = match addr {
2301 MemAddr::Addr32(addr) => addr.into(),
2302 MemAddr::Addr64(addr) => addr,
2303 };
Balint Dobszayde0dc802025-02-28 14:16:52 +01002304 a[2] = if version >= Version(1, 3) {
2305 page_cnt.unwrap().into()
2306 } else {
2307 assert!(page_cnt.is_none());
2308 0
2309 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01002310 }
2311 Interface::MemPermSet {
2312 addr,
2313 page_cnt,
2314 mem_perm,
2315 } => {
2316 a[1] = match addr {
2317 MemAddr::Addr32(addr) => addr.into(),
2318 MemAddr::Addr64(addr) => addr,
2319 };
2320 a[2] = page_cnt.into();
Imre Kisdcb7df22025-06-06 15:24:40 +02002321 a[3] = u32::from(mem_perm).into();
Balint Dobszay3aad9572025-01-17 16:54:11 +01002322 }
Imre Kis189f18c2025-05-26 19:33:05 +02002323 Interface::ConsoleLog { chars } => match chars {
2324 ConsoleLogChars::Chars32(ConsoleLogChars32 {
2325 char_cnt,
2326 char_lists,
2327 }) => {
2328 a[1] = char_cnt.into();
2329 a[2] = char_lists[0].into();
2330 a[3] = char_lists[1].into();
2331 a[4] = char_lists[2].into();
2332 a[5] = char_lists[3].into();
2333 a[6] = char_lists[4].into();
2334 a[7] = char_lists[5].into();
Balint Dobszay3aad9572025-01-17 16:54:11 +01002335 }
Imre Kis189f18c2025-05-26 19:33:05 +02002336 _ => panic!("{:#x?} requires 18 registers", chars),
2337 },
Tomás González7ffb6132025-04-03 12:28:58 +01002338 Interface::NotificationBitmapCreate { vm_id, vcpu_cnt } => {
2339 a[1] = vm_id.into();
2340 a[2] = vcpu_cnt.into();
2341 }
2342 Interface::NotificationBitmapDestroy { vm_id } => {
2343 a[1] = vm_id.into();
2344 }
2345 Interface::NotificationBind {
2346 sender_id,
2347 receiver_id,
2348 flags,
2349 bitmap,
2350 } => {
2351 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
2352 a[2] = u32::from(flags).into();
2353 a[3] = bitmap & 0xffff_ffff;
2354 a[4] = bitmap >> 32;
2355 }
Imre Kis3571f2c2025-05-26 19:29:23 +02002356 Interface::NotificationUnbind {
Tomás González7ffb6132025-04-03 12:28:58 +01002357 sender_id,
2358 receiver_id,
2359 bitmap,
2360 } => {
2361 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
2362 a[3] = bitmap & 0xffff_ffff;
2363 a[4] = bitmap >> 32;
2364 }
2365 Interface::NotificationSet {
2366 sender_id,
2367 receiver_id,
2368 flags,
2369 bitmap,
2370 } => {
2371 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
2372 a[2] = u32::from(flags).into();
2373 a[3] = bitmap & 0xffff_ffff;
2374 a[4] = bitmap >> 32;
2375 }
2376 Interface::NotificationGet {
2377 vcpu_id,
2378 endpoint_id,
2379 flags,
2380 } => {
2381 a[1] = (u64::from(vcpu_id) << 16) | u64::from(endpoint_id);
2382 a[2] = u32::from(flags).into();
2383 }
2384 Interface::NotificationInfoGet { .. } => {}
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01002385 Interface::El3IntrHandle => {}
Balint Dobszayde0dc802025-02-28 14:16:52 +01002386 _ => panic!("{:#x?} requires 18 registers", self),
2387 }
2388 }
2389
2390 fn pack_regs18(&self, version: Version, a: &mut [u64; 18]) {
2391 assert!(version >= Version(1, 2));
2392
Balint Dobszayde0dc802025-02-28 14:16:52 +01002393 if let Some(function_id) = self.function_id() {
2394 a[0] = function_id as u64;
2395 }
2396
2397 match *self {
2398 Interface::Success { target_info, args } => {
Imre Kise521a282025-06-13 13:29:24 +02002399 a[1] = u32::from(target_info).into();
Balint Dobszayde0dc802025-02-28 14:16:52 +01002400 match args {
Imre Kis54773b62025-04-10 13:47:39 +02002401 SuccessArgs::Args64_2(regs) => a[2..18].copy_from_slice(&regs[..16]),
Balint Dobszayde0dc802025-02-28 14:16:52 +01002402 _ => panic!("{:#x?} requires 8 registers", args),
2403 }
2404 }
2405 Interface::MsgSendDirectReq2 {
2406 src_id,
2407 dst_id,
2408 uuid,
2409 args,
2410 } => {
2411 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00002412 let (uuid_msb, uuid_lsb) = uuid.as_u64_pair();
2413 (a[2], a[3]) = (uuid_msb.swap_bytes(), uuid_lsb.swap_bytes());
Balint Dobszayde0dc802025-02-28 14:16:52 +01002414 a[4..18].copy_from_slice(&args.0[..14]);
2415 }
2416 Interface::MsgSendDirectResp2 {
2417 src_id,
2418 dst_id,
2419 args,
2420 } => {
2421 a[1] = ((src_id as u64) << 16) | dst_id as u64;
2422 a[2] = 0;
2423 a[3] = 0;
2424 a[4..18].copy_from_slice(&args.0[..14]);
2425 }
Imre Kis189f18c2025-05-26 19:33:05 +02002426 Interface::ConsoleLog { chars: char_lists } => match char_lists {
2427 ConsoleLogChars::Chars64(ConsoleLogChars64 {
2428 char_cnt,
2429 char_lists,
2430 }) => {
2431 a[1] = char_cnt.into();
2432 a[2..18].copy_from_slice(&char_lists[..16])
Balint Dobszayde0dc802025-02-28 14:16:52 +01002433 }
Imre Kis189f18c2025-05-26 19:33:05 +02002434 _ => panic!("{:#x?} requires 8 registers", char_lists),
2435 },
Tomás González0a058bc2025-03-11 11:20:55 +00002436 Interface::PartitionInfoGetRegs {
2437 uuid,
2438 start_index,
2439 info_tag,
2440 } => {
2441 if start_index == 0 && info_tag != 0 {
2442 panic!("Information Tag MBZ if start index is 0: {:#x?}", self);
2443 }
2444 let (uuid_msb, uuid_lsb) = uuid.as_u64_pair();
2445 (a[1], a[2]) = (uuid_msb.swap_bytes(), uuid_lsb.swap_bytes());
2446 a[3] = (u64::from(info_tag) << 16) | u64::from(start_index);
2447 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01002448 _ => panic!("{:#x?} requires 8 registers", self),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002449 }
2450 }
2451
Balint Dobszaya5846852025-02-26 15:38:53 +01002452 /// Helper function to create an `FFA_SUCCESS` interface without any arguments.
Balint Dobszay3aad9572025-01-17 16:54:11 +01002453 pub fn success32_noargs() -> Self {
2454 Self::Success {
Imre Kise521a282025-06-13 13:29:24 +02002455 target_info: TargetInfo::default(),
Imre Kis54773b62025-04-10 13:47:39 +02002456 args: SuccessArgs::Args32([0; 6]),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002457 }
2458 }
2459
Balint Dobszaya5846852025-02-26 15:38:53 +01002460 /// Helper function to create an `FFA_ERROR` interface with an error code.
Balint Dobszay3aad9572025-01-17 16:54:11 +01002461 pub fn error(error_code: FfaError) -> Self {
2462 Self::Error {
Imre Kise521a282025-06-13 13:29:24 +02002463 target_info: TargetInfo::default(),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002464 error_code,
Balint Dobszayb727aab2025-04-07 10:24:59 +02002465 error_arg: 0,
Balint Dobszay3aad9572025-01-17 16:54:11 +01002466 }
2467 }
2468}
2469
Tomás González0a058bc2025-03-11 11:20:55 +00002470#[cfg(test)]
2471mod tests {
2472 use super::*;
2473
2474 #[test]
Balint Dobszay5ded5922025-06-13 12:06:53 +02002475 fn version_reg_count() {
2476 assert!(!Version(1, 1).needs_18_regs());
2477 assert!(Version(1, 2).needs_18_regs())
2478 }
2479
2480 #[test]
Tomás González0a058bc2025-03-11 11:20:55 +00002481 fn part_info_get_regs() {
2482 let uuid = Uuid::parse_str("a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8").unwrap();
2483 let uuid_bytes = uuid.as_bytes();
2484 let test_info_tag = 0b1101_1101;
2485 let test_start_index = 0b1101;
2486 let start_index_and_tag = (test_info_tag << 16) | test_start_index;
2487 let version = Version(1, 2);
2488
2489 // From spec:
2490 // Bytes[0...7] of UUID with byte 0 in the low-order bits.
Balint Dobszayb2e9bed2025-04-15 12:57:36 +02002491 let reg_x1 = ((uuid_bytes[7] as u64) << 56)
2492 | ((uuid_bytes[6] as u64) << 48)
2493 | ((uuid_bytes[5] as u64) << 40)
2494 | ((uuid_bytes[4] as u64) << 32)
2495 | ((uuid_bytes[3] as u64) << 24)
2496 | ((uuid_bytes[2] as u64) << 16)
2497 | ((uuid_bytes[1] as u64) << 8)
Tomás González0a058bc2025-03-11 11:20:55 +00002498 | (uuid_bytes[0] as u64);
2499
2500 // From spec:
2501 // Bytes[8...15] of UUID with byte 8 in the low-order bits.
Balint Dobszayb2e9bed2025-04-15 12:57:36 +02002502 let reg_x2 = ((uuid_bytes[15] as u64) << 56)
2503 | ((uuid_bytes[14] as u64) << 48)
2504 | ((uuid_bytes[13] as u64) << 40)
2505 | ((uuid_bytes[12] as u64) << 32)
2506 | ((uuid_bytes[11] as u64) << 24)
2507 | ((uuid_bytes[10] as u64) << 16)
2508 | ((uuid_bytes[9] as u64) << 8)
Tomás González0a058bc2025-03-11 11:20:55 +00002509 | (uuid_bytes[8] as u64);
2510
2511 // First, test for wrong tag:
2512 {
2513 let mut regs = [0u64; 18];
2514 regs[0] = FuncId::PartitionInfoGetRegs as u64;
2515 regs[1] = reg_x1;
2516 regs[2] = reg_x2;
2517 regs[3] = test_info_tag << 16;
2518
2519 assert!(Interface::from_regs(version, &regs).is_err_and(
2520 |e| e == Error::InvalidInformationTag(test_info_tag.try_into().unwrap())
2521 ));
2522 }
2523
2524 // Test for regs -> Interface -> regs
2525 {
2526 let mut orig_regs = [0u64; 18];
2527 orig_regs[0] = FuncId::PartitionInfoGetRegs as u64;
2528 orig_regs[1] = reg_x1;
2529 orig_regs[2] = reg_x2;
2530 orig_regs[3] = start_index_and_tag;
2531
Balint Dobszayb2e9bed2025-04-15 12:57:36 +02002532 let mut test_regs = orig_regs;
2533 let interface = Interface::from_regs(version, &test_regs).unwrap();
Tomás González0a058bc2025-03-11 11:20:55 +00002534 match &interface {
2535 Interface::PartitionInfoGetRegs {
2536 info_tag,
2537 start_index,
2538 uuid: int_uuid,
2539 } => {
2540 assert_eq!(u64::from(*info_tag), test_info_tag);
2541 assert_eq!(u64::from(*start_index), test_start_index);
2542 assert_eq!(*int_uuid, uuid);
2543 }
2544 _ => panic!("Expecting Interface::PartitionInfoGetRegs!"),
2545 }
2546 test_regs.fill(0);
2547 interface.to_regs(version, &mut test_regs);
2548 assert_eq!(orig_regs, test_regs);
2549 }
2550
2551 // Test for Interface -> regs -> Interface
2552 {
2553 let interface = Interface::PartitionInfoGetRegs {
2554 info_tag: test_info_tag.try_into().unwrap(),
2555 start_index: test_start_index.try_into().unwrap(),
2556 uuid,
2557 };
2558
2559 let mut regs: [u64; 18] = [0; 18];
2560 interface.to_regs(version, &mut regs);
2561
2562 assert_eq!(Some(FuncId::PartitionInfoGetRegs), interface.function_id());
2563 assert_eq!(regs[0], interface.function_id().unwrap() as u64);
2564 assert_eq!(regs[1], reg_x1);
2565 assert_eq!(regs[2], reg_x2);
2566 assert_eq!(regs[3], (test_info_tag << 16) | test_start_index);
2567
2568 assert_eq!(Interface::from_regs(version, &regs).unwrap(), interface);
2569 }
2570 }
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00002571
2572 #[test]
2573 fn msg_send_direct_req2() {
2574 let uuid = Uuid::parse_str("a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8").unwrap();
2575 let uuid_bytes = uuid.as_bytes();
2576
2577 // From spec:
2578 // Bytes[0...7] of UUID with byte 0 in the low-order bits.
Balint Dobszayb2e9bed2025-04-15 12:57:36 +02002579 let reg_x2 = ((uuid_bytes[7] as u64) << 56)
2580 | ((uuid_bytes[6] as u64) << 48)
2581 | ((uuid_bytes[5] as u64) << 40)
2582 | ((uuid_bytes[4] as u64) << 32)
2583 | ((uuid_bytes[3] as u64) << 24)
2584 | ((uuid_bytes[2] as u64) << 16)
2585 | ((uuid_bytes[1] as u64) << 8)
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00002586 | (uuid_bytes[0] as u64);
2587
2588 // From spec:
2589 // Bytes[8...15] of UUID with byte 8 in the low-order bits.
Balint Dobszayb2e9bed2025-04-15 12:57:36 +02002590 let reg_x3 = ((uuid_bytes[15] as u64) << 56)
2591 | ((uuid_bytes[14] as u64) << 48)
2592 | ((uuid_bytes[13] as u64) << 40)
2593 | ((uuid_bytes[12] as u64) << 32)
2594 | ((uuid_bytes[11] as u64) << 24)
2595 | ((uuid_bytes[10] as u64) << 16)
2596 | ((uuid_bytes[9] as u64) << 8)
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00002597 | (uuid_bytes[8] as u64);
2598
2599 let test_sender = 0b1101_1101;
2600 let test_receiver = 0b1101;
2601 let test_sender_receiver = (test_sender << 16) | test_receiver;
2602 let version = Version(1, 2);
2603
2604 // Test for regs -> Interface -> regs
2605 {
2606 let mut orig_regs = [0u64; 18];
2607 orig_regs[0] = FuncId::MsgSendDirectReq64_2 as u64;
2608 orig_regs[1] = test_sender_receiver;
2609 orig_regs[2] = reg_x2;
2610 orig_regs[3] = reg_x3;
2611
Balint Dobszayb2e9bed2025-04-15 12:57:36 +02002612 let mut test_regs = orig_regs;
2613 let interface = Interface::from_regs(version, &test_regs).unwrap();
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00002614 match &interface {
2615 Interface::MsgSendDirectReq2 {
2616 dst_id,
2617 src_id,
2618 args: _,
2619 uuid: int_uuid,
2620 } => {
2621 assert_eq!(u64::from(*src_id), test_sender);
2622 assert_eq!(u64::from(*dst_id), test_receiver);
2623 assert_eq!(*int_uuid, uuid);
2624 }
2625 _ => panic!("Expecting Interface::MsgSendDirectReq2!"),
2626 }
2627 test_regs.fill(0);
2628 interface.to_regs(version, &mut test_regs);
2629 assert_eq!(orig_regs, test_regs);
2630 }
2631
2632 // Test for Interface -> regs -> Interface
2633 {
2634 let rest_of_regs: [u64; 14] = [0; 14];
2635
2636 let interface = Interface::MsgSendDirectReq2 {
2637 src_id: test_sender.try_into().unwrap(),
2638 dst_id: test_receiver.try_into().unwrap(),
2639 uuid,
2640 args: DirectMsg2Args(rest_of_regs),
2641 };
2642
2643 let mut regs: [u64; 18] = [0; 18];
2644 interface.to_regs(version, &mut regs);
2645
2646 assert_eq!(Some(FuncId::MsgSendDirectReq64_2), interface.function_id());
2647 assert_eq!(regs[0], interface.function_id().unwrap() as u64);
2648 assert_eq!(regs[1], test_sender_receiver);
2649 assert_eq!(regs[2], reg_x2);
2650 assert_eq!(regs[3], reg_x3);
2651 assert_eq!(regs[4], 0);
2652
2653 assert_eq!(Interface::from_regs(version, &regs).unwrap(), interface);
2654 }
2655 }
Tomás González6ccba0a2025-04-09 13:31:29 +01002656
2657 #[test]
2658 fn is_32bit() {
2659 let interface_64 = Interface::MsgSendDirectReq {
2660 src_id: 0,
2661 dst_id: 1,
2662 args: DirectMsgArgs::Args64([0, 0, 0, 0, 0]),
2663 };
2664 assert!(!interface_64.is_32bit());
2665
2666 let interface_32 = Interface::MsgSendDirectReq {
2667 src_id: 0,
2668 dst_id: 1,
2669 args: DirectMsgArgs::Args32([0, 0, 0, 0, 0]),
2670 };
2671 assert!(interface_32.is_32bit());
2672 }
Imre Kis787c5002025-04-10 14:25:51 +02002673
2674 #[test]
2675 fn success_args_notification_info_get32() {
2676 let mut notifications = SuccessArgsNotificationInfoGet32::default();
2677
2678 // 16.7.1.1 Example usage
2679 notifications.add_list(0x0000, &[0, 2, 3]).unwrap();
2680 notifications.add_list(0x0000, &[4, 6]).unwrap();
2681 notifications.add_list(0x0002, &[]).unwrap();
2682 notifications.add_list(0x0003, &[1]).unwrap();
2683
2684 let args: SuccessArgs = notifications.into();
2685 assert_eq!(
2686 SuccessArgs::Args32([
2687 0x0004_b200,
2688 0x0000_0000,
2689 0x0003_0002,
2690 0x0004_0000,
2691 0x0002_0006,
2692 0x0001_0003
2693 ]),
2694 args
2695 );
2696
2697 let notifications = SuccessArgsNotificationInfoGet32::try_from(args).unwrap();
2698 let mut iter = notifications.iter();
2699 assert_eq!(Some((0x0000, &[0, 2, 3][..])), iter.next());
2700 assert_eq!(Some((0x0000, &[4, 6][..])), iter.next());
2701 assert_eq!(Some((0x0002, &[][..])), iter.next());
2702 assert_eq!(Some((0x0003, &[1][..])), iter.next());
2703 }
2704
2705 #[test]
2706 fn success_args_notification_info_get64() {
2707 let mut notifications = SuccessArgsNotificationInfoGet64::default();
2708
2709 // 16.7.1.1 Example usage
2710 notifications.add_list(0x0000, &[0, 2, 3]).unwrap();
2711 notifications.add_list(0x0000, &[4, 6]).unwrap();
2712 notifications.add_list(0x0002, &[]).unwrap();
2713 notifications.add_list(0x0003, &[1]).unwrap();
2714
2715 let args: SuccessArgs = notifications.into();
2716 assert_eq!(
2717 SuccessArgs::Args64([
2718 0x0004_b200,
2719 0x0003_0002_0000_0000,
2720 0x0002_0006_0004_0000,
2721 0x0000_0000_0001_0003,
2722 0x0000_0000_0000_0000,
2723 0x0000_0000_0000_0000,
2724 ]),
2725 args
2726 );
2727
2728 let notifications = SuccessArgsNotificationInfoGet64::try_from(args).unwrap();
2729 let mut iter = notifications.iter();
2730 assert_eq!(Some((0x0000, &[0, 2, 3][..])), iter.next());
2731 assert_eq!(Some((0x0000, &[4, 6][..])), iter.next());
2732 assert_eq!(Some((0x0002, &[][..])), iter.next());
2733 assert_eq!(Some((0x0003, &[1][..])), iter.next());
2734 }
Tomás González0a058bc2025-03-11 11:20:55 +00002735}