blob: a92e7921065beab19105364f58667d9196c11a2f [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 Kis356395d2025-06-13 13:49:06 +020065 #[error("Invalid memory reclaim flags {0}")]
66 InvalidMemReclaimFlags(u32),
Imre Kisdcb7df22025-06-06 15:24:40 +020067 #[error("Memory management error")]
68 MemoryManagementError(#[from] memory_management::Error),
Balint Dobszay3aad9572025-01-17 16:54:11 +010069}
70
71impl From<Error> for FfaError {
72 fn from(value: Error) -> Self {
73 match value {
Balint Dobszay82c71dd2025-04-15 10:16:44 +020074 Error::UnrecognisedFunctionId(_)
75 | Error::UnrecognisedFeatureId(_)
76 | Error::InvalidVersionForFunctionId(..) => Self::NotSupported,
Tomás González0a058bc2025-03-11 11:20:55 +000077 Error::InvalidInformationTag(_) => Self::Retry,
Tomás González4d5b0ba2025-03-03 17:15:55 +000078 Error::UnrecognisedErrorCode(_)
79 | Error::UnrecognisedFwkMsg(_)
80 | Error::InvalidVersion(_)
Tomás González092202a2025-03-05 11:56:45 +000081 | Error::InvalidMsgWaitFlag(_)
Imre Kisa2fd69b2025-06-13 13:39:47 +020082 | Error::InvalidMsgSend2Flag(_)
Tomás González4d5b0ba2025-03-03 17:15:55 +000083 | Error::UnrecognisedVmAvailabilityStatus(_)
Tomás González7ffb6132025-04-03 12:28:58 +010084 | Error::InvalidNotificationSetFlag(_)
85 | Error::InvalidVmId(_)
Imre Kise295adb2025-04-10 13:26:28 +020086 | Error::UnrecognisedWarmBootType(_)
Imre Kis839eaef2025-04-11 17:38:36 +020087 | Error::InvalidPartitionInfoGetFlag(_)
Imre Kis787c5002025-04-10 14:25:51 +020088 | Error::InvalidSuccessArgsVariant
Imre Kis92b663e2025-04-10 14:15:05 +020089 | Error::InvalidNotificationCount
Imre Kis189f18c2025-05-26 19:33:05 +020090 | Error::InvalidPartitionInfoGetRegsResponse
Imre Kisdcb7df22025-06-06 15:24:40 +020091 | Error::InvalidCharacterCount(_)
Imre Kis356395d2025-06-13 13:49:06 +020092 | Error::InvalidMemReclaimFlags(_)
Imre Kisdcb7df22025-06-06 15:24:40 +020093 | Error::MemoryManagementError(_) => Self::InvalidParameters,
Balint Dobszay3aad9572025-01-17 16:54:11 +010094 }
95 }
96}
Balint Dobszay5bf492f2024-07-29 17:21:32 +020097
Balint Dobszaya5846852025-02-26 15:38:53 +010098/// 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 +020099#[derive(PartialEq, Clone, Copy)]
100pub enum Instance {
Balint Dobszaya5846852025-02-26 15:38:53 +0100101 /// The instance between the SPMC and SPMD.
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200102 SecurePhysical,
Balint Dobszaya5846852025-02-26 15:38:53 +0100103 /// The instance between the SPMC and a physical SP (contains the SP's endpoint ID).
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200104 SecureVirtual(u16),
105}
106
Balint Dobszaya5846852025-02-26 15:38:53 +0100107/// Function IDs of the various FF-A interfaces.
Andrew Walbran969b9252024-11-25 15:35:42 +0000108#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
Balint Dobszay3aad9572025-01-17 16:54:11 +0100109#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedFunctionId))]
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200110#[repr(u32)]
111pub enum FuncId {
112 Error = 0x84000060,
113 Success32 = 0x84000061,
114 Success64 = 0xc4000061,
115 Interrupt = 0x84000062,
116 Version = 0x84000063,
117 Features = 0x84000064,
118 RxAcquire = 0x84000084,
119 RxRelease = 0x84000065,
120 RxTxMap32 = 0x84000066,
121 RxTxMap64 = 0xc4000066,
122 RxTxUnmap = 0x84000067,
123 PartitionInfoGet = 0x84000068,
Balint Dobszaye6aa4862025-02-28 16:37:56 +0100124 PartitionInfoGetRegs = 0xc400008b,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200125 IdGet = 0x84000069,
126 SpmIdGet = 0x84000085,
Balint Dobszaye6aa4862025-02-28 16:37:56 +0100127 ConsoleLog32 = 0x8400008a,
128 ConsoleLog64 = 0xc400008a,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200129 MsgWait = 0x8400006b,
130 Yield = 0x8400006c,
131 Run = 0x8400006d,
132 NormalWorldResume = 0x8400007c,
133 MsgSend2 = 0x84000086,
134 MsgSendDirectReq32 = 0x8400006f,
135 MsgSendDirectReq64 = 0xc400006f,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100136 MsgSendDirectReq64_2 = 0xc400008d,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200137 MsgSendDirectResp32 = 0x84000070,
138 MsgSendDirectResp64 = 0xc4000070,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100139 MsgSendDirectResp64_2 = 0xc400008e,
Balint Dobszaye6aa4862025-02-28 16:37:56 +0100140 NotificationBitmapCreate = 0x8400007d,
141 NotificationBitmapDestroy = 0x8400007e,
142 NotificationBind = 0x8400007f,
143 NotificationUnbind = 0x84000080,
144 NotificationSet = 0x84000081,
145 NotificationGet = 0x84000082,
146 NotificationInfoGet32 = 0x84000083,
147 NotificationInfoGet64 = 0xc4000083,
148 El3IntrHandle = 0x8400008c,
Tomás González17b92442025-03-10 16:45:04 +0000149 SecondaryEpRegister32 = 0x84000087,
150 SecondaryEpRegister64 = 0xc4000087,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200151 MemDonate32 = 0x84000071,
152 MemDonate64 = 0xc4000071,
153 MemLend32 = 0x84000072,
154 MemLend64 = 0xc4000072,
155 MemShare32 = 0x84000073,
156 MemShare64 = 0xc4000073,
157 MemRetrieveReq32 = 0x84000074,
158 MemRetrieveReq64 = 0xc4000074,
159 MemRetrieveResp = 0x84000075,
160 MemRelinquish = 0x84000076,
161 MemReclaim = 0x84000077,
162 MemPermGet32 = 0x84000088,
163 MemPermGet64 = 0xc4000088,
164 MemPermSet32 = 0x84000089,
165 MemPermSet64 = 0xc4000089,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200166}
167
Balint Dobszayde0dc802025-02-28 14:16:52 +0100168impl FuncId {
169 /// Returns true if this is a 32-bit call, or false if it is a 64-bit call.
170 pub fn is_32bit(&self) -> bool {
Tomás González6ccba0a2025-04-09 13:31:29 +0100171 u32::from(*self) & (1 << 30) == 0
Balint Dobszayde0dc802025-02-28 14:16:52 +0100172 }
Balint Dobszay82c71dd2025-04-15 10:16:44 +0200173
174 /// Returns the FF-A version that has introduced the function ID.
175 pub fn minimum_ffa_version(&self) -> Version {
176 match self {
177 FuncId::Error
178 | FuncId::Success32
179 | FuncId::Success64
180 | FuncId::Interrupt
181 | FuncId::Version
182 | FuncId::Features
183 | FuncId::RxRelease
184 | FuncId::RxTxMap32
185 | FuncId::RxTxMap64
186 | FuncId::RxTxUnmap
187 | FuncId::PartitionInfoGet
188 | FuncId::IdGet
189 | FuncId::MsgWait
190 | FuncId::Yield
191 | FuncId::Run
192 | FuncId::NormalWorldResume
193 | FuncId::MsgSendDirectReq32
194 | FuncId::MsgSendDirectReq64
195 | FuncId::MsgSendDirectResp32
196 | FuncId::MsgSendDirectResp64
197 | FuncId::MemDonate32
198 | FuncId::MemDonate64
199 | FuncId::MemLend32
200 | FuncId::MemLend64
201 | FuncId::MemShare32
202 | FuncId::MemShare64
203 | FuncId::MemRetrieveReq32
204 | FuncId::MemRetrieveReq64
205 | FuncId::MemRetrieveResp
206 | FuncId::MemRelinquish
207 | FuncId::MemReclaim => Version(1, 0),
208
209 FuncId::RxAcquire
210 | FuncId::SpmIdGet
211 | FuncId::MsgSend2
212 | FuncId::MemPermGet32
213 | FuncId::MemPermGet64
214 | FuncId::MemPermSet32
215 | FuncId::MemPermSet64
216 | FuncId::NotificationBitmapCreate
217 | FuncId::NotificationBitmapDestroy
218 | FuncId::NotificationBind
219 | FuncId::NotificationUnbind
220 | FuncId::NotificationSet
221 | FuncId::NotificationGet
222 | FuncId::NotificationInfoGet32
223 | FuncId::NotificationInfoGet64
224 | FuncId::SecondaryEpRegister32
225 | FuncId::SecondaryEpRegister64 => Version(1, 1),
226
227 FuncId::PartitionInfoGetRegs
228 | FuncId::ConsoleLog32
229 | FuncId::ConsoleLog64
230 | FuncId::MsgSendDirectReq64_2
231 | FuncId::MsgSendDirectResp64_2
232 | FuncId::El3IntrHandle => Version(1, 2),
233 }
234 }
Balint Dobszayde0dc802025-02-28 14:16:52 +0100235}
236
Balint Dobszaya5846852025-02-26 15:38:53 +0100237/// Error status codes used by the `FFA_ERROR` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100238#[derive(Clone, Copy, Debug, Eq, Error, IntoPrimitive, PartialEq, TryFromPrimitive)]
239#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedErrorCode))]
240#[repr(i32)]
241pub enum FfaError {
242 #[error("Not supported")]
243 NotSupported = -1,
244 #[error("Invalid parameters")]
245 InvalidParameters = -2,
246 #[error("No memory")]
247 NoMemory = -3,
248 #[error("Busy")]
249 Busy = -4,
250 #[error("Interrupted")]
251 Interrupted = -5,
252 #[error("Denied")]
253 Denied = -6,
254 #[error("Retry")]
255 Retry = -7,
256 #[error("Aborted")]
257 Aborted = -8,
258 #[error("No data")]
259 NoData = -9,
260}
261
Balint Dobszaya5846852025-02-26 15:38:53 +0100262/// Endpoint ID and vCPU ID pair, used by `FFA_ERROR`, `FFA_INTERRUPT` and `FFA_RUN` interfaces.
Imre Kise521a282025-06-13 13:29:24 +0200263#[derive(Debug, Default, Eq, PartialEq, Clone, Copy)]
Balint Dobszay3aad9572025-01-17 16:54:11 +0100264pub struct TargetInfo {
265 pub endpoint_id: u16,
266 pub vcpu_id: u16,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200267}
268
Balint Dobszay3aad9572025-01-17 16:54:11 +0100269impl From<u32> for TargetInfo {
270 fn from(value: u32) -> Self {
271 Self {
272 endpoint_id: (value >> 16) as u16,
273 vcpu_id: value as u16,
274 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200275 }
276}
277
Balint Dobszay3aad9572025-01-17 16:54:11 +0100278impl From<TargetInfo> for u32 {
279 fn from(value: TargetInfo) -> Self {
Balint Dobszaye9a3e762025-02-26 17:29:57 +0100280 ((value.endpoint_id as u32) << 16) | value.vcpu_id as u32
Andrew Walbran0d315812024-11-25 15:36:36 +0000281 }
Balint Dobszay3aad9572025-01-17 16:54:11 +0100282}
Andrew Walbran0d315812024-11-25 15:36:36 +0000283
Imre Kis839eaef2025-04-11 17:38:36 +0200284/// Generic arguments of the `FFA_SUCCESS` interface. The interpretation of the arguments depends on
285/// the interface that initiated the request. The application code has knowledge of the request, so
286/// it has to convert `SuccessArgs` into/from a specific success args structure that matches the
287/// request.
Imre Kis4e9d8bc2025-04-10 13:48:26 +0200288///
289/// The current specialized success arguments types are:
290/// * `FFA_FEATURES` - [`SuccessArgsFeatures`]
Imre Kisbbef2872025-04-10 14:11:29 +0200291/// * `FFA_ID_GET` - [`SuccessArgsIdGet`]
292/// * `FFA_SPM_ID_GET` - [`SuccessArgsSpmIdGet`]
Imre Kis61c34092025-04-10 14:14:38 +0200293/// * `FFA_PARTITION_INFO_GET` - [`partition_info::SuccessArgsPartitionInfoGet`]
Imre Kis92b663e2025-04-10 14:15:05 +0200294/// * `FFA_PARTITION_INFO_GET_REGS` - [`partition_info::SuccessArgsPartitionInfoGetRegs`]
Imre Kis9959e062025-04-10 14:16:10 +0200295/// * `FFA_NOTIFICATION_GET` - [`SuccessArgsNotificationGet`]
Imre Kis787c5002025-04-10 14:25:51 +0200296/// * `FFA_NOTIFICATION_INFO_GET_32` - [`SuccessArgsNotificationInfoGet32`]
297/// * `FFA_NOTIFICATION_INFO_GET_64` - [`SuccessArgsNotificationInfoGet64`]
Balint Dobszay3aad9572025-01-17 16:54:11 +0100298#[derive(Debug, Eq, PartialEq, Clone, Copy)]
299pub enum SuccessArgs {
Imre Kis54773b62025-04-10 13:47:39 +0200300 Args32([u32; 6]),
301 Args64([u64; 6]),
302 Args64_2([u64; 16]),
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200303}
304
Imre Kis839eaef2025-04-11 17:38:36 +0200305impl SuccessArgs {
306 fn try_get_args32(self) -> Result<[u32; 6], Error> {
307 match self {
308 SuccessArgs::Args32(args) => Ok(args),
309 SuccessArgs::Args64(_) | SuccessArgs::Args64_2(_) => {
310 Err(Error::InvalidSuccessArgsVariant)
311 }
312 }
313 }
314
315 fn try_get_args64(self) -> Result<[u64; 6], Error> {
316 match self {
317 SuccessArgs::Args64(args) => Ok(args),
318 SuccessArgs::Args32(_) | SuccessArgs::Args64_2(_) => {
319 Err(Error::InvalidSuccessArgsVariant)
320 }
321 }
322 }
323
324 fn try_get_args64_2(self) -> Result<[u64; 16], Error> {
325 match self {
326 SuccessArgs::Args64_2(args) => Ok(args),
327 SuccessArgs::Args32(_) | SuccessArgs::Args64(_) => {
328 Err(Error::InvalidSuccessArgsVariant)
329 }
330 }
331 }
332}
333
Tomás González17b92442025-03-10 16:45:04 +0000334/// Entrypoint address argument for `FFA_SECONDARY_EP_REGISTER` interface.
335#[derive(Debug, Eq, PartialEq, Clone, Copy)]
336pub enum SecondaryEpRegisterAddr {
337 Addr32(u32),
338 Addr64(u64),
339}
340
Balint Dobszaya5846852025-02-26 15:38:53 +0100341/// Version number of the FF-A implementation, `.0` is the major, `.1` is minor the version.
Balint Dobszayde0dc802025-02-28 14:16:52 +0100342#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord)]
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200343pub struct Version(pub u16, pub u16);
344
Tomás González1f794352025-03-03 16:47:06 +0000345impl Version {
Tomás González83146af2025-03-04 11:32:41 +0000346 // The FF-A spec mandates that bit[31] of a version number must be 0
347 const MBZ_BITS: u32 = 1 << 31;
348
Tomás González1f794352025-03-03 16:47:06 +0000349 /// Returns whether the caller's version (self) is compatible with the callee's version (input
350 /// parameter)
351 pub fn is_compatible_to(&self, callee_version: &Version) -> bool {
352 self.0 == callee_version.0 && self.1 <= callee_version.1
353 }
Balint Dobszay5ded5922025-06-13 12:06:53 +0200354
355 /// Returns true if the specified FF-A version uses 18 registers for calls, false if it uses 8.
356 pub fn needs_18_regs(&self) -> bool {
357 *self >= Version(1, 2)
358 }
Tomás González1f794352025-03-03 16:47:06 +0000359}
360
Tomás González83146af2025-03-04 11:32:41 +0000361impl TryFrom<u32> for Version {
362 type Error = Error;
363
364 fn try_from(val: u32) -> Result<Self, Self::Error> {
365 if (val & Self::MBZ_BITS) != 0 {
366 Err(Error::InvalidVersion(val))
367 } else {
368 Ok(Self((val >> 16) as u16, val as u16))
369 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200370 }
371}
372
373impl From<Version> for u32 {
374 fn from(v: Version) -> Self {
Tomás González83146af2025-03-04 11:32:41 +0000375 let v_u32 = ((v.0 as u32) << 16) | v.1 as u32;
376 assert!(v_u32 & Version::MBZ_BITS == 0);
377 v_u32
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200378 }
379}
380
Andrew Walbran19970ba2024-11-25 15:35:00 +0000381impl Display for Version {
382 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
383 write!(f, "{}.{}", self.0, self.1)
384 }
385}
386
387impl Debug for Version {
388 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
389 Display::fmt(self, f)
390 }
391}
392
Balint Dobszaya5846852025-02-26 15:38:53 +0100393/// Feature IDs used by the `FFA_FEATURES` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100394#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
395#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedFeatureId))]
396#[repr(u8)]
397pub enum FeatureId {
398 NotificationPendingInterrupt = 0x1,
399 ScheduleReceiverInterrupt = 0x2,
400 ManagedExitInterrupt = 0x3,
401}
Balint Dobszayc8802492025-01-15 18:11:39 +0100402
Balint Dobszaya5846852025-02-26 15:38:53 +0100403/// Arguments for the `FFA_FEATURES` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100404#[derive(Debug, Eq, PartialEq, Clone, Copy)]
405pub enum Feature {
406 FuncId(FuncId),
407 FeatureId(FeatureId),
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100408 Unknown(u32),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100409}
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200410
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100411impl From<u32> for Feature {
412 fn from(value: u32) -> Self {
413 // Bit[31] is set for all valid FF-A function IDs so we don't have to check it separately
414 if let Ok(func_id) = value.try_into() {
415 Self::FuncId(func_id)
416 } else if let Ok(feat_id) = (value as u8).try_into() {
417 Self::FeatureId(feat_id)
Balint Dobszay3aad9572025-01-17 16:54:11 +0100418 } else {
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100419 Self::Unknown(value)
420 }
Balint Dobszay3aad9572025-01-17 16:54:11 +0100421 }
422}
423
424impl From<Feature> for u32 {
425 fn from(value: Feature) -> Self {
426 match value {
427 Feature::FuncId(func_id) => (1 << 31) | func_id as u32,
428 Feature::FeatureId(feature_id) => feature_id as u32,
Imre Kis29c8ace2025-04-11 13:49:58 +0200429 Feature::Unknown(id) => id,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100430 }
431 }
432}
433
Imre Kis4e9d8bc2025-04-10 13:48:26 +0200434/// `FFA_FEATURES` specific success argument structure. This type needs further specialization based
435/// on 'FF-A function ID or Feature ID' field of the preceeding `FFA_FEATURES` request.
Imre Kisa9e544c2025-06-13 15:57:54 +0200436#[derive(Debug, Eq, Default, PartialEq, Clone, Copy)]
Imre Kis4e9d8bc2025-04-10 13:48:26 +0200437pub struct SuccessArgsFeatures {
438 pub properties: [u32; 2],
439}
440
441impl From<SuccessArgsFeatures> for SuccessArgs {
442 fn from(value: SuccessArgsFeatures) -> Self {
443 Self::Args32([value.properties[0], value.properties[1], 0, 0, 0, 0])
444 }
445}
446
447impl TryFrom<SuccessArgs> for SuccessArgsFeatures {
448 type Error = Error;
449
450 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
451 let args = value.try_get_args32()?;
452
453 Ok(Self {
454 properties: [args[0], args[1]],
455 })
456 }
457}
458
Balint Dobszaya5846852025-02-26 15:38:53 +0100459/// RXTX buffer descriptor, used by `FFA_RXTX_MAP`.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100460#[derive(Debug, Eq, PartialEq, Clone, Copy)]
461pub enum RxTxAddr {
462 Addr32 { rx: u32, tx: u32 },
463 Addr64 { rx: u64, tx: u64 },
464}
465
Imre Kisbbef2872025-04-10 14:11:29 +0200466/// `FFA_ID_GET` specific success argument structure.
467#[derive(Debug, Eq, PartialEq, Clone, Copy)]
468pub struct SuccessArgsIdGet {
469 pub id: u16,
470}
471
472impl From<SuccessArgsIdGet> for SuccessArgs {
473 fn from(value: SuccessArgsIdGet) -> Self {
474 SuccessArgs::Args32([value.id as u32, 0, 0, 0, 0, 0])
475 }
476}
477
478impl TryFrom<SuccessArgs> for SuccessArgsIdGet {
479 type Error = Error;
480
481 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
482 let args = value.try_get_args32()?;
483 Ok(Self { id: args[0] as u16 })
484 }
485}
486
487/// `FFA_SPM_ID_GET` specific success argument structure.
488#[derive(Debug, Eq, PartialEq, Clone, Copy)]
489pub struct SuccessArgsSpmIdGet {
490 pub id: u16,
491}
492
493impl From<SuccessArgsSpmIdGet> for SuccessArgs {
494 fn from(value: SuccessArgsSpmIdGet) -> Self {
495 SuccessArgs::Args32([value.id as u32, 0, 0, 0, 0, 0])
496 }
497}
498
499impl TryFrom<SuccessArgs> for SuccessArgsSpmIdGet {
500 type Error = Error;
501
502 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
503 let args = value.try_get_args32()?;
504 Ok(Self { id: args[0] as u16 })
505 }
506}
507
Imre Kise295adb2025-04-10 13:26:28 +0200508/// Flags of the `FFA_PARTITION_INFO_GET` interface.
509#[derive(Debug, Eq, PartialEq, Clone, Copy)]
510pub struct PartitionInfoGetFlags {
511 pub count_only: bool,
512}
513
514impl PartitionInfoGetFlags {
515 const RETURN_INFORMATION_TYPE_FLAG: u32 = 1 << 0;
516 const MBZ_BITS: u32 = 0xffff_fffe;
517}
518
519impl TryFrom<u32> for PartitionInfoGetFlags {
520 type Error = Error;
521
522 fn try_from(val: u32) -> Result<Self, Self::Error> {
523 if (val & Self::MBZ_BITS) != 0 {
524 Err(Error::InvalidPartitionInfoGetFlag(val))
525 } else {
526 Ok(Self {
527 count_only: val & Self::RETURN_INFORMATION_TYPE_FLAG != 0,
528 })
529 }
530 }
531}
532
533impl From<PartitionInfoGetFlags> for u32 {
534 fn from(flags: PartitionInfoGetFlags) -> Self {
535 let mut bits: u32 = 0;
536 if flags.count_only {
537 bits |= PartitionInfoGetFlags::RETURN_INFORMATION_TYPE_FLAG;
538 }
539 bits
540 }
541}
542
Imre Kisa2fd69b2025-06-13 13:39:47 +0200543/// Flags field of the FFA_MSG_SEND2 interface.
Imre Kisa9e544c2025-06-13 15:57:54 +0200544#[derive(Debug, Eq, Default, PartialEq, Clone, Copy)]
Imre Kisa2fd69b2025-06-13 13:39:47 +0200545pub struct MsgSend2Flags {
546 pub delay_schedule_receiver: bool,
547}
548
549impl MsgSend2Flags {
550 const DELAY_SCHEDULE_RECEIVER: u32 = 1 << 1;
551 const MBZ_BITS: u32 = 0xffff_fffd;
552}
553
554impl TryFrom<u32> for MsgSend2Flags {
555 type Error = Error;
556
557 fn try_from(val: u32) -> Result<Self, Self::Error> {
558 if (val & Self::MBZ_BITS) != 0 {
559 Err(Error::InvalidMsgSend2Flag(val))
560 } else {
561 Ok(MsgSend2Flags {
562 delay_schedule_receiver: val & Self::DELAY_SCHEDULE_RECEIVER != 0,
563 })
564 }
565 }
566}
567
568impl From<MsgSend2Flags> for u32 {
569 fn from(flags: MsgSend2Flags) -> Self {
570 let mut bits: u32 = 0;
571 if flags.delay_schedule_receiver {
572 bits |= MsgSend2Flags::DELAY_SCHEDULE_RECEIVER;
573 }
574 bits
575 }
576}
577
Tomás González4d5b0ba2025-03-03 17:15:55 +0000578/// Composite type for capturing success and error return codes for the VM availability messages.
579///
580/// Error codes are handled by the `FfaError` type. Having a separate type for errors helps using
581/// `Result<(), FfaError>`. If a single type would include both success and error values,
582/// then `Err(FfaError::Success)` would be incomprehensible.
583#[derive(Debug, Eq, PartialEq, Clone, Copy)]
584pub enum VmAvailabilityStatus {
585 Success,
586 Error(FfaError),
587}
588
589impl TryFrom<i32> for VmAvailabilityStatus {
590 type Error = Error;
591 fn try_from(value: i32) -> Result<Self, <Self as TryFrom<i32>>::Error> {
592 Ok(match value {
593 0 => Self::Success,
594 error_code => Self::Error(FfaError::try_from(error_code)?),
595 })
596 }
597}
598
599impl From<VmAvailabilityStatus> for i32 {
600 fn from(value: VmAvailabilityStatus) -> Self {
601 match value {
602 VmAvailabilityStatus::Success => 0,
603 VmAvailabilityStatus::Error(error_code) => error_code.into(),
604 }
605 }
606}
607
608/// Arguments for the Power Warm Boot `FFA_MSG_SEND_DIRECT_REQ` interface.
609#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
610#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedWarmBootType))]
611#[repr(u32)]
612pub enum WarmBootType {
613 ExitFromSuspend = 0,
614 ExitFromLowPower = 1,
615}
616
Balint Dobszaya5846852025-02-26 15:38:53 +0100617/// Arguments for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}` interfaces.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100618#[derive(Debug, Eq, PartialEq, Clone, Copy)]
619pub enum DirectMsgArgs {
620 Args32([u32; 5]),
621 Args64([u64; 5]),
Tomás González4d5b0ba2025-03-03 17:15:55 +0000622 /// Message for forwarding FFA_VERSION call from Normal world to the SPMC
623 VersionReq {
624 version: Version,
625 },
626 /// Response message to forwarded FFA_VERSION call from the Normal world
627 /// Contains the version returned by the SPMC or None
628 VersionResp {
629 version: Option<Version>,
630 },
631 /// Message for a power management operation initiated by a PSCI function
632 PowerPsciReq32 {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000633 // params[i]: Input parameter in w[i] in PSCI function invocation at EL3.
Tomás González67f92c72025-03-20 16:50:42 +0000634 // params[0]: Function ID.
635 params: [u32; 4],
Tomás González4d5b0ba2025-03-03 17:15:55 +0000636 },
637 /// Message for a power management operation initiated by a PSCI function
638 PowerPsciReq64 {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000639 // params[i]: Input parameter in x[i] in PSCI function invocation at EL3.
Tomás González67f92c72025-03-20 16:50:42 +0000640 // params[0]: Function ID.
641 params: [u64; 4],
Tomás González4d5b0ba2025-03-03 17:15:55 +0000642 },
643 /// Message for a warm boot
644 PowerWarmBootReq {
645 boot_type: WarmBootType,
646 },
647 /// Response message to indicate return status of the last power management request message
648 /// Return error code SUCCESS or DENIED as defined in PSCI spec. Caller is left to do the
649 /// parsing of the return status.
650 PowerPsciResp {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000651 psci_status: i32,
652 },
653 /// Message to signal creation of a VM
654 VmCreated {
655 // Globally unique Handle to identify a memory region that contains IMPLEMENTATION DEFINED
656 // information associated with the created VM.
657 // The invalid memory region handle must be specified by the Hypervisor if this field is not
658 // used.
659 handle: memory_management::Handle,
660 vm_id: u16,
661 },
662 /// Message to acknowledge creation of a VM
663 VmCreatedAck {
664 sp_status: VmAvailabilityStatus,
665 },
666 /// Message to signal destruction of a VM
667 VmDestructed {
668 // Globally unique Handle to identify a memory region that contains IMPLEMENTATION DEFINED
669 // information associated with the created VM.
670 // The invalid memory region handle must be specified by the Hypervisor if this field is not
671 // used.
672 handle: memory_management::Handle,
673 vm_id: u16,
674 },
675 /// Message to acknowledge destruction of a VM
676 VmDestructedAck {
677 sp_status: VmAvailabilityStatus,
678 },
679}
680
681impl DirectMsgArgs {
682 // Flags for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}` interfaces.
683
684 const FWK_MSG_BITS: u32 = 1 << 31;
685 const VERSION_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b1000;
686 const VERSION_RESP: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b1001;
687 const POWER_PSCI_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS;
688 const POWER_WARM_BOOT_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0001;
689 const POWER_PSCI_RESP: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0010;
690 const VM_CREATED: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0100;
691 const VM_CREATED_ACK: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0101;
692 const VM_DESTRUCTED: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0110;
693 const VM_DESTRUCTED_ACK: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0111;
Balint Dobszay3aad9572025-01-17 16:54:11 +0100694}
695
Balint Dobszayde0dc802025-02-28 14:16:52 +0100696/// Arguments for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}2` interfaces.
697#[derive(Debug, Eq, PartialEq, Clone, Copy)]
Imre Kisc739e0e2025-05-30 11:49:25 +0200698pub struct DirectMsg2Args(pub [u64; 14]);
Balint Dobszayde0dc802025-02-28 14:16:52 +0100699
Imre Kisa9e544c2025-06-13 15:57:54 +0200700#[derive(Debug, Default, Eq, PartialEq, Clone, Copy)]
Tomás González092202a2025-03-05 11:56:45 +0000701pub struct MsgWaitFlags {
Imre Kisc739e0e2025-05-30 11:49:25 +0200702 pub retain_rx_buffer: bool,
Tomás González092202a2025-03-05 11:56:45 +0000703}
704
705impl MsgWaitFlags {
706 const RETAIN_RX_BUFFER: u32 = 0x01;
707 const MBZ_BITS: u32 = 0xfffe;
708}
709
710impl TryFrom<u32> for MsgWaitFlags {
711 type Error = Error;
712
713 fn try_from(val: u32) -> Result<Self, Self::Error> {
714 if (val & Self::MBZ_BITS) != 0 {
715 Err(Error::InvalidMsgWaitFlag(val))
716 } else {
717 Ok(MsgWaitFlags {
718 retain_rx_buffer: val & Self::RETAIN_RX_BUFFER != 0,
719 })
720 }
721 }
722}
723
724impl From<MsgWaitFlags> for u32 {
725 fn from(flags: MsgWaitFlags) -> Self {
726 let mut bits: u32 = 0;
727 if flags.retain_rx_buffer {
728 bits |= MsgWaitFlags::RETAIN_RX_BUFFER;
729 }
730 bits
731 }
732}
733
Balint Dobszaya5846852025-02-26 15:38:53 +0100734/// Descriptor for a dynamically allocated memory buffer that contains the memory transaction
Tomás Gonzálezf268e322025-03-05 11:18:11 +0000735/// descriptor.
736///
737/// Used by `FFA_MEM_{DONATE,LEND,SHARE,RETRIEVE_REQ}` interfaces, only when the TX buffer is not
738/// used to transmit the transaction descriptor.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100739#[derive(Debug, Eq, PartialEq, Clone, Copy)]
740pub enum MemOpBuf {
741 Buf32 { addr: u32, page_cnt: u32 },
742 Buf64 { addr: u64, page_cnt: u32 },
743}
744
Balint Dobszaya5846852025-02-26 15:38:53 +0100745/// Memory address argument for `FFA_MEM_PERM_{GET,SET}` interfaces.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100746#[derive(Debug, Eq, PartialEq, Clone, Copy)]
747pub enum MemAddr {
748 Addr32(u32),
749 Addr64(u64),
750}
751
Balint Dobszay5cc04d62025-06-16 13:22:15 +0200752impl MemAddr {
753 /// Returns the contained address.
754 pub fn address(&self) -> u64 {
755 match self {
756 MemAddr::Addr32(a) => (*a).into(),
757 MemAddr::Addr64(a) => *a,
758 }
759 }
760}
761
Balint Dobszayde0dc802025-02-28 14:16:52 +0100762/// Argument for the `FFA_CONSOLE_LOG` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100763#[derive(Debug, Eq, PartialEq, Clone, Copy)]
764pub enum ConsoleLogChars {
Imre Kis189f18c2025-05-26 19:33:05 +0200765 Chars32(ConsoleLogChars32),
766 Chars64(ConsoleLogChars64),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100767}
768
Imre Kis189f18c2025-05-26 19:33:05 +0200769/// Generic type for storing `FFA_CONSOLE_LOG` character payload and its length in bytes.
770#[derive(Debug, Default, Eq, PartialEq, Clone, Copy)]
771pub struct LogChars<T>
772where
773 T: IntoBytes + FromBytes + Immutable,
774{
775 char_cnt: u8,
776 char_lists: T,
777}
778
779impl<T> LogChars<T>
780where
781 T: IntoBytes + FromBytes + Immutable,
782{
783 const MAX_LENGTH: u8 = core::mem::size_of::<T>() as u8;
784
785 /// Returns true if there are no characters in the structure.
786 pub fn empty(&self) -> bool {
787 self.char_cnt == 0
788 }
789
790 /// Returns true if the structure is full.
791 pub fn full(&self) -> bool {
792 self.char_cnt as usize >= core::mem::size_of::<T>()
793 }
794
795 /// Returns the payload bytes.
796 pub fn bytes(&self) -> &[u8] {
797 &self.char_lists.as_bytes()[..self.char_cnt as usize]
798 }
799
800 /// Append byte slice to the end of the characters.
801 pub fn push(&mut self, source: &[u8]) -> usize {
802 let empty_area = &mut self.char_lists.as_mut_bytes()[self.char_cnt.into()..];
803 let len = empty_area.len().min(source.len());
804
805 empty_area[..len].copy_from_slice(&source[..len]);
806 self.char_cnt += len as u8;
807
808 len
809 }
810}
811
812/// Specialized type for 32-bit `FFA_CONSOLE_LOG` payload.
813pub type ConsoleLogChars32 = LogChars<[u32; 6]>;
814
815/// Specialized type for 64-bit `FFA_CONSOLE_LOG` payload.
816pub type ConsoleLogChars64 = LogChars<[u64; 16]>;
817
Tomás González7ffb6132025-04-03 12:28:58 +0100818#[derive(Debug, Eq, PartialEq, Clone, Copy)]
819pub struct NotificationBindFlags {
Imre Kisc739e0e2025-05-30 11:49:25 +0200820 pub per_vcpu_notification: bool,
Tomás González7ffb6132025-04-03 12:28:58 +0100821}
822
823impl NotificationBindFlags {
824 const PER_VCPU_NOTIFICATION: u32 = 1;
825}
826
827impl From<NotificationBindFlags> for u32 {
828 fn from(flags: NotificationBindFlags) -> Self {
829 let mut bits: u32 = 0;
830 if flags.per_vcpu_notification {
831 bits |= NotificationBindFlags::PER_VCPU_NOTIFICATION;
832 }
833 bits
834 }
835}
836
837impl From<u32> for NotificationBindFlags {
838 fn from(flags: u32) -> Self {
839 Self {
840 per_vcpu_notification: flags & Self::PER_VCPU_NOTIFICATION != 0,
841 }
842 }
843}
844
845#[derive(Debug, Eq, PartialEq, Clone, Copy)]
846pub struct NotificationSetFlags {
Imre Kisc739e0e2025-05-30 11:49:25 +0200847 pub delay_schedule_receiver: bool,
848 pub vcpu_id: Option<u16>,
Tomás González7ffb6132025-04-03 12:28:58 +0100849}
850
851impl NotificationSetFlags {
852 const PER_VCP_NOTIFICATION: u32 = 1 << 0;
853 const DELAY_SCHEDULE_RECEIVER: u32 = 1 << 1;
854 const VCPU_ID_SHIFT: u32 = 16;
855
856 const MBZ_BITS: u32 = 0xfffc;
857}
858
859impl From<NotificationSetFlags> for u32 {
860 fn from(flags: NotificationSetFlags) -> Self {
861 let mut bits: u32 = 0;
862
863 if flags.delay_schedule_receiver {
864 bits |= NotificationSetFlags::DELAY_SCHEDULE_RECEIVER;
865 }
866 if let Some(vcpu_id) = flags.vcpu_id {
867 bits |= NotificationSetFlags::PER_VCP_NOTIFICATION;
868 bits |= u32::from(vcpu_id) << NotificationSetFlags::VCPU_ID_SHIFT;
869 }
870
871 bits
872 }
873}
874
875impl TryFrom<u32> for NotificationSetFlags {
876 type Error = Error;
877
878 fn try_from(flags: u32) -> Result<Self, Self::Error> {
879 if (flags & Self::MBZ_BITS) != 0 {
880 return Err(Error::InvalidNotificationSetFlag(flags));
881 }
882
883 let tentative_vcpu_id = (flags >> Self::VCPU_ID_SHIFT) as u16;
884
885 let vcpu_id = if (flags & Self::PER_VCP_NOTIFICATION) != 0 {
886 Some(tentative_vcpu_id)
887 } else {
888 if tentative_vcpu_id != 0 {
889 return Err(Error::InvalidNotificationSetFlag(flags));
890 }
891 None
892 };
893
894 Ok(Self {
895 delay_schedule_receiver: (flags & Self::DELAY_SCHEDULE_RECEIVER) != 0,
896 vcpu_id,
897 })
898 }
899}
900
901#[derive(Debug, Eq, PartialEq, Clone, Copy)]
902pub struct NotificationGetFlags {
Imre Kisc739e0e2025-05-30 11:49:25 +0200903 pub sp_bitmap_id: bool,
904 pub vm_bitmap_id: bool,
905 pub spm_bitmap_id: bool,
906 pub hyp_bitmap_id: bool,
Tomás González7ffb6132025-04-03 12:28:58 +0100907}
908
909impl NotificationGetFlags {
910 const SP_BITMAP_ID: u32 = 1;
911 const VM_BITMAP_ID: u32 = 1 << 1;
912 const SPM_BITMAP_ID: u32 = 1 << 2;
913 const HYP_BITMAP_ID: u32 = 1 << 3;
914}
915
916impl From<NotificationGetFlags> for u32 {
917 fn from(flags: NotificationGetFlags) -> Self {
918 let mut bits: u32 = 0;
919 if flags.sp_bitmap_id {
920 bits |= NotificationGetFlags::SP_BITMAP_ID;
921 }
922 if flags.vm_bitmap_id {
923 bits |= NotificationGetFlags::VM_BITMAP_ID;
924 }
925 if flags.spm_bitmap_id {
926 bits |= NotificationGetFlags::SPM_BITMAP_ID;
927 }
928 if flags.hyp_bitmap_id {
929 bits |= NotificationGetFlags::HYP_BITMAP_ID;
930 }
931 bits
932 }
933}
934
935impl From<u32> for NotificationGetFlags {
936 // This is a "from" instead of a "try_from" because Reserved Bits are SBZ, *not* MBZ.
937 fn from(flags: u32) -> Self {
938 Self {
939 sp_bitmap_id: (flags & Self::SP_BITMAP_ID) != 0,
940 vm_bitmap_id: (flags & Self::VM_BITMAP_ID) != 0,
941 spm_bitmap_id: (flags & Self::SPM_BITMAP_ID) != 0,
942 hyp_bitmap_id: (flags & Self::HYP_BITMAP_ID) != 0,
943 }
944 }
945}
946
Imre Kis9959e062025-04-10 14:16:10 +0200947/// `FFA_NOTIFICATION_GET` specific success argument structure.
948#[derive(Debug, Eq, PartialEq, Clone, Copy)]
949pub struct SuccessArgsNotificationGet {
950 pub sp_notifications: Option<u64>,
951 pub vm_notifications: Option<u64>,
952 pub spm_notifications: Option<u32>,
953 pub hypervisor_notifications: Option<u32>,
954}
955
956impl From<SuccessArgsNotificationGet> for SuccessArgs {
957 fn from(value: SuccessArgsNotificationGet) -> Self {
958 let mut args = [0; 6];
959
960 if let Some(bitmap) = value.sp_notifications {
961 args[0] = bitmap as u32;
962 args[1] = (bitmap >> 32) as u32;
963 }
964
965 if let Some(bitmap) = value.vm_notifications {
966 args[2] = bitmap as u32;
967 args[3] = (bitmap >> 32) as u32;
968 }
969
970 if let Some(bitmap) = value.spm_notifications {
971 args[4] = bitmap;
972 }
973
974 if let Some(bitmap) = value.hypervisor_notifications {
975 args[5] = bitmap;
976 }
977
978 Self::Args32(args)
979 }
980}
981
982impl TryFrom<(NotificationGetFlags, SuccessArgs)> for SuccessArgsNotificationGet {
983 type Error = Error;
984
985 fn try_from(value: (NotificationGetFlags, SuccessArgs)) -> Result<Self, Self::Error> {
986 let (flags, value) = value;
987 let args = value.try_get_args32()?;
988
989 let sp_notifications = if flags.sp_bitmap_id {
990 Some(u64::from(args[0]) | (u64::from(args[1]) << 32))
991 } else {
992 None
993 };
994
995 let vm_notifications = if flags.vm_bitmap_id {
996 Some(u64::from(args[2]) | (u64::from(args[3]) << 32))
997 } else {
998 None
999 };
1000
1001 let spm_notifications = if flags.spm_bitmap_id {
1002 Some(args[4])
1003 } else {
1004 None
1005 };
1006
1007 let hypervisor_notifications = if flags.hyp_bitmap_id {
1008 Some(args[5])
1009 } else {
1010 None
1011 };
1012
1013 Ok(Self {
1014 sp_notifications,
1015 vm_notifications,
1016 spm_notifications,
1017 hypervisor_notifications,
1018 })
1019 }
1020}
Imre Kis787c5002025-04-10 14:25:51 +02001021
1022/// `FFA_NOTIFICATION_INFO_GET` specific success argument structure. The `MAX_COUNT` parameter
1023/// depends on the 32-bit or 64-bit packing.
1024#[derive(Debug, Eq, PartialEq, Clone, Copy)]
1025pub struct SuccessArgsNotificationInfoGet<const MAX_COUNT: usize> {
1026 pub more_pending_notifications: bool,
1027 list_count: usize,
1028 id_counts: [u8; MAX_COUNT],
1029 ids: [u16; MAX_COUNT],
1030}
1031
1032impl<const MAX_COUNT: usize> Default for SuccessArgsNotificationInfoGet<MAX_COUNT> {
1033 fn default() -> Self {
1034 Self {
1035 more_pending_notifications: false,
1036 list_count: 0,
1037 id_counts: [0; MAX_COUNT],
1038 ids: [0; MAX_COUNT],
1039 }
1040 }
1041}
1042
1043impl<const MAX_COUNT: usize> SuccessArgsNotificationInfoGet<MAX_COUNT> {
1044 const MORE_PENDING_NOTIFICATIONS_FLAG: u64 = 1 << 0;
1045 const LIST_COUNT_SHIFT: usize = 7;
1046 const LIST_COUNT_MASK: u64 = 0x1f;
1047 const ID_COUNT_SHIFT: usize = 12;
1048 const ID_COUNT_MASK: u64 = 0x03;
1049 const ID_COUNT_BITS: usize = 2;
1050
1051 pub fn add_list(&mut self, endpoint: u16, vcpu_ids: &[u16]) -> Result<(), Error> {
1052 if self.list_count >= MAX_COUNT || vcpu_ids.len() > Self::ID_COUNT_MASK as usize {
1053 return Err(Error::InvalidNotificationCount);
1054 }
1055
1056 // Each list contains at least one ID: the partition ID, followed by vCPU IDs. The number
1057 // of vCPU IDs is recorded in `id_counts`.
1058 let mut current_id_index = self.list_count + self.id_counts.iter().sum::<u8>() as usize;
1059 if current_id_index + 1 + vcpu_ids.len() > MAX_COUNT {
1060 // The new list does not fit into the available space for IDs.
1061 return Err(Error::InvalidNotificationCount);
1062 }
1063
1064 self.id_counts[self.list_count] = vcpu_ids.len() as u8;
1065 self.list_count += 1;
1066
1067 // The first ID is the endpoint ID.
1068 self.ids[current_id_index] = endpoint;
1069 current_id_index += 1;
1070
1071 // Insert the vCPU IDs.
1072 self.ids[current_id_index..current_id_index + vcpu_ids.len()].copy_from_slice(vcpu_ids);
1073
1074 Ok(())
1075 }
1076
1077 pub fn iter(&self) -> NotificationInfoGetIterator<'_> {
1078 NotificationInfoGetIterator {
1079 list_index: 0,
1080 id_index: 0,
1081 id_count: &self.id_counts[0..self.list_count],
1082 ids: &self.ids,
1083 }
1084 }
1085
1086 /// Pack flags field and IDs.
1087 fn pack(self) -> (u64, [u16; MAX_COUNT]) {
1088 let mut flags = if self.more_pending_notifications {
1089 Self::MORE_PENDING_NOTIFICATIONS_FLAG
1090 } else {
1091 0
1092 };
1093
1094 flags |= (self.list_count as u64) << Self::LIST_COUNT_SHIFT;
1095 for (count, shift) in self.id_counts.iter().take(self.list_count).zip(
1096 (Self::ID_COUNT_SHIFT..Self::ID_COUNT_SHIFT + Self::ID_COUNT_BITS * MAX_COUNT)
1097 .step_by(Self::ID_COUNT_BITS),
1098 ) {
1099 flags |= u64::from(*count) << shift;
1100 }
1101
1102 (flags, self.ids)
1103 }
1104
1105 /// Unpack flags field and IDs.
1106 fn unpack(flags: u64, ids: [u16; MAX_COUNT]) -> Result<Self, Error> {
1107 let count_of_lists = ((flags >> Self::LIST_COUNT_SHIFT) & Self::LIST_COUNT_MASK) as usize;
1108
1109 if count_of_lists > MAX_COUNT {
1110 return Err(Error::InvalidNotificationCount);
1111 }
1112
1113 let mut count_of_ids = [0; MAX_COUNT];
1114 let mut count_of_ids_bits = flags >> Self::ID_COUNT_SHIFT;
1115
1116 for id in count_of_ids.iter_mut().take(count_of_lists) {
1117 *id = (count_of_ids_bits & Self::ID_COUNT_MASK) as u8;
1118 count_of_ids_bits >>= Self::ID_COUNT_BITS;
1119 }
1120
Imre Kis7846c9f2025-04-15 09:45:00 +02001121 let id_field_count = count_of_lists + count_of_ids.iter().sum::<u8>() as usize;
1122 if id_field_count > MAX_COUNT {
1123 return Err(Error::InvalidNotificationCount);
1124 }
1125
Imre Kis787c5002025-04-10 14:25:51 +02001126 Ok(Self {
1127 more_pending_notifications: (flags & Self::MORE_PENDING_NOTIFICATIONS_FLAG) != 0,
1128 list_count: count_of_lists,
1129 id_counts: count_of_ids,
1130 ids,
1131 })
1132 }
1133}
1134
1135/// `FFA_NOTIFICATION_INFO_GET_32` specific success argument structure.
1136pub type SuccessArgsNotificationInfoGet32 = SuccessArgsNotificationInfoGet<10>;
1137
1138impl From<SuccessArgsNotificationInfoGet32> for SuccessArgs {
1139 fn from(value: SuccessArgsNotificationInfoGet32) -> Self {
1140 let (flags, ids) = value.pack();
1141 let id_regs: [u32; 5] = transmute!(ids);
1142
1143 let mut args = [0; 6];
1144 args[0] = flags as u32;
1145 args[1..6].copy_from_slice(&id_regs);
1146
1147 SuccessArgs::Args32(args)
1148 }
1149}
1150
1151impl TryFrom<SuccessArgs> for SuccessArgsNotificationInfoGet32 {
1152 type Error = Error;
1153
1154 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
1155 let args = value.try_get_args32()?;
1156 let flags = args[0].into();
1157 let id_regs: [u32; 5] = args[1..6].try_into().unwrap();
1158 Self::unpack(flags, transmute!(id_regs))
1159 }
1160}
1161
1162/// `FFA_NOTIFICATION_INFO_GET_64` specific success argument structure.
1163pub type SuccessArgsNotificationInfoGet64 = SuccessArgsNotificationInfoGet<20>;
1164
1165impl From<SuccessArgsNotificationInfoGet64> for SuccessArgs {
1166 fn from(value: SuccessArgsNotificationInfoGet64) -> Self {
1167 let (flags, ids) = value.pack();
1168 let id_regs: [u64; 5] = transmute!(ids);
1169
1170 let mut args = [0; 6];
1171 args[0] = flags;
1172 args[1..6].copy_from_slice(&id_regs);
1173
1174 SuccessArgs::Args64(args)
1175 }
1176}
1177
1178impl TryFrom<SuccessArgs> for SuccessArgsNotificationInfoGet64 {
1179 type Error = Error;
1180
1181 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
1182 let args = value.try_get_args64()?;
1183 let flags = args[0];
1184 let id_regs: [u64; 5] = args[1..6].try_into().unwrap();
1185 Self::unpack(flags, transmute!(id_regs))
1186 }
1187}
1188
1189pub struct NotificationInfoGetIterator<'a> {
1190 list_index: usize,
1191 id_index: usize,
1192 id_count: &'a [u8],
1193 ids: &'a [u16],
1194}
1195
1196impl<'a> Iterator for NotificationInfoGetIterator<'a> {
1197 type Item = (u16, &'a [u16]);
1198
1199 fn next(&mut self) -> Option<Self::Item> {
1200 if self.list_index < self.id_count.len() {
1201 let partition_id = self.ids[self.id_index];
1202 let id_range =
1203 (self.id_index + 1)..=(self.id_index + self.id_count[self.list_index] as usize);
1204
1205 self.id_index += 1 + self.id_count[self.list_index] as usize;
1206 self.list_index += 1;
1207
1208 Some((partition_id, &self.ids[id_range]))
1209 } else {
1210 None
1211 }
1212 }
1213}
1214
Tomás Gonzálezf268e322025-03-05 11:18:11 +00001215/// FF-A "message types", the terminology used by the spec is "interfaces".
1216///
1217/// The interfaces are used by FF-A components for communication at an FF-A instance. The spec also
1218/// describes the valid FF-A instances and conduits for each interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +01001219#[derive(Debug, Eq, PartialEq, Clone, Copy)]
1220pub enum Interface {
1221 Error {
1222 target_info: TargetInfo,
1223 error_code: FfaError,
Balint Dobszayb727aab2025-04-07 10:24:59 +02001224 error_arg: u32,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001225 },
1226 Success {
Imre Kise521a282025-06-13 13:29:24 +02001227 target_info: TargetInfo,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001228 args: SuccessArgs,
1229 },
1230 Interrupt {
1231 target_info: TargetInfo,
1232 interrupt_id: u32,
1233 },
1234 Version {
1235 input_version: Version,
1236 },
1237 VersionOut {
1238 output_version: Version,
1239 },
1240 Features {
1241 feat_id: Feature,
1242 input_properties: u32,
1243 },
1244 RxAcquire {
1245 vm_id: u16,
1246 },
1247 RxRelease {
1248 vm_id: u16,
1249 },
1250 RxTxMap {
1251 addr: RxTxAddr,
1252 page_cnt: u32,
1253 },
1254 RxTxUnmap {
1255 id: u16,
1256 },
1257 PartitionInfoGet {
1258 uuid: Uuid,
Imre Kise295adb2025-04-10 13:26:28 +02001259 flags: PartitionInfoGetFlags,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001260 },
Tomás González0a058bc2025-03-11 11:20:55 +00001261 PartitionInfoGetRegs {
1262 uuid: Uuid,
1263 start_index: u16,
1264 info_tag: u16,
1265 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001266 IdGet,
1267 SpmIdGet,
Tomás González092202a2025-03-05 11:56:45 +00001268 MsgWait {
1269 flags: Option<MsgWaitFlags>,
1270 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001271 Yield,
1272 Run {
1273 target_info: TargetInfo,
1274 },
1275 NormalWorldResume,
Tomás González17b92442025-03-10 16:45:04 +00001276 SecondaryEpRegister {
1277 entrypoint: SecondaryEpRegisterAddr,
1278 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001279 MsgSend2 {
1280 sender_vm_id: u16,
Imre Kisa2fd69b2025-06-13 13:39:47 +02001281 flags: MsgSend2Flags,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001282 },
1283 MsgSendDirectReq {
1284 src_id: u16,
1285 dst_id: u16,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001286 args: DirectMsgArgs,
1287 },
1288 MsgSendDirectResp {
1289 src_id: u16,
1290 dst_id: u16,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001291 args: DirectMsgArgs,
1292 },
Balint Dobszayde0dc802025-02-28 14:16:52 +01001293 MsgSendDirectReq2 {
1294 src_id: u16,
1295 dst_id: u16,
1296 uuid: Uuid,
1297 args: DirectMsg2Args,
1298 },
1299 MsgSendDirectResp2 {
1300 src_id: u16,
1301 dst_id: u16,
1302 args: DirectMsg2Args,
1303 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001304 MemDonate {
1305 total_len: u32,
1306 frag_len: u32,
1307 buf: Option<MemOpBuf>,
1308 },
1309 MemLend {
1310 total_len: u32,
1311 frag_len: u32,
1312 buf: Option<MemOpBuf>,
1313 },
1314 MemShare {
1315 total_len: u32,
1316 frag_len: u32,
1317 buf: Option<MemOpBuf>,
1318 },
1319 MemRetrieveReq {
1320 total_len: u32,
1321 frag_len: u32,
1322 buf: Option<MemOpBuf>,
1323 },
1324 MemRetrieveResp {
1325 total_len: u32,
1326 frag_len: u32,
1327 },
1328 MemRelinquish,
1329 MemReclaim {
1330 handle: memory_management::Handle,
Imre Kis356395d2025-06-13 13:49:06 +02001331 flags: memory_management::MemReclaimFlags,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001332 },
1333 MemPermGet {
1334 addr: MemAddr,
Balint Dobszayde0dc802025-02-28 14:16:52 +01001335 page_cnt: Option<u32>,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001336 },
1337 MemPermSet {
1338 addr: MemAddr,
1339 page_cnt: u32,
Imre Kisdcb7df22025-06-06 15:24:40 +02001340 mem_perm: memory_management::MemPermissionsGetSet,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001341 },
1342 ConsoleLog {
Imre Kis189f18c2025-05-26 19:33:05 +02001343 chars: ConsoleLogChars,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001344 },
Tomás González7ffb6132025-04-03 12:28:58 +01001345 NotificationBitmapCreate {
1346 vm_id: u16,
1347 vcpu_cnt: u32,
1348 },
1349 NotificationBitmapDestroy {
1350 vm_id: u16,
1351 },
1352 NotificationBind {
1353 sender_id: u16,
1354 receiver_id: u16,
1355 flags: NotificationBindFlags,
1356 bitmap: u64,
1357 },
Imre Kis3571f2c2025-05-26 19:29:23 +02001358 NotificationUnbind {
Tomás González7ffb6132025-04-03 12:28:58 +01001359 sender_id: u16,
1360 receiver_id: u16,
1361 bitmap: u64,
1362 },
1363 NotificationSet {
1364 sender_id: u16,
1365 receiver_id: u16,
1366 flags: NotificationSetFlags,
1367 bitmap: u64,
1368 },
1369 NotificationGet {
1370 vcpu_id: u16,
1371 endpoint_id: u16,
1372 flags: NotificationGetFlags,
1373 },
1374 NotificationInfoGet {
1375 is_32bit: bool,
1376 },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01001377 El3IntrHandle,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001378}
1379
Balint Dobszayde0dc802025-02-28 14:16:52 +01001380impl Interface {
1381 /// Returns the function ID for the call, if it has one.
1382 pub fn function_id(&self) -> Option<FuncId> {
1383 match self {
1384 Interface::Error { .. } => Some(FuncId::Error),
1385 Interface::Success { args, .. } => match args {
Imre Kis54773b62025-04-10 13:47:39 +02001386 SuccessArgs::Args32(..) => Some(FuncId::Success32),
1387 SuccessArgs::Args64(..) | SuccessArgs::Args64_2(..) => Some(FuncId::Success64),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001388 },
1389 Interface::Interrupt { .. } => Some(FuncId::Interrupt),
1390 Interface::Version { .. } => Some(FuncId::Version),
1391 Interface::VersionOut { .. } => None,
1392 Interface::Features { .. } => Some(FuncId::Features),
1393 Interface::RxAcquire { .. } => Some(FuncId::RxAcquire),
1394 Interface::RxRelease { .. } => Some(FuncId::RxRelease),
1395 Interface::RxTxMap { addr, .. } => match addr {
1396 RxTxAddr::Addr32 { .. } => Some(FuncId::RxTxMap32),
1397 RxTxAddr::Addr64 { .. } => Some(FuncId::RxTxMap64),
1398 },
1399 Interface::RxTxUnmap { .. } => Some(FuncId::RxTxUnmap),
1400 Interface::PartitionInfoGet { .. } => Some(FuncId::PartitionInfoGet),
Tomás González0a058bc2025-03-11 11:20:55 +00001401 Interface::PartitionInfoGetRegs { .. } => Some(FuncId::PartitionInfoGetRegs),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001402 Interface::IdGet => Some(FuncId::IdGet),
1403 Interface::SpmIdGet => Some(FuncId::SpmIdGet),
Tomás González092202a2025-03-05 11:56:45 +00001404 Interface::MsgWait { .. } => Some(FuncId::MsgWait),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001405 Interface::Yield => Some(FuncId::Yield),
1406 Interface::Run { .. } => Some(FuncId::Run),
1407 Interface::NormalWorldResume => Some(FuncId::NormalWorldResume),
Tomás González17b92442025-03-10 16:45:04 +00001408 Interface::SecondaryEpRegister { entrypoint } => match entrypoint {
1409 SecondaryEpRegisterAddr::Addr32 { .. } => Some(FuncId::SecondaryEpRegister32),
1410 SecondaryEpRegisterAddr::Addr64 { .. } => Some(FuncId::SecondaryEpRegister64),
1411 },
Balint Dobszayde0dc802025-02-28 14:16:52 +01001412 Interface::MsgSend2 { .. } => Some(FuncId::MsgSend2),
1413 Interface::MsgSendDirectReq { args, .. } => match args {
1414 DirectMsgArgs::Args32(_) => Some(FuncId::MsgSendDirectReq32),
1415 DirectMsgArgs::Args64(_) => Some(FuncId::MsgSendDirectReq64),
Tomás González4d5b0ba2025-03-03 17:15:55 +00001416 DirectMsgArgs::VersionReq { .. } => Some(FuncId::MsgSendDirectReq32),
1417 DirectMsgArgs::PowerPsciReq32 { .. } => Some(FuncId::MsgSendDirectReq32),
1418 DirectMsgArgs::PowerPsciReq64 { .. } => Some(FuncId::MsgSendDirectReq64),
1419 DirectMsgArgs::PowerWarmBootReq { .. } => Some(FuncId::MsgSendDirectReq32),
1420 DirectMsgArgs::VmCreated { .. } => Some(FuncId::MsgSendDirectReq32),
1421 DirectMsgArgs::VmDestructed { .. } => Some(FuncId::MsgSendDirectReq32),
Balint Dobszay3c1c89a2025-04-25 17:36:46 +02001422 _ => panic!("Invalid direct request arguments: {:#?}", args),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001423 },
1424 Interface::MsgSendDirectResp { args, .. } => match args {
1425 DirectMsgArgs::Args32(_) => Some(FuncId::MsgSendDirectResp32),
1426 DirectMsgArgs::Args64(_) => Some(FuncId::MsgSendDirectResp64),
Tomás González4d5b0ba2025-03-03 17:15:55 +00001427 DirectMsgArgs::VersionResp { .. } => Some(FuncId::MsgSendDirectResp32),
1428 DirectMsgArgs::PowerPsciResp { .. } => Some(FuncId::MsgSendDirectResp32),
1429 DirectMsgArgs::VmCreatedAck { .. } => Some(FuncId::MsgSendDirectResp32),
1430 DirectMsgArgs::VmDestructedAck { .. } => Some(FuncId::MsgSendDirectResp32),
Balint Dobszay3c1c89a2025-04-25 17:36:46 +02001431 _ => panic!("Invalid direct response arguments: {:#?}", args),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001432 },
1433 Interface::MsgSendDirectReq2 { .. } => Some(FuncId::MsgSendDirectReq64_2),
1434 Interface::MsgSendDirectResp2 { .. } => Some(FuncId::MsgSendDirectResp64_2),
1435 Interface::MemDonate { buf, .. } => match buf {
1436 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemDonate64),
1437 _ => Some(FuncId::MemDonate32),
1438 },
1439 Interface::MemLend { buf, .. } => match buf {
1440 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemLend64),
1441 _ => Some(FuncId::MemLend32),
1442 },
1443 Interface::MemShare { buf, .. } => match buf {
1444 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemShare64),
1445 _ => Some(FuncId::MemShare32),
1446 },
1447 Interface::MemRetrieveReq { buf, .. } => match buf {
1448 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemRetrieveReq64),
1449 _ => Some(FuncId::MemRetrieveReq32),
1450 },
1451 Interface::MemRetrieveResp { .. } => Some(FuncId::MemRetrieveResp),
1452 Interface::MemRelinquish => Some(FuncId::MemRelinquish),
1453 Interface::MemReclaim { .. } => Some(FuncId::MemReclaim),
1454 Interface::MemPermGet { addr, .. } => match addr {
1455 MemAddr::Addr32(_) => Some(FuncId::MemPermGet32),
1456 MemAddr::Addr64(_) => Some(FuncId::MemPermGet64),
1457 },
1458 Interface::MemPermSet { addr, .. } => match addr {
1459 MemAddr::Addr32(_) => Some(FuncId::MemPermSet32),
1460 MemAddr::Addr64(_) => Some(FuncId::MemPermSet64),
1461 },
Imre Kis189f18c2025-05-26 19:33:05 +02001462 Interface::ConsoleLog { chars, .. } => match chars {
1463 ConsoleLogChars::Chars32(_) => Some(FuncId::ConsoleLog32),
1464 ConsoleLogChars::Chars64(_) => Some(FuncId::ConsoleLog64),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001465 },
Tomás González7ffb6132025-04-03 12:28:58 +01001466 Interface::NotificationBitmapCreate { .. } => Some(FuncId::NotificationBitmapCreate),
1467 Interface::NotificationBitmapDestroy { .. } => Some(FuncId::NotificationBitmapDestroy),
1468 Interface::NotificationBind { .. } => Some(FuncId::NotificationBind),
Imre Kis3571f2c2025-05-26 19:29:23 +02001469 Interface::NotificationUnbind { .. } => Some(FuncId::NotificationUnbind),
Tomás González7ffb6132025-04-03 12:28:58 +01001470 Interface::NotificationSet { .. } => Some(FuncId::NotificationSet),
1471 Interface::NotificationGet { .. } => Some(FuncId::NotificationGet),
1472 Interface::NotificationInfoGet { is_32bit } => match is_32bit {
1473 true => Some(FuncId::NotificationInfoGet32),
1474 false => Some(FuncId::NotificationInfoGet64),
1475 },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01001476 Interface::El3IntrHandle => Some(FuncId::El3IntrHandle),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001477 }
1478 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01001479
Balint Dobszayde0dc802025-02-28 14:16:52 +01001480 /// Returns true if this is a 32-bit call, or false if it is a 64-bit call.
1481 pub fn is_32bit(&self) -> bool {
Balint Dobszay3c1c89a2025-04-25 17:36:46 +02001482 if matches!(self, Self::VersionOut { .. }) {
1483 return true;
1484 }
1485
Balint Dobszayde0dc802025-02-28 14:16:52 +01001486 self.function_id().unwrap().is_32bit()
1487 }
1488
Balint Dobszay82c71dd2025-04-15 10:16:44 +02001489 /// Returns the FF-A version that has introduced the function ID.
1490 pub fn minimum_ffa_version(&self) -> Version {
Balint Dobszay3c1c89a2025-04-25 17:36:46 +02001491 if matches!(self, Self::VersionOut { .. }) {
1492 return Version(1, 0);
1493 }
1494
Balint Dobszay82c71dd2025-04-15 10:16:44 +02001495 self.function_id().unwrap().minimum_ffa_version()
1496 }
1497
Balint Dobszayde0dc802025-02-28 14:16:52 +01001498 /// Parse interface from register contents. The caller must ensure that the `regs` argument has
1499 /// the correct length: 8 registers for FF-A v1.1 and lower, 18 registers for v1.2 and higher.
1500 pub fn from_regs(version: Version, regs: &[u64]) -> Result<Self, Error> {
Balint Dobszay82c71dd2025-04-15 10:16:44 +02001501 let func_id = FuncId::try_from(regs[0] as u32)?;
1502 if version < func_id.minimum_ffa_version() {
1503 return Err(Error::InvalidVersionForFunctionId(version, func_id));
1504 }
1505
Balint Dobszayde0dc802025-02-28 14:16:52 +01001506 let reg_cnt = regs.len();
1507
1508 let msg = match reg_cnt {
1509 8 => {
1510 assert!(version <= Version(1, 1));
1511 Interface::unpack_regs8(version, regs.try_into().unwrap())?
1512 }
1513 18 => {
1514 assert!(version >= Version(1, 2));
Balint Dobszay82c71dd2025-04-15 10:16:44 +02001515 match func_id {
Balint Dobszayde0dc802025-02-28 14:16:52 +01001516 FuncId::ConsoleLog64
1517 | FuncId::Success64
1518 | FuncId::MsgSendDirectReq64_2
Tomás González0a058bc2025-03-11 11:20:55 +00001519 | FuncId::MsgSendDirectResp64_2
1520 | FuncId::PartitionInfoGetRegs => {
Balint Dobszayde0dc802025-02-28 14:16:52 +01001521 Interface::unpack_regs18(version, regs.try_into().unwrap())?
1522 }
1523 _ => Interface::unpack_regs8(version, regs[..8].try_into().unwrap())?,
1524 }
1525 }
1526 _ => panic!(
1527 "Invalid number of registers ({}) for FF-A version {}",
1528 reg_cnt, version
1529 ),
1530 };
1531
1532 Ok(msg)
1533 }
1534
1535 fn unpack_regs8(version: Version, regs: &[u64; 8]) -> Result<Self, Error> {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001536 let fid = FuncId::try_from(regs[0] as u32)?;
1537
1538 let msg = match fid {
1539 FuncId::Error => Self::Error {
1540 target_info: (regs[1] as u32).into(),
1541 error_code: FfaError::try_from(regs[2] as i32)?,
Balint Dobszayb727aab2025-04-07 10:24:59 +02001542 error_arg: regs[3] as u32,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001543 },
1544 FuncId::Success32 => Self::Success {
Imre Kise521a282025-06-13 13:29:24 +02001545 target_info: (regs[1] as u32).into(),
Imre Kis54773b62025-04-10 13:47:39 +02001546 args: SuccessArgs::Args32([
Balint Dobszay3aad9572025-01-17 16:54:11 +01001547 regs[2] as u32,
1548 regs[3] as u32,
1549 regs[4] as u32,
1550 regs[5] as u32,
1551 regs[6] as u32,
1552 regs[7] as u32,
1553 ]),
1554 },
1555 FuncId::Success64 => Self::Success {
Imre Kise521a282025-06-13 13:29:24 +02001556 target_info: (regs[1] as u32).into(),
Imre Kis54773b62025-04-10 13:47:39 +02001557 args: SuccessArgs::Args64([regs[2], regs[3], regs[4], regs[5], regs[6], regs[7]]),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001558 },
1559 FuncId::Interrupt => Self::Interrupt {
1560 target_info: (regs[1] as u32).into(),
1561 interrupt_id: regs[2] as u32,
1562 },
1563 FuncId::Version => Self::Version {
Tomás González83146af2025-03-04 11:32:41 +00001564 input_version: (regs[1] as u32).try_into()?,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001565 },
1566 FuncId::Features => Self::Features {
Balint Dobszayc31e0b92025-03-03 20:16:56 +01001567 feat_id: (regs[1] as u32).into(),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001568 input_properties: regs[2] as u32,
1569 },
1570 FuncId::RxAcquire => Self::RxAcquire {
1571 vm_id: regs[1] as u16,
1572 },
1573 FuncId::RxRelease => Self::RxRelease {
1574 vm_id: regs[1] as u16,
1575 },
1576 FuncId::RxTxMap32 => {
1577 let addr = RxTxAddr::Addr32 {
1578 rx: regs[2] as u32,
1579 tx: regs[1] as u32,
1580 };
1581 let page_cnt = regs[3] as u32;
1582
1583 Self::RxTxMap { addr, page_cnt }
1584 }
1585 FuncId::RxTxMap64 => {
1586 let addr = RxTxAddr::Addr64 {
1587 rx: regs[2],
1588 tx: regs[1],
1589 };
1590 let page_cnt = regs[3] as u32;
1591
1592 Self::RxTxMap { addr, page_cnt }
1593 }
1594 FuncId::RxTxUnmap => Self::RxTxUnmap { id: regs[1] as u16 },
1595 FuncId::PartitionInfoGet => {
1596 let uuid_words = [
1597 regs[1] as u32,
1598 regs[2] as u32,
1599 regs[3] as u32,
1600 regs[4] as u32,
1601 ];
1602 let mut bytes: [u8; 16] = [0; 16];
1603 for (i, b) in uuid_words.iter().flat_map(|w| w.to_le_bytes()).enumerate() {
1604 bytes[i] = b;
1605 }
1606 Self::PartitionInfoGet {
1607 uuid: Uuid::from_bytes(bytes),
Imre Kise295adb2025-04-10 13:26:28 +02001608 flags: PartitionInfoGetFlags::try_from(regs[5] as u32)?,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001609 }
1610 }
1611 FuncId::IdGet => Self::IdGet,
1612 FuncId::SpmIdGet => Self::SpmIdGet,
Tomás González092202a2025-03-05 11:56:45 +00001613 FuncId::MsgWait => Self::MsgWait {
1614 flags: if version >= Version(1, 2) {
1615 Some(MsgWaitFlags::try_from(regs[2] as u32)?)
1616 } else {
1617 None
1618 },
1619 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001620 FuncId::Yield => Self::Yield,
1621 FuncId::Run => Self::Run {
1622 target_info: (regs[1] as u32).into(),
1623 },
1624 FuncId::NormalWorldResume => Self::NormalWorldResume,
Tomás González17b92442025-03-10 16:45:04 +00001625 FuncId::SecondaryEpRegister32 => Self::SecondaryEpRegister {
1626 entrypoint: SecondaryEpRegisterAddr::Addr32(regs[1] as u32),
1627 },
1628 FuncId::SecondaryEpRegister64 => Self::SecondaryEpRegister {
1629 entrypoint: SecondaryEpRegisterAddr::Addr64(regs[1]),
1630 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001631 FuncId::MsgSend2 => Self::MsgSend2 {
1632 sender_vm_id: regs[1] as u16,
Imre Kisa2fd69b2025-06-13 13:39:47 +02001633 flags: (regs[2] as u32).try_into()?,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001634 },
1635 FuncId::MsgSendDirectReq32 => Self::MsgSendDirectReq {
1636 src_id: (regs[1] >> 16) as u16,
1637 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001638 args: if (regs[2] as u32 & DirectMsgArgs::FWK_MSG_BITS) != 0 {
1639 match regs[2] as u32 {
1640 DirectMsgArgs::VERSION_REQ => DirectMsgArgs::VersionReq {
1641 version: Version::try_from(regs[3] as u32)?,
1642 },
1643 DirectMsgArgs::POWER_PSCI_REQ => DirectMsgArgs::PowerPsciReq32 {
Tomás González67f92c72025-03-20 16:50:42 +00001644 params: [
1645 regs[3] as u32,
1646 regs[4] as u32,
1647 regs[5] as u32,
1648 regs[6] as u32,
1649 ],
Tomás González4d5b0ba2025-03-03 17:15:55 +00001650 },
1651 DirectMsgArgs::POWER_WARM_BOOT_REQ => DirectMsgArgs::PowerWarmBootReq {
1652 boot_type: WarmBootType::try_from(regs[3] as u32)?,
1653 },
1654 DirectMsgArgs::VM_CREATED => DirectMsgArgs::VmCreated {
1655 handle: memory_management::Handle::from([
1656 regs[3] as u32,
1657 regs[4] as u32,
1658 ]),
1659 vm_id: regs[5] as u16,
1660 },
1661 DirectMsgArgs::VM_DESTRUCTED => DirectMsgArgs::VmDestructed {
1662 handle: memory_management::Handle::from([
1663 regs[3] as u32,
1664 regs[4] as u32,
1665 ]),
1666 vm_id: regs[5] as u16,
1667 },
1668 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1669 }
1670 } else {
1671 DirectMsgArgs::Args32([
1672 regs[3] as u32,
1673 regs[4] as u32,
1674 regs[5] as u32,
1675 regs[6] as u32,
1676 regs[7] as u32,
1677 ])
1678 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001679 },
1680 FuncId::MsgSendDirectReq64 => Self::MsgSendDirectReq {
1681 src_id: (regs[1] >> 16) as u16,
1682 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001683 args: if (regs[2] & DirectMsgArgs::FWK_MSG_BITS as u64) != 0 {
1684 match regs[2] as u32 {
1685 DirectMsgArgs::POWER_PSCI_REQ => DirectMsgArgs::PowerPsciReq64 {
Tomás González67f92c72025-03-20 16:50:42 +00001686 params: [regs[3], regs[4], regs[5], regs[6]],
Tomás González4d5b0ba2025-03-03 17:15:55 +00001687 },
1688 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1689 }
1690 } else {
1691 DirectMsgArgs::Args64([regs[3], regs[4], regs[5], regs[6], regs[7]])
1692 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001693 },
1694 FuncId::MsgSendDirectResp32 => Self::MsgSendDirectResp {
1695 src_id: (regs[1] >> 16) as u16,
1696 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001697 args: if (regs[2] as u32 & DirectMsgArgs::FWK_MSG_BITS) != 0 {
1698 match regs[2] as u32 {
1699 DirectMsgArgs::VERSION_RESP => {
1700 if regs[3] as i32 == FfaError::NotSupported.into() {
1701 DirectMsgArgs::VersionResp { version: None }
1702 } else {
1703 DirectMsgArgs::VersionResp {
1704 version: Some(Version::try_from(regs[3] as u32)?),
1705 }
1706 }
1707 }
1708 DirectMsgArgs::POWER_PSCI_RESP => DirectMsgArgs::PowerPsciResp {
1709 psci_status: regs[3] as i32,
1710 },
1711 DirectMsgArgs::VM_CREATED_ACK => DirectMsgArgs::VmCreatedAck {
1712 sp_status: (regs[3] as i32).try_into()?,
1713 },
1714 DirectMsgArgs::VM_DESTRUCTED_ACK => DirectMsgArgs::VmDestructedAck {
1715 sp_status: (regs[3] as i32).try_into()?,
1716 },
1717 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1718 }
1719 } else {
1720 DirectMsgArgs::Args32([
1721 regs[3] as u32,
1722 regs[4] as u32,
1723 regs[5] as u32,
1724 regs[6] as u32,
1725 regs[7] as u32,
1726 ])
1727 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001728 },
1729 FuncId::MsgSendDirectResp64 => Self::MsgSendDirectResp {
1730 src_id: (regs[1] >> 16) as u16,
1731 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001732 args: if (regs[2] & DirectMsgArgs::FWK_MSG_BITS as u64) != 0 {
1733 return Err(Error::UnrecognisedFwkMsg(regs[2] as u32));
1734 } else {
1735 DirectMsgArgs::Args64([regs[3], regs[4], regs[5], regs[6], regs[7]])
1736 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001737 },
1738 FuncId::MemDonate32 => Self::MemDonate {
1739 total_len: regs[1] as u32,
1740 frag_len: regs[2] as u32,
1741 buf: if regs[3] != 0 && regs[4] != 0 {
1742 Some(MemOpBuf::Buf32 {
1743 addr: regs[3] as u32,
1744 page_cnt: regs[4] as u32,
1745 })
1746 } else {
1747 None
1748 },
1749 },
1750 FuncId::MemDonate64 => Self::MemDonate {
1751 total_len: regs[1] as u32,
1752 frag_len: regs[2] as u32,
1753 buf: if regs[3] != 0 && regs[4] != 0 {
1754 Some(MemOpBuf::Buf64 {
1755 addr: regs[3],
1756 page_cnt: regs[4] as u32,
1757 })
1758 } else {
1759 None
1760 },
1761 },
1762 FuncId::MemLend32 => Self::MemLend {
1763 total_len: regs[1] as u32,
1764 frag_len: regs[2] as u32,
1765 buf: if regs[3] != 0 && regs[4] != 0 {
1766 Some(MemOpBuf::Buf32 {
1767 addr: regs[3] as u32,
1768 page_cnt: regs[4] as u32,
1769 })
1770 } else {
1771 None
1772 },
1773 },
1774 FuncId::MemLend64 => Self::MemLend {
1775 total_len: regs[1] as u32,
1776 frag_len: regs[2] as u32,
1777 buf: if regs[3] != 0 && regs[4] != 0 {
1778 Some(MemOpBuf::Buf64 {
1779 addr: regs[3],
1780 page_cnt: regs[4] as u32,
1781 })
1782 } else {
1783 None
1784 },
1785 },
1786 FuncId::MemShare32 => Self::MemShare {
1787 total_len: regs[1] as u32,
1788 frag_len: regs[2] as u32,
1789 buf: if regs[3] != 0 && regs[4] != 0 {
1790 Some(MemOpBuf::Buf32 {
1791 addr: regs[3] as u32,
1792 page_cnt: regs[4] as u32,
1793 })
1794 } else {
1795 None
1796 },
1797 },
1798 FuncId::MemShare64 => Self::MemShare {
1799 total_len: regs[1] as u32,
1800 frag_len: regs[2] as u32,
1801 buf: if regs[3] != 0 && regs[4] != 0 {
1802 Some(MemOpBuf::Buf64 {
1803 addr: regs[3],
1804 page_cnt: regs[4] as u32,
1805 })
1806 } else {
1807 None
1808 },
1809 },
1810 FuncId::MemRetrieveReq32 => Self::MemRetrieveReq {
1811 total_len: regs[1] as u32,
1812 frag_len: regs[2] as u32,
1813 buf: if regs[3] != 0 && regs[4] != 0 {
1814 Some(MemOpBuf::Buf32 {
1815 addr: regs[3] as u32,
1816 page_cnt: regs[4] as u32,
1817 })
1818 } else {
1819 None
1820 },
1821 },
1822 FuncId::MemRetrieveReq64 => Self::MemRetrieveReq {
1823 total_len: regs[1] as u32,
1824 frag_len: regs[2] as u32,
1825 buf: if regs[3] != 0 && regs[4] != 0 {
1826 Some(MemOpBuf::Buf64 {
1827 addr: regs[3],
1828 page_cnt: regs[4] as u32,
1829 })
1830 } else {
1831 None
1832 },
1833 },
1834 FuncId::MemRetrieveResp => Self::MemRetrieveResp {
1835 total_len: regs[1] as u32,
1836 frag_len: regs[2] as u32,
1837 },
1838 FuncId::MemRelinquish => Self::MemRelinquish,
1839 FuncId::MemReclaim => Self::MemReclaim {
1840 handle: memory_management::Handle::from([regs[1] as u32, regs[2] as u32]),
Imre Kis356395d2025-06-13 13:49:06 +02001841 flags: (regs[3] as u32).try_into()?,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001842 },
1843 FuncId::MemPermGet32 => Self::MemPermGet {
1844 addr: MemAddr::Addr32(regs[1] as u32),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001845 page_cnt: if version >= Version(1, 3) {
1846 Some(regs[2] as u32)
1847 } else {
1848 None
1849 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001850 },
1851 FuncId::MemPermGet64 => Self::MemPermGet {
1852 addr: MemAddr::Addr64(regs[1]),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001853 page_cnt: if version >= Version(1, 3) {
1854 Some(regs[2] as u32)
1855 } else {
1856 None
1857 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001858 },
1859 FuncId::MemPermSet32 => Self::MemPermSet {
1860 addr: MemAddr::Addr32(regs[1] as u32),
1861 page_cnt: regs[2] as u32,
Imre Kisdcb7df22025-06-06 15:24:40 +02001862 mem_perm: (regs[3] as u32).try_into()?,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001863 },
1864 FuncId::MemPermSet64 => Self::MemPermSet {
1865 addr: MemAddr::Addr64(regs[1]),
1866 page_cnt: regs[2] as u32,
Imre Kisdcb7df22025-06-06 15:24:40 +02001867 mem_perm: (regs[3] as u32).try_into()?,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001868 },
Imre Kis189f18c2025-05-26 19:33:05 +02001869 FuncId::ConsoleLog32 => {
1870 let char_cnt = regs[1] as u8;
1871 if char_cnt > ConsoleLogChars32::MAX_LENGTH {
1872 return Err(Error::InvalidCharacterCount(char_cnt));
1873 }
1874
1875 Self::ConsoleLog {
1876 chars: ConsoleLogChars::Chars32(ConsoleLogChars32 {
1877 char_cnt,
1878 char_lists: [
1879 regs[2] as u32,
1880 regs[3] as u32,
1881 regs[4] as u32,
1882 regs[5] as u32,
1883 regs[6] as u32,
1884 regs[7] as u32,
1885 ],
1886 }),
1887 }
1888 }
Tomás González7ffb6132025-04-03 12:28:58 +01001889 FuncId::NotificationBitmapCreate => {
1890 let tentative_vm_id = regs[1] as u32;
1891 if (tentative_vm_id >> 16) != 0 {
1892 return Err(Error::InvalidVmId(tentative_vm_id));
1893 }
1894 Self::NotificationBitmapCreate {
1895 vm_id: tentative_vm_id as u16,
1896 vcpu_cnt: regs[2] as u32,
1897 }
1898 }
1899 FuncId::NotificationBitmapDestroy => {
1900 let tentative_vm_id = regs[1] as u32;
1901 if (tentative_vm_id >> 16) != 0 {
1902 return Err(Error::InvalidVmId(tentative_vm_id));
1903 }
1904 Self::NotificationBitmapDestroy {
1905 vm_id: tentative_vm_id as u16,
1906 }
1907 }
1908 FuncId::NotificationBind => Self::NotificationBind {
1909 sender_id: (regs[1] >> 16) as u16,
1910 receiver_id: regs[1] as u16,
1911 flags: (regs[2] as u32).into(),
1912 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1913 },
Imre Kis3571f2c2025-05-26 19:29:23 +02001914 FuncId::NotificationUnbind => Self::NotificationUnbind {
Tomás González7ffb6132025-04-03 12:28:58 +01001915 sender_id: (regs[1] >> 16) as u16,
1916 receiver_id: regs[1] as u16,
1917 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1918 },
1919 FuncId::NotificationSet => Self::NotificationSet {
1920 sender_id: (regs[1] >> 16) as u16,
1921 receiver_id: regs[1] as u16,
1922 flags: (regs[2] as u32).try_into()?,
1923 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1924 },
1925 FuncId::NotificationGet => Self::NotificationGet {
1926 vcpu_id: (regs[1] >> 16) as u16,
1927 endpoint_id: regs[1] as u16,
1928 flags: (regs[2] as u32).into(),
1929 },
1930 FuncId::NotificationInfoGet32 => Self::NotificationInfoGet { is_32bit: true },
1931 FuncId::NotificationInfoGet64 => Self::NotificationInfoGet { is_32bit: false },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01001932 FuncId::El3IntrHandle => Self::El3IntrHandle,
Balint Dobszayde0dc802025-02-28 14:16:52 +01001933 _ => panic!("Invalid number of registers (8) for function {:#x?}", fid),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001934 };
1935
1936 Ok(msg)
1937 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01001938
Balint Dobszayde0dc802025-02-28 14:16:52 +01001939 fn unpack_regs18(version: Version, regs: &[u64; 18]) -> Result<Self, Error> {
1940 assert!(version >= Version(1, 2));
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001941
Balint Dobszayde0dc802025-02-28 14:16:52 +01001942 let fid = FuncId::try_from(regs[0] as u32)?;
1943
1944 let msg = match fid {
1945 FuncId::Success64 => Self::Success {
Imre Kise521a282025-06-13 13:29:24 +02001946 target_info: (regs[1] as u32).into(),
Imre Kis54773b62025-04-10 13:47:39 +02001947 args: SuccessArgs::Args64_2(regs[2..18].try_into().unwrap()),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001948 },
1949 FuncId::MsgSendDirectReq64_2 => Self::MsgSendDirectReq2 {
1950 src_id: (regs[1] >> 16) as u16,
1951 dst_id: regs[1] as u16,
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00001952 uuid: Uuid::from_u64_pair(regs[2].swap_bytes(), regs[3].swap_bytes()),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001953 args: DirectMsg2Args(regs[4..18].try_into().unwrap()),
1954 },
1955 FuncId::MsgSendDirectResp64_2 => Self::MsgSendDirectResp2 {
1956 src_id: (regs[1] >> 16) as u16,
1957 dst_id: regs[1] as u16,
1958 args: DirectMsg2Args(regs[4..18].try_into().unwrap()),
1959 },
Imre Kis189f18c2025-05-26 19:33:05 +02001960 FuncId::ConsoleLog64 => {
1961 let char_cnt = regs[1] as u8;
1962 if char_cnt > ConsoleLogChars64::MAX_LENGTH {
1963 return Err(Error::InvalidCharacterCount(char_cnt));
1964 }
1965
1966 Self::ConsoleLog {
1967 chars: ConsoleLogChars::Chars64(ConsoleLogChars64 {
1968 char_cnt,
1969 char_lists: regs[2..18].try_into().unwrap(),
1970 }),
1971 }
1972 }
Tomás González0a058bc2025-03-11 11:20:55 +00001973 FuncId::PartitionInfoGetRegs => {
1974 // Bits[15:0]: Start index
1975 let start_index = (regs[3] & 0xffff) as u16;
1976 let info_tag = ((regs[3] >> 16) & 0xffff) as u16;
1977 Self::PartitionInfoGetRegs {
1978 uuid: Uuid::from_u64_pair(regs[1].swap_bytes(), regs[2].swap_bytes()),
1979 start_index,
1980 info_tag: if start_index == 0 && info_tag != 0 {
1981 return Err(Error::InvalidInformationTag(info_tag));
1982 } else {
1983 info_tag
1984 },
1985 }
1986 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001987 _ => panic!("Invalid number of registers (18) for function {:#x?}", fid),
1988 };
1989
1990 Ok(msg)
Balint Dobszay3aad9572025-01-17 16:54:11 +01001991 }
1992
Balint Dobszaya5846852025-02-26 15:38:53 +01001993 /// Create register contents for an interface.
Balint Dobszayde0dc802025-02-28 14:16:52 +01001994 pub fn to_regs(&self, version: Version, regs: &mut [u64]) {
Balint Dobszay82c71dd2025-04-15 10:16:44 +02001995 assert!(self.minimum_ffa_version() <= version);
1996
Balint Dobszayde0dc802025-02-28 14:16:52 +01001997 let reg_cnt = regs.len();
1998
1999 match reg_cnt {
2000 8 => {
2001 assert!(version <= Version(1, 1));
Balint Dobszay91bea9b2025-04-09 13:16:06 +02002002 regs.fill(0);
2003
Balint Dobszayde0dc802025-02-28 14:16:52 +01002004 self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
2005 }
2006 18 => {
2007 assert!(version >= Version(1, 2));
Balint Dobszay91bea9b2025-04-09 13:16:06 +02002008 regs.fill(0);
Balint Dobszayde0dc802025-02-28 14:16:52 +01002009
2010 match self {
2011 Interface::ConsoleLog {
Imre Kis189f18c2025-05-26 19:33:05 +02002012 chars: ConsoleLogChars::Chars64(_),
Balint Dobszayde0dc802025-02-28 14:16:52 +01002013 ..
2014 }
2015 | Interface::Success {
Imre Kis54773b62025-04-10 13:47:39 +02002016 args: SuccessArgs::Args64_2(_),
Balint Dobszayde0dc802025-02-28 14:16:52 +01002017 ..
2018 }
2019 | Interface::MsgSendDirectReq2 { .. }
Tomás González0a058bc2025-03-11 11:20:55 +00002020 | Interface::MsgSendDirectResp2 { .. }
2021 | Interface::PartitionInfoGetRegs { .. } => {
Balint Dobszayde0dc802025-02-28 14:16:52 +01002022 self.pack_regs18(version, regs.try_into().unwrap());
2023 }
2024 _ => {
2025 self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
2026 }
2027 }
2028 }
2029 _ => panic!("Invalid number of registers {}", reg_cnt),
2030 }
2031 }
2032
2033 fn pack_regs8(&self, version: Version, a: &mut [u64; 8]) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01002034 if let Some(function_id) = self.function_id() {
2035 a[0] = function_id as u64;
2036 }
2037
2038 match *self {
2039 Interface::Error {
2040 target_info,
2041 error_code,
Balint Dobszayb727aab2025-04-07 10:24:59 +02002042 error_arg,
Balint Dobszay3aad9572025-01-17 16:54:11 +01002043 } => {
2044 a[1] = u32::from(target_info).into();
2045 a[2] = (error_code as u32).into();
Balint Dobszayb727aab2025-04-07 10:24:59 +02002046 a[3] = error_arg.into();
Balint Dobszay3aad9572025-01-17 16:54:11 +01002047 }
2048 Interface::Success { target_info, args } => {
Imre Kise521a282025-06-13 13:29:24 +02002049 a[1] = u32::from(target_info).into();
Balint Dobszay3aad9572025-01-17 16:54:11 +01002050 match args {
Imre Kis54773b62025-04-10 13:47:39 +02002051 SuccessArgs::Args32(regs) => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01002052 a[2] = regs[0].into();
2053 a[3] = regs[1].into();
2054 a[4] = regs[2].into();
2055 a[5] = regs[3].into();
2056 a[6] = regs[4].into();
2057 a[7] = regs[5].into();
2058 }
Imre Kis54773b62025-04-10 13:47:39 +02002059 SuccessArgs::Args64(regs) => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01002060 a[2] = regs[0];
2061 a[3] = regs[1];
2062 a[4] = regs[2];
2063 a[5] = regs[3];
2064 a[6] = regs[4];
2065 a[7] = regs[5];
2066 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01002067 _ => panic!("{:#x?} requires 18 registers", args),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002068 }
2069 }
2070 Interface::Interrupt {
2071 target_info,
2072 interrupt_id,
2073 } => {
2074 a[1] = u32::from(target_info).into();
2075 a[2] = interrupt_id.into();
2076 }
2077 Interface::Version { input_version } => {
2078 a[1] = u32::from(input_version).into();
2079 }
2080 Interface::VersionOut { output_version } => {
2081 a[0] = u32::from(output_version).into();
2082 }
2083 Interface::Features {
2084 feat_id,
2085 input_properties,
2086 } => {
2087 a[1] = u32::from(feat_id).into();
2088 a[2] = input_properties.into();
2089 }
2090 Interface::RxAcquire { vm_id } => {
2091 a[1] = vm_id.into();
2092 }
2093 Interface::RxRelease { vm_id } => {
2094 a[1] = vm_id.into();
2095 }
2096 Interface::RxTxMap { addr, page_cnt } => {
2097 match addr {
2098 RxTxAddr::Addr32 { rx, tx } => {
2099 a[1] = tx.into();
2100 a[2] = rx.into();
2101 }
2102 RxTxAddr::Addr64 { rx, tx } => {
2103 a[1] = tx;
2104 a[2] = rx;
2105 }
2106 }
2107 a[3] = page_cnt.into();
2108 }
2109 Interface::RxTxUnmap { id } => {
2110 a[1] = id.into();
2111 }
2112 Interface::PartitionInfoGet { uuid, flags } => {
2113 let bytes = uuid.into_bytes();
2114 a[1] = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]).into();
2115 a[2] = u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]).into();
2116 a[3] = u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]).into();
2117 a[4] = u32::from_le_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]).into();
Imre Kise295adb2025-04-10 13:26:28 +02002118 a[5] = u32::from(flags).into();
Balint Dobszay3aad9572025-01-17 16:54:11 +01002119 }
Tomás González092202a2025-03-05 11:56:45 +00002120 Interface::MsgWait { flags } => {
2121 if version >= Version(1, 2) {
2122 if let Some(flags) = flags {
2123 a[2] = u32::from(flags).into();
2124 }
2125 }
2126 }
2127 Interface::IdGet | Interface::SpmIdGet | Interface::Yield => {}
Balint Dobszay3aad9572025-01-17 16:54:11 +01002128 Interface::Run { target_info } => {
2129 a[1] = u32::from(target_info).into();
2130 }
2131 Interface::NormalWorldResume => {}
Tomás González17b92442025-03-10 16:45:04 +00002132 Interface::SecondaryEpRegister { entrypoint } => match entrypoint {
2133 SecondaryEpRegisterAddr::Addr32(addr) => a[1] = addr as u64,
2134 SecondaryEpRegisterAddr::Addr64(addr) => a[1] = addr,
2135 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01002136 Interface::MsgSend2 {
2137 sender_vm_id,
2138 flags,
2139 } => {
2140 a[1] = sender_vm_id.into();
Imre Kisa2fd69b2025-06-13 13:39:47 +02002141 a[2] = u32::from(flags).into();
Balint Dobszay3aad9572025-01-17 16:54:11 +01002142 }
2143 Interface::MsgSendDirectReq {
2144 src_id,
2145 dst_id,
Balint Dobszay3aad9572025-01-17 16:54:11 +01002146 args,
2147 } => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +01002148 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Balint Dobszay3aad9572025-01-17 16:54:11 +01002149 match args {
2150 DirectMsgArgs::Args32(args) => {
2151 a[3] = args[0].into();
2152 a[4] = args[1].into();
2153 a[5] = args[2].into();
2154 a[6] = args[3].into();
2155 a[7] = args[4].into();
2156 }
2157 DirectMsgArgs::Args64(args) => {
2158 a[3] = args[0];
2159 a[4] = args[1];
2160 a[5] = args[2];
2161 a[6] = args[3];
2162 a[7] = args[4];
2163 }
Tomás González4d5b0ba2025-03-03 17:15:55 +00002164 DirectMsgArgs::VersionReq { version } => {
2165 a[2] = DirectMsgArgs::VERSION_REQ.into();
2166 a[3] = u32::from(version).into();
2167 }
Tomás González67f92c72025-03-20 16:50:42 +00002168 DirectMsgArgs::PowerPsciReq32 { params } => {
Tomás González4d5b0ba2025-03-03 17:15:55 +00002169 a[2] = DirectMsgArgs::POWER_PSCI_REQ.into();
Tomás González67f92c72025-03-20 16:50:42 +00002170 a[3] = params[0].into();
2171 a[4] = params[1].into();
2172 a[5] = params[2].into();
2173 a[6] = params[3].into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00002174 }
Tomás González67f92c72025-03-20 16:50:42 +00002175 DirectMsgArgs::PowerPsciReq64 { params } => {
Tomás González4d5b0ba2025-03-03 17:15:55 +00002176 a[2] = DirectMsgArgs::POWER_PSCI_REQ.into();
Tomás González67f92c72025-03-20 16:50:42 +00002177 a[3] = params[0];
2178 a[4] = params[1];
2179 a[5] = params[2];
2180 a[6] = params[3];
Tomás González4d5b0ba2025-03-03 17:15:55 +00002181 }
2182 DirectMsgArgs::PowerWarmBootReq { boot_type } => {
2183 a[2] = DirectMsgArgs::POWER_WARM_BOOT_REQ.into();
2184 a[3] = u32::from(boot_type).into();
2185 }
2186 DirectMsgArgs::VmCreated { handle, vm_id } => {
2187 a[2] = DirectMsgArgs::VM_CREATED.into();
2188 let handle_regs: [u32; 2] = handle.into();
2189 a[3] = handle_regs[0].into();
2190 a[4] = handle_regs[1].into();
2191 a[5] = vm_id.into();
2192 }
2193 DirectMsgArgs::VmDestructed { handle, vm_id } => {
2194 a[2] = DirectMsgArgs::VM_DESTRUCTED.into();
2195 let handle_regs: [u32; 2] = handle.into();
2196 a[3] = handle_regs[0].into();
2197 a[4] = handle_regs[1].into();
2198 a[5] = vm_id.into();
2199 }
2200 _ => panic!("Malformed MsgSendDirectReq interface"),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002201 }
2202 }
2203 Interface::MsgSendDirectResp {
2204 src_id,
2205 dst_id,
Balint Dobszay3aad9572025-01-17 16:54:11 +01002206 args,
2207 } => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +01002208 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Balint Dobszay3aad9572025-01-17 16:54:11 +01002209 match args {
2210 DirectMsgArgs::Args32(args) => {
2211 a[3] = args[0].into();
2212 a[4] = args[1].into();
2213 a[5] = args[2].into();
2214 a[6] = args[3].into();
2215 a[7] = args[4].into();
2216 }
2217 DirectMsgArgs::Args64(args) => {
2218 a[3] = args[0];
2219 a[4] = args[1];
2220 a[5] = args[2];
2221 a[6] = args[3];
2222 a[7] = args[4];
2223 }
Tomás González4d5b0ba2025-03-03 17:15:55 +00002224 DirectMsgArgs::VersionResp { version } => {
2225 a[2] = DirectMsgArgs::VERSION_RESP.into();
2226 match version {
Tomás González67f92c72025-03-20 16:50:42 +00002227 None => a[3] = (i32::from(FfaError::NotSupported) as u32).into(),
Tomás González4d5b0ba2025-03-03 17:15:55 +00002228 Some(ver) => a[3] = u32::from(ver).into(),
2229 }
2230 }
2231 DirectMsgArgs::PowerPsciResp { psci_status } => {
2232 a[2] = DirectMsgArgs::POWER_PSCI_RESP.into();
Imre Kisb2d3c882025-04-11 14:19:35 +02002233 a[3] = (psci_status as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00002234 }
2235 DirectMsgArgs::VmCreatedAck { sp_status } => {
2236 a[2] = DirectMsgArgs::VM_CREATED_ACK.into();
Tomás González67f92c72025-03-20 16:50:42 +00002237 a[3] = (i32::from(sp_status) as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00002238 }
2239 DirectMsgArgs::VmDestructedAck { sp_status } => {
2240 a[2] = DirectMsgArgs::VM_DESTRUCTED_ACK.into();
Tomás González67f92c72025-03-20 16:50:42 +00002241 a[3] = (i32::from(sp_status) as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00002242 }
2243 _ => panic!("Malformed MsgSendDirectResp interface"),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002244 }
2245 }
2246 Interface::MemDonate {
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::MemLend {
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::MemShare {
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::MemRetrieveReq {
2286 total_len,
2287 frag_len,
2288 buf,
2289 } => {
2290 a[1] = total_len.into();
2291 a[2] = frag_len.into();
2292 (a[3], a[4]) = match buf {
2293 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
2294 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
2295 None => (0, 0),
2296 };
2297 }
2298 Interface::MemRetrieveResp {
2299 total_len,
2300 frag_len,
2301 } => {
2302 a[1] = total_len.into();
2303 a[2] = frag_len.into();
2304 }
2305 Interface::MemRelinquish => {}
2306 Interface::MemReclaim { handle, flags } => {
2307 let handle_regs: [u32; 2] = handle.into();
2308 a[1] = handle_regs[0].into();
2309 a[2] = handle_regs[1].into();
Imre Kis356395d2025-06-13 13:49:06 +02002310 a[3] = u32::from(flags).into();
Balint Dobszay3aad9572025-01-17 16:54:11 +01002311 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01002312 Interface::MemPermGet { addr, page_cnt } => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01002313 a[1] = match addr {
2314 MemAddr::Addr32(addr) => addr.into(),
2315 MemAddr::Addr64(addr) => addr,
2316 };
Balint Dobszayde0dc802025-02-28 14:16:52 +01002317 a[2] = if version >= Version(1, 3) {
2318 page_cnt.unwrap().into()
2319 } else {
2320 assert!(page_cnt.is_none());
2321 0
2322 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01002323 }
2324 Interface::MemPermSet {
2325 addr,
2326 page_cnt,
2327 mem_perm,
2328 } => {
2329 a[1] = match addr {
2330 MemAddr::Addr32(addr) => addr.into(),
2331 MemAddr::Addr64(addr) => addr,
2332 };
2333 a[2] = page_cnt.into();
Imre Kisdcb7df22025-06-06 15:24:40 +02002334 a[3] = u32::from(mem_perm).into();
Balint Dobszay3aad9572025-01-17 16:54:11 +01002335 }
Imre Kis189f18c2025-05-26 19:33:05 +02002336 Interface::ConsoleLog { chars } => match chars {
2337 ConsoleLogChars::Chars32(ConsoleLogChars32 {
2338 char_cnt,
2339 char_lists,
2340 }) => {
2341 a[1] = char_cnt.into();
2342 a[2] = char_lists[0].into();
2343 a[3] = char_lists[1].into();
2344 a[4] = char_lists[2].into();
2345 a[5] = char_lists[3].into();
2346 a[6] = char_lists[4].into();
2347 a[7] = char_lists[5].into();
Balint Dobszay3aad9572025-01-17 16:54:11 +01002348 }
Imre Kis189f18c2025-05-26 19:33:05 +02002349 _ => panic!("{:#x?} requires 18 registers", chars),
2350 },
Tomás González7ffb6132025-04-03 12:28:58 +01002351 Interface::NotificationBitmapCreate { vm_id, vcpu_cnt } => {
2352 a[1] = vm_id.into();
2353 a[2] = vcpu_cnt.into();
2354 }
2355 Interface::NotificationBitmapDestroy { vm_id } => {
2356 a[1] = vm_id.into();
2357 }
2358 Interface::NotificationBind {
2359 sender_id,
2360 receiver_id,
2361 flags,
2362 bitmap,
2363 } => {
2364 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
2365 a[2] = u32::from(flags).into();
2366 a[3] = bitmap & 0xffff_ffff;
2367 a[4] = bitmap >> 32;
2368 }
Imre Kis3571f2c2025-05-26 19:29:23 +02002369 Interface::NotificationUnbind {
Tomás González7ffb6132025-04-03 12:28:58 +01002370 sender_id,
2371 receiver_id,
2372 bitmap,
2373 } => {
2374 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
2375 a[3] = bitmap & 0xffff_ffff;
2376 a[4] = bitmap >> 32;
2377 }
2378 Interface::NotificationSet {
2379 sender_id,
2380 receiver_id,
2381 flags,
2382 bitmap,
2383 } => {
2384 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
2385 a[2] = u32::from(flags).into();
2386 a[3] = bitmap & 0xffff_ffff;
2387 a[4] = bitmap >> 32;
2388 }
2389 Interface::NotificationGet {
2390 vcpu_id,
2391 endpoint_id,
2392 flags,
2393 } => {
2394 a[1] = (u64::from(vcpu_id) << 16) | u64::from(endpoint_id);
2395 a[2] = u32::from(flags).into();
2396 }
2397 Interface::NotificationInfoGet { .. } => {}
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01002398 Interface::El3IntrHandle => {}
Balint Dobszayde0dc802025-02-28 14:16:52 +01002399 _ => panic!("{:#x?} requires 18 registers", self),
2400 }
2401 }
2402
2403 fn pack_regs18(&self, version: Version, a: &mut [u64; 18]) {
2404 assert!(version >= Version(1, 2));
2405
Balint Dobszayde0dc802025-02-28 14:16:52 +01002406 if let Some(function_id) = self.function_id() {
2407 a[0] = function_id as u64;
2408 }
2409
2410 match *self {
2411 Interface::Success { target_info, args } => {
Imre Kise521a282025-06-13 13:29:24 +02002412 a[1] = u32::from(target_info).into();
Balint Dobszayde0dc802025-02-28 14:16:52 +01002413 match args {
Imre Kis54773b62025-04-10 13:47:39 +02002414 SuccessArgs::Args64_2(regs) => a[2..18].copy_from_slice(&regs[..16]),
Balint Dobszayde0dc802025-02-28 14:16:52 +01002415 _ => panic!("{:#x?} requires 8 registers", args),
2416 }
2417 }
2418 Interface::MsgSendDirectReq2 {
2419 src_id,
2420 dst_id,
2421 uuid,
2422 args,
2423 } => {
2424 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00002425 let (uuid_msb, uuid_lsb) = uuid.as_u64_pair();
2426 (a[2], a[3]) = (uuid_msb.swap_bytes(), uuid_lsb.swap_bytes());
Balint Dobszayde0dc802025-02-28 14:16:52 +01002427 a[4..18].copy_from_slice(&args.0[..14]);
2428 }
2429 Interface::MsgSendDirectResp2 {
2430 src_id,
2431 dst_id,
2432 args,
2433 } => {
2434 a[1] = ((src_id as u64) << 16) | dst_id as u64;
2435 a[2] = 0;
2436 a[3] = 0;
2437 a[4..18].copy_from_slice(&args.0[..14]);
2438 }
Imre Kis189f18c2025-05-26 19:33:05 +02002439 Interface::ConsoleLog { chars: char_lists } => match char_lists {
2440 ConsoleLogChars::Chars64(ConsoleLogChars64 {
2441 char_cnt,
2442 char_lists,
2443 }) => {
2444 a[1] = char_cnt.into();
2445 a[2..18].copy_from_slice(&char_lists[..16])
Balint Dobszayde0dc802025-02-28 14:16:52 +01002446 }
Imre Kis189f18c2025-05-26 19:33:05 +02002447 _ => panic!("{:#x?} requires 8 registers", char_lists),
2448 },
Tomás González0a058bc2025-03-11 11:20:55 +00002449 Interface::PartitionInfoGetRegs {
2450 uuid,
2451 start_index,
2452 info_tag,
2453 } => {
2454 if start_index == 0 && info_tag != 0 {
2455 panic!("Information Tag MBZ if start index is 0: {:#x?}", self);
2456 }
2457 let (uuid_msb, uuid_lsb) = uuid.as_u64_pair();
2458 (a[1], a[2]) = (uuid_msb.swap_bytes(), uuid_lsb.swap_bytes());
2459 a[3] = (u64::from(info_tag) << 16) | u64::from(start_index);
2460 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01002461 _ => panic!("{:#x?} requires 8 registers", self),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002462 }
2463 }
2464
Balint Dobszaya5846852025-02-26 15:38:53 +01002465 /// Helper function to create an `FFA_SUCCESS` interface without any arguments.
Balint Dobszay3aad9572025-01-17 16:54:11 +01002466 pub fn success32_noargs() -> Self {
2467 Self::Success {
Imre Kise521a282025-06-13 13:29:24 +02002468 target_info: TargetInfo::default(),
Imre Kis54773b62025-04-10 13:47:39 +02002469 args: SuccessArgs::Args32([0; 6]),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002470 }
2471 }
2472
Balint Dobszaya5846852025-02-26 15:38:53 +01002473 /// Helper function to create an `FFA_ERROR` interface with an error code.
Balint Dobszay3aad9572025-01-17 16:54:11 +01002474 pub fn error(error_code: FfaError) -> Self {
2475 Self::Error {
Imre Kise521a282025-06-13 13:29:24 +02002476 target_info: TargetInfo::default(),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002477 error_code,
Balint Dobszayb727aab2025-04-07 10:24:59 +02002478 error_arg: 0,
Balint Dobszay3aad9572025-01-17 16:54:11 +01002479 }
2480 }
2481}
2482
Tomás González0a058bc2025-03-11 11:20:55 +00002483#[cfg(test)]
2484mod tests {
2485 use super::*;
2486
2487 #[test]
Balint Dobszay5ded5922025-06-13 12:06:53 +02002488 fn version_reg_count() {
2489 assert!(!Version(1, 1).needs_18_regs());
2490 assert!(Version(1, 2).needs_18_regs())
2491 }
2492
2493 #[test]
Tomás González0a058bc2025-03-11 11:20:55 +00002494 fn part_info_get_regs() {
2495 let uuid = Uuid::parse_str("a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8").unwrap();
2496 let uuid_bytes = uuid.as_bytes();
2497 let test_info_tag = 0b1101_1101;
2498 let test_start_index = 0b1101;
2499 let start_index_and_tag = (test_info_tag << 16) | test_start_index;
2500 let version = Version(1, 2);
2501
2502 // From spec:
2503 // Bytes[0...7] of UUID with byte 0 in the low-order bits.
Balint Dobszayb2e9bed2025-04-15 12:57:36 +02002504 let reg_x1 = ((uuid_bytes[7] as u64) << 56)
2505 | ((uuid_bytes[6] as u64) << 48)
2506 | ((uuid_bytes[5] as u64) << 40)
2507 | ((uuid_bytes[4] as u64) << 32)
2508 | ((uuid_bytes[3] as u64) << 24)
2509 | ((uuid_bytes[2] as u64) << 16)
2510 | ((uuid_bytes[1] as u64) << 8)
Tomás González0a058bc2025-03-11 11:20:55 +00002511 | (uuid_bytes[0] as u64);
2512
2513 // From spec:
2514 // Bytes[8...15] of UUID with byte 8 in the low-order bits.
Balint Dobszayb2e9bed2025-04-15 12:57:36 +02002515 let reg_x2 = ((uuid_bytes[15] as u64) << 56)
2516 | ((uuid_bytes[14] as u64) << 48)
2517 | ((uuid_bytes[13] as u64) << 40)
2518 | ((uuid_bytes[12] as u64) << 32)
2519 | ((uuid_bytes[11] as u64) << 24)
2520 | ((uuid_bytes[10] as u64) << 16)
2521 | ((uuid_bytes[9] as u64) << 8)
Tomás González0a058bc2025-03-11 11:20:55 +00002522 | (uuid_bytes[8] as u64);
2523
2524 // First, test for wrong tag:
2525 {
2526 let mut regs = [0u64; 18];
2527 regs[0] = FuncId::PartitionInfoGetRegs as u64;
2528 regs[1] = reg_x1;
2529 regs[2] = reg_x2;
2530 regs[3] = test_info_tag << 16;
2531
2532 assert!(Interface::from_regs(version, &regs).is_err_and(
2533 |e| e == Error::InvalidInformationTag(test_info_tag.try_into().unwrap())
2534 ));
2535 }
2536
2537 // Test for regs -> Interface -> regs
2538 {
2539 let mut orig_regs = [0u64; 18];
2540 orig_regs[0] = FuncId::PartitionInfoGetRegs as u64;
2541 orig_regs[1] = reg_x1;
2542 orig_regs[2] = reg_x2;
2543 orig_regs[3] = start_index_and_tag;
2544
Balint Dobszayb2e9bed2025-04-15 12:57:36 +02002545 let mut test_regs = orig_regs;
2546 let interface = Interface::from_regs(version, &test_regs).unwrap();
Tomás González0a058bc2025-03-11 11:20:55 +00002547 match &interface {
2548 Interface::PartitionInfoGetRegs {
2549 info_tag,
2550 start_index,
2551 uuid: int_uuid,
2552 } => {
2553 assert_eq!(u64::from(*info_tag), test_info_tag);
2554 assert_eq!(u64::from(*start_index), test_start_index);
2555 assert_eq!(*int_uuid, uuid);
2556 }
2557 _ => panic!("Expecting Interface::PartitionInfoGetRegs!"),
2558 }
2559 test_regs.fill(0);
2560 interface.to_regs(version, &mut test_regs);
2561 assert_eq!(orig_regs, test_regs);
2562 }
2563
2564 // Test for Interface -> regs -> Interface
2565 {
2566 let interface = Interface::PartitionInfoGetRegs {
2567 info_tag: test_info_tag.try_into().unwrap(),
2568 start_index: test_start_index.try_into().unwrap(),
2569 uuid,
2570 };
2571
2572 let mut regs: [u64; 18] = [0; 18];
2573 interface.to_regs(version, &mut regs);
2574
2575 assert_eq!(Some(FuncId::PartitionInfoGetRegs), interface.function_id());
2576 assert_eq!(regs[0], interface.function_id().unwrap() as u64);
2577 assert_eq!(regs[1], reg_x1);
2578 assert_eq!(regs[2], reg_x2);
2579 assert_eq!(regs[3], (test_info_tag << 16) | test_start_index);
2580
2581 assert_eq!(Interface::from_regs(version, &regs).unwrap(), interface);
2582 }
2583 }
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00002584
2585 #[test]
2586 fn msg_send_direct_req2() {
2587 let uuid = Uuid::parse_str("a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8").unwrap();
2588 let uuid_bytes = uuid.as_bytes();
2589
2590 // From spec:
2591 // Bytes[0...7] of UUID with byte 0 in the low-order bits.
Balint Dobszayb2e9bed2025-04-15 12:57:36 +02002592 let reg_x2 = ((uuid_bytes[7] as u64) << 56)
2593 | ((uuid_bytes[6] as u64) << 48)
2594 | ((uuid_bytes[5] as u64) << 40)
2595 | ((uuid_bytes[4] as u64) << 32)
2596 | ((uuid_bytes[3] as u64) << 24)
2597 | ((uuid_bytes[2] as u64) << 16)
2598 | ((uuid_bytes[1] as u64) << 8)
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00002599 | (uuid_bytes[0] as u64);
2600
2601 // From spec:
2602 // Bytes[8...15] of UUID with byte 8 in the low-order bits.
Balint Dobszayb2e9bed2025-04-15 12:57:36 +02002603 let reg_x3 = ((uuid_bytes[15] as u64) << 56)
2604 | ((uuid_bytes[14] as u64) << 48)
2605 | ((uuid_bytes[13] as u64) << 40)
2606 | ((uuid_bytes[12] as u64) << 32)
2607 | ((uuid_bytes[11] as u64) << 24)
2608 | ((uuid_bytes[10] as u64) << 16)
2609 | ((uuid_bytes[9] as u64) << 8)
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00002610 | (uuid_bytes[8] as u64);
2611
2612 let test_sender = 0b1101_1101;
2613 let test_receiver = 0b1101;
2614 let test_sender_receiver = (test_sender << 16) | test_receiver;
2615 let version = Version(1, 2);
2616
2617 // Test for regs -> Interface -> regs
2618 {
2619 let mut orig_regs = [0u64; 18];
2620 orig_regs[0] = FuncId::MsgSendDirectReq64_2 as u64;
2621 orig_regs[1] = test_sender_receiver;
2622 orig_regs[2] = reg_x2;
2623 orig_regs[3] = reg_x3;
2624
Balint Dobszayb2e9bed2025-04-15 12:57:36 +02002625 let mut test_regs = orig_regs;
2626 let interface = Interface::from_regs(version, &test_regs).unwrap();
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00002627 match &interface {
2628 Interface::MsgSendDirectReq2 {
2629 dst_id,
2630 src_id,
2631 args: _,
2632 uuid: int_uuid,
2633 } => {
2634 assert_eq!(u64::from(*src_id), test_sender);
2635 assert_eq!(u64::from(*dst_id), test_receiver);
2636 assert_eq!(*int_uuid, uuid);
2637 }
2638 _ => panic!("Expecting Interface::MsgSendDirectReq2!"),
2639 }
2640 test_regs.fill(0);
2641 interface.to_regs(version, &mut test_regs);
2642 assert_eq!(orig_regs, test_regs);
2643 }
2644
2645 // Test for Interface -> regs -> Interface
2646 {
2647 let rest_of_regs: [u64; 14] = [0; 14];
2648
2649 let interface = Interface::MsgSendDirectReq2 {
2650 src_id: test_sender.try_into().unwrap(),
2651 dst_id: test_receiver.try_into().unwrap(),
2652 uuid,
2653 args: DirectMsg2Args(rest_of_regs),
2654 };
2655
2656 let mut regs: [u64; 18] = [0; 18];
2657 interface.to_regs(version, &mut regs);
2658
2659 assert_eq!(Some(FuncId::MsgSendDirectReq64_2), interface.function_id());
2660 assert_eq!(regs[0], interface.function_id().unwrap() as u64);
2661 assert_eq!(regs[1], test_sender_receiver);
2662 assert_eq!(regs[2], reg_x2);
2663 assert_eq!(regs[3], reg_x3);
2664 assert_eq!(regs[4], 0);
2665
2666 assert_eq!(Interface::from_regs(version, &regs).unwrap(), interface);
2667 }
2668 }
Tomás González6ccba0a2025-04-09 13:31:29 +01002669
2670 #[test]
2671 fn is_32bit() {
2672 let interface_64 = Interface::MsgSendDirectReq {
2673 src_id: 0,
2674 dst_id: 1,
2675 args: DirectMsgArgs::Args64([0, 0, 0, 0, 0]),
2676 };
2677 assert!(!interface_64.is_32bit());
2678
2679 let interface_32 = Interface::MsgSendDirectReq {
2680 src_id: 0,
2681 dst_id: 1,
2682 args: DirectMsgArgs::Args32([0, 0, 0, 0, 0]),
2683 };
2684 assert!(interface_32.is_32bit());
2685 }
Imre Kis787c5002025-04-10 14:25:51 +02002686
2687 #[test]
2688 fn success_args_notification_info_get32() {
2689 let mut notifications = SuccessArgsNotificationInfoGet32::default();
2690
2691 // 16.7.1.1 Example usage
2692 notifications.add_list(0x0000, &[0, 2, 3]).unwrap();
2693 notifications.add_list(0x0000, &[4, 6]).unwrap();
2694 notifications.add_list(0x0002, &[]).unwrap();
2695 notifications.add_list(0x0003, &[1]).unwrap();
2696
2697 let args: SuccessArgs = notifications.into();
2698 assert_eq!(
2699 SuccessArgs::Args32([
2700 0x0004_b200,
2701 0x0000_0000,
2702 0x0003_0002,
2703 0x0004_0000,
2704 0x0002_0006,
2705 0x0001_0003
2706 ]),
2707 args
2708 );
2709
2710 let notifications = SuccessArgsNotificationInfoGet32::try_from(args).unwrap();
2711 let mut iter = notifications.iter();
2712 assert_eq!(Some((0x0000, &[0, 2, 3][..])), iter.next());
2713 assert_eq!(Some((0x0000, &[4, 6][..])), iter.next());
2714 assert_eq!(Some((0x0002, &[][..])), iter.next());
2715 assert_eq!(Some((0x0003, &[1][..])), iter.next());
2716 }
2717
2718 #[test]
2719 fn success_args_notification_info_get64() {
2720 let mut notifications = SuccessArgsNotificationInfoGet64::default();
2721
2722 // 16.7.1.1 Example usage
2723 notifications.add_list(0x0000, &[0, 2, 3]).unwrap();
2724 notifications.add_list(0x0000, &[4, 6]).unwrap();
2725 notifications.add_list(0x0002, &[]).unwrap();
2726 notifications.add_list(0x0003, &[1]).unwrap();
2727
2728 let args: SuccessArgs = notifications.into();
2729 assert_eq!(
2730 SuccessArgs::Args64([
2731 0x0004_b200,
2732 0x0003_0002_0000_0000,
2733 0x0002_0006_0004_0000,
2734 0x0000_0000_0001_0003,
2735 0x0000_0000_0000_0000,
2736 0x0000_0000_0000_0000,
2737 ]),
2738 args
2739 );
2740
2741 let notifications = SuccessArgsNotificationInfoGet64::try_from(args).unwrap();
2742 let mut iter = notifications.iter();
2743 assert_eq!(Some((0x0000, &[0, 2, 3][..])), iter.next());
2744 assert_eq!(Some((0x0000, &[4, 6][..])), iter.next());
2745 assert_eq!(Some((0x0002, &[][..])), iter.next());
2746 assert_eq!(Some((0x0003, &[1][..])), iter.next());
2747 }
Tomás González0a058bc2025-03-11 11:20:55 +00002748}