blob: 0a7b6cabf7d1b5f69b2875c53c1dad8258213531 [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`]
Imre Kis9959e062025-04-10 14:16:10 +0200211/// * `FFA_NOTIFICATION_GET` - [`SuccessArgsNotificationGet`]
Balint Dobszay3aad9572025-01-17 16:54:11 +0100212#[derive(Debug, Eq, PartialEq, Clone, Copy)]
213pub enum SuccessArgs {
Imre Kis54773b62025-04-10 13:47:39 +0200214 Args32([u32; 6]),
215 Args64([u64; 6]),
216 Args64_2([u64; 16]),
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200217}
218
Imre Kis839eaef2025-04-11 17:38:36 +0200219impl SuccessArgs {
220 fn try_get_args32(self) -> Result<[u32; 6], Error> {
221 match self {
222 SuccessArgs::Args32(args) => Ok(args),
223 SuccessArgs::Args64(_) | SuccessArgs::Args64_2(_) => {
224 Err(Error::InvalidSuccessArgsVariant)
225 }
226 }
227 }
228
229 fn try_get_args64(self) -> Result<[u64; 6], Error> {
230 match self {
231 SuccessArgs::Args64(args) => Ok(args),
232 SuccessArgs::Args32(_) | SuccessArgs::Args64_2(_) => {
233 Err(Error::InvalidSuccessArgsVariant)
234 }
235 }
236 }
237
238 fn try_get_args64_2(self) -> Result<[u64; 16], Error> {
239 match self {
240 SuccessArgs::Args64_2(args) => Ok(args),
241 SuccessArgs::Args32(_) | SuccessArgs::Args64(_) => {
242 Err(Error::InvalidSuccessArgsVariant)
243 }
244 }
245 }
246}
247
Tomás González17b92442025-03-10 16:45:04 +0000248/// Entrypoint address argument for `FFA_SECONDARY_EP_REGISTER` interface.
249#[derive(Debug, Eq, PartialEq, Clone, Copy)]
250pub enum SecondaryEpRegisterAddr {
251 Addr32(u32),
252 Addr64(u64),
253}
254
Balint Dobszaya5846852025-02-26 15:38:53 +0100255/// Version number of the FF-A implementation, `.0` is the major, `.1` is minor the version.
Balint Dobszayde0dc802025-02-28 14:16:52 +0100256#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord)]
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200257pub struct Version(pub u16, pub u16);
258
Tomás González1f794352025-03-03 16:47:06 +0000259impl Version {
Tomás González83146af2025-03-04 11:32:41 +0000260 // The FF-A spec mandates that bit[31] of a version number must be 0
261 const MBZ_BITS: u32 = 1 << 31;
262
Tomás González1f794352025-03-03 16:47:06 +0000263 /// Returns whether the caller's version (self) is compatible with the callee's version (input
264 /// parameter)
265 pub fn is_compatible_to(&self, callee_version: &Version) -> bool {
266 self.0 == callee_version.0 && self.1 <= callee_version.1
267 }
268}
269
Tomás González83146af2025-03-04 11:32:41 +0000270impl TryFrom<u32> for Version {
271 type Error = Error;
272
273 fn try_from(val: u32) -> Result<Self, Self::Error> {
274 if (val & Self::MBZ_BITS) != 0 {
275 Err(Error::InvalidVersion(val))
276 } else {
277 Ok(Self((val >> 16) as u16, val as u16))
278 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200279 }
280}
281
282impl From<Version> for u32 {
283 fn from(v: Version) -> Self {
Tomás González83146af2025-03-04 11:32:41 +0000284 let v_u32 = ((v.0 as u32) << 16) | v.1 as u32;
285 assert!(v_u32 & Version::MBZ_BITS == 0);
286 v_u32
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200287 }
288}
289
Andrew Walbran19970ba2024-11-25 15:35:00 +0000290impl Display for Version {
291 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
292 write!(f, "{}.{}", self.0, self.1)
293 }
294}
295
296impl Debug for Version {
297 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
298 Display::fmt(self, f)
299 }
300}
301
Balint Dobszaya5846852025-02-26 15:38:53 +0100302/// Feature IDs used by the `FFA_FEATURES` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100303#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
304#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedFeatureId))]
305#[repr(u8)]
306pub enum FeatureId {
307 NotificationPendingInterrupt = 0x1,
308 ScheduleReceiverInterrupt = 0x2,
309 ManagedExitInterrupt = 0x3,
310}
Balint Dobszayc8802492025-01-15 18:11:39 +0100311
Balint Dobszaya5846852025-02-26 15:38:53 +0100312/// Arguments for the `FFA_FEATURES` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100313#[derive(Debug, Eq, PartialEq, Clone, Copy)]
314pub enum Feature {
315 FuncId(FuncId),
316 FeatureId(FeatureId),
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100317 Unknown(u32),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100318}
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200319
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100320impl From<u32> for Feature {
321 fn from(value: u32) -> Self {
322 // Bit[31] is set for all valid FF-A function IDs so we don't have to check it separately
323 if let Ok(func_id) = value.try_into() {
324 Self::FuncId(func_id)
325 } else if let Ok(feat_id) = (value as u8).try_into() {
326 Self::FeatureId(feat_id)
Balint Dobszay3aad9572025-01-17 16:54:11 +0100327 } else {
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100328 Self::Unknown(value)
329 }
Balint Dobszay3aad9572025-01-17 16:54:11 +0100330 }
331}
332
333impl From<Feature> for u32 {
334 fn from(value: Feature) -> Self {
335 match value {
336 Feature::FuncId(func_id) => (1 << 31) | func_id as u32,
337 Feature::FeatureId(feature_id) => feature_id as u32,
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100338 Feature::Unknown(id) => panic!("Unknown feature or function ID {:#x?}", id),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100339 }
340 }
341}
342
Imre Kis4e9d8bc2025-04-10 13:48:26 +0200343/// `FFA_FEATURES` specific success argument structure. This type needs further specialization based
344/// on 'FF-A function ID or Feature ID' field of the preceeding `FFA_FEATURES` request.
345#[derive(Debug, Eq, PartialEq, Clone, Copy)]
346pub struct SuccessArgsFeatures {
347 pub properties: [u32; 2],
348}
349
350impl From<SuccessArgsFeatures> for SuccessArgs {
351 fn from(value: SuccessArgsFeatures) -> Self {
352 Self::Args32([value.properties[0], value.properties[1], 0, 0, 0, 0])
353 }
354}
355
356impl TryFrom<SuccessArgs> for SuccessArgsFeatures {
357 type Error = Error;
358
359 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
360 let args = value.try_get_args32()?;
361
362 Ok(Self {
363 properties: [args[0], args[1]],
364 })
365 }
366}
367
Balint Dobszaya5846852025-02-26 15:38:53 +0100368/// RXTX buffer descriptor, used by `FFA_RXTX_MAP`.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100369#[derive(Debug, Eq, PartialEq, Clone, Copy)]
370pub enum RxTxAddr {
371 Addr32 { rx: u32, tx: u32 },
372 Addr64 { rx: u64, tx: u64 },
373}
374
Imre Kisbbef2872025-04-10 14:11:29 +0200375/// `FFA_ID_GET` specific success argument structure.
376#[derive(Debug, Eq, PartialEq, Clone, Copy)]
377pub struct SuccessArgsIdGet {
378 pub id: u16,
379}
380
381impl From<SuccessArgsIdGet> for SuccessArgs {
382 fn from(value: SuccessArgsIdGet) -> Self {
383 SuccessArgs::Args32([value.id as u32, 0, 0, 0, 0, 0])
384 }
385}
386
387impl TryFrom<SuccessArgs> for SuccessArgsIdGet {
388 type Error = Error;
389
390 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
391 let args = value.try_get_args32()?;
392 Ok(Self { id: args[0] as u16 })
393 }
394}
395
396/// `FFA_SPM_ID_GET` specific success argument structure.
397#[derive(Debug, Eq, PartialEq, Clone, Copy)]
398pub struct SuccessArgsSpmIdGet {
399 pub id: u16,
400}
401
402impl From<SuccessArgsSpmIdGet> for SuccessArgs {
403 fn from(value: SuccessArgsSpmIdGet) -> Self {
404 SuccessArgs::Args32([value.id as u32, 0, 0, 0, 0, 0])
405 }
406}
407
408impl TryFrom<SuccessArgs> for SuccessArgsSpmIdGet {
409 type Error = Error;
410
411 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
412 let args = value.try_get_args32()?;
413 Ok(Self { id: args[0] as u16 })
414 }
415}
416
Imre Kise295adb2025-04-10 13:26:28 +0200417/// Flags of the `FFA_PARTITION_INFO_GET` interface.
418#[derive(Debug, Eq, PartialEq, Clone, Copy)]
419pub struct PartitionInfoGetFlags {
420 pub count_only: bool,
421}
422
423impl PartitionInfoGetFlags {
424 const RETURN_INFORMATION_TYPE_FLAG: u32 = 1 << 0;
425 const MBZ_BITS: u32 = 0xffff_fffe;
426}
427
428impl TryFrom<u32> for PartitionInfoGetFlags {
429 type Error = Error;
430
431 fn try_from(val: u32) -> Result<Self, Self::Error> {
432 if (val & Self::MBZ_BITS) != 0 {
433 Err(Error::InvalidPartitionInfoGetFlag(val))
434 } else {
435 Ok(Self {
436 count_only: val & Self::RETURN_INFORMATION_TYPE_FLAG != 0,
437 })
438 }
439 }
440}
441
442impl From<PartitionInfoGetFlags> for u32 {
443 fn from(flags: PartitionInfoGetFlags) -> Self {
444 let mut bits: u32 = 0;
445 if flags.count_only {
446 bits |= PartitionInfoGetFlags::RETURN_INFORMATION_TYPE_FLAG;
447 }
448 bits
449 }
450}
451
Tomás González4d5b0ba2025-03-03 17:15:55 +0000452/// Composite type for capturing success and error return codes for the VM availability messages.
453///
454/// Error codes are handled by the `FfaError` type. Having a separate type for errors helps using
455/// `Result<(), FfaError>`. If a single type would include both success and error values,
456/// then `Err(FfaError::Success)` would be incomprehensible.
457#[derive(Debug, Eq, PartialEq, Clone, Copy)]
458pub enum VmAvailabilityStatus {
459 Success,
460 Error(FfaError),
461}
462
463impl TryFrom<i32> for VmAvailabilityStatus {
464 type Error = Error;
465 fn try_from(value: i32) -> Result<Self, <Self as TryFrom<i32>>::Error> {
466 Ok(match value {
467 0 => Self::Success,
468 error_code => Self::Error(FfaError::try_from(error_code)?),
469 })
470 }
471}
472
473impl From<VmAvailabilityStatus> for i32 {
474 fn from(value: VmAvailabilityStatus) -> Self {
475 match value {
476 VmAvailabilityStatus::Success => 0,
477 VmAvailabilityStatus::Error(error_code) => error_code.into(),
478 }
479 }
480}
481
482/// Arguments for the Power Warm Boot `FFA_MSG_SEND_DIRECT_REQ` interface.
483#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
484#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedWarmBootType))]
485#[repr(u32)]
486pub enum WarmBootType {
487 ExitFromSuspend = 0,
488 ExitFromLowPower = 1,
489}
490
Balint Dobszaya5846852025-02-26 15:38:53 +0100491/// Arguments for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}` interfaces.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100492#[derive(Debug, Eq, PartialEq, Clone, Copy)]
493pub enum DirectMsgArgs {
494 Args32([u32; 5]),
495 Args64([u64; 5]),
Tomás González4d5b0ba2025-03-03 17:15:55 +0000496 /// Message for forwarding FFA_VERSION call from Normal world to the SPMC
497 VersionReq {
498 version: Version,
499 },
500 /// Response message to forwarded FFA_VERSION call from the Normal world
501 /// Contains the version returned by the SPMC or None
502 VersionResp {
503 version: Option<Version>,
504 },
505 /// Message for a power management operation initiated by a PSCI function
506 PowerPsciReq32 {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000507 // params[i]: Input parameter in w[i] in PSCI function invocation at EL3.
Tomás González67f92c72025-03-20 16:50:42 +0000508 // params[0]: Function ID.
509 params: [u32; 4],
Tomás González4d5b0ba2025-03-03 17:15:55 +0000510 },
511 /// Message for a power management operation initiated by a PSCI function
512 PowerPsciReq64 {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000513 // params[i]: Input parameter in x[i] in PSCI function invocation at EL3.
Tomás González67f92c72025-03-20 16:50:42 +0000514 // params[0]: Function ID.
515 params: [u64; 4],
Tomás González4d5b0ba2025-03-03 17:15:55 +0000516 },
517 /// Message for a warm boot
518 PowerWarmBootReq {
519 boot_type: WarmBootType,
520 },
521 /// Response message to indicate return status of the last power management request message
522 /// Return error code SUCCESS or DENIED as defined in PSCI spec. Caller is left to do the
523 /// parsing of the return status.
524 PowerPsciResp {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000525 psci_status: i32,
526 },
527 /// Message to signal creation of a VM
528 VmCreated {
529 // Globally unique Handle to identify a memory region that contains IMPLEMENTATION DEFINED
530 // information associated with the created VM.
531 // The invalid memory region handle must be specified by the Hypervisor if this field is not
532 // used.
533 handle: memory_management::Handle,
534 vm_id: u16,
535 },
536 /// Message to acknowledge creation of a VM
537 VmCreatedAck {
538 sp_status: VmAvailabilityStatus,
539 },
540 /// Message to signal destruction of a VM
541 VmDestructed {
542 // Globally unique Handle to identify a memory region that contains IMPLEMENTATION DEFINED
543 // information associated with the created VM.
544 // The invalid memory region handle must be specified by the Hypervisor if this field is not
545 // used.
546 handle: memory_management::Handle,
547 vm_id: u16,
548 },
549 /// Message to acknowledge destruction of a VM
550 VmDestructedAck {
551 sp_status: VmAvailabilityStatus,
552 },
553}
554
555impl DirectMsgArgs {
556 // Flags for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}` interfaces.
557
558 const FWK_MSG_BITS: u32 = 1 << 31;
559 const VERSION_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b1000;
560 const VERSION_RESP: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b1001;
561 const POWER_PSCI_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS;
562 const POWER_WARM_BOOT_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0001;
563 const POWER_PSCI_RESP: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0010;
564 const VM_CREATED: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0100;
565 const VM_CREATED_ACK: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0101;
566 const VM_DESTRUCTED: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0110;
567 const VM_DESTRUCTED_ACK: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0111;
Balint Dobszay3aad9572025-01-17 16:54:11 +0100568}
569
Balint Dobszayde0dc802025-02-28 14:16:52 +0100570/// Arguments for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}2` interfaces.
571#[derive(Debug, Eq, PartialEq, Clone, Copy)]
572pub struct DirectMsg2Args([u64; 14]);
573
Tomás González092202a2025-03-05 11:56:45 +0000574#[derive(Debug, Eq, PartialEq, Clone, Copy)]
575pub struct MsgWaitFlags {
576 retain_rx_buffer: bool,
577}
578
579impl MsgWaitFlags {
580 const RETAIN_RX_BUFFER: u32 = 0x01;
581 const MBZ_BITS: u32 = 0xfffe;
582}
583
584impl TryFrom<u32> for MsgWaitFlags {
585 type Error = Error;
586
587 fn try_from(val: u32) -> Result<Self, Self::Error> {
588 if (val & Self::MBZ_BITS) != 0 {
589 Err(Error::InvalidMsgWaitFlag(val))
590 } else {
591 Ok(MsgWaitFlags {
592 retain_rx_buffer: val & Self::RETAIN_RX_BUFFER != 0,
593 })
594 }
595 }
596}
597
598impl From<MsgWaitFlags> for u32 {
599 fn from(flags: MsgWaitFlags) -> Self {
600 let mut bits: u32 = 0;
601 if flags.retain_rx_buffer {
602 bits |= MsgWaitFlags::RETAIN_RX_BUFFER;
603 }
604 bits
605 }
606}
607
Balint Dobszaya5846852025-02-26 15:38:53 +0100608/// Descriptor for a dynamically allocated memory buffer that contains the memory transaction
Tomás Gonzálezf268e322025-03-05 11:18:11 +0000609/// descriptor.
610///
611/// Used by `FFA_MEM_{DONATE,LEND,SHARE,RETRIEVE_REQ}` interfaces, only when the TX buffer is not
612/// used to transmit the transaction descriptor.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100613#[derive(Debug, Eq, PartialEq, Clone, Copy)]
614pub enum MemOpBuf {
615 Buf32 { addr: u32, page_cnt: u32 },
616 Buf64 { addr: u64, page_cnt: u32 },
617}
618
Balint Dobszaya5846852025-02-26 15:38:53 +0100619/// Memory address argument for `FFA_MEM_PERM_{GET,SET}` interfaces.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100620#[derive(Debug, Eq, PartialEq, Clone, Copy)]
621pub enum MemAddr {
622 Addr32(u32),
623 Addr64(u64),
624}
625
Balint Dobszayde0dc802025-02-28 14:16:52 +0100626/// Argument for the `FFA_CONSOLE_LOG` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100627#[derive(Debug, Eq, PartialEq, Clone, Copy)]
628pub enum ConsoleLogChars {
629 Reg32([u32; 6]),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100630 Reg64([u64; 16]),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100631}
632
Tomás González7ffb6132025-04-03 12:28:58 +0100633#[derive(Debug, Eq, PartialEq, Clone, Copy)]
634pub struct NotificationBindFlags {
635 per_vcpu_notification: bool,
636}
637
638impl NotificationBindFlags {
639 const PER_VCPU_NOTIFICATION: u32 = 1;
640}
641
642impl From<NotificationBindFlags> for u32 {
643 fn from(flags: NotificationBindFlags) -> Self {
644 let mut bits: u32 = 0;
645 if flags.per_vcpu_notification {
646 bits |= NotificationBindFlags::PER_VCPU_NOTIFICATION;
647 }
648 bits
649 }
650}
651
652impl From<u32> for NotificationBindFlags {
653 fn from(flags: u32) -> Self {
654 Self {
655 per_vcpu_notification: flags & Self::PER_VCPU_NOTIFICATION != 0,
656 }
657 }
658}
659
660#[derive(Debug, Eq, PartialEq, Clone, Copy)]
661pub struct NotificationSetFlags {
662 delay_schedule_receiver: bool,
663 vcpu_id: Option<u16>,
664}
665
666impl NotificationSetFlags {
667 const PER_VCP_NOTIFICATION: u32 = 1 << 0;
668 const DELAY_SCHEDULE_RECEIVER: u32 = 1 << 1;
669 const VCPU_ID_SHIFT: u32 = 16;
670
671 const MBZ_BITS: u32 = 0xfffc;
672}
673
674impl From<NotificationSetFlags> for u32 {
675 fn from(flags: NotificationSetFlags) -> Self {
676 let mut bits: u32 = 0;
677
678 if flags.delay_schedule_receiver {
679 bits |= NotificationSetFlags::DELAY_SCHEDULE_RECEIVER;
680 }
681 if let Some(vcpu_id) = flags.vcpu_id {
682 bits |= NotificationSetFlags::PER_VCP_NOTIFICATION;
683 bits |= u32::from(vcpu_id) << NotificationSetFlags::VCPU_ID_SHIFT;
684 }
685
686 bits
687 }
688}
689
690impl TryFrom<u32> for NotificationSetFlags {
691 type Error = Error;
692
693 fn try_from(flags: u32) -> Result<Self, Self::Error> {
694 if (flags & Self::MBZ_BITS) != 0 {
695 return Err(Error::InvalidNotificationSetFlag(flags));
696 }
697
698 let tentative_vcpu_id = (flags >> Self::VCPU_ID_SHIFT) as u16;
699
700 let vcpu_id = if (flags & Self::PER_VCP_NOTIFICATION) != 0 {
701 Some(tentative_vcpu_id)
702 } else {
703 if tentative_vcpu_id != 0 {
704 return Err(Error::InvalidNotificationSetFlag(flags));
705 }
706 None
707 };
708
709 Ok(Self {
710 delay_schedule_receiver: (flags & Self::DELAY_SCHEDULE_RECEIVER) != 0,
711 vcpu_id,
712 })
713 }
714}
715
716#[derive(Debug, Eq, PartialEq, Clone, Copy)]
717pub struct NotificationGetFlags {
718 sp_bitmap_id: bool,
719 vm_bitmap_id: bool,
720 spm_bitmap_id: bool,
721 hyp_bitmap_id: bool,
722}
723
724impl NotificationGetFlags {
725 const SP_BITMAP_ID: u32 = 1;
726 const VM_BITMAP_ID: u32 = 1 << 1;
727 const SPM_BITMAP_ID: u32 = 1 << 2;
728 const HYP_BITMAP_ID: u32 = 1 << 3;
729}
730
731impl From<NotificationGetFlags> for u32 {
732 fn from(flags: NotificationGetFlags) -> Self {
733 let mut bits: u32 = 0;
734 if flags.sp_bitmap_id {
735 bits |= NotificationGetFlags::SP_BITMAP_ID;
736 }
737 if flags.vm_bitmap_id {
738 bits |= NotificationGetFlags::VM_BITMAP_ID;
739 }
740 if flags.spm_bitmap_id {
741 bits |= NotificationGetFlags::SPM_BITMAP_ID;
742 }
743 if flags.hyp_bitmap_id {
744 bits |= NotificationGetFlags::HYP_BITMAP_ID;
745 }
746 bits
747 }
748}
749
750impl From<u32> for NotificationGetFlags {
751 // This is a "from" instead of a "try_from" because Reserved Bits are SBZ, *not* MBZ.
752 fn from(flags: u32) -> Self {
753 Self {
754 sp_bitmap_id: (flags & Self::SP_BITMAP_ID) != 0,
755 vm_bitmap_id: (flags & Self::VM_BITMAP_ID) != 0,
756 spm_bitmap_id: (flags & Self::SPM_BITMAP_ID) != 0,
757 hyp_bitmap_id: (flags & Self::HYP_BITMAP_ID) != 0,
758 }
759 }
760}
761
Imre Kis9959e062025-04-10 14:16:10 +0200762/// `FFA_NOTIFICATION_GET` specific success argument structure.
763#[derive(Debug, Eq, PartialEq, Clone, Copy)]
764pub struct SuccessArgsNotificationGet {
765 pub sp_notifications: Option<u64>,
766 pub vm_notifications: Option<u64>,
767 pub spm_notifications: Option<u32>,
768 pub hypervisor_notifications: Option<u32>,
769}
770
771impl From<SuccessArgsNotificationGet> for SuccessArgs {
772 fn from(value: SuccessArgsNotificationGet) -> Self {
773 let mut args = [0; 6];
774
775 if let Some(bitmap) = value.sp_notifications {
776 args[0] = bitmap as u32;
777 args[1] = (bitmap >> 32) as u32;
778 }
779
780 if let Some(bitmap) = value.vm_notifications {
781 args[2] = bitmap as u32;
782 args[3] = (bitmap >> 32) as u32;
783 }
784
785 if let Some(bitmap) = value.spm_notifications {
786 args[4] = bitmap;
787 }
788
789 if let Some(bitmap) = value.hypervisor_notifications {
790 args[5] = bitmap;
791 }
792
793 Self::Args32(args)
794 }
795}
796
797impl TryFrom<(NotificationGetFlags, SuccessArgs)> for SuccessArgsNotificationGet {
798 type Error = Error;
799
800 fn try_from(value: (NotificationGetFlags, SuccessArgs)) -> Result<Self, Self::Error> {
801 let (flags, value) = value;
802 let args = value.try_get_args32()?;
803
804 let sp_notifications = if flags.sp_bitmap_id {
805 Some(u64::from(args[0]) | (u64::from(args[1]) << 32))
806 } else {
807 None
808 };
809
810 let vm_notifications = if flags.vm_bitmap_id {
811 Some(u64::from(args[2]) | (u64::from(args[3]) << 32))
812 } else {
813 None
814 };
815
816 let spm_notifications = if flags.spm_bitmap_id {
817 Some(args[4])
818 } else {
819 None
820 };
821
822 let hypervisor_notifications = if flags.hyp_bitmap_id {
823 Some(args[5])
824 } else {
825 None
826 };
827
828 Ok(Self {
829 sp_notifications,
830 vm_notifications,
831 spm_notifications,
832 hypervisor_notifications,
833 })
834 }
835}
Tomás Gonzálezf268e322025-03-05 11:18:11 +0000836/// FF-A "message types", the terminology used by the spec is "interfaces".
837///
838/// The interfaces are used by FF-A components for communication at an FF-A instance. The spec also
839/// describes the valid FF-A instances and conduits for each interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100840#[derive(Debug, Eq, PartialEq, Clone, Copy)]
841pub enum Interface {
842 Error {
843 target_info: TargetInfo,
844 error_code: FfaError,
Balint Dobszayb727aab2025-04-07 10:24:59 +0200845 error_arg: u32,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100846 },
847 Success {
848 target_info: u32,
849 args: SuccessArgs,
850 },
851 Interrupt {
852 target_info: TargetInfo,
853 interrupt_id: u32,
854 },
855 Version {
856 input_version: Version,
857 },
858 VersionOut {
859 output_version: Version,
860 },
861 Features {
862 feat_id: Feature,
863 input_properties: u32,
864 },
865 RxAcquire {
866 vm_id: u16,
867 },
868 RxRelease {
869 vm_id: u16,
870 },
871 RxTxMap {
872 addr: RxTxAddr,
873 page_cnt: u32,
874 },
875 RxTxUnmap {
876 id: u16,
877 },
878 PartitionInfoGet {
879 uuid: Uuid,
Imre Kise295adb2025-04-10 13:26:28 +0200880 flags: PartitionInfoGetFlags,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100881 },
Tomás González0a058bc2025-03-11 11:20:55 +0000882 PartitionInfoGetRegs {
883 uuid: Uuid,
884 start_index: u16,
885 info_tag: u16,
886 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100887 IdGet,
888 SpmIdGet,
Tomás González092202a2025-03-05 11:56:45 +0000889 MsgWait {
890 flags: Option<MsgWaitFlags>,
891 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100892 Yield,
893 Run {
894 target_info: TargetInfo,
895 },
896 NormalWorldResume,
Tomás González17b92442025-03-10 16:45:04 +0000897 SecondaryEpRegister {
898 entrypoint: SecondaryEpRegisterAddr,
899 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100900 MsgSend2 {
901 sender_vm_id: u16,
902 flags: u32,
903 },
904 MsgSendDirectReq {
905 src_id: u16,
906 dst_id: u16,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100907 args: DirectMsgArgs,
908 },
909 MsgSendDirectResp {
910 src_id: u16,
911 dst_id: u16,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100912 args: DirectMsgArgs,
913 },
Balint Dobszayde0dc802025-02-28 14:16:52 +0100914 MsgSendDirectReq2 {
915 src_id: u16,
916 dst_id: u16,
917 uuid: Uuid,
918 args: DirectMsg2Args,
919 },
920 MsgSendDirectResp2 {
921 src_id: u16,
922 dst_id: u16,
923 args: DirectMsg2Args,
924 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100925 MemDonate {
926 total_len: u32,
927 frag_len: u32,
928 buf: Option<MemOpBuf>,
929 },
930 MemLend {
931 total_len: u32,
932 frag_len: u32,
933 buf: Option<MemOpBuf>,
934 },
935 MemShare {
936 total_len: u32,
937 frag_len: u32,
938 buf: Option<MemOpBuf>,
939 },
940 MemRetrieveReq {
941 total_len: u32,
942 frag_len: u32,
943 buf: Option<MemOpBuf>,
944 },
945 MemRetrieveResp {
946 total_len: u32,
947 frag_len: u32,
948 },
949 MemRelinquish,
950 MemReclaim {
951 handle: memory_management::Handle,
952 flags: u32,
953 },
954 MemPermGet {
955 addr: MemAddr,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100956 page_cnt: Option<u32>,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100957 },
958 MemPermSet {
959 addr: MemAddr,
960 page_cnt: u32,
961 mem_perm: u32,
962 },
963 ConsoleLog {
964 char_cnt: u8,
965 char_lists: ConsoleLogChars,
966 },
Tomás González7ffb6132025-04-03 12:28:58 +0100967 NotificationBitmapCreate {
968 vm_id: u16,
969 vcpu_cnt: u32,
970 },
971 NotificationBitmapDestroy {
972 vm_id: u16,
973 },
974 NotificationBind {
975 sender_id: u16,
976 receiver_id: u16,
977 flags: NotificationBindFlags,
978 bitmap: u64,
979 },
980 NotificationUnBind {
981 sender_id: u16,
982 receiver_id: u16,
983 bitmap: u64,
984 },
985 NotificationSet {
986 sender_id: u16,
987 receiver_id: u16,
988 flags: NotificationSetFlags,
989 bitmap: u64,
990 },
991 NotificationGet {
992 vcpu_id: u16,
993 endpoint_id: u16,
994 flags: NotificationGetFlags,
995 },
996 NotificationInfoGet {
997 is_32bit: bool,
998 },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +0100999 El3IntrHandle,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001000}
1001
Balint Dobszayde0dc802025-02-28 14:16:52 +01001002impl Interface {
1003 /// Returns the function ID for the call, if it has one.
1004 pub fn function_id(&self) -> Option<FuncId> {
1005 match self {
1006 Interface::Error { .. } => Some(FuncId::Error),
1007 Interface::Success { args, .. } => match args {
Imre Kis54773b62025-04-10 13:47:39 +02001008 SuccessArgs::Args32(..) => Some(FuncId::Success32),
1009 SuccessArgs::Args64(..) | SuccessArgs::Args64_2(..) => Some(FuncId::Success64),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001010 },
1011 Interface::Interrupt { .. } => Some(FuncId::Interrupt),
1012 Interface::Version { .. } => Some(FuncId::Version),
1013 Interface::VersionOut { .. } => None,
1014 Interface::Features { .. } => Some(FuncId::Features),
1015 Interface::RxAcquire { .. } => Some(FuncId::RxAcquire),
1016 Interface::RxRelease { .. } => Some(FuncId::RxRelease),
1017 Interface::RxTxMap { addr, .. } => match addr {
1018 RxTxAddr::Addr32 { .. } => Some(FuncId::RxTxMap32),
1019 RxTxAddr::Addr64 { .. } => Some(FuncId::RxTxMap64),
1020 },
1021 Interface::RxTxUnmap { .. } => Some(FuncId::RxTxUnmap),
1022 Interface::PartitionInfoGet { .. } => Some(FuncId::PartitionInfoGet),
Tomás González0a058bc2025-03-11 11:20:55 +00001023 Interface::PartitionInfoGetRegs { .. } => Some(FuncId::PartitionInfoGetRegs),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001024 Interface::IdGet => Some(FuncId::IdGet),
1025 Interface::SpmIdGet => Some(FuncId::SpmIdGet),
Tomás González092202a2025-03-05 11:56:45 +00001026 Interface::MsgWait { .. } => Some(FuncId::MsgWait),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001027 Interface::Yield => Some(FuncId::Yield),
1028 Interface::Run { .. } => Some(FuncId::Run),
1029 Interface::NormalWorldResume => Some(FuncId::NormalWorldResume),
Tomás González17b92442025-03-10 16:45:04 +00001030 Interface::SecondaryEpRegister { entrypoint } => match entrypoint {
1031 SecondaryEpRegisterAddr::Addr32 { .. } => Some(FuncId::SecondaryEpRegister32),
1032 SecondaryEpRegisterAddr::Addr64 { .. } => Some(FuncId::SecondaryEpRegister64),
1033 },
Balint Dobszayde0dc802025-02-28 14:16:52 +01001034 Interface::MsgSend2 { .. } => Some(FuncId::MsgSend2),
1035 Interface::MsgSendDirectReq { args, .. } => match args {
1036 DirectMsgArgs::Args32(_) => Some(FuncId::MsgSendDirectReq32),
1037 DirectMsgArgs::Args64(_) => Some(FuncId::MsgSendDirectReq64),
Tomás González4d5b0ba2025-03-03 17:15:55 +00001038 DirectMsgArgs::VersionReq { .. } => Some(FuncId::MsgSendDirectReq32),
1039 DirectMsgArgs::PowerPsciReq32 { .. } => Some(FuncId::MsgSendDirectReq32),
1040 DirectMsgArgs::PowerPsciReq64 { .. } => Some(FuncId::MsgSendDirectReq64),
1041 DirectMsgArgs::PowerWarmBootReq { .. } => Some(FuncId::MsgSendDirectReq32),
1042 DirectMsgArgs::VmCreated { .. } => Some(FuncId::MsgSendDirectReq32),
1043 DirectMsgArgs::VmDestructed { .. } => Some(FuncId::MsgSendDirectReq32),
1044 _ => None,
Balint Dobszayde0dc802025-02-28 14:16:52 +01001045 },
1046 Interface::MsgSendDirectResp { args, .. } => match args {
1047 DirectMsgArgs::Args32(_) => Some(FuncId::MsgSendDirectResp32),
1048 DirectMsgArgs::Args64(_) => Some(FuncId::MsgSendDirectResp64),
Tomás González4d5b0ba2025-03-03 17:15:55 +00001049 DirectMsgArgs::VersionResp { .. } => Some(FuncId::MsgSendDirectResp32),
1050 DirectMsgArgs::PowerPsciResp { .. } => Some(FuncId::MsgSendDirectResp32),
1051 DirectMsgArgs::VmCreatedAck { .. } => Some(FuncId::MsgSendDirectResp32),
1052 DirectMsgArgs::VmDestructedAck { .. } => Some(FuncId::MsgSendDirectResp32),
1053 _ => None,
Balint Dobszayde0dc802025-02-28 14:16:52 +01001054 },
1055 Interface::MsgSendDirectReq2 { .. } => Some(FuncId::MsgSendDirectReq64_2),
1056 Interface::MsgSendDirectResp2 { .. } => Some(FuncId::MsgSendDirectResp64_2),
1057 Interface::MemDonate { buf, .. } => match buf {
1058 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemDonate64),
1059 _ => Some(FuncId::MemDonate32),
1060 },
1061 Interface::MemLend { buf, .. } => match buf {
1062 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemLend64),
1063 _ => Some(FuncId::MemLend32),
1064 },
1065 Interface::MemShare { buf, .. } => match buf {
1066 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemShare64),
1067 _ => Some(FuncId::MemShare32),
1068 },
1069 Interface::MemRetrieveReq { buf, .. } => match buf {
1070 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemRetrieveReq64),
1071 _ => Some(FuncId::MemRetrieveReq32),
1072 },
1073 Interface::MemRetrieveResp { .. } => Some(FuncId::MemRetrieveResp),
1074 Interface::MemRelinquish => Some(FuncId::MemRelinquish),
1075 Interface::MemReclaim { .. } => Some(FuncId::MemReclaim),
1076 Interface::MemPermGet { addr, .. } => match addr {
1077 MemAddr::Addr32(_) => Some(FuncId::MemPermGet32),
1078 MemAddr::Addr64(_) => Some(FuncId::MemPermGet64),
1079 },
1080 Interface::MemPermSet { addr, .. } => match addr {
1081 MemAddr::Addr32(_) => Some(FuncId::MemPermSet32),
1082 MemAddr::Addr64(_) => Some(FuncId::MemPermSet64),
1083 },
1084 Interface::ConsoleLog { char_lists, .. } => match char_lists {
1085 ConsoleLogChars::Reg32(_) => Some(FuncId::ConsoleLog32),
1086 ConsoleLogChars::Reg64(_) => Some(FuncId::ConsoleLog64),
1087 },
Tomás González7ffb6132025-04-03 12:28:58 +01001088 Interface::NotificationBitmapCreate { .. } => Some(FuncId::NotificationBitmapCreate),
1089 Interface::NotificationBitmapDestroy { .. } => Some(FuncId::NotificationBitmapDestroy),
1090 Interface::NotificationBind { .. } => Some(FuncId::NotificationBind),
1091 Interface::NotificationUnBind { .. } => Some(FuncId::NotificationUnbind),
1092 Interface::NotificationSet { .. } => Some(FuncId::NotificationSet),
1093 Interface::NotificationGet { .. } => Some(FuncId::NotificationGet),
1094 Interface::NotificationInfoGet { is_32bit } => match is_32bit {
1095 true => Some(FuncId::NotificationInfoGet32),
1096 false => Some(FuncId::NotificationInfoGet64),
1097 },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01001098 Interface::El3IntrHandle => Some(FuncId::El3IntrHandle),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001099 }
1100 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01001101
Balint Dobszayde0dc802025-02-28 14:16:52 +01001102 /// Returns true if this is a 32-bit call, or false if it is a 64-bit call.
1103 pub fn is_32bit(&self) -> bool {
1104 // TODO: self should always have a function ID?
1105 self.function_id().unwrap().is_32bit()
1106 }
1107
1108 /// Parse interface from register contents. The caller must ensure that the `regs` argument has
1109 /// the correct length: 8 registers for FF-A v1.1 and lower, 18 registers for v1.2 and higher.
1110 pub fn from_regs(version: Version, regs: &[u64]) -> Result<Self, Error> {
1111 let reg_cnt = regs.len();
1112
1113 let msg = match reg_cnt {
1114 8 => {
1115 assert!(version <= Version(1, 1));
1116 Interface::unpack_regs8(version, regs.try_into().unwrap())?
1117 }
1118 18 => {
1119 assert!(version >= Version(1, 2));
1120 match FuncId::try_from(regs[0] as u32)? {
1121 FuncId::ConsoleLog64
1122 | FuncId::Success64
1123 | FuncId::MsgSendDirectReq64_2
Tomás González0a058bc2025-03-11 11:20:55 +00001124 | FuncId::MsgSendDirectResp64_2
1125 | FuncId::PartitionInfoGetRegs => {
Balint Dobszayde0dc802025-02-28 14:16:52 +01001126 Interface::unpack_regs18(version, regs.try_into().unwrap())?
1127 }
1128 _ => Interface::unpack_regs8(version, regs[..8].try_into().unwrap())?,
1129 }
1130 }
1131 _ => panic!(
1132 "Invalid number of registers ({}) for FF-A version {}",
1133 reg_cnt, version
1134 ),
1135 };
1136
1137 Ok(msg)
1138 }
1139
1140 fn unpack_regs8(version: Version, regs: &[u64; 8]) -> Result<Self, Error> {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001141 let fid = FuncId::try_from(regs[0] as u32)?;
1142
1143 let msg = match fid {
1144 FuncId::Error => Self::Error {
1145 target_info: (regs[1] as u32).into(),
1146 error_code: FfaError::try_from(regs[2] as i32)?,
Balint Dobszayb727aab2025-04-07 10:24:59 +02001147 error_arg: regs[3] as u32,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001148 },
1149 FuncId::Success32 => Self::Success {
1150 target_info: regs[1] as u32,
Imre Kis54773b62025-04-10 13:47:39 +02001151 args: SuccessArgs::Args32([
Balint Dobszay3aad9572025-01-17 16:54:11 +01001152 regs[2] as u32,
1153 regs[3] as u32,
1154 regs[4] as u32,
1155 regs[5] as u32,
1156 regs[6] as u32,
1157 regs[7] as u32,
1158 ]),
1159 },
1160 FuncId::Success64 => Self::Success {
1161 target_info: regs[1] as u32,
Imre Kis54773b62025-04-10 13:47:39 +02001162 args: SuccessArgs::Args64([regs[2], regs[3], regs[4], regs[5], regs[6], regs[7]]),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001163 },
1164 FuncId::Interrupt => Self::Interrupt {
1165 target_info: (regs[1] as u32).into(),
1166 interrupt_id: regs[2] as u32,
1167 },
1168 FuncId::Version => Self::Version {
Tomás González83146af2025-03-04 11:32:41 +00001169 input_version: (regs[1] as u32).try_into()?,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001170 },
1171 FuncId::Features => Self::Features {
Balint Dobszayc31e0b92025-03-03 20:16:56 +01001172 feat_id: (regs[1] as u32).into(),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001173 input_properties: regs[2] as u32,
1174 },
1175 FuncId::RxAcquire => Self::RxAcquire {
1176 vm_id: regs[1] as u16,
1177 },
1178 FuncId::RxRelease => Self::RxRelease {
1179 vm_id: regs[1] as u16,
1180 },
1181 FuncId::RxTxMap32 => {
1182 let addr = RxTxAddr::Addr32 {
1183 rx: regs[2] as u32,
1184 tx: regs[1] as u32,
1185 };
1186 let page_cnt = regs[3] as u32;
1187
1188 Self::RxTxMap { addr, page_cnt }
1189 }
1190 FuncId::RxTxMap64 => {
1191 let addr = RxTxAddr::Addr64 {
1192 rx: regs[2],
1193 tx: regs[1],
1194 };
1195 let page_cnt = regs[3] as u32;
1196
1197 Self::RxTxMap { addr, page_cnt }
1198 }
1199 FuncId::RxTxUnmap => Self::RxTxUnmap { id: regs[1] as u16 },
1200 FuncId::PartitionInfoGet => {
1201 let uuid_words = [
1202 regs[1] as u32,
1203 regs[2] as u32,
1204 regs[3] as u32,
1205 regs[4] as u32,
1206 ];
1207 let mut bytes: [u8; 16] = [0; 16];
1208 for (i, b) in uuid_words.iter().flat_map(|w| w.to_le_bytes()).enumerate() {
1209 bytes[i] = b;
1210 }
1211 Self::PartitionInfoGet {
1212 uuid: Uuid::from_bytes(bytes),
Imre Kise295adb2025-04-10 13:26:28 +02001213 flags: PartitionInfoGetFlags::try_from(regs[5] as u32)?,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001214 }
1215 }
1216 FuncId::IdGet => Self::IdGet,
1217 FuncId::SpmIdGet => Self::SpmIdGet,
Tomás González092202a2025-03-05 11:56:45 +00001218 FuncId::MsgWait => Self::MsgWait {
1219 flags: if version >= Version(1, 2) {
1220 Some(MsgWaitFlags::try_from(regs[2] as u32)?)
1221 } else {
1222 None
1223 },
1224 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001225 FuncId::Yield => Self::Yield,
1226 FuncId::Run => Self::Run {
1227 target_info: (regs[1] as u32).into(),
1228 },
1229 FuncId::NormalWorldResume => Self::NormalWorldResume,
Tomás González17b92442025-03-10 16:45:04 +00001230 FuncId::SecondaryEpRegister32 => Self::SecondaryEpRegister {
1231 entrypoint: SecondaryEpRegisterAddr::Addr32(regs[1] as u32),
1232 },
1233 FuncId::SecondaryEpRegister64 => Self::SecondaryEpRegister {
1234 entrypoint: SecondaryEpRegisterAddr::Addr64(regs[1]),
1235 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001236 FuncId::MsgSend2 => Self::MsgSend2 {
1237 sender_vm_id: regs[1] as u16,
1238 flags: regs[2] as u32,
1239 },
1240 FuncId::MsgSendDirectReq32 => Self::MsgSendDirectReq {
1241 src_id: (regs[1] >> 16) as u16,
1242 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001243 args: if (regs[2] as u32 & DirectMsgArgs::FWK_MSG_BITS) != 0 {
1244 match regs[2] as u32 {
1245 DirectMsgArgs::VERSION_REQ => DirectMsgArgs::VersionReq {
1246 version: Version::try_from(regs[3] as u32)?,
1247 },
1248 DirectMsgArgs::POWER_PSCI_REQ => DirectMsgArgs::PowerPsciReq32 {
Tomás González67f92c72025-03-20 16:50:42 +00001249 params: [
1250 regs[3] as u32,
1251 regs[4] as u32,
1252 regs[5] as u32,
1253 regs[6] as u32,
1254 ],
Tomás González4d5b0ba2025-03-03 17:15:55 +00001255 },
1256 DirectMsgArgs::POWER_WARM_BOOT_REQ => DirectMsgArgs::PowerWarmBootReq {
1257 boot_type: WarmBootType::try_from(regs[3] as u32)?,
1258 },
1259 DirectMsgArgs::VM_CREATED => DirectMsgArgs::VmCreated {
1260 handle: memory_management::Handle::from([
1261 regs[3] as u32,
1262 regs[4] as u32,
1263 ]),
1264 vm_id: regs[5] as u16,
1265 },
1266 DirectMsgArgs::VM_DESTRUCTED => DirectMsgArgs::VmDestructed {
1267 handle: memory_management::Handle::from([
1268 regs[3] as u32,
1269 regs[4] as u32,
1270 ]),
1271 vm_id: regs[5] as u16,
1272 },
1273 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1274 }
1275 } else {
1276 DirectMsgArgs::Args32([
1277 regs[3] as u32,
1278 regs[4] as u32,
1279 regs[5] as u32,
1280 regs[6] as u32,
1281 regs[7] as u32,
1282 ])
1283 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001284 },
1285 FuncId::MsgSendDirectReq64 => Self::MsgSendDirectReq {
1286 src_id: (regs[1] >> 16) as u16,
1287 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001288 args: if (regs[2] & DirectMsgArgs::FWK_MSG_BITS as u64) != 0 {
1289 match regs[2] as u32 {
1290 DirectMsgArgs::POWER_PSCI_REQ => DirectMsgArgs::PowerPsciReq64 {
Tomás González67f92c72025-03-20 16:50:42 +00001291 params: [regs[3], regs[4], regs[5], regs[6]],
Tomás González4d5b0ba2025-03-03 17:15:55 +00001292 },
1293 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1294 }
1295 } else {
1296 DirectMsgArgs::Args64([regs[3], regs[4], regs[5], regs[6], regs[7]])
1297 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001298 },
1299 FuncId::MsgSendDirectResp32 => Self::MsgSendDirectResp {
1300 src_id: (regs[1] >> 16) as u16,
1301 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001302 args: if (regs[2] as u32 & DirectMsgArgs::FWK_MSG_BITS) != 0 {
1303 match regs[2] as u32 {
1304 DirectMsgArgs::VERSION_RESP => {
1305 if regs[3] as i32 == FfaError::NotSupported.into() {
1306 DirectMsgArgs::VersionResp { version: None }
1307 } else {
1308 DirectMsgArgs::VersionResp {
1309 version: Some(Version::try_from(regs[3] as u32)?),
1310 }
1311 }
1312 }
1313 DirectMsgArgs::POWER_PSCI_RESP => DirectMsgArgs::PowerPsciResp {
1314 psci_status: regs[3] as i32,
1315 },
1316 DirectMsgArgs::VM_CREATED_ACK => DirectMsgArgs::VmCreatedAck {
1317 sp_status: (regs[3] as i32).try_into()?,
1318 },
1319 DirectMsgArgs::VM_DESTRUCTED_ACK => DirectMsgArgs::VmDestructedAck {
1320 sp_status: (regs[3] as i32).try_into()?,
1321 },
1322 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1323 }
1324 } else {
1325 DirectMsgArgs::Args32([
1326 regs[3] as u32,
1327 regs[4] as u32,
1328 regs[5] as u32,
1329 regs[6] as u32,
1330 regs[7] as u32,
1331 ])
1332 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001333 },
1334 FuncId::MsgSendDirectResp64 => Self::MsgSendDirectResp {
1335 src_id: (regs[1] >> 16) as u16,
1336 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001337 args: if (regs[2] & DirectMsgArgs::FWK_MSG_BITS as u64) != 0 {
1338 return Err(Error::UnrecognisedFwkMsg(regs[2] as u32));
1339 } else {
1340 DirectMsgArgs::Args64([regs[3], regs[4], regs[5], regs[6], regs[7]])
1341 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001342 },
1343 FuncId::MemDonate32 => Self::MemDonate {
1344 total_len: regs[1] as u32,
1345 frag_len: regs[2] as u32,
1346 buf: if regs[3] != 0 && regs[4] != 0 {
1347 Some(MemOpBuf::Buf32 {
1348 addr: regs[3] as u32,
1349 page_cnt: regs[4] as u32,
1350 })
1351 } else {
1352 None
1353 },
1354 },
1355 FuncId::MemDonate64 => Self::MemDonate {
1356 total_len: regs[1] as u32,
1357 frag_len: regs[2] as u32,
1358 buf: if regs[3] != 0 && regs[4] != 0 {
1359 Some(MemOpBuf::Buf64 {
1360 addr: regs[3],
1361 page_cnt: regs[4] as u32,
1362 })
1363 } else {
1364 None
1365 },
1366 },
1367 FuncId::MemLend32 => Self::MemLend {
1368 total_len: regs[1] as u32,
1369 frag_len: regs[2] as u32,
1370 buf: if regs[3] != 0 && regs[4] != 0 {
1371 Some(MemOpBuf::Buf32 {
1372 addr: regs[3] as u32,
1373 page_cnt: regs[4] as u32,
1374 })
1375 } else {
1376 None
1377 },
1378 },
1379 FuncId::MemLend64 => Self::MemLend {
1380 total_len: regs[1] as u32,
1381 frag_len: regs[2] as u32,
1382 buf: if regs[3] != 0 && regs[4] != 0 {
1383 Some(MemOpBuf::Buf64 {
1384 addr: regs[3],
1385 page_cnt: regs[4] as u32,
1386 })
1387 } else {
1388 None
1389 },
1390 },
1391 FuncId::MemShare32 => Self::MemShare {
1392 total_len: regs[1] as u32,
1393 frag_len: regs[2] as u32,
1394 buf: if regs[3] != 0 && regs[4] != 0 {
1395 Some(MemOpBuf::Buf32 {
1396 addr: regs[3] as u32,
1397 page_cnt: regs[4] as u32,
1398 })
1399 } else {
1400 None
1401 },
1402 },
1403 FuncId::MemShare64 => Self::MemShare {
1404 total_len: regs[1] as u32,
1405 frag_len: regs[2] as u32,
1406 buf: if regs[3] != 0 && regs[4] != 0 {
1407 Some(MemOpBuf::Buf64 {
1408 addr: regs[3],
1409 page_cnt: regs[4] as u32,
1410 })
1411 } else {
1412 None
1413 },
1414 },
1415 FuncId::MemRetrieveReq32 => Self::MemRetrieveReq {
1416 total_len: regs[1] as u32,
1417 frag_len: regs[2] as u32,
1418 buf: if regs[3] != 0 && regs[4] != 0 {
1419 Some(MemOpBuf::Buf32 {
1420 addr: regs[3] as u32,
1421 page_cnt: regs[4] as u32,
1422 })
1423 } else {
1424 None
1425 },
1426 },
1427 FuncId::MemRetrieveReq64 => Self::MemRetrieveReq {
1428 total_len: regs[1] as u32,
1429 frag_len: regs[2] as u32,
1430 buf: if regs[3] != 0 && regs[4] != 0 {
1431 Some(MemOpBuf::Buf64 {
1432 addr: regs[3],
1433 page_cnt: regs[4] as u32,
1434 })
1435 } else {
1436 None
1437 },
1438 },
1439 FuncId::MemRetrieveResp => Self::MemRetrieveResp {
1440 total_len: regs[1] as u32,
1441 frag_len: regs[2] as u32,
1442 },
1443 FuncId::MemRelinquish => Self::MemRelinquish,
1444 FuncId::MemReclaim => Self::MemReclaim {
1445 handle: memory_management::Handle::from([regs[1] as u32, regs[2] as u32]),
1446 flags: regs[3] as u32,
1447 },
1448 FuncId::MemPermGet32 => Self::MemPermGet {
1449 addr: MemAddr::Addr32(regs[1] as u32),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001450 page_cnt: if version >= Version(1, 3) {
1451 Some(regs[2] as u32)
1452 } else {
1453 None
1454 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001455 },
1456 FuncId::MemPermGet64 => Self::MemPermGet {
1457 addr: MemAddr::Addr64(regs[1]),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001458 page_cnt: if version >= Version(1, 3) {
1459 Some(regs[2] as u32)
1460 } else {
1461 None
1462 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001463 },
1464 FuncId::MemPermSet32 => Self::MemPermSet {
1465 addr: MemAddr::Addr32(regs[1] as u32),
1466 page_cnt: regs[2] as u32,
1467 mem_perm: regs[3] as u32,
1468 },
1469 FuncId::MemPermSet64 => Self::MemPermSet {
1470 addr: MemAddr::Addr64(regs[1]),
1471 page_cnt: regs[2] as u32,
1472 mem_perm: regs[3] as u32,
1473 },
1474 FuncId::ConsoleLog32 => Self::ConsoleLog {
1475 char_cnt: regs[1] as u8,
1476 char_lists: ConsoleLogChars::Reg32([
1477 regs[2] as u32,
1478 regs[3] as u32,
1479 regs[4] as u32,
1480 regs[5] as u32,
1481 regs[6] as u32,
1482 regs[7] as u32,
1483 ]),
1484 },
Tomás González7ffb6132025-04-03 12:28:58 +01001485 FuncId::NotificationBitmapCreate => {
1486 let tentative_vm_id = regs[1] as u32;
1487 if (tentative_vm_id >> 16) != 0 {
1488 return Err(Error::InvalidVmId(tentative_vm_id));
1489 }
1490 Self::NotificationBitmapCreate {
1491 vm_id: tentative_vm_id as u16,
1492 vcpu_cnt: regs[2] as u32,
1493 }
1494 }
1495 FuncId::NotificationBitmapDestroy => {
1496 let tentative_vm_id = regs[1] as u32;
1497 if (tentative_vm_id >> 16) != 0 {
1498 return Err(Error::InvalidVmId(tentative_vm_id));
1499 }
1500 Self::NotificationBitmapDestroy {
1501 vm_id: tentative_vm_id as u16,
1502 }
1503 }
1504 FuncId::NotificationBind => Self::NotificationBind {
1505 sender_id: (regs[1] >> 16) as u16,
1506 receiver_id: regs[1] as u16,
1507 flags: (regs[2] as u32).into(),
1508 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1509 },
1510 FuncId::NotificationUnbind => Self::NotificationUnBind {
1511 sender_id: (regs[1] >> 16) as u16,
1512 receiver_id: regs[1] as u16,
1513 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1514 },
1515 FuncId::NotificationSet => Self::NotificationSet {
1516 sender_id: (regs[1] >> 16) as u16,
1517 receiver_id: regs[1] as u16,
1518 flags: (regs[2] as u32).try_into()?,
1519 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1520 },
1521 FuncId::NotificationGet => Self::NotificationGet {
1522 vcpu_id: (regs[1] >> 16) as u16,
1523 endpoint_id: regs[1] as u16,
1524 flags: (regs[2] as u32).into(),
1525 },
1526 FuncId::NotificationInfoGet32 => Self::NotificationInfoGet { is_32bit: true },
1527 FuncId::NotificationInfoGet64 => Self::NotificationInfoGet { is_32bit: false },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01001528 FuncId::El3IntrHandle => Self::El3IntrHandle,
Balint Dobszayde0dc802025-02-28 14:16:52 +01001529 _ => panic!("Invalid number of registers (8) for function {:#x?}", fid),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001530 };
1531
1532 Ok(msg)
1533 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01001534
Balint Dobszayde0dc802025-02-28 14:16:52 +01001535 fn unpack_regs18(version: Version, regs: &[u64; 18]) -> Result<Self, Error> {
1536 assert!(version >= Version(1, 2));
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001537
Balint Dobszayde0dc802025-02-28 14:16:52 +01001538 let fid = FuncId::try_from(regs[0] as u32)?;
1539
1540 let msg = match fid {
1541 FuncId::Success64 => Self::Success {
1542 target_info: regs[1] as u32,
Imre Kis54773b62025-04-10 13:47:39 +02001543 args: SuccessArgs::Args64_2(regs[2..18].try_into().unwrap()),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001544 },
1545 FuncId::MsgSendDirectReq64_2 => Self::MsgSendDirectReq2 {
1546 src_id: (regs[1] >> 16) as u16,
1547 dst_id: regs[1] as u16,
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00001548 uuid: Uuid::from_u64_pair(regs[2].swap_bytes(), regs[3].swap_bytes()),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001549 args: DirectMsg2Args(regs[4..18].try_into().unwrap()),
1550 },
1551 FuncId::MsgSendDirectResp64_2 => Self::MsgSendDirectResp2 {
1552 src_id: (regs[1] >> 16) as u16,
1553 dst_id: regs[1] as u16,
1554 args: DirectMsg2Args(regs[4..18].try_into().unwrap()),
1555 },
1556 FuncId::ConsoleLog64 => Self::ConsoleLog {
1557 char_cnt: regs[1] as u8,
1558 char_lists: ConsoleLogChars::Reg64(regs[2..18].try_into().unwrap()),
1559 },
Tomás González0a058bc2025-03-11 11:20:55 +00001560 FuncId::PartitionInfoGetRegs => {
1561 // Bits[15:0]: Start index
1562 let start_index = (regs[3] & 0xffff) as u16;
1563 let info_tag = ((regs[3] >> 16) & 0xffff) as u16;
1564 Self::PartitionInfoGetRegs {
1565 uuid: Uuid::from_u64_pair(regs[1].swap_bytes(), regs[2].swap_bytes()),
1566 start_index,
1567 info_tag: if start_index == 0 && info_tag != 0 {
1568 return Err(Error::InvalidInformationTag(info_tag));
1569 } else {
1570 info_tag
1571 },
1572 }
1573 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001574 _ => panic!("Invalid number of registers (18) for function {:#x?}", fid),
1575 };
1576
1577 Ok(msg)
Balint Dobszay3aad9572025-01-17 16:54:11 +01001578 }
1579
Balint Dobszaya5846852025-02-26 15:38:53 +01001580 /// Create register contents for an interface.
Balint Dobszayde0dc802025-02-28 14:16:52 +01001581 pub fn to_regs(&self, version: Version, regs: &mut [u64]) {
1582 let reg_cnt = regs.len();
1583
1584 match reg_cnt {
1585 8 => {
1586 assert!(version <= Version(1, 1));
Balint Dobszay91bea9b2025-04-09 13:16:06 +02001587 regs.fill(0);
1588
Balint Dobszayde0dc802025-02-28 14:16:52 +01001589 self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
1590 }
1591 18 => {
1592 assert!(version >= Version(1, 2));
Balint Dobszay91bea9b2025-04-09 13:16:06 +02001593 regs.fill(0);
Balint Dobszayde0dc802025-02-28 14:16:52 +01001594
1595 match self {
1596 Interface::ConsoleLog {
1597 char_lists: ConsoleLogChars::Reg64(_),
1598 ..
1599 }
1600 | Interface::Success {
Imre Kis54773b62025-04-10 13:47:39 +02001601 args: SuccessArgs::Args64_2(_),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001602 ..
1603 }
1604 | Interface::MsgSendDirectReq2 { .. }
Tomás González0a058bc2025-03-11 11:20:55 +00001605 | Interface::MsgSendDirectResp2 { .. }
1606 | Interface::PartitionInfoGetRegs { .. } => {
Balint Dobszayde0dc802025-02-28 14:16:52 +01001607 self.pack_regs18(version, regs.try_into().unwrap());
1608 }
1609 _ => {
1610 self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
1611 }
1612 }
1613 }
1614 _ => panic!("Invalid number of registers {}", reg_cnt),
1615 }
1616 }
1617
1618 fn pack_regs8(&self, version: Version, a: &mut [u64; 8]) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001619 if let Some(function_id) = self.function_id() {
1620 a[0] = function_id as u64;
1621 }
1622
1623 match *self {
1624 Interface::Error {
1625 target_info,
1626 error_code,
Balint Dobszayb727aab2025-04-07 10:24:59 +02001627 error_arg,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001628 } => {
1629 a[1] = u32::from(target_info).into();
1630 a[2] = (error_code as u32).into();
Balint Dobszayb727aab2025-04-07 10:24:59 +02001631 a[3] = error_arg.into();
Balint Dobszay3aad9572025-01-17 16:54:11 +01001632 }
1633 Interface::Success { target_info, args } => {
1634 a[1] = target_info.into();
1635 match args {
Imre Kis54773b62025-04-10 13:47:39 +02001636 SuccessArgs::Args32(regs) => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001637 a[2] = regs[0].into();
1638 a[3] = regs[1].into();
1639 a[4] = regs[2].into();
1640 a[5] = regs[3].into();
1641 a[6] = regs[4].into();
1642 a[7] = regs[5].into();
1643 }
Imre Kis54773b62025-04-10 13:47:39 +02001644 SuccessArgs::Args64(regs) => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001645 a[2] = regs[0];
1646 a[3] = regs[1];
1647 a[4] = regs[2];
1648 a[5] = regs[3];
1649 a[6] = regs[4];
1650 a[7] = regs[5];
1651 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001652 _ => panic!("{:#x?} requires 18 registers", args),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001653 }
1654 }
1655 Interface::Interrupt {
1656 target_info,
1657 interrupt_id,
1658 } => {
1659 a[1] = u32::from(target_info).into();
1660 a[2] = interrupt_id.into();
1661 }
1662 Interface::Version { input_version } => {
1663 a[1] = u32::from(input_version).into();
1664 }
1665 Interface::VersionOut { output_version } => {
1666 a[0] = u32::from(output_version).into();
1667 }
1668 Interface::Features {
1669 feat_id,
1670 input_properties,
1671 } => {
1672 a[1] = u32::from(feat_id).into();
1673 a[2] = input_properties.into();
1674 }
1675 Interface::RxAcquire { vm_id } => {
1676 a[1] = vm_id.into();
1677 }
1678 Interface::RxRelease { vm_id } => {
1679 a[1] = vm_id.into();
1680 }
1681 Interface::RxTxMap { addr, page_cnt } => {
1682 match addr {
1683 RxTxAddr::Addr32 { rx, tx } => {
1684 a[1] = tx.into();
1685 a[2] = rx.into();
1686 }
1687 RxTxAddr::Addr64 { rx, tx } => {
1688 a[1] = tx;
1689 a[2] = rx;
1690 }
1691 }
1692 a[3] = page_cnt.into();
1693 }
1694 Interface::RxTxUnmap { id } => {
1695 a[1] = id.into();
1696 }
1697 Interface::PartitionInfoGet { uuid, flags } => {
1698 let bytes = uuid.into_bytes();
1699 a[1] = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]).into();
1700 a[2] = u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]).into();
1701 a[3] = u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]).into();
1702 a[4] = u32::from_le_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]).into();
Imre Kise295adb2025-04-10 13:26:28 +02001703 a[5] = u32::from(flags).into();
Balint Dobszay3aad9572025-01-17 16:54:11 +01001704 }
Tomás González092202a2025-03-05 11:56:45 +00001705 Interface::MsgWait { flags } => {
1706 if version >= Version(1, 2) {
1707 if let Some(flags) = flags {
1708 a[2] = u32::from(flags).into();
1709 }
1710 }
1711 }
1712 Interface::IdGet | Interface::SpmIdGet | Interface::Yield => {}
Balint Dobszay3aad9572025-01-17 16:54:11 +01001713 Interface::Run { target_info } => {
1714 a[1] = u32::from(target_info).into();
1715 }
1716 Interface::NormalWorldResume => {}
Tomás González17b92442025-03-10 16:45:04 +00001717 Interface::SecondaryEpRegister { entrypoint } => match entrypoint {
1718 SecondaryEpRegisterAddr::Addr32(addr) => a[1] = addr as u64,
1719 SecondaryEpRegisterAddr::Addr64(addr) => a[1] = addr,
1720 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001721 Interface::MsgSend2 {
1722 sender_vm_id,
1723 flags,
1724 } => {
1725 a[1] = sender_vm_id.into();
1726 a[2] = flags.into();
1727 }
1728 Interface::MsgSendDirectReq {
1729 src_id,
1730 dst_id,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001731 args,
1732 } => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +01001733 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Balint Dobszay3aad9572025-01-17 16:54:11 +01001734 match args {
1735 DirectMsgArgs::Args32(args) => {
1736 a[3] = args[0].into();
1737 a[4] = args[1].into();
1738 a[5] = args[2].into();
1739 a[6] = args[3].into();
1740 a[7] = args[4].into();
1741 }
1742 DirectMsgArgs::Args64(args) => {
1743 a[3] = args[0];
1744 a[4] = args[1];
1745 a[5] = args[2];
1746 a[6] = args[3];
1747 a[7] = args[4];
1748 }
Tomás González4d5b0ba2025-03-03 17:15:55 +00001749 DirectMsgArgs::VersionReq { version } => {
1750 a[2] = DirectMsgArgs::VERSION_REQ.into();
1751 a[3] = u32::from(version).into();
1752 }
Tomás González67f92c72025-03-20 16:50:42 +00001753 DirectMsgArgs::PowerPsciReq32 { params } => {
Tomás González4d5b0ba2025-03-03 17:15:55 +00001754 a[2] = DirectMsgArgs::POWER_PSCI_REQ.into();
Tomás González67f92c72025-03-20 16:50:42 +00001755 a[3] = params[0].into();
1756 a[4] = params[1].into();
1757 a[5] = params[2].into();
1758 a[6] = params[3].into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00001759 }
Tomás González67f92c72025-03-20 16:50:42 +00001760 DirectMsgArgs::PowerPsciReq64 { params } => {
Tomás González4d5b0ba2025-03-03 17:15:55 +00001761 a[2] = DirectMsgArgs::POWER_PSCI_REQ.into();
Tomás González67f92c72025-03-20 16:50:42 +00001762 a[3] = params[0];
1763 a[4] = params[1];
1764 a[5] = params[2];
1765 a[6] = params[3];
Tomás González4d5b0ba2025-03-03 17:15:55 +00001766 }
1767 DirectMsgArgs::PowerWarmBootReq { boot_type } => {
1768 a[2] = DirectMsgArgs::POWER_WARM_BOOT_REQ.into();
1769 a[3] = u32::from(boot_type).into();
1770 }
1771 DirectMsgArgs::VmCreated { handle, vm_id } => {
1772 a[2] = DirectMsgArgs::VM_CREATED.into();
1773 let handle_regs: [u32; 2] = handle.into();
1774 a[3] = handle_regs[0].into();
1775 a[4] = handle_regs[1].into();
1776 a[5] = vm_id.into();
1777 }
1778 DirectMsgArgs::VmDestructed { handle, vm_id } => {
1779 a[2] = DirectMsgArgs::VM_DESTRUCTED.into();
1780 let handle_regs: [u32; 2] = handle.into();
1781 a[3] = handle_regs[0].into();
1782 a[4] = handle_regs[1].into();
1783 a[5] = vm_id.into();
1784 }
1785 _ => panic!("Malformed MsgSendDirectReq interface"),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001786 }
1787 }
1788 Interface::MsgSendDirectResp {
1789 src_id,
1790 dst_id,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001791 args,
1792 } => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +01001793 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Balint Dobszay3aad9572025-01-17 16:54:11 +01001794 match args {
1795 DirectMsgArgs::Args32(args) => {
1796 a[3] = args[0].into();
1797 a[4] = args[1].into();
1798 a[5] = args[2].into();
1799 a[6] = args[3].into();
1800 a[7] = args[4].into();
1801 }
1802 DirectMsgArgs::Args64(args) => {
1803 a[3] = args[0];
1804 a[4] = args[1];
1805 a[5] = args[2];
1806 a[6] = args[3];
1807 a[7] = args[4];
1808 }
Tomás González4d5b0ba2025-03-03 17:15:55 +00001809 DirectMsgArgs::VersionResp { version } => {
1810 a[2] = DirectMsgArgs::VERSION_RESP.into();
1811 match version {
Tomás González67f92c72025-03-20 16:50:42 +00001812 None => a[3] = (i32::from(FfaError::NotSupported) as u32).into(),
Tomás González4d5b0ba2025-03-03 17:15:55 +00001813 Some(ver) => a[3] = u32::from(ver).into(),
1814 }
1815 }
1816 DirectMsgArgs::PowerPsciResp { psci_status } => {
1817 a[2] = DirectMsgArgs::POWER_PSCI_RESP.into();
1818 a[3] = psci_status as u64;
1819 }
1820 DirectMsgArgs::VmCreatedAck { sp_status } => {
1821 a[2] = DirectMsgArgs::VM_CREATED_ACK.into();
Tomás González67f92c72025-03-20 16:50:42 +00001822 a[3] = (i32::from(sp_status) as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00001823 }
1824 DirectMsgArgs::VmDestructedAck { sp_status } => {
1825 a[2] = DirectMsgArgs::VM_DESTRUCTED_ACK.into();
Tomás González67f92c72025-03-20 16:50:42 +00001826 a[3] = (i32::from(sp_status) as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00001827 }
1828 _ => panic!("Malformed MsgSendDirectResp interface"),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001829 }
1830 }
1831 Interface::MemDonate {
1832 total_len,
1833 frag_len,
1834 buf,
1835 } => {
1836 a[1] = total_len.into();
1837 a[2] = frag_len.into();
1838 (a[3], a[4]) = match buf {
1839 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1840 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1841 None => (0, 0),
1842 };
1843 }
1844 Interface::MemLend {
1845 total_len,
1846 frag_len,
1847 buf,
1848 } => {
1849 a[1] = total_len.into();
1850 a[2] = frag_len.into();
1851 (a[3], a[4]) = match buf {
1852 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1853 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1854 None => (0, 0),
1855 };
1856 }
1857 Interface::MemShare {
1858 total_len,
1859 frag_len,
1860 buf,
1861 } => {
1862 a[1] = total_len.into();
1863 a[2] = frag_len.into();
1864 (a[3], a[4]) = match buf {
1865 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1866 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1867 None => (0, 0),
1868 };
1869 }
1870 Interface::MemRetrieveReq {
1871 total_len,
1872 frag_len,
1873 buf,
1874 } => {
1875 a[1] = total_len.into();
1876 a[2] = frag_len.into();
1877 (a[3], a[4]) = match buf {
1878 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1879 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1880 None => (0, 0),
1881 };
1882 }
1883 Interface::MemRetrieveResp {
1884 total_len,
1885 frag_len,
1886 } => {
1887 a[1] = total_len.into();
1888 a[2] = frag_len.into();
1889 }
1890 Interface::MemRelinquish => {}
1891 Interface::MemReclaim { handle, flags } => {
1892 let handle_regs: [u32; 2] = handle.into();
1893 a[1] = handle_regs[0].into();
1894 a[2] = handle_regs[1].into();
1895 a[3] = flags.into();
1896 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001897 Interface::MemPermGet { addr, page_cnt } => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001898 a[1] = match addr {
1899 MemAddr::Addr32(addr) => addr.into(),
1900 MemAddr::Addr64(addr) => addr,
1901 };
Balint Dobszayde0dc802025-02-28 14:16:52 +01001902 a[2] = if version >= Version(1, 3) {
1903 page_cnt.unwrap().into()
1904 } else {
1905 assert!(page_cnt.is_none());
1906 0
1907 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01001908 }
1909 Interface::MemPermSet {
1910 addr,
1911 page_cnt,
1912 mem_perm,
1913 } => {
1914 a[1] = match addr {
1915 MemAddr::Addr32(addr) => addr.into(),
1916 MemAddr::Addr64(addr) => addr,
1917 };
1918 a[2] = page_cnt.into();
1919 a[3] = mem_perm.into();
1920 }
1921 Interface::ConsoleLog {
1922 char_cnt,
1923 char_lists,
1924 } => {
1925 a[1] = char_cnt.into();
1926 match char_lists {
1927 ConsoleLogChars::Reg32(regs) => {
1928 a[2] = regs[0].into();
1929 a[3] = regs[1].into();
1930 a[4] = regs[2].into();
1931 a[5] = regs[3].into();
1932 a[6] = regs[4].into();
1933 a[7] = regs[5].into();
1934 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001935 _ => panic!("{:#x?} requires 18 registers", char_lists),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001936 }
1937 }
Tomás González7ffb6132025-04-03 12:28:58 +01001938 Interface::NotificationBitmapCreate { vm_id, vcpu_cnt } => {
1939 a[1] = vm_id.into();
1940 a[2] = vcpu_cnt.into();
1941 }
1942 Interface::NotificationBitmapDestroy { vm_id } => {
1943 a[1] = vm_id.into();
1944 }
1945 Interface::NotificationBind {
1946 sender_id,
1947 receiver_id,
1948 flags,
1949 bitmap,
1950 } => {
1951 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
1952 a[2] = u32::from(flags).into();
1953 a[3] = bitmap & 0xffff_ffff;
1954 a[4] = bitmap >> 32;
1955 }
1956 Interface::NotificationUnBind {
1957 sender_id,
1958 receiver_id,
1959 bitmap,
1960 } => {
1961 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
1962 a[3] = bitmap & 0xffff_ffff;
1963 a[4] = bitmap >> 32;
1964 }
1965 Interface::NotificationSet {
1966 sender_id,
1967 receiver_id,
1968 flags,
1969 bitmap,
1970 } => {
1971 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
1972 a[2] = u32::from(flags).into();
1973 a[3] = bitmap & 0xffff_ffff;
1974 a[4] = bitmap >> 32;
1975 }
1976 Interface::NotificationGet {
1977 vcpu_id,
1978 endpoint_id,
1979 flags,
1980 } => {
1981 a[1] = (u64::from(vcpu_id) << 16) | u64::from(endpoint_id);
1982 a[2] = u32::from(flags).into();
1983 }
1984 Interface::NotificationInfoGet { .. } => {}
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01001985 Interface::El3IntrHandle => {}
Balint Dobszayde0dc802025-02-28 14:16:52 +01001986 _ => panic!("{:#x?} requires 18 registers", self),
1987 }
1988 }
1989
1990 fn pack_regs18(&self, version: Version, a: &mut [u64; 18]) {
1991 assert!(version >= Version(1, 2));
1992
Balint Dobszayde0dc802025-02-28 14:16:52 +01001993 if let Some(function_id) = self.function_id() {
1994 a[0] = function_id as u64;
1995 }
1996
1997 match *self {
1998 Interface::Success { target_info, args } => {
1999 a[1] = target_info.into();
2000 match args {
Imre Kis54773b62025-04-10 13:47:39 +02002001 SuccessArgs::Args64_2(regs) => a[2..18].copy_from_slice(&regs[..16]),
Balint Dobszayde0dc802025-02-28 14:16:52 +01002002 _ => panic!("{:#x?} requires 8 registers", args),
2003 }
2004 }
2005 Interface::MsgSendDirectReq2 {
2006 src_id,
2007 dst_id,
2008 uuid,
2009 args,
2010 } => {
2011 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00002012 let (uuid_msb, uuid_lsb) = uuid.as_u64_pair();
2013 (a[2], a[3]) = (uuid_msb.swap_bytes(), uuid_lsb.swap_bytes());
Balint Dobszayde0dc802025-02-28 14:16:52 +01002014 a[4..18].copy_from_slice(&args.0[..14]);
2015 }
2016 Interface::MsgSendDirectResp2 {
2017 src_id,
2018 dst_id,
2019 args,
2020 } => {
2021 a[1] = ((src_id as u64) << 16) | dst_id as u64;
2022 a[2] = 0;
2023 a[3] = 0;
2024 a[4..18].copy_from_slice(&args.0[..14]);
2025 }
2026 Interface::ConsoleLog {
2027 char_cnt,
2028 char_lists,
2029 } => {
2030 a[1] = char_cnt.into();
2031 match char_lists {
2032 ConsoleLogChars::Reg64(regs) => a[2..18].copy_from_slice(&regs[..16]),
2033 _ => panic!("{:#x?} requires 8 registers", char_lists),
2034 }
2035 }
Tomás González0a058bc2025-03-11 11:20:55 +00002036 Interface::PartitionInfoGetRegs {
2037 uuid,
2038 start_index,
2039 info_tag,
2040 } => {
2041 if start_index == 0 && info_tag != 0 {
2042 panic!("Information Tag MBZ if start index is 0: {:#x?}", self);
2043 }
2044 let (uuid_msb, uuid_lsb) = uuid.as_u64_pair();
2045 (a[1], a[2]) = (uuid_msb.swap_bytes(), uuid_lsb.swap_bytes());
2046 a[3] = (u64::from(info_tag) << 16) | u64::from(start_index);
2047 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01002048 _ => panic!("{:#x?} requires 8 registers", self),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002049 }
2050 }
2051
Balint Dobszaya5846852025-02-26 15:38:53 +01002052 /// Helper function to create an `FFA_SUCCESS` interface without any arguments.
Balint Dobszay3aad9572025-01-17 16:54:11 +01002053 pub fn success32_noargs() -> Self {
2054 Self::Success {
2055 target_info: 0,
Imre Kis54773b62025-04-10 13:47:39 +02002056 args: SuccessArgs::Args32([0; 6]),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002057 }
2058 }
2059
Tomás González4c8c7d22025-03-10 17:14:57 +00002060 /// Helper function to create an `FFA_SUCCESS` interface without any arguments.
2061 pub fn success64_noargs() -> Self {
2062 Self::Success {
2063 target_info: 0,
Imre Kis54773b62025-04-10 13:47:39 +02002064 args: SuccessArgs::Args64([0; 6]),
Tomás González4c8c7d22025-03-10 17:14:57 +00002065 }
2066 }
2067
Balint Dobszaya5846852025-02-26 15:38:53 +01002068 /// Helper function to create an `FFA_ERROR` interface with an error code.
Balint Dobszay3aad9572025-01-17 16:54:11 +01002069 pub fn error(error_code: FfaError) -> Self {
2070 Self::Error {
2071 target_info: TargetInfo {
2072 endpoint_id: 0,
2073 vcpu_id: 0,
2074 },
2075 error_code,
Balint Dobszayb727aab2025-04-07 10:24:59 +02002076 error_arg: 0,
Balint Dobszay3aad9572025-01-17 16:54:11 +01002077 }
2078 }
2079}
2080
Balint Dobszaya5846852025-02-26 15:38:53 +01002081/// Maximum number of characters transmitted in a single `FFA_CONSOLE_LOG32` message.
2082pub const CONSOLE_LOG_32_MAX_CHAR_CNT: u8 = 24;
Balint Dobszayde0dc802025-02-28 14:16:52 +01002083/// Maximum number of characters transmitted in a single `FFA_CONSOLE_LOG64` message.
2084pub const CONSOLE_LOG_64_MAX_CHAR_CNT: u8 = 128;
Balint Dobszay3aad9572025-01-17 16:54:11 +01002085
Balint Dobszaya5846852025-02-26 15:38:53 +01002086/// Helper function to convert the "Tightly packed list of characters" format used by the
2087/// `FFA_CONSOLE_LOG` interface into a byte slice.
Balint Dobszay3aad9572025-01-17 16:54:11 +01002088pub fn parse_console_log(
2089 char_cnt: u8,
2090 char_lists: &ConsoleLogChars,
2091 log_bytes: &mut [u8],
2092) -> Result<(), FfaError> {
2093 match char_lists {
2094 ConsoleLogChars::Reg32(regs) => {
Balint Dobszaya5846852025-02-26 15:38:53 +01002095 if !(1..=CONSOLE_LOG_32_MAX_CHAR_CNT).contains(&char_cnt) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01002096 return Err(FfaError::InvalidParameters);
2097 }
2098 for (i, reg) in regs.iter().enumerate() {
2099 log_bytes[4 * i..4 * (i + 1)].copy_from_slice(&reg.to_le_bytes());
2100 }
2101 }
2102 ConsoleLogChars::Reg64(regs) => {
Balint Dobszaya5846852025-02-26 15:38:53 +01002103 if !(1..=CONSOLE_LOG_64_MAX_CHAR_CNT).contains(&char_cnt) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01002104 return Err(FfaError::InvalidParameters);
2105 }
2106 for (i, reg) in regs.iter().enumerate() {
2107 log_bytes[8 * i..8 * (i + 1)].copy_from_slice(&reg.to_le_bytes());
2108 }
2109 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +02002110 }
2111
Balint Dobszayc8802492025-01-15 18:11:39 +01002112 Ok(())
Balint Dobszay5bf492f2024-07-29 17:21:32 +02002113}
Tomás González0a058bc2025-03-11 11:20:55 +00002114
2115#[cfg(test)]
2116mod tests {
2117 use super::*;
2118
2119 #[test]
2120 fn part_info_get_regs() {
2121 let uuid = Uuid::parse_str("a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8").unwrap();
2122 let uuid_bytes = uuid.as_bytes();
2123 let test_info_tag = 0b1101_1101;
2124 let test_start_index = 0b1101;
2125 let start_index_and_tag = (test_info_tag << 16) | test_start_index;
2126 let version = Version(1, 2);
2127
2128 // From spec:
2129 // Bytes[0...7] of UUID with byte 0 in the low-order bits.
2130 let reg_x1 = (uuid_bytes[7] as u64) << 56
2131 | (uuid_bytes[6] as u64) << 48
2132 | (uuid_bytes[5] as u64) << 40
2133 | (uuid_bytes[4] as u64) << 32
2134 | (uuid_bytes[3] as u64) << 24
2135 | (uuid_bytes[2] as u64) << 16
2136 | (uuid_bytes[1] as u64) << 8
2137 | (uuid_bytes[0] as u64);
2138
2139 // From spec:
2140 // Bytes[8...15] of UUID with byte 8 in the low-order bits.
2141 let reg_x2 = (uuid_bytes[15] as u64) << 56
2142 | (uuid_bytes[14] as u64) << 48
2143 | (uuid_bytes[13] as u64) << 40
2144 | (uuid_bytes[12] as u64) << 32
2145 | (uuid_bytes[11] as u64) << 24
2146 | (uuid_bytes[10] as u64) << 16
2147 | (uuid_bytes[9] as u64) << 8
2148 | (uuid_bytes[8] as u64);
2149
2150 // First, test for wrong tag:
2151 {
2152 let mut regs = [0u64; 18];
2153 regs[0] = FuncId::PartitionInfoGetRegs as u64;
2154 regs[1] = reg_x1;
2155 regs[2] = reg_x2;
2156 regs[3] = test_info_tag << 16;
2157
2158 assert!(Interface::from_regs(version, &regs).is_err_and(
2159 |e| e == Error::InvalidInformationTag(test_info_tag.try_into().unwrap())
2160 ));
2161 }
2162
2163 // Test for regs -> Interface -> regs
2164 {
2165 let mut orig_regs = [0u64; 18];
2166 orig_regs[0] = FuncId::PartitionInfoGetRegs as u64;
2167 orig_regs[1] = reg_x1;
2168 orig_regs[2] = reg_x2;
2169 orig_regs[3] = start_index_and_tag;
2170
2171 let mut test_regs = orig_regs.clone();
2172 let interface = Interface::from_regs(version, &mut test_regs).unwrap();
2173 match &interface {
2174 Interface::PartitionInfoGetRegs {
2175 info_tag,
2176 start_index,
2177 uuid: int_uuid,
2178 } => {
2179 assert_eq!(u64::from(*info_tag), test_info_tag);
2180 assert_eq!(u64::from(*start_index), test_start_index);
2181 assert_eq!(*int_uuid, uuid);
2182 }
2183 _ => panic!("Expecting Interface::PartitionInfoGetRegs!"),
2184 }
2185 test_regs.fill(0);
2186 interface.to_regs(version, &mut test_regs);
2187 assert_eq!(orig_regs, test_regs);
2188 }
2189
2190 // Test for Interface -> regs -> Interface
2191 {
2192 let interface = Interface::PartitionInfoGetRegs {
2193 info_tag: test_info_tag.try_into().unwrap(),
2194 start_index: test_start_index.try_into().unwrap(),
2195 uuid,
2196 };
2197
2198 let mut regs: [u64; 18] = [0; 18];
2199 interface.to_regs(version, &mut regs);
2200
2201 assert_eq!(Some(FuncId::PartitionInfoGetRegs), interface.function_id());
2202 assert_eq!(regs[0], interface.function_id().unwrap() as u64);
2203 assert_eq!(regs[1], reg_x1);
2204 assert_eq!(regs[2], reg_x2);
2205 assert_eq!(regs[3], (test_info_tag << 16) | test_start_index);
2206
2207 assert_eq!(Interface::from_regs(version, &regs).unwrap(), interface);
2208 }
2209 }
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00002210
2211 #[test]
2212 fn msg_send_direct_req2() {
2213 let uuid = Uuid::parse_str("a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8").unwrap();
2214 let uuid_bytes = uuid.as_bytes();
2215
2216 // From spec:
2217 // Bytes[0...7] of UUID with byte 0 in the low-order bits.
2218 let reg_x2 = (uuid_bytes[7] as u64) << 56
2219 | (uuid_bytes[6] as u64) << 48
2220 | (uuid_bytes[5] as u64) << 40
2221 | (uuid_bytes[4] as u64) << 32
2222 | (uuid_bytes[3] as u64) << 24
2223 | (uuid_bytes[2] as u64) << 16
2224 | (uuid_bytes[1] as u64) << 8
2225 | (uuid_bytes[0] as u64);
2226
2227 // From spec:
2228 // Bytes[8...15] of UUID with byte 8 in the low-order bits.
2229 let reg_x3 = (uuid_bytes[15] as u64) << 56
2230 | (uuid_bytes[14] as u64) << 48
2231 | (uuid_bytes[13] as u64) << 40
2232 | (uuid_bytes[12] as u64) << 32
2233 | (uuid_bytes[11] as u64) << 24
2234 | (uuid_bytes[10] as u64) << 16
2235 | (uuid_bytes[9] as u64) << 8
2236 | (uuid_bytes[8] as u64);
2237
2238 let test_sender = 0b1101_1101;
2239 let test_receiver = 0b1101;
2240 let test_sender_receiver = (test_sender << 16) | test_receiver;
2241 let version = Version(1, 2);
2242
2243 // Test for regs -> Interface -> regs
2244 {
2245 let mut orig_regs = [0u64; 18];
2246 orig_regs[0] = FuncId::MsgSendDirectReq64_2 as u64;
2247 orig_regs[1] = test_sender_receiver;
2248 orig_regs[2] = reg_x2;
2249 orig_regs[3] = reg_x3;
2250
2251 let mut test_regs = orig_regs.clone();
2252 let interface = Interface::from_regs(version, &mut test_regs).unwrap();
2253 match &interface {
2254 Interface::MsgSendDirectReq2 {
2255 dst_id,
2256 src_id,
2257 args: _,
2258 uuid: int_uuid,
2259 } => {
2260 assert_eq!(u64::from(*src_id), test_sender);
2261 assert_eq!(u64::from(*dst_id), test_receiver);
2262 assert_eq!(*int_uuid, uuid);
2263 }
2264 _ => panic!("Expecting Interface::MsgSendDirectReq2!"),
2265 }
2266 test_regs.fill(0);
2267 interface.to_regs(version, &mut test_regs);
2268 assert_eq!(orig_regs, test_regs);
2269 }
2270
2271 // Test for Interface -> regs -> Interface
2272 {
2273 let rest_of_regs: [u64; 14] = [0; 14];
2274
2275 let interface = Interface::MsgSendDirectReq2 {
2276 src_id: test_sender.try_into().unwrap(),
2277 dst_id: test_receiver.try_into().unwrap(),
2278 uuid,
2279 args: DirectMsg2Args(rest_of_regs),
2280 };
2281
2282 let mut regs: [u64; 18] = [0; 18];
2283 interface.to_regs(version, &mut regs);
2284
2285 assert_eq!(Some(FuncId::MsgSendDirectReq64_2), interface.function_id());
2286 assert_eq!(regs[0], interface.function_id().unwrap() as u64);
2287 assert_eq!(regs[1], test_sender_receiver);
2288 assert_eq!(regs[2], reg_x2);
2289 assert_eq!(regs[3], reg_x3);
2290 assert_eq!(regs[4], 0);
2291
2292 assert_eq!(Interface::from_regs(version, &regs).unwrap(), interface);
2293 }
2294 }
Tomás González6ccba0a2025-04-09 13:31:29 +01002295
2296 #[test]
2297 fn is_32bit() {
2298 let interface_64 = Interface::MsgSendDirectReq {
2299 src_id: 0,
2300 dst_id: 1,
2301 args: DirectMsgArgs::Args64([0, 0, 0, 0, 0]),
2302 };
2303 assert!(!interface_64.is_32bit());
2304
2305 let interface_32 = Interface::MsgSendDirectReq {
2306 src_id: 0,
2307 dst_id: 1,
2308 args: DirectMsgArgs::Args32([0, 0, 0, 0, 0]),
2309 };
2310 assert!(interface_32.is_32bit());
2311 }
Tomás González0a058bc2025-03-11 11:20:55 +00002312}