blob: 616ce02aca85b8d08932160ad6c2b4b2bd51725a [file] [log] [blame]
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001// SPDX-FileCopyrightText: Copyright 2023 Arm Limited and/or its affiliates <open-source-office@arm.com>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4#![cfg_attr(not(test), no_std)]
Balint Dobszaya5846852025-02-26 15:38:53 +01005#![deny(clippy::undocumented_unsafe_blocks)]
6#![deny(unsafe_op_in_unsafe_fn)]
7#![doc = include_str!("../README.md")]
Balint Dobszay5bf492f2024-07-29 17:21:32 +02008
Andrew Walbran19970ba2024-11-25 15:35:00 +00009use core::fmt::{self, Debug, Display, Formatter};
Andrew Walbran44029a02024-11-25 15:34:31 +000010use num_enum::{IntoPrimitive, TryFromPrimitive};
11use thiserror::Error;
Balint Dobszay5bf492f2024-07-29 17:21:32 +020012use uuid::Uuid;
13
14pub mod boot_info;
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +010015mod ffa_v1_1;
Balint Dobszayde0dc802025-02-28 14:16:52 +010016mod ffa_v1_2;
Balint Dobszay5bf492f2024-07-29 17:21:32 +020017pub mod memory_management;
18pub mod partition_info;
19
Balint Dobszaya5846852025-02-26 15:38:53 +010020/// Constant for 4K page size. On many occasions the FF-A spec defines memory size as count of 4K
21/// pages, regardless of the current translation granule.
Balint Dobszay3aad9572025-01-17 16:54:11 +010022pub const FFA_PAGE_SIZE_4K: usize = 4096;
23
Balint Dobszaya5846852025-02-26 15:38:53 +010024/// Rich error types returned by this module. Should be converted to [`crate::FfaError`] when used
25/// with the `FFA_ERROR` interface.
Tomás González0a058bc2025-03-11 11:20:55 +000026#[derive(Debug, Error, PartialEq)]
Balint Dobszay3aad9572025-01-17 16:54:11 +010027pub enum Error {
28 #[error("Unrecognised FF-A function ID {0}")]
29 UnrecognisedFunctionId(u32),
30 #[error("Unrecognised FF-A feature ID {0}")]
31 UnrecognisedFeatureId(u8),
32 #[error("Unrecognised FF-A error code {0}")]
33 UnrecognisedErrorCode(i32),
Tomás González4d5b0ba2025-03-03 17:15:55 +000034 #[error("Unrecognised FF-A Framework Message {0}")]
35 UnrecognisedFwkMsg(u32),
Tomás González092202a2025-03-05 11:56:45 +000036 #[error("Invalid FF-A Msg Wait Flag {0}")]
37 InvalidMsgWaitFlag(u32),
Tomás González4d5b0ba2025-03-03 17:15:55 +000038 #[error("Unrecognised VM availability status {0}")]
39 UnrecognisedVmAvailabilityStatus(i32),
40 #[error("Unrecognised FF-A Warm Boot Type {0}")]
41 UnrecognisedWarmBootType(u32),
42 #[error("Invalid version {0}")]
43 InvalidVersion(u32),
Tomás González0a058bc2025-03-11 11:20:55 +000044 #[error("Invalid Information Tag {0}")]
45 InvalidInformationTag(u16),
Tomás González7ffb6132025-04-03 12:28:58 +010046 #[error("Invalid Flag for Notification Set")]
47 InvalidNotificationSetFlag(u32),
48 #[error("Invalid Vm ID")]
49 InvalidVmId(u32),
Imre Kise295adb2025-04-10 13:26:28 +020050 #[error("Invalid FF-A Partition Info Get Flag {0}")]
51 InvalidPartitionInfoGetFlag(u32),
Imre Kis839eaef2025-04-11 17:38:36 +020052 #[error("Invalid success argument variant")]
53 InvalidSuccessArgsVariant,
Balint Dobszay3aad9572025-01-17 16:54:11 +010054}
55
56impl From<Error> for FfaError {
57 fn from(value: Error) -> Self {
58 match value {
59 Error::UnrecognisedFunctionId(_) | Error::UnrecognisedFeatureId(_) => {
60 Self::NotSupported
61 }
Tomás González0a058bc2025-03-11 11:20:55 +000062 Error::InvalidInformationTag(_) => Self::Retry,
Tomás González4d5b0ba2025-03-03 17:15:55 +000063 Error::UnrecognisedErrorCode(_)
64 | Error::UnrecognisedFwkMsg(_)
65 | Error::InvalidVersion(_)
Tomás González092202a2025-03-05 11:56:45 +000066 | Error::InvalidMsgWaitFlag(_)
Tomás González4d5b0ba2025-03-03 17:15:55 +000067 | Error::UnrecognisedVmAvailabilityStatus(_)
Tomás González7ffb6132025-04-03 12:28:58 +010068 | Error::InvalidNotificationSetFlag(_)
69 | Error::InvalidVmId(_)
Imre Kise295adb2025-04-10 13:26:28 +020070 | Error::UnrecognisedWarmBootType(_)
Imre Kis839eaef2025-04-11 17:38:36 +020071 | Error::InvalidPartitionInfoGetFlag(_)
72 | Error::InvalidSuccessArgsVariant => Self::InvalidParameters,
Balint Dobszay3aad9572025-01-17 16:54:11 +010073 }
74 }
75}
Balint Dobszay5bf492f2024-07-29 17:21:32 +020076
Balint Dobszaya5846852025-02-26 15:38:53 +010077/// 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 +020078#[derive(PartialEq, Clone, Copy)]
79pub enum Instance {
Balint Dobszaya5846852025-02-26 15:38:53 +010080 /// The instance between the SPMC and SPMD.
Balint Dobszay5bf492f2024-07-29 17:21:32 +020081 SecurePhysical,
Balint Dobszaya5846852025-02-26 15:38:53 +010082 /// The instance between the SPMC and a physical SP (contains the SP's endpoint ID).
Balint Dobszay5bf492f2024-07-29 17:21:32 +020083 SecureVirtual(u16),
84}
85
Balint Dobszaya5846852025-02-26 15:38:53 +010086/// Function IDs of the various FF-A interfaces.
Andrew Walbran969b9252024-11-25 15:35:42 +000087#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
Balint Dobszay3aad9572025-01-17 16:54:11 +010088#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedFunctionId))]
Balint Dobszay5bf492f2024-07-29 17:21:32 +020089#[repr(u32)]
90pub enum FuncId {
91 Error = 0x84000060,
92 Success32 = 0x84000061,
93 Success64 = 0xc4000061,
94 Interrupt = 0x84000062,
95 Version = 0x84000063,
96 Features = 0x84000064,
97 RxAcquire = 0x84000084,
98 RxRelease = 0x84000065,
99 RxTxMap32 = 0x84000066,
100 RxTxMap64 = 0xc4000066,
101 RxTxUnmap = 0x84000067,
102 PartitionInfoGet = 0x84000068,
Balint Dobszaye6aa4862025-02-28 16:37:56 +0100103 PartitionInfoGetRegs = 0xc400008b,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200104 IdGet = 0x84000069,
105 SpmIdGet = 0x84000085,
Balint Dobszaye6aa4862025-02-28 16:37:56 +0100106 ConsoleLog32 = 0x8400008a,
107 ConsoleLog64 = 0xc400008a,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200108 MsgWait = 0x8400006b,
109 Yield = 0x8400006c,
110 Run = 0x8400006d,
111 NormalWorldResume = 0x8400007c,
112 MsgSend2 = 0x84000086,
113 MsgSendDirectReq32 = 0x8400006f,
114 MsgSendDirectReq64 = 0xc400006f,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100115 MsgSendDirectReq64_2 = 0xc400008d,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200116 MsgSendDirectResp32 = 0x84000070,
117 MsgSendDirectResp64 = 0xc4000070,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100118 MsgSendDirectResp64_2 = 0xc400008e,
Balint Dobszaye6aa4862025-02-28 16:37:56 +0100119 NotificationBitmapCreate = 0x8400007d,
120 NotificationBitmapDestroy = 0x8400007e,
121 NotificationBind = 0x8400007f,
122 NotificationUnbind = 0x84000080,
123 NotificationSet = 0x84000081,
124 NotificationGet = 0x84000082,
125 NotificationInfoGet32 = 0x84000083,
126 NotificationInfoGet64 = 0xc4000083,
127 El3IntrHandle = 0x8400008c,
Tomás González17b92442025-03-10 16:45:04 +0000128 SecondaryEpRegister32 = 0x84000087,
129 SecondaryEpRegister64 = 0xc4000087,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200130 MemDonate32 = 0x84000071,
131 MemDonate64 = 0xc4000071,
132 MemLend32 = 0x84000072,
133 MemLend64 = 0xc4000072,
134 MemShare32 = 0x84000073,
135 MemShare64 = 0xc4000073,
136 MemRetrieveReq32 = 0x84000074,
137 MemRetrieveReq64 = 0xc4000074,
138 MemRetrieveResp = 0x84000075,
139 MemRelinquish = 0x84000076,
140 MemReclaim = 0x84000077,
141 MemPermGet32 = 0x84000088,
142 MemPermGet64 = 0xc4000088,
143 MemPermSet32 = 0x84000089,
144 MemPermSet64 = 0xc4000089,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200145}
146
Balint Dobszayde0dc802025-02-28 14:16:52 +0100147impl FuncId {
148 /// Returns true if this is a 32-bit call, or false if it is a 64-bit call.
149 pub fn is_32bit(&self) -> bool {
Tomás González6ccba0a2025-04-09 13:31:29 +0100150 u32::from(*self) & (1 << 30) == 0
Balint Dobszayde0dc802025-02-28 14:16:52 +0100151 }
152}
153
Balint Dobszaya5846852025-02-26 15:38:53 +0100154/// Error status codes used by the `FFA_ERROR` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100155#[derive(Clone, Copy, Debug, Eq, Error, IntoPrimitive, PartialEq, TryFromPrimitive)]
156#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedErrorCode))]
157#[repr(i32)]
158pub enum FfaError {
159 #[error("Not supported")]
160 NotSupported = -1,
161 #[error("Invalid parameters")]
162 InvalidParameters = -2,
163 #[error("No memory")]
164 NoMemory = -3,
165 #[error("Busy")]
166 Busy = -4,
167 #[error("Interrupted")]
168 Interrupted = -5,
169 #[error("Denied")]
170 Denied = -6,
171 #[error("Retry")]
172 Retry = -7,
173 #[error("Aborted")]
174 Aborted = -8,
175 #[error("No data")]
176 NoData = -9,
177}
178
Balint Dobszaya5846852025-02-26 15:38:53 +0100179/// Endpoint ID and vCPU ID pair, used by `FFA_ERROR`, `FFA_INTERRUPT` and `FFA_RUN` interfaces.
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200180#[derive(Debug, Eq, PartialEq, Clone, Copy)]
Balint Dobszay3aad9572025-01-17 16:54:11 +0100181pub struct TargetInfo {
182 pub endpoint_id: u16,
183 pub vcpu_id: u16,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200184}
185
Balint Dobszay3aad9572025-01-17 16:54:11 +0100186impl From<u32> for TargetInfo {
187 fn from(value: u32) -> Self {
188 Self {
189 endpoint_id: (value >> 16) as u16,
190 vcpu_id: value as u16,
191 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200192 }
193}
194
Balint Dobszay3aad9572025-01-17 16:54:11 +0100195impl From<TargetInfo> for u32 {
196 fn from(value: TargetInfo) -> Self {
Balint Dobszaye9a3e762025-02-26 17:29:57 +0100197 ((value.endpoint_id as u32) << 16) | value.vcpu_id as u32
Andrew Walbran0d315812024-11-25 15:36:36 +0000198 }
Balint Dobszay3aad9572025-01-17 16:54:11 +0100199}
Andrew Walbran0d315812024-11-25 15:36:36 +0000200
Imre Kis839eaef2025-04-11 17:38:36 +0200201/// Generic arguments of the `FFA_SUCCESS` interface. The interpretation of the arguments depends on
202/// the interface that initiated the request. The application code has knowledge of the request, so
203/// it has to convert `SuccessArgs` into/from a specific success args structure that matches the
204/// request.
Imre Kis4e9d8bc2025-04-10 13:48:26 +0200205///
206/// The current specialized success arguments types are:
207/// * `FFA_FEATURES` - [`SuccessArgsFeatures`]
Imre Kisbbef2872025-04-10 14:11:29 +0200208/// * `FFA_ID_GET` - [`SuccessArgsIdGet`]
209/// * `FFA_SPM_ID_GET` - [`SuccessArgsSpmIdGet`]
Imre Kis61c34092025-04-10 14:14:38 +0200210/// * `FFA_PARTITION_INFO_GET` - [`partition_info::SuccessArgsPartitionInfoGet`]
Balint Dobszay3aad9572025-01-17 16:54:11 +0100211#[derive(Debug, Eq, PartialEq, Clone, Copy)]
212pub enum SuccessArgs {
Imre Kis54773b62025-04-10 13:47:39 +0200213 Args32([u32; 6]),
214 Args64([u64; 6]),
215 Args64_2([u64; 16]),
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200216}
217
Imre Kis839eaef2025-04-11 17:38:36 +0200218impl SuccessArgs {
219 fn try_get_args32(self) -> Result<[u32; 6], Error> {
220 match self {
221 SuccessArgs::Args32(args) => Ok(args),
222 SuccessArgs::Args64(_) | SuccessArgs::Args64_2(_) => {
223 Err(Error::InvalidSuccessArgsVariant)
224 }
225 }
226 }
227
228 fn try_get_args64(self) -> Result<[u64; 6], Error> {
229 match self {
230 SuccessArgs::Args64(args) => Ok(args),
231 SuccessArgs::Args32(_) | SuccessArgs::Args64_2(_) => {
232 Err(Error::InvalidSuccessArgsVariant)
233 }
234 }
235 }
236
237 fn try_get_args64_2(self) -> Result<[u64; 16], Error> {
238 match self {
239 SuccessArgs::Args64_2(args) => Ok(args),
240 SuccessArgs::Args32(_) | SuccessArgs::Args64(_) => {
241 Err(Error::InvalidSuccessArgsVariant)
242 }
243 }
244 }
245}
246
Tomás González17b92442025-03-10 16:45:04 +0000247/// Entrypoint address argument for `FFA_SECONDARY_EP_REGISTER` interface.
248#[derive(Debug, Eq, PartialEq, Clone, Copy)]
249pub enum SecondaryEpRegisterAddr {
250 Addr32(u32),
251 Addr64(u64),
252}
253
Balint Dobszaya5846852025-02-26 15:38:53 +0100254/// Version number of the FF-A implementation, `.0` is the major, `.1` is minor the version.
Balint Dobszayde0dc802025-02-28 14:16:52 +0100255#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord)]
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200256pub struct Version(pub u16, pub u16);
257
Tomás González1f794352025-03-03 16:47:06 +0000258impl Version {
Tomás González83146af2025-03-04 11:32:41 +0000259 // The FF-A spec mandates that bit[31] of a version number must be 0
260 const MBZ_BITS: u32 = 1 << 31;
261
Tomás González1f794352025-03-03 16:47:06 +0000262 /// Returns whether the caller's version (self) is compatible with the callee's version (input
263 /// parameter)
264 pub fn is_compatible_to(&self, callee_version: &Version) -> bool {
265 self.0 == callee_version.0 && self.1 <= callee_version.1
266 }
267}
268
Tomás González83146af2025-03-04 11:32:41 +0000269impl TryFrom<u32> for Version {
270 type Error = Error;
271
272 fn try_from(val: u32) -> Result<Self, Self::Error> {
273 if (val & Self::MBZ_BITS) != 0 {
274 Err(Error::InvalidVersion(val))
275 } else {
276 Ok(Self((val >> 16) as u16, val as u16))
277 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200278 }
279}
280
281impl From<Version> for u32 {
282 fn from(v: Version) -> Self {
Tomás González83146af2025-03-04 11:32:41 +0000283 let v_u32 = ((v.0 as u32) << 16) | v.1 as u32;
284 assert!(v_u32 & Version::MBZ_BITS == 0);
285 v_u32
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200286 }
287}
288
Andrew Walbran19970ba2024-11-25 15:35:00 +0000289impl Display for Version {
290 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
291 write!(f, "{}.{}", self.0, self.1)
292 }
293}
294
295impl Debug for Version {
296 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
297 Display::fmt(self, f)
298 }
299}
300
Balint Dobszaya5846852025-02-26 15:38:53 +0100301/// Feature IDs used by the `FFA_FEATURES` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100302#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
303#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedFeatureId))]
304#[repr(u8)]
305pub enum FeatureId {
306 NotificationPendingInterrupt = 0x1,
307 ScheduleReceiverInterrupt = 0x2,
308 ManagedExitInterrupt = 0x3,
309}
Balint Dobszayc8802492025-01-15 18:11:39 +0100310
Balint Dobszaya5846852025-02-26 15:38:53 +0100311/// Arguments for the `FFA_FEATURES` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100312#[derive(Debug, Eq, PartialEq, Clone, Copy)]
313pub enum Feature {
314 FuncId(FuncId),
315 FeatureId(FeatureId),
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100316 Unknown(u32),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100317}
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200318
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100319impl From<u32> for Feature {
320 fn from(value: u32) -> Self {
321 // Bit[31] is set for all valid FF-A function IDs so we don't have to check it separately
322 if let Ok(func_id) = value.try_into() {
323 Self::FuncId(func_id)
324 } else if let Ok(feat_id) = (value as u8).try_into() {
325 Self::FeatureId(feat_id)
Balint Dobszay3aad9572025-01-17 16:54:11 +0100326 } else {
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100327 Self::Unknown(value)
328 }
Balint Dobszay3aad9572025-01-17 16:54:11 +0100329 }
330}
331
332impl From<Feature> for u32 {
333 fn from(value: Feature) -> Self {
334 match value {
335 Feature::FuncId(func_id) => (1 << 31) | func_id as u32,
336 Feature::FeatureId(feature_id) => feature_id as u32,
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100337 Feature::Unknown(id) => panic!("Unknown feature or function ID {:#x?}", id),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100338 }
339 }
340}
341
Imre Kis4e9d8bc2025-04-10 13:48:26 +0200342/// `FFA_FEATURES` specific success argument structure. This type needs further specialization based
343/// on 'FF-A function ID or Feature ID' field of the preceeding `FFA_FEATURES` request.
344#[derive(Debug, Eq, PartialEq, Clone, Copy)]
345pub struct SuccessArgsFeatures {
346 pub properties: [u32; 2],
347}
348
349impl From<SuccessArgsFeatures> for SuccessArgs {
350 fn from(value: SuccessArgsFeatures) -> Self {
351 Self::Args32([value.properties[0], value.properties[1], 0, 0, 0, 0])
352 }
353}
354
355impl TryFrom<SuccessArgs> for SuccessArgsFeatures {
356 type Error = Error;
357
358 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
359 let args = value.try_get_args32()?;
360
361 Ok(Self {
362 properties: [args[0], args[1]],
363 })
364 }
365}
366
Balint Dobszaya5846852025-02-26 15:38:53 +0100367/// RXTX buffer descriptor, used by `FFA_RXTX_MAP`.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100368#[derive(Debug, Eq, PartialEq, Clone, Copy)]
369pub enum RxTxAddr {
370 Addr32 { rx: u32, tx: u32 },
371 Addr64 { rx: u64, tx: u64 },
372}
373
Imre Kisbbef2872025-04-10 14:11:29 +0200374/// `FFA_ID_GET` specific success argument structure.
375#[derive(Debug, Eq, PartialEq, Clone, Copy)]
376pub struct SuccessArgsIdGet {
377 pub id: u16,
378}
379
380impl From<SuccessArgsIdGet> for SuccessArgs {
381 fn from(value: SuccessArgsIdGet) -> Self {
382 SuccessArgs::Args32([value.id as u32, 0, 0, 0, 0, 0])
383 }
384}
385
386impl TryFrom<SuccessArgs> for SuccessArgsIdGet {
387 type Error = Error;
388
389 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
390 let args = value.try_get_args32()?;
391 Ok(Self { id: args[0] as u16 })
392 }
393}
394
395/// `FFA_SPM_ID_GET` specific success argument structure.
396#[derive(Debug, Eq, PartialEq, Clone, Copy)]
397pub struct SuccessArgsSpmIdGet {
398 pub id: u16,
399}
400
401impl From<SuccessArgsSpmIdGet> for SuccessArgs {
402 fn from(value: SuccessArgsSpmIdGet) -> Self {
403 SuccessArgs::Args32([value.id as u32, 0, 0, 0, 0, 0])
404 }
405}
406
407impl TryFrom<SuccessArgs> for SuccessArgsSpmIdGet {
408 type Error = Error;
409
410 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
411 let args = value.try_get_args32()?;
412 Ok(Self { id: args[0] as u16 })
413 }
414}
415
Imre Kise295adb2025-04-10 13:26:28 +0200416/// Flags of the `FFA_PARTITION_INFO_GET` interface.
417#[derive(Debug, Eq, PartialEq, Clone, Copy)]
418pub struct PartitionInfoGetFlags {
419 pub count_only: bool,
420}
421
422impl PartitionInfoGetFlags {
423 const RETURN_INFORMATION_TYPE_FLAG: u32 = 1 << 0;
424 const MBZ_BITS: u32 = 0xffff_fffe;
425}
426
427impl TryFrom<u32> for PartitionInfoGetFlags {
428 type Error = Error;
429
430 fn try_from(val: u32) -> Result<Self, Self::Error> {
431 if (val & Self::MBZ_BITS) != 0 {
432 Err(Error::InvalidPartitionInfoGetFlag(val))
433 } else {
434 Ok(Self {
435 count_only: val & Self::RETURN_INFORMATION_TYPE_FLAG != 0,
436 })
437 }
438 }
439}
440
441impl From<PartitionInfoGetFlags> for u32 {
442 fn from(flags: PartitionInfoGetFlags) -> Self {
443 let mut bits: u32 = 0;
444 if flags.count_only {
445 bits |= PartitionInfoGetFlags::RETURN_INFORMATION_TYPE_FLAG;
446 }
447 bits
448 }
449}
450
Tomás González4d5b0ba2025-03-03 17:15:55 +0000451/// Composite type for capturing success and error return codes for the VM availability messages.
452///
453/// Error codes are handled by the `FfaError` type. Having a separate type for errors helps using
454/// `Result<(), FfaError>`. If a single type would include both success and error values,
455/// then `Err(FfaError::Success)` would be incomprehensible.
456#[derive(Debug, Eq, PartialEq, Clone, Copy)]
457pub enum VmAvailabilityStatus {
458 Success,
459 Error(FfaError),
460}
461
462impl TryFrom<i32> for VmAvailabilityStatus {
463 type Error = Error;
464 fn try_from(value: i32) -> Result<Self, <Self as TryFrom<i32>>::Error> {
465 Ok(match value {
466 0 => Self::Success,
467 error_code => Self::Error(FfaError::try_from(error_code)?),
468 })
469 }
470}
471
472impl From<VmAvailabilityStatus> for i32 {
473 fn from(value: VmAvailabilityStatus) -> Self {
474 match value {
475 VmAvailabilityStatus::Success => 0,
476 VmAvailabilityStatus::Error(error_code) => error_code.into(),
477 }
478 }
479}
480
481/// Arguments for the Power Warm Boot `FFA_MSG_SEND_DIRECT_REQ` interface.
482#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
483#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedWarmBootType))]
484#[repr(u32)]
485pub enum WarmBootType {
486 ExitFromSuspend = 0,
487 ExitFromLowPower = 1,
488}
489
Balint Dobszaya5846852025-02-26 15:38:53 +0100490/// Arguments for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}` interfaces.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100491#[derive(Debug, Eq, PartialEq, Clone, Copy)]
492pub enum DirectMsgArgs {
493 Args32([u32; 5]),
494 Args64([u64; 5]),
Tomás González4d5b0ba2025-03-03 17:15:55 +0000495 /// Message for forwarding FFA_VERSION call from Normal world to the SPMC
496 VersionReq {
497 version: Version,
498 },
499 /// Response message to forwarded FFA_VERSION call from the Normal world
500 /// Contains the version returned by the SPMC or None
501 VersionResp {
502 version: Option<Version>,
503 },
504 /// Message for a power management operation initiated by a PSCI function
505 PowerPsciReq32 {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000506 // params[i]: Input parameter in w[i] in PSCI function invocation at EL3.
Tomás González67f92c72025-03-20 16:50:42 +0000507 // params[0]: Function ID.
508 params: [u32; 4],
Tomás González4d5b0ba2025-03-03 17:15:55 +0000509 },
510 /// Message for a power management operation initiated by a PSCI function
511 PowerPsciReq64 {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000512 // params[i]: Input parameter in x[i] in PSCI function invocation at EL3.
Tomás González67f92c72025-03-20 16:50:42 +0000513 // params[0]: Function ID.
514 params: [u64; 4],
Tomás González4d5b0ba2025-03-03 17:15:55 +0000515 },
516 /// Message for a warm boot
517 PowerWarmBootReq {
518 boot_type: WarmBootType,
519 },
520 /// Response message to indicate return status of the last power management request message
521 /// Return error code SUCCESS or DENIED as defined in PSCI spec. Caller is left to do the
522 /// parsing of the return status.
523 PowerPsciResp {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000524 psci_status: i32,
525 },
526 /// Message to signal creation of a VM
527 VmCreated {
528 // Globally unique Handle to identify a memory region that contains IMPLEMENTATION DEFINED
529 // information associated with the created VM.
530 // The invalid memory region handle must be specified by the Hypervisor if this field is not
531 // used.
532 handle: memory_management::Handle,
533 vm_id: u16,
534 },
535 /// Message to acknowledge creation of a VM
536 VmCreatedAck {
537 sp_status: VmAvailabilityStatus,
538 },
539 /// Message to signal destruction of a VM
540 VmDestructed {
541 // Globally unique Handle to identify a memory region that contains IMPLEMENTATION DEFINED
542 // information associated with the created VM.
543 // The invalid memory region handle must be specified by the Hypervisor if this field is not
544 // used.
545 handle: memory_management::Handle,
546 vm_id: u16,
547 },
548 /// Message to acknowledge destruction of a VM
549 VmDestructedAck {
550 sp_status: VmAvailabilityStatus,
551 },
552}
553
554impl DirectMsgArgs {
555 // Flags for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}` interfaces.
556
557 const FWK_MSG_BITS: u32 = 1 << 31;
558 const VERSION_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b1000;
559 const VERSION_RESP: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b1001;
560 const POWER_PSCI_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS;
561 const POWER_WARM_BOOT_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0001;
562 const POWER_PSCI_RESP: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0010;
563 const VM_CREATED: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0100;
564 const VM_CREATED_ACK: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0101;
565 const VM_DESTRUCTED: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0110;
566 const VM_DESTRUCTED_ACK: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0111;
Balint Dobszay3aad9572025-01-17 16:54:11 +0100567}
568
Balint Dobszayde0dc802025-02-28 14:16:52 +0100569/// Arguments for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}2` interfaces.
570#[derive(Debug, Eq, PartialEq, Clone, Copy)]
571pub struct DirectMsg2Args([u64; 14]);
572
Tomás González092202a2025-03-05 11:56:45 +0000573#[derive(Debug, Eq, PartialEq, Clone, Copy)]
574pub struct MsgWaitFlags {
575 retain_rx_buffer: bool,
576}
577
578impl MsgWaitFlags {
579 const RETAIN_RX_BUFFER: u32 = 0x01;
580 const MBZ_BITS: u32 = 0xfffe;
581}
582
583impl TryFrom<u32> for MsgWaitFlags {
584 type Error = Error;
585
586 fn try_from(val: u32) -> Result<Self, Self::Error> {
587 if (val & Self::MBZ_BITS) != 0 {
588 Err(Error::InvalidMsgWaitFlag(val))
589 } else {
590 Ok(MsgWaitFlags {
591 retain_rx_buffer: val & Self::RETAIN_RX_BUFFER != 0,
592 })
593 }
594 }
595}
596
597impl From<MsgWaitFlags> for u32 {
598 fn from(flags: MsgWaitFlags) -> Self {
599 let mut bits: u32 = 0;
600 if flags.retain_rx_buffer {
601 bits |= MsgWaitFlags::RETAIN_RX_BUFFER;
602 }
603 bits
604 }
605}
606
Balint Dobszaya5846852025-02-26 15:38:53 +0100607/// Descriptor for a dynamically allocated memory buffer that contains the memory transaction
Tomás Gonzálezf268e322025-03-05 11:18:11 +0000608/// descriptor.
609///
610/// Used by `FFA_MEM_{DONATE,LEND,SHARE,RETRIEVE_REQ}` interfaces, only when the TX buffer is not
611/// used to transmit the transaction descriptor.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100612#[derive(Debug, Eq, PartialEq, Clone, Copy)]
613pub enum MemOpBuf {
614 Buf32 { addr: u32, page_cnt: u32 },
615 Buf64 { addr: u64, page_cnt: u32 },
616}
617
Balint Dobszaya5846852025-02-26 15:38:53 +0100618/// Memory address argument for `FFA_MEM_PERM_{GET,SET}` interfaces.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100619#[derive(Debug, Eq, PartialEq, Clone, Copy)]
620pub enum MemAddr {
621 Addr32(u32),
622 Addr64(u64),
623}
624
Balint Dobszayde0dc802025-02-28 14:16:52 +0100625/// Argument for the `FFA_CONSOLE_LOG` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100626#[derive(Debug, Eq, PartialEq, Clone, Copy)]
627pub enum ConsoleLogChars {
628 Reg32([u32; 6]),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100629 Reg64([u64; 16]),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100630}
631
Tomás González7ffb6132025-04-03 12:28:58 +0100632#[derive(Debug, Eq, PartialEq, Clone, Copy)]
633pub struct NotificationBindFlags {
634 per_vcpu_notification: bool,
635}
636
637impl NotificationBindFlags {
638 const PER_VCPU_NOTIFICATION: u32 = 1;
639}
640
641impl From<NotificationBindFlags> for u32 {
642 fn from(flags: NotificationBindFlags) -> Self {
643 let mut bits: u32 = 0;
644 if flags.per_vcpu_notification {
645 bits |= NotificationBindFlags::PER_VCPU_NOTIFICATION;
646 }
647 bits
648 }
649}
650
651impl From<u32> for NotificationBindFlags {
652 fn from(flags: u32) -> Self {
653 Self {
654 per_vcpu_notification: flags & Self::PER_VCPU_NOTIFICATION != 0,
655 }
656 }
657}
658
659#[derive(Debug, Eq, PartialEq, Clone, Copy)]
660pub struct NotificationSetFlags {
661 delay_schedule_receiver: bool,
662 vcpu_id: Option<u16>,
663}
664
665impl NotificationSetFlags {
666 const PER_VCP_NOTIFICATION: u32 = 1 << 0;
667 const DELAY_SCHEDULE_RECEIVER: u32 = 1 << 1;
668 const VCPU_ID_SHIFT: u32 = 16;
669
670 const MBZ_BITS: u32 = 0xfffc;
671}
672
673impl From<NotificationSetFlags> for u32 {
674 fn from(flags: NotificationSetFlags) -> Self {
675 let mut bits: u32 = 0;
676
677 if flags.delay_schedule_receiver {
678 bits |= NotificationSetFlags::DELAY_SCHEDULE_RECEIVER;
679 }
680 if let Some(vcpu_id) = flags.vcpu_id {
681 bits |= NotificationSetFlags::PER_VCP_NOTIFICATION;
682 bits |= u32::from(vcpu_id) << NotificationSetFlags::VCPU_ID_SHIFT;
683 }
684
685 bits
686 }
687}
688
689impl TryFrom<u32> for NotificationSetFlags {
690 type Error = Error;
691
692 fn try_from(flags: u32) -> Result<Self, Self::Error> {
693 if (flags & Self::MBZ_BITS) != 0 {
694 return Err(Error::InvalidNotificationSetFlag(flags));
695 }
696
697 let tentative_vcpu_id = (flags >> Self::VCPU_ID_SHIFT) as u16;
698
699 let vcpu_id = if (flags & Self::PER_VCP_NOTIFICATION) != 0 {
700 Some(tentative_vcpu_id)
701 } else {
702 if tentative_vcpu_id != 0 {
703 return Err(Error::InvalidNotificationSetFlag(flags));
704 }
705 None
706 };
707
708 Ok(Self {
709 delay_schedule_receiver: (flags & Self::DELAY_SCHEDULE_RECEIVER) != 0,
710 vcpu_id,
711 })
712 }
713}
714
715#[derive(Debug, Eq, PartialEq, Clone, Copy)]
716pub struct NotificationGetFlags {
717 sp_bitmap_id: bool,
718 vm_bitmap_id: bool,
719 spm_bitmap_id: bool,
720 hyp_bitmap_id: bool,
721}
722
723impl NotificationGetFlags {
724 const SP_BITMAP_ID: u32 = 1;
725 const VM_BITMAP_ID: u32 = 1 << 1;
726 const SPM_BITMAP_ID: u32 = 1 << 2;
727 const HYP_BITMAP_ID: u32 = 1 << 3;
728}
729
730impl From<NotificationGetFlags> for u32 {
731 fn from(flags: NotificationGetFlags) -> Self {
732 let mut bits: u32 = 0;
733 if flags.sp_bitmap_id {
734 bits |= NotificationGetFlags::SP_BITMAP_ID;
735 }
736 if flags.vm_bitmap_id {
737 bits |= NotificationGetFlags::VM_BITMAP_ID;
738 }
739 if flags.spm_bitmap_id {
740 bits |= NotificationGetFlags::SPM_BITMAP_ID;
741 }
742 if flags.hyp_bitmap_id {
743 bits |= NotificationGetFlags::HYP_BITMAP_ID;
744 }
745 bits
746 }
747}
748
749impl From<u32> for NotificationGetFlags {
750 // This is a "from" instead of a "try_from" because Reserved Bits are SBZ, *not* MBZ.
751 fn from(flags: u32) -> Self {
752 Self {
753 sp_bitmap_id: (flags & Self::SP_BITMAP_ID) != 0,
754 vm_bitmap_id: (flags & Self::VM_BITMAP_ID) != 0,
755 spm_bitmap_id: (flags & Self::SPM_BITMAP_ID) != 0,
756 hyp_bitmap_id: (flags & Self::HYP_BITMAP_ID) != 0,
757 }
758 }
759}
760
Tomás Gonzálezf268e322025-03-05 11:18:11 +0000761/// FF-A "message types", the terminology used by the spec is "interfaces".
762///
763/// The interfaces are used by FF-A components for communication at an FF-A instance. The spec also
764/// describes the valid FF-A instances and conduits for each interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100765#[derive(Debug, Eq, PartialEq, Clone, Copy)]
766pub enum Interface {
767 Error {
768 target_info: TargetInfo,
769 error_code: FfaError,
Balint Dobszayb727aab2025-04-07 10:24:59 +0200770 error_arg: u32,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100771 },
772 Success {
773 target_info: u32,
774 args: SuccessArgs,
775 },
776 Interrupt {
777 target_info: TargetInfo,
778 interrupt_id: u32,
779 },
780 Version {
781 input_version: Version,
782 },
783 VersionOut {
784 output_version: Version,
785 },
786 Features {
787 feat_id: Feature,
788 input_properties: u32,
789 },
790 RxAcquire {
791 vm_id: u16,
792 },
793 RxRelease {
794 vm_id: u16,
795 },
796 RxTxMap {
797 addr: RxTxAddr,
798 page_cnt: u32,
799 },
800 RxTxUnmap {
801 id: u16,
802 },
803 PartitionInfoGet {
804 uuid: Uuid,
Imre Kise295adb2025-04-10 13:26:28 +0200805 flags: PartitionInfoGetFlags,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100806 },
Tomás González0a058bc2025-03-11 11:20:55 +0000807 PartitionInfoGetRegs {
808 uuid: Uuid,
809 start_index: u16,
810 info_tag: u16,
811 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100812 IdGet,
813 SpmIdGet,
Tomás González092202a2025-03-05 11:56:45 +0000814 MsgWait {
815 flags: Option<MsgWaitFlags>,
816 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100817 Yield,
818 Run {
819 target_info: TargetInfo,
820 },
821 NormalWorldResume,
Tomás González17b92442025-03-10 16:45:04 +0000822 SecondaryEpRegister {
823 entrypoint: SecondaryEpRegisterAddr,
824 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100825 MsgSend2 {
826 sender_vm_id: u16,
827 flags: u32,
828 },
829 MsgSendDirectReq {
830 src_id: u16,
831 dst_id: u16,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100832 args: DirectMsgArgs,
833 },
834 MsgSendDirectResp {
835 src_id: u16,
836 dst_id: u16,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100837 args: DirectMsgArgs,
838 },
Balint Dobszayde0dc802025-02-28 14:16:52 +0100839 MsgSendDirectReq2 {
840 src_id: u16,
841 dst_id: u16,
842 uuid: Uuid,
843 args: DirectMsg2Args,
844 },
845 MsgSendDirectResp2 {
846 src_id: u16,
847 dst_id: u16,
848 args: DirectMsg2Args,
849 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100850 MemDonate {
851 total_len: u32,
852 frag_len: u32,
853 buf: Option<MemOpBuf>,
854 },
855 MemLend {
856 total_len: u32,
857 frag_len: u32,
858 buf: Option<MemOpBuf>,
859 },
860 MemShare {
861 total_len: u32,
862 frag_len: u32,
863 buf: Option<MemOpBuf>,
864 },
865 MemRetrieveReq {
866 total_len: u32,
867 frag_len: u32,
868 buf: Option<MemOpBuf>,
869 },
870 MemRetrieveResp {
871 total_len: u32,
872 frag_len: u32,
873 },
874 MemRelinquish,
875 MemReclaim {
876 handle: memory_management::Handle,
877 flags: u32,
878 },
879 MemPermGet {
880 addr: MemAddr,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100881 page_cnt: Option<u32>,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100882 },
883 MemPermSet {
884 addr: MemAddr,
885 page_cnt: u32,
886 mem_perm: u32,
887 },
888 ConsoleLog {
889 char_cnt: u8,
890 char_lists: ConsoleLogChars,
891 },
Tomás González7ffb6132025-04-03 12:28:58 +0100892 NotificationBitmapCreate {
893 vm_id: u16,
894 vcpu_cnt: u32,
895 },
896 NotificationBitmapDestroy {
897 vm_id: u16,
898 },
899 NotificationBind {
900 sender_id: u16,
901 receiver_id: u16,
902 flags: NotificationBindFlags,
903 bitmap: u64,
904 },
905 NotificationUnBind {
906 sender_id: u16,
907 receiver_id: u16,
908 bitmap: u64,
909 },
910 NotificationSet {
911 sender_id: u16,
912 receiver_id: u16,
913 flags: NotificationSetFlags,
914 bitmap: u64,
915 },
916 NotificationGet {
917 vcpu_id: u16,
918 endpoint_id: u16,
919 flags: NotificationGetFlags,
920 },
921 NotificationInfoGet {
922 is_32bit: bool,
923 },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +0100924 El3IntrHandle,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100925}
926
Balint Dobszayde0dc802025-02-28 14:16:52 +0100927impl Interface {
928 /// Returns the function ID for the call, if it has one.
929 pub fn function_id(&self) -> Option<FuncId> {
930 match self {
931 Interface::Error { .. } => Some(FuncId::Error),
932 Interface::Success { args, .. } => match args {
Imre Kis54773b62025-04-10 13:47:39 +0200933 SuccessArgs::Args32(..) => Some(FuncId::Success32),
934 SuccessArgs::Args64(..) | SuccessArgs::Args64_2(..) => Some(FuncId::Success64),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100935 },
936 Interface::Interrupt { .. } => Some(FuncId::Interrupt),
937 Interface::Version { .. } => Some(FuncId::Version),
938 Interface::VersionOut { .. } => None,
939 Interface::Features { .. } => Some(FuncId::Features),
940 Interface::RxAcquire { .. } => Some(FuncId::RxAcquire),
941 Interface::RxRelease { .. } => Some(FuncId::RxRelease),
942 Interface::RxTxMap { addr, .. } => match addr {
943 RxTxAddr::Addr32 { .. } => Some(FuncId::RxTxMap32),
944 RxTxAddr::Addr64 { .. } => Some(FuncId::RxTxMap64),
945 },
946 Interface::RxTxUnmap { .. } => Some(FuncId::RxTxUnmap),
947 Interface::PartitionInfoGet { .. } => Some(FuncId::PartitionInfoGet),
Tomás González0a058bc2025-03-11 11:20:55 +0000948 Interface::PartitionInfoGetRegs { .. } => Some(FuncId::PartitionInfoGetRegs),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100949 Interface::IdGet => Some(FuncId::IdGet),
950 Interface::SpmIdGet => Some(FuncId::SpmIdGet),
Tomás González092202a2025-03-05 11:56:45 +0000951 Interface::MsgWait { .. } => Some(FuncId::MsgWait),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100952 Interface::Yield => Some(FuncId::Yield),
953 Interface::Run { .. } => Some(FuncId::Run),
954 Interface::NormalWorldResume => Some(FuncId::NormalWorldResume),
Tomás González17b92442025-03-10 16:45:04 +0000955 Interface::SecondaryEpRegister { entrypoint } => match entrypoint {
956 SecondaryEpRegisterAddr::Addr32 { .. } => Some(FuncId::SecondaryEpRegister32),
957 SecondaryEpRegisterAddr::Addr64 { .. } => Some(FuncId::SecondaryEpRegister64),
958 },
Balint Dobszayde0dc802025-02-28 14:16:52 +0100959 Interface::MsgSend2 { .. } => Some(FuncId::MsgSend2),
960 Interface::MsgSendDirectReq { args, .. } => match args {
961 DirectMsgArgs::Args32(_) => Some(FuncId::MsgSendDirectReq32),
962 DirectMsgArgs::Args64(_) => Some(FuncId::MsgSendDirectReq64),
Tomás González4d5b0ba2025-03-03 17:15:55 +0000963 DirectMsgArgs::VersionReq { .. } => Some(FuncId::MsgSendDirectReq32),
964 DirectMsgArgs::PowerPsciReq32 { .. } => Some(FuncId::MsgSendDirectReq32),
965 DirectMsgArgs::PowerPsciReq64 { .. } => Some(FuncId::MsgSendDirectReq64),
966 DirectMsgArgs::PowerWarmBootReq { .. } => Some(FuncId::MsgSendDirectReq32),
967 DirectMsgArgs::VmCreated { .. } => Some(FuncId::MsgSendDirectReq32),
968 DirectMsgArgs::VmDestructed { .. } => Some(FuncId::MsgSendDirectReq32),
969 _ => None,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100970 },
971 Interface::MsgSendDirectResp { args, .. } => match args {
972 DirectMsgArgs::Args32(_) => Some(FuncId::MsgSendDirectResp32),
973 DirectMsgArgs::Args64(_) => Some(FuncId::MsgSendDirectResp64),
Tomás González4d5b0ba2025-03-03 17:15:55 +0000974 DirectMsgArgs::VersionResp { .. } => Some(FuncId::MsgSendDirectResp32),
975 DirectMsgArgs::PowerPsciResp { .. } => Some(FuncId::MsgSendDirectResp32),
976 DirectMsgArgs::VmCreatedAck { .. } => Some(FuncId::MsgSendDirectResp32),
977 DirectMsgArgs::VmDestructedAck { .. } => Some(FuncId::MsgSendDirectResp32),
978 _ => None,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100979 },
980 Interface::MsgSendDirectReq2 { .. } => Some(FuncId::MsgSendDirectReq64_2),
981 Interface::MsgSendDirectResp2 { .. } => Some(FuncId::MsgSendDirectResp64_2),
982 Interface::MemDonate { buf, .. } => match buf {
983 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemDonate64),
984 _ => Some(FuncId::MemDonate32),
985 },
986 Interface::MemLend { buf, .. } => match buf {
987 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemLend64),
988 _ => Some(FuncId::MemLend32),
989 },
990 Interface::MemShare { buf, .. } => match buf {
991 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemShare64),
992 _ => Some(FuncId::MemShare32),
993 },
994 Interface::MemRetrieveReq { buf, .. } => match buf {
995 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemRetrieveReq64),
996 _ => Some(FuncId::MemRetrieveReq32),
997 },
998 Interface::MemRetrieveResp { .. } => Some(FuncId::MemRetrieveResp),
999 Interface::MemRelinquish => Some(FuncId::MemRelinquish),
1000 Interface::MemReclaim { .. } => Some(FuncId::MemReclaim),
1001 Interface::MemPermGet { addr, .. } => match addr {
1002 MemAddr::Addr32(_) => Some(FuncId::MemPermGet32),
1003 MemAddr::Addr64(_) => Some(FuncId::MemPermGet64),
1004 },
1005 Interface::MemPermSet { addr, .. } => match addr {
1006 MemAddr::Addr32(_) => Some(FuncId::MemPermSet32),
1007 MemAddr::Addr64(_) => Some(FuncId::MemPermSet64),
1008 },
1009 Interface::ConsoleLog { char_lists, .. } => match char_lists {
1010 ConsoleLogChars::Reg32(_) => Some(FuncId::ConsoleLog32),
1011 ConsoleLogChars::Reg64(_) => Some(FuncId::ConsoleLog64),
1012 },
Tomás González7ffb6132025-04-03 12:28:58 +01001013 Interface::NotificationBitmapCreate { .. } => Some(FuncId::NotificationBitmapCreate),
1014 Interface::NotificationBitmapDestroy { .. } => Some(FuncId::NotificationBitmapDestroy),
1015 Interface::NotificationBind { .. } => Some(FuncId::NotificationBind),
1016 Interface::NotificationUnBind { .. } => Some(FuncId::NotificationUnbind),
1017 Interface::NotificationSet { .. } => Some(FuncId::NotificationSet),
1018 Interface::NotificationGet { .. } => Some(FuncId::NotificationGet),
1019 Interface::NotificationInfoGet { is_32bit } => match is_32bit {
1020 true => Some(FuncId::NotificationInfoGet32),
1021 false => Some(FuncId::NotificationInfoGet64),
1022 },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01001023 Interface::El3IntrHandle => Some(FuncId::El3IntrHandle),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001024 }
1025 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01001026
Balint Dobszayde0dc802025-02-28 14:16:52 +01001027 /// Returns true if this is a 32-bit call, or false if it is a 64-bit call.
1028 pub fn is_32bit(&self) -> bool {
1029 // TODO: self should always have a function ID?
1030 self.function_id().unwrap().is_32bit()
1031 }
1032
1033 /// Parse interface from register contents. The caller must ensure that the `regs` argument has
1034 /// the correct length: 8 registers for FF-A v1.1 and lower, 18 registers for v1.2 and higher.
1035 pub fn from_regs(version: Version, regs: &[u64]) -> Result<Self, Error> {
1036 let reg_cnt = regs.len();
1037
1038 let msg = match reg_cnt {
1039 8 => {
1040 assert!(version <= Version(1, 1));
1041 Interface::unpack_regs8(version, regs.try_into().unwrap())?
1042 }
1043 18 => {
1044 assert!(version >= Version(1, 2));
1045 match FuncId::try_from(regs[0] as u32)? {
1046 FuncId::ConsoleLog64
1047 | FuncId::Success64
1048 | FuncId::MsgSendDirectReq64_2
Tomás González0a058bc2025-03-11 11:20:55 +00001049 | FuncId::MsgSendDirectResp64_2
1050 | FuncId::PartitionInfoGetRegs => {
Balint Dobszayde0dc802025-02-28 14:16:52 +01001051 Interface::unpack_regs18(version, regs.try_into().unwrap())?
1052 }
1053 _ => Interface::unpack_regs8(version, regs[..8].try_into().unwrap())?,
1054 }
1055 }
1056 _ => panic!(
1057 "Invalid number of registers ({}) for FF-A version {}",
1058 reg_cnt, version
1059 ),
1060 };
1061
1062 Ok(msg)
1063 }
1064
1065 fn unpack_regs8(version: Version, regs: &[u64; 8]) -> Result<Self, Error> {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001066 let fid = FuncId::try_from(regs[0] as u32)?;
1067
1068 let msg = match fid {
1069 FuncId::Error => Self::Error {
1070 target_info: (regs[1] as u32).into(),
1071 error_code: FfaError::try_from(regs[2] as i32)?,
Balint Dobszayb727aab2025-04-07 10:24:59 +02001072 error_arg: regs[3] as u32,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001073 },
1074 FuncId::Success32 => Self::Success {
1075 target_info: regs[1] as u32,
Imre Kis54773b62025-04-10 13:47:39 +02001076 args: SuccessArgs::Args32([
Balint Dobszay3aad9572025-01-17 16:54:11 +01001077 regs[2] as u32,
1078 regs[3] as u32,
1079 regs[4] as u32,
1080 regs[5] as u32,
1081 regs[6] as u32,
1082 regs[7] as u32,
1083 ]),
1084 },
1085 FuncId::Success64 => Self::Success {
1086 target_info: regs[1] as u32,
Imre Kis54773b62025-04-10 13:47:39 +02001087 args: SuccessArgs::Args64([regs[2], regs[3], regs[4], regs[5], regs[6], regs[7]]),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001088 },
1089 FuncId::Interrupt => Self::Interrupt {
1090 target_info: (regs[1] as u32).into(),
1091 interrupt_id: regs[2] as u32,
1092 },
1093 FuncId::Version => Self::Version {
Tomás González83146af2025-03-04 11:32:41 +00001094 input_version: (regs[1] as u32).try_into()?,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001095 },
1096 FuncId::Features => Self::Features {
Balint Dobszayc31e0b92025-03-03 20:16:56 +01001097 feat_id: (regs[1] as u32).into(),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001098 input_properties: regs[2] as u32,
1099 },
1100 FuncId::RxAcquire => Self::RxAcquire {
1101 vm_id: regs[1] as u16,
1102 },
1103 FuncId::RxRelease => Self::RxRelease {
1104 vm_id: regs[1] as u16,
1105 },
1106 FuncId::RxTxMap32 => {
1107 let addr = RxTxAddr::Addr32 {
1108 rx: regs[2] as u32,
1109 tx: regs[1] as u32,
1110 };
1111 let page_cnt = regs[3] as u32;
1112
1113 Self::RxTxMap { addr, page_cnt }
1114 }
1115 FuncId::RxTxMap64 => {
1116 let addr = RxTxAddr::Addr64 {
1117 rx: regs[2],
1118 tx: regs[1],
1119 };
1120 let page_cnt = regs[3] as u32;
1121
1122 Self::RxTxMap { addr, page_cnt }
1123 }
1124 FuncId::RxTxUnmap => Self::RxTxUnmap { id: regs[1] as u16 },
1125 FuncId::PartitionInfoGet => {
1126 let uuid_words = [
1127 regs[1] as u32,
1128 regs[2] as u32,
1129 regs[3] as u32,
1130 regs[4] as u32,
1131 ];
1132 let mut bytes: [u8; 16] = [0; 16];
1133 for (i, b) in uuid_words.iter().flat_map(|w| w.to_le_bytes()).enumerate() {
1134 bytes[i] = b;
1135 }
1136 Self::PartitionInfoGet {
1137 uuid: Uuid::from_bytes(bytes),
Imre Kise295adb2025-04-10 13:26:28 +02001138 flags: PartitionInfoGetFlags::try_from(regs[5] as u32)?,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001139 }
1140 }
1141 FuncId::IdGet => Self::IdGet,
1142 FuncId::SpmIdGet => Self::SpmIdGet,
Tomás González092202a2025-03-05 11:56:45 +00001143 FuncId::MsgWait => Self::MsgWait {
1144 flags: if version >= Version(1, 2) {
1145 Some(MsgWaitFlags::try_from(regs[2] as u32)?)
1146 } else {
1147 None
1148 },
1149 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001150 FuncId::Yield => Self::Yield,
1151 FuncId::Run => Self::Run {
1152 target_info: (regs[1] as u32).into(),
1153 },
1154 FuncId::NormalWorldResume => Self::NormalWorldResume,
Tomás González17b92442025-03-10 16:45:04 +00001155 FuncId::SecondaryEpRegister32 => Self::SecondaryEpRegister {
1156 entrypoint: SecondaryEpRegisterAddr::Addr32(regs[1] as u32),
1157 },
1158 FuncId::SecondaryEpRegister64 => Self::SecondaryEpRegister {
1159 entrypoint: SecondaryEpRegisterAddr::Addr64(regs[1]),
1160 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001161 FuncId::MsgSend2 => Self::MsgSend2 {
1162 sender_vm_id: regs[1] as u16,
1163 flags: regs[2] as u32,
1164 },
1165 FuncId::MsgSendDirectReq32 => Self::MsgSendDirectReq {
1166 src_id: (regs[1] >> 16) as u16,
1167 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001168 args: if (regs[2] as u32 & DirectMsgArgs::FWK_MSG_BITS) != 0 {
1169 match regs[2] as u32 {
1170 DirectMsgArgs::VERSION_REQ => DirectMsgArgs::VersionReq {
1171 version: Version::try_from(regs[3] as u32)?,
1172 },
1173 DirectMsgArgs::POWER_PSCI_REQ => DirectMsgArgs::PowerPsciReq32 {
Tomás González67f92c72025-03-20 16:50:42 +00001174 params: [
1175 regs[3] as u32,
1176 regs[4] as u32,
1177 regs[5] as u32,
1178 regs[6] as u32,
1179 ],
Tomás González4d5b0ba2025-03-03 17:15:55 +00001180 },
1181 DirectMsgArgs::POWER_WARM_BOOT_REQ => DirectMsgArgs::PowerWarmBootReq {
1182 boot_type: WarmBootType::try_from(regs[3] as u32)?,
1183 },
1184 DirectMsgArgs::VM_CREATED => DirectMsgArgs::VmCreated {
1185 handle: memory_management::Handle::from([
1186 regs[3] as u32,
1187 regs[4] as u32,
1188 ]),
1189 vm_id: regs[5] as u16,
1190 },
1191 DirectMsgArgs::VM_DESTRUCTED => DirectMsgArgs::VmDestructed {
1192 handle: memory_management::Handle::from([
1193 regs[3] as u32,
1194 regs[4] as u32,
1195 ]),
1196 vm_id: regs[5] as u16,
1197 },
1198 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1199 }
1200 } else {
1201 DirectMsgArgs::Args32([
1202 regs[3] as u32,
1203 regs[4] as u32,
1204 regs[5] as u32,
1205 regs[6] as u32,
1206 regs[7] as u32,
1207 ])
1208 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001209 },
1210 FuncId::MsgSendDirectReq64 => Self::MsgSendDirectReq {
1211 src_id: (regs[1] >> 16) as u16,
1212 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001213 args: if (regs[2] & DirectMsgArgs::FWK_MSG_BITS as u64) != 0 {
1214 match regs[2] as u32 {
1215 DirectMsgArgs::POWER_PSCI_REQ => DirectMsgArgs::PowerPsciReq64 {
Tomás González67f92c72025-03-20 16:50:42 +00001216 params: [regs[3], regs[4], regs[5], regs[6]],
Tomás González4d5b0ba2025-03-03 17:15:55 +00001217 },
1218 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1219 }
1220 } else {
1221 DirectMsgArgs::Args64([regs[3], regs[4], regs[5], regs[6], regs[7]])
1222 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001223 },
1224 FuncId::MsgSendDirectResp32 => Self::MsgSendDirectResp {
1225 src_id: (regs[1] >> 16) as u16,
1226 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001227 args: if (regs[2] as u32 & DirectMsgArgs::FWK_MSG_BITS) != 0 {
1228 match regs[2] as u32 {
1229 DirectMsgArgs::VERSION_RESP => {
1230 if regs[3] as i32 == FfaError::NotSupported.into() {
1231 DirectMsgArgs::VersionResp { version: None }
1232 } else {
1233 DirectMsgArgs::VersionResp {
1234 version: Some(Version::try_from(regs[3] as u32)?),
1235 }
1236 }
1237 }
1238 DirectMsgArgs::POWER_PSCI_RESP => DirectMsgArgs::PowerPsciResp {
1239 psci_status: regs[3] as i32,
1240 },
1241 DirectMsgArgs::VM_CREATED_ACK => DirectMsgArgs::VmCreatedAck {
1242 sp_status: (regs[3] as i32).try_into()?,
1243 },
1244 DirectMsgArgs::VM_DESTRUCTED_ACK => DirectMsgArgs::VmDestructedAck {
1245 sp_status: (regs[3] as i32).try_into()?,
1246 },
1247 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1248 }
1249 } else {
1250 DirectMsgArgs::Args32([
1251 regs[3] as u32,
1252 regs[4] as u32,
1253 regs[5] as u32,
1254 regs[6] as u32,
1255 regs[7] as u32,
1256 ])
1257 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001258 },
1259 FuncId::MsgSendDirectResp64 => Self::MsgSendDirectResp {
1260 src_id: (regs[1] >> 16) as u16,
1261 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001262 args: if (regs[2] & DirectMsgArgs::FWK_MSG_BITS as u64) != 0 {
1263 return Err(Error::UnrecognisedFwkMsg(regs[2] as u32));
1264 } else {
1265 DirectMsgArgs::Args64([regs[3], regs[4], regs[5], regs[6], regs[7]])
1266 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001267 },
1268 FuncId::MemDonate32 => Self::MemDonate {
1269 total_len: regs[1] as u32,
1270 frag_len: regs[2] as u32,
1271 buf: if regs[3] != 0 && regs[4] != 0 {
1272 Some(MemOpBuf::Buf32 {
1273 addr: regs[3] as u32,
1274 page_cnt: regs[4] as u32,
1275 })
1276 } else {
1277 None
1278 },
1279 },
1280 FuncId::MemDonate64 => Self::MemDonate {
1281 total_len: regs[1] as u32,
1282 frag_len: regs[2] as u32,
1283 buf: if regs[3] != 0 && regs[4] != 0 {
1284 Some(MemOpBuf::Buf64 {
1285 addr: regs[3],
1286 page_cnt: regs[4] as u32,
1287 })
1288 } else {
1289 None
1290 },
1291 },
1292 FuncId::MemLend32 => Self::MemLend {
1293 total_len: regs[1] as u32,
1294 frag_len: regs[2] as u32,
1295 buf: if regs[3] != 0 && regs[4] != 0 {
1296 Some(MemOpBuf::Buf32 {
1297 addr: regs[3] as u32,
1298 page_cnt: regs[4] as u32,
1299 })
1300 } else {
1301 None
1302 },
1303 },
1304 FuncId::MemLend64 => Self::MemLend {
1305 total_len: regs[1] as u32,
1306 frag_len: regs[2] as u32,
1307 buf: if regs[3] != 0 && regs[4] != 0 {
1308 Some(MemOpBuf::Buf64 {
1309 addr: regs[3],
1310 page_cnt: regs[4] as u32,
1311 })
1312 } else {
1313 None
1314 },
1315 },
1316 FuncId::MemShare32 => Self::MemShare {
1317 total_len: regs[1] as u32,
1318 frag_len: regs[2] as u32,
1319 buf: if regs[3] != 0 && regs[4] != 0 {
1320 Some(MemOpBuf::Buf32 {
1321 addr: regs[3] as u32,
1322 page_cnt: regs[4] as u32,
1323 })
1324 } else {
1325 None
1326 },
1327 },
1328 FuncId::MemShare64 => Self::MemShare {
1329 total_len: regs[1] as u32,
1330 frag_len: regs[2] as u32,
1331 buf: if regs[3] != 0 && regs[4] != 0 {
1332 Some(MemOpBuf::Buf64 {
1333 addr: regs[3],
1334 page_cnt: regs[4] as u32,
1335 })
1336 } else {
1337 None
1338 },
1339 },
1340 FuncId::MemRetrieveReq32 => Self::MemRetrieveReq {
1341 total_len: regs[1] as u32,
1342 frag_len: regs[2] as u32,
1343 buf: if regs[3] != 0 && regs[4] != 0 {
1344 Some(MemOpBuf::Buf32 {
1345 addr: regs[3] as u32,
1346 page_cnt: regs[4] as u32,
1347 })
1348 } else {
1349 None
1350 },
1351 },
1352 FuncId::MemRetrieveReq64 => Self::MemRetrieveReq {
1353 total_len: regs[1] as u32,
1354 frag_len: regs[2] as u32,
1355 buf: if regs[3] != 0 && regs[4] != 0 {
1356 Some(MemOpBuf::Buf64 {
1357 addr: regs[3],
1358 page_cnt: regs[4] as u32,
1359 })
1360 } else {
1361 None
1362 },
1363 },
1364 FuncId::MemRetrieveResp => Self::MemRetrieveResp {
1365 total_len: regs[1] as u32,
1366 frag_len: regs[2] as u32,
1367 },
1368 FuncId::MemRelinquish => Self::MemRelinquish,
1369 FuncId::MemReclaim => Self::MemReclaim {
1370 handle: memory_management::Handle::from([regs[1] as u32, regs[2] as u32]),
1371 flags: regs[3] as u32,
1372 },
1373 FuncId::MemPermGet32 => Self::MemPermGet {
1374 addr: MemAddr::Addr32(regs[1] as u32),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001375 page_cnt: if version >= Version(1, 3) {
1376 Some(regs[2] as u32)
1377 } else {
1378 None
1379 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001380 },
1381 FuncId::MemPermGet64 => Self::MemPermGet {
1382 addr: MemAddr::Addr64(regs[1]),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001383 page_cnt: if version >= Version(1, 3) {
1384 Some(regs[2] as u32)
1385 } else {
1386 None
1387 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001388 },
1389 FuncId::MemPermSet32 => Self::MemPermSet {
1390 addr: MemAddr::Addr32(regs[1] as u32),
1391 page_cnt: regs[2] as u32,
1392 mem_perm: regs[3] as u32,
1393 },
1394 FuncId::MemPermSet64 => Self::MemPermSet {
1395 addr: MemAddr::Addr64(regs[1]),
1396 page_cnt: regs[2] as u32,
1397 mem_perm: regs[3] as u32,
1398 },
1399 FuncId::ConsoleLog32 => Self::ConsoleLog {
1400 char_cnt: regs[1] as u8,
1401 char_lists: ConsoleLogChars::Reg32([
1402 regs[2] as u32,
1403 regs[3] as u32,
1404 regs[4] as u32,
1405 regs[5] as u32,
1406 regs[6] as u32,
1407 regs[7] as u32,
1408 ]),
1409 },
Tomás González7ffb6132025-04-03 12:28:58 +01001410 FuncId::NotificationBitmapCreate => {
1411 let tentative_vm_id = regs[1] as u32;
1412 if (tentative_vm_id >> 16) != 0 {
1413 return Err(Error::InvalidVmId(tentative_vm_id));
1414 }
1415 Self::NotificationBitmapCreate {
1416 vm_id: tentative_vm_id as u16,
1417 vcpu_cnt: regs[2] as u32,
1418 }
1419 }
1420 FuncId::NotificationBitmapDestroy => {
1421 let tentative_vm_id = regs[1] as u32;
1422 if (tentative_vm_id >> 16) != 0 {
1423 return Err(Error::InvalidVmId(tentative_vm_id));
1424 }
1425 Self::NotificationBitmapDestroy {
1426 vm_id: tentative_vm_id as u16,
1427 }
1428 }
1429 FuncId::NotificationBind => Self::NotificationBind {
1430 sender_id: (regs[1] >> 16) as u16,
1431 receiver_id: regs[1] as u16,
1432 flags: (regs[2] as u32).into(),
1433 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1434 },
1435 FuncId::NotificationUnbind => Self::NotificationUnBind {
1436 sender_id: (regs[1] >> 16) as u16,
1437 receiver_id: regs[1] as u16,
1438 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1439 },
1440 FuncId::NotificationSet => Self::NotificationSet {
1441 sender_id: (regs[1] >> 16) as u16,
1442 receiver_id: regs[1] as u16,
1443 flags: (regs[2] as u32).try_into()?,
1444 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1445 },
1446 FuncId::NotificationGet => Self::NotificationGet {
1447 vcpu_id: (regs[1] >> 16) as u16,
1448 endpoint_id: regs[1] as u16,
1449 flags: (regs[2] as u32).into(),
1450 },
1451 FuncId::NotificationInfoGet32 => Self::NotificationInfoGet { is_32bit: true },
1452 FuncId::NotificationInfoGet64 => Self::NotificationInfoGet { is_32bit: false },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01001453 FuncId::El3IntrHandle => Self::El3IntrHandle,
Balint Dobszayde0dc802025-02-28 14:16:52 +01001454 _ => panic!("Invalid number of registers (8) for function {:#x?}", fid),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001455 };
1456
1457 Ok(msg)
1458 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01001459
Balint Dobszayde0dc802025-02-28 14:16:52 +01001460 fn unpack_regs18(version: Version, regs: &[u64; 18]) -> Result<Self, Error> {
1461 assert!(version >= Version(1, 2));
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001462
Balint Dobszayde0dc802025-02-28 14:16:52 +01001463 let fid = FuncId::try_from(regs[0] as u32)?;
1464
1465 let msg = match fid {
1466 FuncId::Success64 => Self::Success {
1467 target_info: regs[1] as u32,
Imre Kis54773b62025-04-10 13:47:39 +02001468 args: SuccessArgs::Args64_2(regs[2..18].try_into().unwrap()),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001469 },
1470 FuncId::MsgSendDirectReq64_2 => Self::MsgSendDirectReq2 {
1471 src_id: (regs[1] >> 16) as u16,
1472 dst_id: regs[1] as u16,
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00001473 uuid: Uuid::from_u64_pair(regs[2].swap_bytes(), regs[3].swap_bytes()),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001474 args: DirectMsg2Args(regs[4..18].try_into().unwrap()),
1475 },
1476 FuncId::MsgSendDirectResp64_2 => Self::MsgSendDirectResp2 {
1477 src_id: (regs[1] >> 16) as u16,
1478 dst_id: regs[1] as u16,
1479 args: DirectMsg2Args(regs[4..18].try_into().unwrap()),
1480 },
1481 FuncId::ConsoleLog64 => Self::ConsoleLog {
1482 char_cnt: regs[1] as u8,
1483 char_lists: ConsoleLogChars::Reg64(regs[2..18].try_into().unwrap()),
1484 },
Tomás González0a058bc2025-03-11 11:20:55 +00001485 FuncId::PartitionInfoGetRegs => {
1486 // Bits[15:0]: Start index
1487 let start_index = (regs[3] & 0xffff) as u16;
1488 let info_tag = ((regs[3] >> 16) & 0xffff) as u16;
1489 Self::PartitionInfoGetRegs {
1490 uuid: Uuid::from_u64_pair(regs[1].swap_bytes(), regs[2].swap_bytes()),
1491 start_index,
1492 info_tag: if start_index == 0 && info_tag != 0 {
1493 return Err(Error::InvalidInformationTag(info_tag));
1494 } else {
1495 info_tag
1496 },
1497 }
1498 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001499 _ => panic!("Invalid number of registers (18) for function {:#x?}", fid),
1500 };
1501
1502 Ok(msg)
Balint Dobszay3aad9572025-01-17 16:54:11 +01001503 }
1504
Balint Dobszaya5846852025-02-26 15:38:53 +01001505 /// Create register contents for an interface.
Balint Dobszayde0dc802025-02-28 14:16:52 +01001506 pub fn to_regs(&self, version: Version, regs: &mut [u64]) {
1507 let reg_cnt = regs.len();
1508
1509 match reg_cnt {
1510 8 => {
1511 assert!(version <= Version(1, 1));
Balint Dobszay91bea9b2025-04-09 13:16:06 +02001512 regs.fill(0);
1513
Balint Dobszayde0dc802025-02-28 14:16:52 +01001514 self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
1515 }
1516 18 => {
1517 assert!(version >= Version(1, 2));
Balint Dobszay91bea9b2025-04-09 13:16:06 +02001518 regs.fill(0);
Balint Dobszayde0dc802025-02-28 14:16:52 +01001519
1520 match self {
1521 Interface::ConsoleLog {
1522 char_lists: ConsoleLogChars::Reg64(_),
1523 ..
1524 }
1525 | Interface::Success {
Imre Kis54773b62025-04-10 13:47:39 +02001526 args: SuccessArgs::Args64_2(_),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001527 ..
1528 }
1529 | Interface::MsgSendDirectReq2 { .. }
Tomás González0a058bc2025-03-11 11:20:55 +00001530 | Interface::MsgSendDirectResp2 { .. }
1531 | Interface::PartitionInfoGetRegs { .. } => {
Balint Dobszayde0dc802025-02-28 14:16:52 +01001532 self.pack_regs18(version, regs.try_into().unwrap());
1533 }
1534 _ => {
1535 self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
1536 }
1537 }
1538 }
1539 _ => panic!("Invalid number of registers {}", reg_cnt),
1540 }
1541 }
1542
1543 fn pack_regs8(&self, version: Version, a: &mut [u64; 8]) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001544 if let Some(function_id) = self.function_id() {
1545 a[0] = function_id as u64;
1546 }
1547
1548 match *self {
1549 Interface::Error {
1550 target_info,
1551 error_code,
Balint Dobszayb727aab2025-04-07 10:24:59 +02001552 error_arg,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001553 } => {
1554 a[1] = u32::from(target_info).into();
1555 a[2] = (error_code as u32).into();
Balint Dobszayb727aab2025-04-07 10:24:59 +02001556 a[3] = error_arg.into();
Balint Dobszay3aad9572025-01-17 16:54:11 +01001557 }
1558 Interface::Success { target_info, args } => {
1559 a[1] = target_info.into();
1560 match args {
Imre Kis54773b62025-04-10 13:47:39 +02001561 SuccessArgs::Args32(regs) => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001562 a[2] = regs[0].into();
1563 a[3] = regs[1].into();
1564 a[4] = regs[2].into();
1565 a[5] = regs[3].into();
1566 a[6] = regs[4].into();
1567 a[7] = regs[5].into();
1568 }
Imre Kis54773b62025-04-10 13:47:39 +02001569 SuccessArgs::Args64(regs) => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001570 a[2] = regs[0];
1571 a[3] = regs[1];
1572 a[4] = regs[2];
1573 a[5] = regs[3];
1574 a[6] = regs[4];
1575 a[7] = regs[5];
1576 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001577 _ => panic!("{:#x?} requires 18 registers", args),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001578 }
1579 }
1580 Interface::Interrupt {
1581 target_info,
1582 interrupt_id,
1583 } => {
1584 a[1] = u32::from(target_info).into();
1585 a[2] = interrupt_id.into();
1586 }
1587 Interface::Version { input_version } => {
1588 a[1] = u32::from(input_version).into();
1589 }
1590 Interface::VersionOut { output_version } => {
1591 a[0] = u32::from(output_version).into();
1592 }
1593 Interface::Features {
1594 feat_id,
1595 input_properties,
1596 } => {
1597 a[1] = u32::from(feat_id).into();
1598 a[2] = input_properties.into();
1599 }
1600 Interface::RxAcquire { vm_id } => {
1601 a[1] = vm_id.into();
1602 }
1603 Interface::RxRelease { vm_id } => {
1604 a[1] = vm_id.into();
1605 }
1606 Interface::RxTxMap { addr, page_cnt } => {
1607 match addr {
1608 RxTxAddr::Addr32 { rx, tx } => {
1609 a[1] = tx.into();
1610 a[2] = rx.into();
1611 }
1612 RxTxAddr::Addr64 { rx, tx } => {
1613 a[1] = tx;
1614 a[2] = rx;
1615 }
1616 }
1617 a[3] = page_cnt.into();
1618 }
1619 Interface::RxTxUnmap { id } => {
1620 a[1] = id.into();
1621 }
1622 Interface::PartitionInfoGet { uuid, flags } => {
1623 let bytes = uuid.into_bytes();
1624 a[1] = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]).into();
1625 a[2] = u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]).into();
1626 a[3] = u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]).into();
1627 a[4] = u32::from_le_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]).into();
Imre Kise295adb2025-04-10 13:26:28 +02001628 a[5] = u32::from(flags).into();
Balint Dobszay3aad9572025-01-17 16:54:11 +01001629 }
Tomás González092202a2025-03-05 11:56:45 +00001630 Interface::MsgWait { flags } => {
1631 if version >= Version(1, 2) {
1632 if let Some(flags) = flags {
1633 a[2] = u32::from(flags).into();
1634 }
1635 }
1636 }
1637 Interface::IdGet | Interface::SpmIdGet | Interface::Yield => {}
Balint Dobszay3aad9572025-01-17 16:54:11 +01001638 Interface::Run { target_info } => {
1639 a[1] = u32::from(target_info).into();
1640 }
1641 Interface::NormalWorldResume => {}
Tomás González17b92442025-03-10 16:45:04 +00001642 Interface::SecondaryEpRegister { entrypoint } => match entrypoint {
1643 SecondaryEpRegisterAddr::Addr32(addr) => a[1] = addr as u64,
1644 SecondaryEpRegisterAddr::Addr64(addr) => a[1] = addr,
1645 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001646 Interface::MsgSend2 {
1647 sender_vm_id,
1648 flags,
1649 } => {
1650 a[1] = sender_vm_id.into();
1651 a[2] = flags.into();
1652 }
1653 Interface::MsgSendDirectReq {
1654 src_id,
1655 dst_id,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001656 args,
1657 } => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +01001658 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Balint Dobszay3aad9572025-01-17 16:54:11 +01001659 match args {
1660 DirectMsgArgs::Args32(args) => {
1661 a[3] = args[0].into();
1662 a[4] = args[1].into();
1663 a[5] = args[2].into();
1664 a[6] = args[3].into();
1665 a[7] = args[4].into();
1666 }
1667 DirectMsgArgs::Args64(args) => {
1668 a[3] = args[0];
1669 a[4] = args[1];
1670 a[5] = args[2];
1671 a[6] = args[3];
1672 a[7] = args[4];
1673 }
Tomás González4d5b0ba2025-03-03 17:15:55 +00001674 DirectMsgArgs::VersionReq { version } => {
1675 a[2] = DirectMsgArgs::VERSION_REQ.into();
1676 a[3] = u32::from(version).into();
1677 }
Tomás González67f92c72025-03-20 16:50:42 +00001678 DirectMsgArgs::PowerPsciReq32 { params } => {
Tomás González4d5b0ba2025-03-03 17:15:55 +00001679 a[2] = DirectMsgArgs::POWER_PSCI_REQ.into();
Tomás González67f92c72025-03-20 16:50:42 +00001680 a[3] = params[0].into();
1681 a[4] = params[1].into();
1682 a[5] = params[2].into();
1683 a[6] = params[3].into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00001684 }
Tomás González67f92c72025-03-20 16:50:42 +00001685 DirectMsgArgs::PowerPsciReq64 { params } => {
Tomás González4d5b0ba2025-03-03 17:15:55 +00001686 a[2] = DirectMsgArgs::POWER_PSCI_REQ.into();
Tomás González67f92c72025-03-20 16:50:42 +00001687 a[3] = params[0];
1688 a[4] = params[1];
1689 a[5] = params[2];
1690 a[6] = params[3];
Tomás González4d5b0ba2025-03-03 17:15:55 +00001691 }
1692 DirectMsgArgs::PowerWarmBootReq { boot_type } => {
1693 a[2] = DirectMsgArgs::POWER_WARM_BOOT_REQ.into();
1694 a[3] = u32::from(boot_type).into();
1695 }
1696 DirectMsgArgs::VmCreated { handle, vm_id } => {
1697 a[2] = DirectMsgArgs::VM_CREATED.into();
1698 let handle_regs: [u32; 2] = handle.into();
1699 a[3] = handle_regs[0].into();
1700 a[4] = handle_regs[1].into();
1701 a[5] = vm_id.into();
1702 }
1703 DirectMsgArgs::VmDestructed { handle, vm_id } => {
1704 a[2] = DirectMsgArgs::VM_DESTRUCTED.into();
1705 let handle_regs: [u32; 2] = handle.into();
1706 a[3] = handle_regs[0].into();
1707 a[4] = handle_regs[1].into();
1708 a[5] = vm_id.into();
1709 }
1710 _ => panic!("Malformed MsgSendDirectReq interface"),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001711 }
1712 }
1713 Interface::MsgSendDirectResp {
1714 src_id,
1715 dst_id,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001716 args,
1717 } => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +01001718 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Balint Dobszay3aad9572025-01-17 16:54:11 +01001719 match args {
1720 DirectMsgArgs::Args32(args) => {
1721 a[3] = args[0].into();
1722 a[4] = args[1].into();
1723 a[5] = args[2].into();
1724 a[6] = args[3].into();
1725 a[7] = args[4].into();
1726 }
1727 DirectMsgArgs::Args64(args) => {
1728 a[3] = args[0];
1729 a[4] = args[1];
1730 a[5] = args[2];
1731 a[6] = args[3];
1732 a[7] = args[4];
1733 }
Tomás González4d5b0ba2025-03-03 17:15:55 +00001734 DirectMsgArgs::VersionResp { version } => {
1735 a[2] = DirectMsgArgs::VERSION_RESP.into();
1736 match version {
Tomás González67f92c72025-03-20 16:50:42 +00001737 None => a[3] = (i32::from(FfaError::NotSupported) as u32).into(),
Tomás González4d5b0ba2025-03-03 17:15:55 +00001738 Some(ver) => a[3] = u32::from(ver).into(),
1739 }
1740 }
1741 DirectMsgArgs::PowerPsciResp { psci_status } => {
1742 a[2] = DirectMsgArgs::POWER_PSCI_RESP.into();
1743 a[3] = psci_status as u64;
1744 }
1745 DirectMsgArgs::VmCreatedAck { sp_status } => {
1746 a[2] = DirectMsgArgs::VM_CREATED_ACK.into();
Tomás González67f92c72025-03-20 16:50:42 +00001747 a[3] = (i32::from(sp_status) as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00001748 }
1749 DirectMsgArgs::VmDestructedAck { sp_status } => {
1750 a[2] = DirectMsgArgs::VM_DESTRUCTED_ACK.into();
Tomás González67f92c72025-03-20 16:50:42 +00001751 a[3] = (i32::from(sp_status) as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00001752 }
1753 _ => panic!("Malformed MsgSendDirectResp interface"),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001754 }
1755 }
1756 Interface::MemDonate {
1757 total_len,
1758 frag_len,
1759 buf,
1760 } => {
1761 a[1] = total_len.into();
1762 a[2] = frag_len.into();
1763 (a[3], a[4]) = match buf {
1764 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1765 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1766 None => (0, 0),
1767 };
1768 }
1769 Interface::MemLend {
1770 total_len,
1771 frag_len,
1772 buf,
1773 } => {
1774 a[1] = total_len.into();
1775 a[2] = frag_len.into();
1776 (a[3], a[4]) = match buf {
1777 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1778 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1779 None => (0, 0),
1780 };
1781 }
1782 Interface::MemShare {
1783 total_len,
1784 frag_len,
1785 buf,
1786 } => {
1787 a[1] = total_len.into();
1788 a[2] = frag_len.into();
1789 (a[3], a[4]) = match buf {
1790 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1791 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1792 None => (0, 0),
1793 };
1794 }
1795 Interface::MemRetrieveReq {
1796 total_len,
1797 frag_len,
1798 buf,
1799 } => {
1800 a[1] = total_len.into();
1801 a[2] = frag_len.into();
1802 (a[3], a[4]) = match buf {
1803 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1804 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1805 None => (0, 0),
1806 };
1807 }
1808 Interface::MemRetrieveResp {
1809 total_len,
1810 frag_len,
1811 } => {
1812 a[1] = total_len.into();
1813 a[2] = frag_len.into();
1814 }
1815 Interface::MemRelinquish => {}
1816 Interface::MemReclaim { handle, flags } => {
1817 let handle_regs: [u32; 2] = handle.into();
1818 a[1] = handle_regs[0].into();
1819 a[2] = handle_regs[1].into();
1820 a[3] = flags.into();
1821 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001822 Interface::MemPermGet { addr, page_cnt } => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001823 a[1] = match addr {
1824 MemAddr::Addr32(addr) => addr.into(),
1825 MemAddr::Addr64(addr) => addr,
1826 };
Balint Dobszayde0dc802025-02-28 14:16:52 +01001827 a[2] = if version >= Version(1, 3) {
1828 page_cnt.unwrap().into()
1829 } else {
1830 assert!(page_cnt.is_none());
1831 0
1832 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01001833 }
1834 Interface::MemPermSet {
1835 addr,
1836 page_cnt,
1837 mem_perm,
1838 } => {
1839 a[1] = match addr {
1840 MemAddr::Addr32(addr) => addr.into(),
1841 MemAddr::Addr64(addr) => addr,
1842 };
1843 a[2] = page_cnt.into();
1844 a[3] = mem_perm.into();
1845 }
1846 Interface::ConsoleLog {
1847 char_cnt,
1848 char_lists,
1849 } => {
1850 a[1] = char_cnt.into();
1851 match char_lists {
1852 ConsoleLogChars::Reg32(regs) => {
1853 a[2] = regs[0].into();
1854 a[3] = regs[1].into();
1855 a[4] = regs[2].into();
1856 a[5] = regs[3].into();
1857 a[6] = regs[4].into();
1858 a[7] = regs[5].into();
1859 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001860 _ => panic!("{:#x?} requires 18 registers", char_lists),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001861 }
1862 }
Tomás González7ffb6132025-04-03 12:28:58 +01001863 Interface::NotificationBitmapCreate { vm_id, vcpu_cnt } => {
1864 a[1] = vm_id.into();
1865 a[2] = vcpu_cnt.into();
1866 }
1867 Interface::NotificationBitmapDestroy { vm_id } => {
1868 a[1] = vm_id.into();
1869 }
1870 Interface::NotificationBind {
1871 sender_id,
1872 receiver_id,
1873 flags,
1874 bitmap,
1875 } => {
1876 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
1877 a[2] = u32::from(flags).into();
1878 a[3] = bitmap & 0xffff_ffff;
1879 a[4] = bitmap >> 32;
1880 }
1881 Interface::NotificationUnBind {
1882 sender_id,
1883 receiver_id,
1884 bitmap,
1885 } => {
1886 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
1887 a[3] = bitmap & 0xffff_ffff;
1888 a[4] = bitmap >> 32;
1889 }
1890 Interface::NotificationSet {
1891 sender_id,
1892 receiver_id,
1893 flags,
1894 bitmap,
1895 } => {
1896 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
1897 a[2] = u32::from(flags).into();
1898 a[3] = bitmap & 0xffff_ffff;
1899 a[4] = bitmap >> 32;
1900 }
1901 Interface::NotificationGet {
1902 vcpu_id,
1903 endpoint_id,
1904 flags,
1905 } => {
1906 a[1] = (u64::from(vcpu_id) << 16) | u64::from(endpoint_id);
1907 a[2] = u32::from(flags).into();
1908 }
1909 Interface::NotificationInfoGet { .. } => {}
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01001910 Interface::El3IntrHandle => {}
Balint Dobszayde0dc802025-02-28 14:16:52 +01001911 _ => panic!("{:#x?} requires 18 registers", self),
1912 }
1913 }
1914
1915 fn pack_regs18(&self, version: Version, a: &mut [u64; 18]) {
1916 assert!(version >= Version(1, 2));
1917
Balint Dobszayde0dc802025-02-28 14:16:52 +01001918 if let Some(function_id) = self.function_id() {
1919 a[0] = function_id as u64;
1920 }
1921
1922 match *self {
1923 Interface::Success { target_info, args } => {
1924 a[1] = target_info.into();
1925 match args {
Imre Kis54773b62025-04-10 13:47:39 +02001926 SuccessArgs::Args64_2(regs) => a[2..18].copy_from_slice(&regs[..16]),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001927 _ => panic!("{:#x?} requires 8 registers", args),
1928 }
1929 }
1930 Interface::MsgSendDirectReq2 {
1931 src_id,
1932 dst_id,
1933 uuid,
1934 args,
1935 } => {
1936 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00001937 let (uuid_msb, uuid_lsb) = uuid.as_u64_pair();
1938 (a[2], a[3]) = (uuid_msb.swap_bytes(), uuid_lsb.swap_bytes());
Balint Dobszayde0dc802025-02-28 14:16:52 +01001939 a[4..18].copy_from_slice(&args.0[..14]);
1940 }
1941 Interface::MsgSendDirectResp2 {
1942 src_id,
1943 dst_id,
1944 args,
1945 } => {
1946 a[1] = ((src_id as u64) << 16) | dst_id as u64;
1947 a[2] = 0;
1948 a[3] = 0;
1949 a[4..18].copy_from_slice(&args.0[..14]);
1950 }
1951 Interface::ConsoleLog {
1952 char_cnt,
1953 char_lists,
1954 } => {
1955 a[1] = char_cnt.into();
1956 match char_lists {
1957 ConsoleLogChars::Reg64(regs) => a[2..18].copy_from_slice(&regs[..16]),
1958 _ => panic!("{:#x?} requires 8 registers", char_lists),
1959 }
1960 }
Tomás González0a058bc2025-03-11 11:20:55 +00001961 Interface::PartitionInfoGetRegs {
1962 uuid,
1963 start_index,
1964 info_tag,
1965 } => {
1966 if start_index == 0 && info_tag != 0 {
1967 panic!("Information Tag MBZ if start index is 0: {:#x?}", self);
1968 }
1969 let (uuid_msb, uuid_lsb) = uuid.as_u64_pair();
1970 (a[1], a[2]) = (uuid_msb.swap_bytes(), uuid_lsb.swap_bytes());
1971 a[3] = (u64::from(info_tag) << 16) | u64::from(start_index);
1972 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001973 _ => panic!("{:#x?} requires 8 registers", self),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001974 }
1975 }
1976
Balint Dobszaya5846852025-02-26 15:38:53 +01001977 /// Helper function to create an `FFA_SUCCESS` interface without any arguments.
Balint Dobszay3aad9572025-01-17 16:54:11 +01001978 pub fn success32_noargs() -> Self {
1979 Self::Success {
1980 target_info: 0,
Imre Kis54773b62025-04-10 13:47:39 +02001981 args: SuccessArgs::Args32([0; 6]),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001982 }
1983 }
1984
Tomás González4c8c7d22025-03-10 17:14:57 +00001985 /// Helper function to create an `FFA_SUCCESS` interface without any arguments.
1986 pub fn success64_noargs() -> Self {
1987 Self::Success {
1988 target_info: 0,
Imre Kis54773b62025-04-10 13:47:39 +02001989 args: SuccessArgs::Args64([0; 6]),
Tomás González4c8c7d22025-03-10 17:14:57 +00001990 }
1991 }
1992
Balint Dobszaya5846852025-02-26 15:38:53 +01001993 /// Helper function to create an `FFA_ERROR` interface with an error code.
Balint Dobszay3aad9572025-01-17 16:54:11 +01001994 pub fn error(error_code: FfaError) -> Self {
1995 Self::Error {
1996 target_info: TargetInfo {
1997 endpoint_id: 0,
1998 vcpu_id: 0,
1999 },
2000 error_code,
Balint Dobszayb727aab2025-04-07 10:24:59 +02002001 error_arg: 0,
Balint Dobszay3aad9572025-01-17 16:54:11 +01002002 }
2003 }
2004}
2005
Balint Dobszaya5846852025-02-26 15:38:53 +01002006/// Maximum number of characters transmitted in a single `FFA_CONSOLE_LOG32` message.
2007pub const CONSOLE_LOG_32_MAX_CHAR_CNT: u8 = 24;
Balint Dobszayde0dc802025-02-28 14:16:52 +01002008/// Maximum number of characters transmitted in a single `FFA_CONSOLE_LOG64` message.
2009pub const CONSOLE_LOG_64_MAX_CHAR_CNT: u8 = 128;
Balint Dobszay3aad9572025-01-17 16:54:11 +01002010
Balint Dobszaya5846852025-02-26 15:38:53 +01002011/// Helper function to convert the "Tightly packed list of characters" format used by the
2012/// `FFA_CONSOLE_LOG` interface into a byte slice.
Balint Dobszay3aad9572025-01-17 16:54:11 +01002013pub fn parse_console_log(
2014 char_cnt: u8,
2015 char_lists: &ConsoleLogChars,
2016 log_bytes: &mut [u8],
2017) -> Result<(), FfaError> {
2018 match char_lists {
2019 ConsoleLogChars::Reg32(regs) => {
Balint Dobszaya5846852025-02-26 15:38:53 +01002020 if !(1..=CONSOLE_LOG_32_MAX_CHAR_CNT).contains(&char_cnt) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01002021 return Err(FfaError::InvalidParameters);
2022 }
2023 for (i, reg) in regs.iter().enumerate() {
2024 log_bytes[4 * i..4 * (i + 1)].copy_from_slice(&reg.to_le_bytes());
2025 }
2026 }
2027 ConsoleLogChars::Reg64(regs) => {
Balint Dobszaya5846852025-02-26 15:38:53 +01002028 if !(1..=CONSOLE_LOG_64_MAX_CHAR_CNT).contains(&char_cnt) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01002029 return Err(FfaError::InvalidParameters);
2030 }
2031 for (i, reg) in regs.iter().enumerate() {
2032 log_bytes[8 * i..8 * (i + 1)].copy_from_slice(&reg.to_le_bytes());
2033 }
2034 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +02002035 }
2036
Balint Dobszayc8802492025-01-15 18:11:39 +01002037 Ok(())
Balint Dobszay5bf492f2024-07-29 17:21:32 +02002038}
Tomás González0a058bc2025-03-11 11:20:55 +00002039
2040#[cfg(test)]
2041mod tests {
2042 use super::*;
2043
2044 #[test]
2045 fn part_info_get_regs() {
2046 let uuid = Uuid::parse_str("a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8").unwrap();
2047 let uuid_bytes = uuid.as_bytes();
2048 let test_info_tag = 0b1101_1101;
2049 let test_start_index = 0b1101;
2050 let start_index_and_tag = (test_info_tag << 16) | test_start_index;
2051 let version = Version(1, 2);
2052
2053 // From spec:
2054 // Bytes[0...7] of UUID with byte 0 in the low-order bits.
2055 let reg_x1 = (uuid_bytes[7] as u64) << 56
2056 | (uuid_bytes[6] as u64) << 48
2057 | (uuid_bytes[5] as u64) << 40
2058 | (uuid_bytes[4] as u64) << 32
2059 | (uuid_bytes[3] as u64) << 24
2060 | (uuid_bytes[2] as u64) << 16
2061 | (uuid_bytes[1] as u64) << 8
2062 | (uuid_bytes[0] as u64);
2063
2064 // From spec:
2065 // Bytes[8...15] of UUID with byte 8 in the low-order bits.
2066 let reg_x2 = (uuid_bytes[15] as u64) << 56
2067 | (uuid_bytes[14] as u64) << 48
2068 | (uuid_bytes[13] as u64) << 40
2069 | (uuid_bytes[12] as u64) << 32
2070 | (uuid_bytes[11] as u64) << 24
2071 | (uuid_bytes[10] as u64) << 16
2072 | (uuid_bytes[9] as u64) << 8
2073 | (uuid_bytes[8] as u64);
2074
2075 // First, test for wrong tag:
2076 {
2077 let mut regs = [0u64; 18];
2078 regs[0] = FuncId::PartitionInfoGetRegs as u64;
2079 regs[1] = reg_x1;
2080 regs[2] = reg_x2;
2081 regs[3] = test_info_tag << 16;
2082
2083 assert!(Interface::from_regs(version, &regs).is_err_and(
2084 |e| e == Error::InvalidInformationTag(test_info_tag.try_into().unwrap())
2085 ));
2086 }
2087
2088 // Test for regs -> Interface -> regs
2089 {
2090 let mut orig_regs = [0u64; 18];
2091 orig_regs[0] = FuncId::PartitionInfoGetRegs as u64;
2092 orig_regs[1] = reg_x1;
2093 orig_regs[2] = reg_x2;
2094 orig_regs[3] = start_index_and_tag;
2095
2096 let mut test_regs = orig_regs.clone();
2097 let interface = Interface::from_regs(version, &mut test_regs).unwrap();
2098 match &interface {
2099 Interface::PartitionInfoGetRegs {
2100 info_tag,
2101 start_index,
2102 uuid: int_uuid,
2103 } => {
2104 assert_eq!(u64::from(*info_tag), test_info_tag);
2105 assert_eq!(u64::from(*start_index), test_start_index);
2106 assert_eq!(*int_uuid, uuid);
2107 }
2108 _ => panic!("Expecting Interface::PartitionInfoGetRegs!"),
2109 }
2110 test_regs.fill(0);
2111 interface.to_regs(version, &mut test_regs);
2112 assert_eq!(orig_regs, test_regs);
2113 }
2114
2115 // Test for Interface -> regs -> Interface
2116 {
2117 let interface = Interface::PartitionInfoGetRegs {
2118 info_tag: test_info_tag.try_into().unwrap(),
2119 start_index: test_start_index.try_into().unwrap(),
2120 uuid,
2121 };
2122
2123 let mut regs: [u64; 18] = [0; 18];
2124 interface.to_regs(version, &mut regs);
2125
2126 assert_eq!(Some(FuncId::PartitionInfoGetRegs), interface.function_id());
2127 assert_eq!(regs[0], interface.function_id().unwrap() as u64);
2128 assert_eq!(regs[1], reg_x1);
2129 assert_eq!(regs[2], reg_x2);
2130 assert_eq!(regs[3], (test_info_tag << 16) | test_start_index);
2131
2132 assert_eq!(Interface::from_regs(version, &regs).unwrap(), interface);
2133 }
2134 }
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00002135
2136 #[test]
2137 fn msg_send_direct_req2() {
2138 let uuid = Uuid::parse_str("a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8").unwrap();
2139 let uuid_bytes = uuid.as_bytes();
2140
2141 // From spec:
2142 // Bytes[0...7] of UUID with byte 0 in the low-order bits.
2143 let reg_x2 = (uuid_bytes[7] as u64) << 56
2144 | (uuid_bytes[6] as u64) << 48
2145 | (uuid_bytes[5] as u64) << 40
2146 | (uuid_bytes[4] as u64) << 32
2147 | (uuid_bytes[3] as u64) << 24
2148 | (uuid_bytes[2] as u64) << 16
2149 | (uuid_bytes[1] as u64) << 8
2150 | (uuid_bytes[0] as u64);
2151
2152 // From spec:
2153 // Bytes[8...15] of UUID with byte 8 in the low-order bits.
2154 let reg_x3 = (uuid_bytes[15] as u64) << 56
2155 | (uuid_bytes[14] as u64) << 48
2156 | (uuid_bytes[13] as u64) << 40
2157 | (uuid_bytes[12] as u64) << 32
2158 | (uuid_bytes[11] as u64) << 24
2159 | (uuid_bytes[10] as u64) << 16
2160 | (uuid_bytes[9] as u64) << 8
2161 | (uuid_bytes[8] as u64);
2162
2163 let test_sender = 0b1101_1101;
2164 let test_receiver = 0b1101;
2165 let test_sender_receiver = (test_sender << 16) | test_receiver;
2166 let version = Version(1, 2);
2167
2168 // Test for regs -> Interface -> regs
2169 {
2170 let mut orig_regs = [0u64; 18];
2171 orig_regs[0] = FuncId::MsgSendDirectReq64_2 as u64;
2172 orig_regs[1] = test_sender_receiver;
2173 orig_regs[2] = reg_x2;
2174 orig_regs[3] = reg_x3;
2175
2176 let mut test_regs = orig_regs.clone();
2177 let interface = Interface::from_regs(version, &mut test_regs).unwrap();
2178 match &interface {
2179 Interface::MsgSendDirectReq2 {
2180 dst_id,
2181 src_id,
2182 args: _,
2183 uuid: int_uuid,
2184 } => {
2185 assert_eq!(u64::from(*src_id), test_sender);
2186 assert_eq!(u64::from(*dst_id), test_receiver);
2187 assert_eq!(*int_uuid, uuid);
2188 }
2189 _ => panic!("Expecting Interface::MsgSendDirectReq2!"),
2190 }
2191 test_regs.fill(0);
2192 interface.to_regs(version, &mut test_regs);
2193 assert_eq!(orig_regs, test_regs);
2194 }
2195
2196 // Test for Interface -> regs -> Interface
2197 {
2198 let rest_of_regs: [u64; 14] = [0; 14];
2199
2200 let interface = Interface::MsgSendDirectReq2 {
2201 src_id: test_sender.try_into().unwrap(),
2202 dst_id: test_receiver.try_into().unwrap(),
2203 uuid,
2204 args: DirectMsg2Args(rest_of_regs),
2205 };
2206
2207 let mut regs: [u64; 18] = [0; 18];
2208 interface.to_regs(version, &mut regs);
2209
2210 assert_eq!(Some(FuncId::MsgSendDirectReq64_2), interface.function_id());
2211 assert_eq!(regs[0], interface.function_id().unwrap() as u64);
2212 assert_eq!(regs[1], test_sender_receiver);
2213 assert_eq!(regs[2], reg_x2);
2214 assert_eq!(regs[3], reg_x3);
2215 assert_eq!(regs[4], 0);
2216
2217 assert_eq!(Interface::from_regs(version, &regs).unwrap(), interface);
2218 }
2219 }
Tomás González6ccba0a2025-04-09 13:31:29 +01002220
2221 #[test]
2222 fn is_32bit() {
2223 let interface_64 = Interface::MsgSendDirectReq {
2224 src_id: 0,
2225 dst_id: 1,
2226 args: DirectMsgArgs::Args64([0, 0, 0, 0, 0]),
2227 };
2228 assert!(!interface_64.is_32bit());
2229
2230 let interface_32 = Interface::MsgSendDirectReq {
2231 src_id: 0,
2232 dst_id: 1,
2233 args: DirectMsgArgs::Args32([0, 0, 0, 0, 0]),
2234 };
2235 assert!(interface_32.is_32bit());
2236 }
Tomás González0a058bc2025-03-11 11:20:55 +00002237}