blob: 174ea9b76caeea157cff59f6b5f96d6582f4ba45 [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`]
Balint Dobszay3aad9572025-01-17 16:54:11 +0100210#[derive(Debug, Eq, PartialEq, Clone, Copy)]
211pub enum SuccessArgs {
Imre Kis54773b62025-04-10 13:47:39 +0200212 Args32([u32; 6]),
213 Args64([u64; 6]),
214 Args64_2([u64; 16]),
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200215}
216
Imre Kis839eaef2025-04-11 17:38:36 +0200217impl SuccessArgs {
218 fn try_get_args32(self) -> Result<[u32; 6], Error> {
219 match self {
220 SuccessArgs::Args32(args) => Ok(args),
221 SuccessArgs::Args64(_) | SuccessArgs::Args64_2(_) => {
222 Err(Error::InvalidSuccessArgsVariant)
223 }
224 }
225 }
226
227 fn try_get_args64(self) -> Result<[u64; 6], Error> {
228 match self {
229 SuccessArgs::Args64(args) => Ok(args),
230 SuccessArgs::Args32(_) | SuccessArgs::Args64_2(_) => {
231 Err(Error::InvalidSuccessArgsVariant)
232 }
233 }
234 }
235
236 fn try_get_args64_2(self) -> Result<[u64; 16], Error> {
237 match self {
238 SuccessArgs::Args64_2(args) => Ok(args),
239 SuccessArgs::Args32(_) | SuccessArgs::Args64(_) => {
240 Err(Error::InvalidSuccessArgsVariant)
241 }
242 }
243 }
244}
245
Tomás González17b92442025-03-10 16:45:04 +0000246/// Entrypoint address argument for `FFA_SECONDARY_EP_REGISTER` interface.
247#[derive(Debug, Eq, PartialEq, Clone, Copy)]
248pub enum SecondaryEpRegisterAddr {
249 Addr32(u32),
250 Addr64(u64),
251}
252
Balint Dobszaya5846852025-02-26 15:38:53 +0100253/// Version number of the FF-A implementation, `.0` is the major, `.1` is minor the version.
Balint Dobszayde0dc802025-02-28 14:16:52 +0100254#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord)]
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200255pub struct Version(pub u16, pub u16);
256
Tomás González1f794352025-03-03 16:47:06 +0000257impl Version {
Tomás González83146af2025-03-04 11:32:41 +0000258 // The FF-A spec mandates that bit[31] of a version number must be 0
259 const MBZ_BITS: u32 = 1 << 31;
260
Tomás González1f794352025-03-03 16:47:06 +0000261 /// Returns whether the caller's version (self) is compatible with the callee's version (input
262 /// parameter)
263 pub fn is_compatible_to(&self, callee_version: &Version) -> bool {
264 self.0 == callee_version.0 && self.1 <= callee_version.1
265 }
266}
267
Tomás González83146af2025-03-04 11:32:41 +0000268impl TryFrom<u32> for Version {
269 type Error = Error;
270
271 fn try_from(val: u32) -> Result<Self, Self::Error> {
272 if (val & Self::MBZ_BITS) != 0 {
273 Err(Error::InvalidVersion(val))
274 } else {
275 Ok(Self((val >> 16) as u16, val as u16))
276 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200277 }
278}
279
280impl From<Version> for u32 {
281 fn from(v: Version) -> Self {
Tomás González83146af2025-03-04 11:32:41 +0000282 let v_u32 = ((v.0 as u32) << 16) | v.1 as u32;
283 assert!(v_u32 & Version::MBZ_BITS == 0);
284 v_u32
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200285 }
286}
287
Andrew Walbran19970ba2024-11-25 15:35:00 +0000288impl Display for Version {
289 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
290 write!(f, "{}.{}", self.0, self.1)
291 }
292}
293
294impl Debug for Version {
295 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
296 Display::fmt(self, f)
297 }
298}
299
Balint Dobszaya5846852025-02-26 15:38:53 +0100300/// Feature IDs used by the `FFA_FEATURES` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100301#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
302#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedFeatureId))]
303#[repr(u8)]
304pub enum FeatureId {
305 NotificationPendingInterrupt = 0x1,
306 ScheduleReceiverInterrupt = 0x2,
307 ManagedExitInterrupt = 0x3,
308}
Balint Dobszayc8802492025-01-15 18:11:39 +0100309
Balint Dobszaya5846852025-02-26 15:38:53 +0100310/// Arguments for the `FFA_FEATURES` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100311#[derive(Debug, Eq, PartialEq, Clone, Copy)]
312pub enum Feature {
313 FuncId(FuncId),
314 FeatureId(FeatureId),
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100315 Unknown(u32),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100316}
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200317
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100318impl From<u32> for Feature {
319 fn from(value: u32) -> Self {
320 // Bit[31] is set for all valid FF-A function IDs so we don't have to check it separately
321 if let Ok(func_id) = value.try_into() {
322 Self::FuncId(func_id)
323 } else if let Ok(feat_id) = (value as u8).try_into() {
324 Self::FeatureId(feat_id)
Balint Dobszay3aad9572025-01-17 16:54:11 +0100325 } else {
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100326 Self::Unknown(value)
327 }
Balint Dobszay3aad9572025-01-17 16:54:11 +0100328 }
329}
330
331impl From<Feature> for u32 {
332 fn from(value: Feature) -> Self {
333 match value {
334 Feature::FuncId(func_id) => (1 << 31) | func_id as u32,
335 Feature::FeatureId(feature_id) => feature_id as u32,
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100336 Feature::Unknown(id) => panic!("Unknown feature or function ID {:#x?}", id),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100337 }
338 }
339}
340
Imre Kis4e9d8bc2025-04-10 13:48:26 +0200341/// `FFA_FEATURES` specific success argument structure. This type needs further specialization based
342/// on 'FF-A function ID or Feature ID' field of the preceeding `FFA_FEATURES` request.
343#[derive(Debug, Eq, PartialEq, Clone, Copy)]
344pub struct SuccessArgsFeatures {
345 pub properties: [u32; 2],
346}
347
348impl From<SuccessArgsFeatures> for SuccessArgs {
349 fn from(value: SuccessArgsFeatures) -> Self {
350 Self::Args32([value.properties[0], value.properties[1], 0, 0, 0, 0])
351 }
352}
353
354impl TryFrom<SuccessArgs> for SuccessArgsFeatures {
355 type Error = Error;
356
357 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
358 let args = value.try_get_args32()?;
359
360 Ok(Self {
361 properties: [args[0], args[1]],
362 })
363 }
364}
365
Balint Dobszaya5846852025-02-26 15:38:53 +0100366/// RXTX buffer descriptor, used by `FFA_RXTX_MAP`.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100367#[derive(Debug, Eq, PartialEq, Clone, Copy)]
368pub enum RxTxAddr {
369 Addr32 { rx: u32, tx: u32 },
370 Addr64 { rx: u64, tx: u64 },
371}
372
Imre Kisbbef2872025-04-10 14:11:29 +0200373/// `FFA_ID_GET` specific success argument structure.
374#[derive(Debug, Eq, PartialEq, Clone, Copy)]
375pub struct SuccessArgsIdGet {
376 pub id: u16,
377}
378
379impl From<SuccessArgsIdGet> for SuccessArgs {
380 fn from(value: SuccessArgsIdGet) -> Self {
381 SuccessArgs::Args32([value.id as u32, 0, 0, 0, 0, 0])
382 }
383}
384
385impl TryFrom<SuccessArgs> for SuccessArgsIdGet {
386 type Error = Error;
387
388 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
389 let args = value.try_get_args32()?;
390 Ok(Self { id: args[0] as u16 })
391 }
392}
393
394/// `FFA_SPM_ID_GET` specific success argument structure.
395#[derive(Debug, Eq, PartialEq, Clone, Copy)]
396pub struct SuccessArgsSpmIdGet {
397 pub id: u16,
398}
399
400impl From<SuccessArgsSpmIdGet> for SuccessArgs {
401 fn from(value: SuccessArgsSpmIdGet) -> Self {
402 SuccessArgs::Args32([value.id as u32, 0, 0, 0, 0, 0])
403 }
404}
405
406impl TryFrom<SuccessArgs> for SuccessArgsSpmIdGet {
407 type Error = Error;
408
409 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
410 let args = value.try_get_args32()?;
411 Ok(Self { id: args[0] as u16 })
412 }
413}
414
Imre Kise295adb2025-04-10 13:26:28 +0200415/// Flags of the `FFA_PARTITION_INFO_GET` interface.
416#[derive(Debug, Eq, PartialEq, Clone, Copy)]
417pub struct PartitionInfoGetFlags {
418 pub count_only: bool,
419}
420
421impl PartitionInfoGetFlags {
422 const RETURN_INFORMATION_TYPE_FLAG: u32 = 1 << 0;
423 const MBZ_BITS: u32 = 0xffff_fffe;
424}
425
426impl TryFrom<u32> for PartitionInfoGetFlags {
427 type Error = Error;
428
429 fn try_from(val: u32) -> Result<Self, Self::Error> {
430 if (val & Self::MBZ_BITS) != 0 {
431 Err(Error::InvalidPartitionInfoGetFlag(val))
432 } else {
433 Ok(Self {
434 count_only: val & Self::RETURN_INFORMATION_TYPE_FLAG != 0,
435 })
436 }
437 }
438}
439
440impl From<PartitionInfoGetFlags> for u32 {
441 fn from(flags: PartitionInfoGetFlags) -> Self {
442 let mut bits: u32 = 0;
443 if flags.count_only {
444 bits |= PartitionInfoGetFlags::RETURN_INFORMATION_TYPE_FLAG;
445 }
446 bits
447 }
448}
449
Tomás González4d5b0ba2025-03-03 17:15:55 +0000450/// Composite type for capturing success and error return codes for the VM availability messages.
451///
452/// Error codes are handled by the `FfaError` type. Having a separate type for errors helps using
453/// `Result<(), FfaError>`. If a single type would include both success and error values,
454/// then `Err(FfaError::Success)` would be incomprehensible.
455#[derive(Debug, Eq, PartialEq, Clone, Copy)]
456pub enum VmAvailabilityStatus {
457 Success,
458 Error(FfaError),
459}
460
461impl TryFrom<i32> for VmAvailabilityStatus {
462 type Error = Error;
463 fn try_from(value: i32) -> Result<Self, <Self as TryFrom<i32>>::Error> {
464 Ok(match value {
465 0 => Self::Success,
466 error_code => Self::Error(FfaError::try_from(error_code)?),
467 })
468 }
469}
470
471impl From<VmAvailabilityStatus> for i32 {
472 fn from(value: VmAvailabilityStatus) -> Self {
473 match value {
474 VmAvailabilityStatus::Success => 0,
475 VmAvailabilityStatus::Error(error_code) => error_code.into(),
476 }
477 }
478}
479
480/// Arguments for the Power Warm Boot `FFA_MSG_SEND_DIRECT_REQ` interface.
481#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
482#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedWarmBootType))]
483#[repr(u32)]
484pub enum WarmBootType {
485 ExitFromSuspend = 0,
486 ExitFromLowPower = 1,
487}
488
Balint Dobszaya5846852025-02-26 15:38:53 +0100489/// Arguments for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}` interfaces.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100490#[derive(Debug, Eq, PartialEq, Clone, Copy)]
491pub enum DirectMsgArgs {
492 Args32([u32; 5]),
493 Args64([u64; 5]),
Tomás González4d5b0ba2025-03-03 17:15:55 +0000494 /// Message for forwarding FFA_VERSION call from Normal world to the SPMC
495 VersionReq {
496 version: Version,
497 },
498 /// Response message to forwarded FFA_VERSION call from the Normal world
499 /// Contains the version returned by the SPMC or None
500 VersionResp {
501 version: Option<Version>,
502 },
503 /// Message for a power management operation initiated by a PSCI function
504 PowerPsciReq32 {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000505 // params[i]: Input parameter in w[i] in PSCI function invocation at EL3.
Tomás González67f92c72025-03-20 16:50:42 +0000506 // params[0]: Function ID.
507 params: [u32; 4],
Tomás González4d5b0ba2025-03-03 17:15:55 +0000508 },
509 /// Message for a power management operation initiated by a PSCI function
510 PowerPsciReq64 {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000511 // params[i]: Input parameter in x[i] in PSCI function invocation at EL3.
Tomás González67f92c72025-03-20 16:50:42 +0000512 // params[0]: Function ID.
513 params: [u64; 4],
Tomás González4d5b0ba2025-03-03 17:15:55 +0000514 },
515 /// Message for a warm boot
516 PowerWarmBootReq {
517 boot_type: WarmBootType,
518 },
519 /// Response message to indicate return status of the last power management request message
520 /// Return error code SUCCESS or DENIED as defined in PSCI spec. Caller is left to do the
521 /// parsing of the return status.
522 PowerPsciResp {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000523 psci_status: i32,
524 },
525 /// Message to signal creation of a VM
526 VmCreated {
527 // Globally unique Handle to identify a memory region that contains IMPLEMENTATION DEFINED
528 // information associated with the created VM.
529 // The invalid memory region handle must be specified by the Hypervisor if this field is not
530 // used.
531 handle: memory_management::Handle,
532 vm_id: u16,
533 },
534 /// Message to acknowledge creation of a VM
535 VmCreatedAck {
536 sp_status: VmAvailabilityStatus,
537 },
538 /// Message to signal destruction of a VM
539 VmDestructed {
540 // Globally unique Handle to identify a memory region that contains IMPLEMENTATION DEFINED
541 // information associated with the created VM.
542 // The invalid memory region handle must be specified by the Hypervisor if this field is not
543 // used.
544 handle: memory_management::Handle,
545 vm_id: u16,
546 },
547 /// Message to acknowledge destruction of a VM
548 VmDestructedAck {
549 sp_status: VmAvailabilityStatus,
550 },
551}
552
553impl DirectMsgArgs {
554 // Flags for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}` interfaces.
555
556 const FWK_MSG_BITS: u32 = 1 << 31;
557 const VERSION_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b1000;
558 const VERSION_RESP: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b1001;
559 const POWER_PSCI_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS;
560 const POWER_WARM_BOOT_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0001;
561 const POWER_PSCI_RESP: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0010;
562 const VM_CREATED: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0100;
563 const VM_CREATED_ACK: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0101;
564 const VM_DESTRUCTED: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0110;
565 const VM_DESTRUCTED_ACK: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0111;
Balint Dobszay3aad9572025-01-17 16:54:11 +0100566}
567
Balint Dobszayde0dc802025-02-28 14:16:52 +0100568/// Arguments for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}2` interfaces.
569#[derive(Debug, Eq, PartialEq, Clone, Copy)]
570pub struct DirectMsg2Args([u64; 14]);
571
Tomás González092202a2025-03-05 11:56:45 +0000572#[derive(Debug, Eq, PartialEq, Clone, Copy)]
573pub struct MsgWaitFlags {
574 retain_rx_buffer: bool,
575}
576
577impl MsgWaitFlags {
578 const RETAIN_RX_BUFFER: u32 = 0x01;
579 const MBZ_BITS: u32 = 0xfffe;
580}
581
582impl TryFrom<u32> for MsgWaitFlags {
583 type Error = Error;
584
585 fn try_from(val: u32) -> Result<Self, Self::Error> {
586 if (val & Self::MBZ_BITS) != 0 {
587 Err(Error::InvalidMsgWaitFlag(val))
588 } else {
589 Ok(MsgWaitFlags {
590 retain_rx_buffer: val & Self::RETAIN_RX_BUFFER != 0,
591 })
592 }
593 }
594}
595
596impl From<MsgWaitFlags> for u32 {
597 fn from(flags: MsgWaitFlags) -> Self {
598 let mut bits: u32 = 0;
599 if flags.retain_rx_buffer {
600 bits |= MsgWaitFlags::RETAIN_RX_BUFFER;
601 }
602 bits
603 }
604}
605
Balint Dobszaya5846852025-02-26 15:38:53 +0100606/// Descriptor for a dynamically allocated memory buffer that contains the memory transaction
Tomás Gonzálezf268e322025-03-05 11:18:11 +0000607/// descriptor.
608///
609/// Used by `FFA_MEM_{DONATE,LEND,SHARE,RETRIEVE_REQ}` interfaces, only when the TX buffer is not
610/// used to transmit the transaction descriptor.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100611#[derive(Debug, Eq, PartialEq, Clone, Copy)]
612pub enum MemOpBuf {
613 Buf32 { addr: u32, page_cnt: u32 },
614 Buf64 { addr: u64, page_cnt: u32 },
615}
616
Balint Dobszaya5846852025-02-26 15:38:53 +0100617/// Memory address argument for `FFA_MEM_PERM_{GET,SET}` interfaces.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100618#[derive(Debug, Eq, PartialEq, Clone, Copy)]
619pub enum MemAddr {
620 Addr32(u32),
621 Addr64(u64),
622}
623
Balint Dobszayde0dc802025-02-28 14:16:52 +0100624/// Argument for the `FFA_CONSOLE_LOG` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100625#[derive(Debug, Eq, PartialEq, Clone, Copy)]
626pub enum ConsoleLogChars {
627 Reg32([u32; 6]),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100628 Reg64([u64; 16]),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100629}
630
Tomás González7ffb6132025-04-03 12:28:58 +0100631#[derive(Debug, Eq, PartialEq, Clone, Copy)]
632pub struct NotificationBindFlags {
633 per_vcpu_notification: bool,
634}
635
636impl NotificationBindFlags {
637 const PER_VCPU_NOTIFICATION: u32 = 1;
638}
639
640impl From<NotificationBindFlags> for u32 {
641 fn from(flags: NotificationBindFlags) -> Self {
642 let mut bits: u32 = 0;
643 if flags.per_vcpu_notification {
644 bits |= NotificationBindFlags::PER_VCPU_NOTIFICATION;
645 }
646 bits
647 }
648}
649
650impl From<u32> for NotificationBindFlags {
651 fn from(flags: u32) -> Self {
652 Self {
653 per_vcpu_notification: flags & Self::PER_VCPU_NOTIFICATION != 0,
654 }
655 }
656}
657
658#[derive(Debug, Eq, PartialEq, Clone, Copy)]
659pub struct NotificationSetFlags {
660 delay_schedule_receiver: bool,
661 vcpu_id: Option<u16>,
662}
663
664impl NotificationSetFlags {
665 const PER_VCP_NOTIFICATION: u32 = 1 << 0;
666 const DELAY_SCHEDULE_RECEIVER: u32 = 1 << 1;
667 const VCPU_ID_SHIFT: u32 = 16;
668
669 const MBZ_BITS: u32 = 0xfffc;
670}
671
672impl From<NotificationSetFlags> for u32 {
673 fn from(flags: NotificationSetFlags) -> Self {
674 let mut bits: u32 = 0;
675
676 if flags.delay_schedule_receiver {
677 bits |= NotificationSetFlags::DELAY_SCHEDULE_RECEIVER;
678 }
679 if let Some(vcpu_id) = flags.vcpu_id {
680 bits |= NotificationSetFlags::PER_VCP_NOTIFICATION;
681 bits |= u32::from(vcpu_id) << NotificationSetFlags::VCPU_ID_SHIFT;
682 }
683
684 bits
685 }
686}
687
688impl TryFrom<u32> for NotificationSetFlags {
689 type Error = Error;
690
691 fn try_from(flags: u32) -> Result<Self, Self::Error> {
692 if (flags & Self::MBZ_BITS) != 0 {
693 return Err(Error::InvalidNotificationSetFlag(flags));
694 }
695
696 let tentative_vcpu_id = (flags >> Self::VCPU_ID_SHIFT) as u16;
697
698 let vcpu_id = if (flags & Self::PER_VCP_NOTIFICATION) != 0 {
699 Some(tentative_vcpu_id)
700 } else {
701 if tentative_vcpu_id != 0 {
702 return Err(Error::InvalidNotificationSetFlag(flags));
703 }
704 None
705 };
706
707 Ok(Self {
708 delay_schedule_receiver: (flags & Self::DELAY_SCHEDULE_RECEIVER) != 0,
709 vcpu_id,
710 })
711 }
712}
713
714#[derive(Debug, Eq, PartialEq, Clone, Copy)]
715pub struct NotificationGetFlags {
716 sp_bitmap_id: bool,
717 vm_bitmap_id: bool,
718 spm_bitmap_id: bool,
719 hyp_bitmap_id: bool,
720}
721
722impl NotificationGetFlags {
723 const SP_BITMAP_ID: u32 = 1;
724 const VM_BITMAP_ID: u32 = 1 << 1;
725 const SPM_BITMAP_ID: u32 = 1 << 2;
726 const HYP_BITMAP_ID: u32 = 1 << 3;
727}
728
729impl From<NotificationGetFlags> for u32 {
730 fn from(flags: NotificationGetFlags) -> Self {
731 let mut bits: u32 = 0;
732 if flags.sp_bitmap_id {
733 bits |= NotificationGetFlags::SP_BITMAP_ID;
734 }
735 if flags.vm_bitmap_id {
736 bits |= NotificationGetFlags::VM_BITMAP_ID;
737 }
738 if flags.spm_bitmap_id {
739 bits |= NotificationGetFlags::SPM_BITMAP_ID;
740 }
741 if flags.hyp_bitmap_id {
742 bits |= NotificationGetFlags::HYP_BITMAP_ID;
743 }
744 bits
745 }
746}
747
748impl From<u32> for NotificationGetFlags {
749 // This is a "from" instead of a "try_from" because Reserved Bits are SBZ, *not* MBZ.
750 fn from(flags: u32) -> Self {
751 Self {
752 sp_bitmap_id: (flags & Self::SP_BITMAP_ID) != 0,
753 vm_bitmap_id: (flags & Self::VM_BITMAP_ID) != 0,
754 spm_bitmap_id: (flags & Self::SPM_BITMAP_ID) != 0,
755 hyp_bitmap_id: (flags & Self::HYP_BITMAP_ID) != 0,
756 }
757 }
758}
759
Tomás Gonzálezf268e322025-03-05 11:18:11 +0000760/// FF-A "message types", the terminology used by the spec is "interfaces".
761///
762/// The interfaces are used by FF-A components for communication at an FF-A instance. The spec also
763/// describes the valid FF-A instances and conduits for each interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100764#[derive(Debug, Eq, PartialEq, Clone, Copy)]
765pub enum Interface {
766 Error {
767 target_info: TargetInfo,
768 error_code: FfaError,
Balint Dobszayb727aab2025-04-07 10:24:59 +0200769 error_arg: u32,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100770 },
771 Success {
772 target_info: u32,
773 args: SuccessArgs,
774 },
775 Interrupt {
776 target_info: TargetInfo,
777 interrupt_id: u32,
778 },
779 Version {
780 input_version: Version,
781 },
782 VersionOut {
783 output_version: Version,
784 },
785 Features {
786 feat_id: Feature,
787 input_properties: u32,
788 },
789 RxAcquire {
790 vm_id: u16,
791 },
792 RxRelease {
793 vm_id: u16,
794 },
795 RxTxMap {
796 addr: RxTxAddr,
797 page_cnt: u32,
798 },
799 RxTxUnmap {
800 id: u16,
801 },
802 PartitionInfoGet {
803 uuid: Uuid,
Imre Kise295adb2025-04-10 13:26:28 +0200804 flags: PartitionInfoGetFlags,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100805 },
Tomás González0a058bc2025-03-11 11:20:55 +0000806 PartitionInfoGetRegs {
807 uuid: Uuid,
808 start_index: u16,
809 info_tag: u16,
810 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100811 IdGet,
812 SpmIdGet,
Tomás González092202a2025-03-05 11:56:45 +0000813 MsgWait {
814 flags: Option<MsgWaitFlags>,
815 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100816 Yield,
817 Run {
818 target_info: TargetInfo,
819 },
820 NormalWorldResume,
Tomás González17b92442025-03-10 16:45:04 +0000821 SecondaryEpRegister {
822 entrypoint: SecondaryEpRegisterAddr,
823 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100824 MsgSend2 {
825 sender_vm_id: u16,
826 flags: u32,
827 },
828 MsgSendDirectReq {
829 src_id: u16,
830 dst_id: u16,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100831 args: DirectMsgArgs,
832 },
833 MsgSendDirectResp {
834 src_id: u16,
835 dst_id: u16,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100836 args: DirectMsgArgs,
837 },
Balint Dobszayde0dc802025-02-28 14:16:52 +0100838 MsgSendDirectReq2 {
839 src_id: u16,
840 dst_id: u16,
841 uuid: Uuid,
842 args: DirectMsg2Args,
843 },
844 MsgSendDirectResp2 {
845 src_id: u16,
846 dst_id: u16,
847 args: DirectMsg2Args,
848 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100849 MemDonate {
850 total_len: u32,
851 frag_len: u32,
852 buf: Option<MemOpBuf>,
853 },
854 MemLend {
855 total_len: u32,
856 frag_len: u32,
857 buf: Option<MemOpBuf>,
858 },
859 MemShare {
860 total_len: u32,
861 frag_len: u32,
862 buf: Option<MemOpBuf>,
863 },
864 MemRetrieveReq {
865 total_len: u32,
866 frag_len: u32,
867 buf: Option<MemOpBuf>,
868 },
869 MemRetrieveResp {
870 total_len: u32,
871 frag_len: u32,
872 },
873 MemRelinquish,
874 MemReclaim {
875 handle: memory_management::Handle,
876 flags: u32,
877 },
878 MemPermGet {
879 addr: MemAddr,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100880 page_cnt: Option<u32>,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100881 },
882 MemPermSet {
883 addr: MemAddr,
884 page_cnt: u32,
885 mem_perm: u32,
886 },
887 ConsoleLog {
888 char_cnt: u8,
889 char_lists: ConsoleLogChars,
890 },
Tomás González7ffb6132025-04-03 12:28:58 +0100891 NotificationBitmapCreate {
892 vm_id: u16,
893 vcpu_cnt: u32,
894 },
895 NotificationBitmapDestroy {
896 vm_id: u16,
897 },
898 NotificationBind {
899 sender_id: u16,
900 receiver_id: u16,
901 flags: NotificationBindFlags,
902 bitmap: u64,
903 },
904 NotificationUnBind {
905 sender_id: u16,
906 receiver_id: u16,
907 bitmap: u64,
908 },
909 NotificationSet {
910 sender_id: u16,
911 receiver_id: u16,
912 flags: NotificationSetFlags,
913 bitmap: u64,
914 },
915 NotificationGet {
916 vcpu_id: u16,
917 endpoint_id: u16,
918 flags: NotificationGetFlags,
919 },
920 NotificationInfoGet {
921 is_32bit: bool,
922 },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +0100923 El3IntrHandle,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100924}
925
Balint Dobszayde0dc802025-02-28 14:16:52 +0100926impl Interface {
927 /// Returns the function ID for the call, if it has one.
928 pub fn function_id(&self) -> Option<FuncId> {
929 match self {
930 Interface::Error { .. } => Some(FuncId::Error),
931 Interface::Success { args, .. } => match args {
Imre Kis54773b62025-04-10 13:47:39 +0200932 SuccessArgs::Args32(..) => Some(FuncId::Success32),
933 SuccessArgs::Args64(..) | SuccessArgs::Args64_2(..) => Some(FuncId::Success64),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100934 },
935 Interface::Interrupt { .. } => Some(FuncId::Interrupt),
936 Interface::Version { .. } => Some(FuncId::Version),
937 Interface::VersionOut { .. } => None,
938 Interface::Features { .. } => Some(FuncId::Features),
939 Interface::RxAcquire { .. } => Some(FuncId::RxAcquire),
940 Interface::RxRelease { .. } => Some(FuncId::RxRelease),
941 Interface::RxTxMap { addr, .. } => match addr {
942 RxTxAddr::Addr32 { .. } => Some(FuncId::RxTxMap32),
943 RxTxAddr::Addr64 { .. } => Some(FuncId::RxTxMap64),
944 },
945 Interface::RxTxUnmap { .. } => Some(FuncId::RxTxUnmap),
946 Interface::PartitionInfoGet { .. } => Some(FuncId::PartitionInfoGet),
Tomás González0a058bc2025-03-11 11:20:55 +0000947 Interface::PartitionInfoGetRegs { .. } => Some(FuncId::PartitionInfoGetRegs),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100948 Interface::IdGet => Some(FuncId::IdGet),
949 Interface::SpmIdGet => Some(FuncId::SpmIdGet),
Tomás González092202a2025-03-05 11:56:45 +0000950 Interface::MsgWait { .. } => Some(FuncId::MsgWait),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100951 Interface::Yield => Some(FuncId::Yield),
952 Interface::Run { .. } => Some(FuncId::Run),
953 Interface::NormalWorldResume => Some(FuncId::NormalWorldResume),
Tomás González17b92442025-03-10 16:45:04 +0000954 Interface::SecondaryEpRegister { entrypoint } => match entrypoint {
955 SecondaryEpRegisterAddr::Addr32 { .. } => Some(FuncId::SecondaryEpRegister32),
956 SecondaryEpRegisterAddr::Addr64 { .. } => Some(FuncId::SecondaryEpRegister64),
957 },
Balint Dobszayde0dc802025-02-28 14:16:52 +0100958 Interface::MsgSend2 { .. } => Some(FuncId::MsgSend2),
959 Interface::MsgSendDirectReq { args, .. } => match args {
960 DirectMsgArgs::Args32(_) => Some(FuncId::MsgSendDirectReq32),
961 DirectMsgArgs::Args64(_) => Some(FuncId::MsgSendDirectReq64),
Tomás González4d5b0ba2025-03-03 17:15:55 +0000962 DirectMsgArgs::VersionReq { .. } => Some(FuncId::MsgSendDirectReq32),
963 DirectMsgArgs::PowerPsciReq32 { .. } => Some(FuncId::MsgSendDirectReq32),
964 DirectMsgArgs::PowerPsciReq64 { .. } => Some(FuncId::MsgSendDirectReq64),
965 DirectMsgArgs::PowerWarmBootReq { .. } => Some(FuncId::MsgSendDirectReq32),
966 DirectMsgArgs::VmCreated { .. } => Some(FuncId::MsgSendDirectReq32),
967 DirectMsgArgs::VmDestructed { .. } => Some(FuncId::MsgSendDirectReq32),
968 _ => None,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100969 },
970 Interface::MsgSendDirectResp { args, .. } => match args {
971 DirectMsgArgs::Args32(_) => Some(FuncId::MsgSendDirectResp32),
972 DirectMsgArgs::Args64(_) => Some(FuncId::MsgSendDirectResp64),
Tomás González4d5b0ba2025-03-03 17:15:55 +0000973 DirectMsgArgs::VersionResp { .. } => Some(FuncId::MsgSendDirectResp32),
974 DirectMsgArgs::PowerPsciResp { .. } => Some(FuncId::MsgSendDirectResp32),
975 DirectMsgArgs::VmCreatedAck { .. } => Some(FuncId::MsgSendDirectResp32),
976 DirectMsgArgs::VmDestructedAck { .. } => Some(FuncId::MsgSendDirectResp32),
977 _ => None,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100978 },
979 Interface::MsgSendDirectReq2 { .. } => Some(FuncId::MsgSendDirectReq64_2),
980 Interface::MsgSendDirectResp2 { .. } => Some(FuncId::MsgSendDirectResp64_2),
981 Interface::MemDonate { buf, .. } => match buf {
982 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemDonate64),
983 _ => Some(FuncId::MemDonate32),
984 },
985 Interface::MemLend { buf, .. } => match buf {
986 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemLend64),
987 _ => Some(FuncId::MemLend32),
988 },
989 Interface::MemShare { buf, .. } => match buf {
990 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemShare64),
991 _ => Some(FuncId::MemShare32),
992 },
993 Interface::MemRetrieveReq { buf, .. } => match buf {
994 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemRetrieveReq64),
995 _ => Some(FuncId::MemRetrieveReq32),
996 },
997 Interface::MemRetrieveResp { .. } => Some(FuncId::MemRetrieveResp),
998 Interface::MemRelinquish => Some(FuncId::MemRelinquish),
999 Interface::MemReclaim { .. } => Some(FuncId::MemReclaim),
1000 Interface::MemPermGet { addr, .. } => match addr {
1001 MemAddr::Addr32(_) => Some(FuncId::MemPermGet32),
1002 MemAddr::Addr64(_) => Some(FuncId::MemPermGet64),
1003 },
1004 Interface::MemPermSet { addr, .. } => match addr {
1005 MemAddr::Addr32(_) => Some(FuncId::MemPermSet32),
1006 MemAddr::Addr64(_) => Some(FuncId::MemPermSet64),
1007 },
1008 Interface::ConsoleLog { char_lists, .. } => match char_lists {
1009 ConsoleLogChars::Reg32(_) => Some(FuncId::ConsoleLog32),
1010 ConsoleLogChars::Reg64(_) => Some(FuncId::ConsoleLog64),
1011 },
Tomás González7ffb6132025-04-03 12:28:58 +01001012 Interface::NotificationBitmapCreate { .. } => Some(FuncId::NotificationBitmapCreate),
1013 Interface::NotificationBitmapDestroy { .. } => Some(FuncId::NotificationBitmapDestroy),
1014 Interface::NotificationBind { .. } => Some(FuncId::NotificationBind),
1015 Interface::NotificationUnBind { .. } => Some(FuncId::NotificationUnbind),
1016 Interface::NotificationSet { .. } => Some(FuncId::NotificationSet),
1017 Interface::NotificationGet { .. } => Some(FuncId::NotificationGet),
1018 Interface::NotificationInfoGet { is_32bit } => match is_32bit {
1019 true => Some(FuncId::NotificationInfoGet32),
1020 false => Some(FuncId::NotificationInfoGet64),
1021 },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01001022 Interface::El3IntrHandle => Some(FuncId::El3IntrHandle),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001023 }
1024 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01001025
Balint Dobszayde0dc802025-02-28 14:16:52 +01001026 /// Returns true if this is a 32-bit call, or false if it is a 64-bit call.
1027 pub fn is_32bit(&self) -> bool {
1028 // TODO: self should always have a function ID?
1029 self.function_id().unwrap().is_32bit()
1030 }
1031
1032 /// Parse interface from register contents. The caller must ensure that the `regs` argument has
1033 /// the correct length: 8 registers for FF-A v1.1 and lower, 18 registers for v1.2 and higher.
1034 pub fn from_regs(version: Version, regs: &[u64]) -> Result<Self, Error> {
1035 let reg_cnt = regs.len();
1036
1037 let msg = match reg_cnt {
1038 8 => {
1039 assert!(version <= Version(1, 1));
1040 Interface::unpack_regs8(version, regs.try_into().unwrap())?
1041 }
1042 18 => {
1043 assert!(version >= Version(1, 2));
1044 match FuncId::try_from(regs[0] as u32)? {
1045 FuncId::ConsoleLog64
1046 | FuncId::Success64
1047 | FuncId::MsgSendDirectReq64_2
Tomás González0a058bc2025-03-11 11:20:55 +00001048 | FuncId::MsgSendDirectResp64_2
1049 | FuncId::PartitionInfoGetRegs => {
Balint Dobszayde0dc802025-02-28 14:16:52 +01001050 Interface::unpack_regs18(version, regs.try_into().unwrap())?
1051 }
1052 _ => Interface::unpack_regs8(version, regs[..8].try_into().unwrap())?,
1053 }
1054 }
1055 _ => panic!(
1056 "Invalid number of registers ({}) for FF-A version {}",
1057 reg_cnt, version
1058 ),
1059 };
1060
1061 Ok(msg)
1062 }
1063
1064 fn unpack_regs8(version: Version, regs: &[u64; 8]) -> Result<Self, Error> {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001065 let fid = FuncId::try_from(regs[0] as u32)?;
1066
1067 let msg = match fid {
1068 FuncId::Error => Self::Error {
1069 target_info: (regs[1] as u32).into(),
1070 error_code: FfaError::try_from(regs[2] as i32)?,
Balint Dobszayb727aab2025-04-07 10:24:59 +02001071 error_arg: regs[3] as u32,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001072 },
1073 FuncId::Success32 => Self::Success {
1074 target_info: regs[1] as u32,
Imre Kis54773b62025-04-10 13:47:39 +02001075 args: SuccessArgs::Args32([
Balint Dobszay3aad9572025-01-17 16:54:11 +01001076 regs[2] as u32,
1077 regs[3] as u32,
1078 regs[4] as u32,
1079 regs[5] as u32,
1080 regs[6] as u32,
1081 regs[7] as u32,
1082 ]),
1083 },
1084 FuncId::Success64 => Self::Success {
1085 target_info: regs[1] as u32,
Imre Kis54773b62025-04-10 13:47:39 +02001086 args: SuccessArgs::Args64([regs[2], regs[3], regs[4], regs[5], regs[6], regs[7]]),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001087 },
1088 FuncId::Interrupt => Self::Interrupt {
1089 target_info: (regs[1] as u32).into(),
1090 interrupt_id: regs[2] as u32,
1091 },
1092 FuncId::Version => Self::Version {
Tomás González83146af2025-03-04 11:32:41 +00001093 input_version: (regs[1] as u32).try_into()?,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001094 },
1095 FuncId::Features => Self::Features {
Balint Dobszayc31e0b92025-03-03 20:16:56 +01001096 feat_id: (regs[1] as u32).into(),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001097 input_properties: regs[2] as u32,
1098 },
1099 FuncId::RxAcquire => Self::RxAcquire {
1100 vm_id: regs[1] as u16,
1101 },
1102 FuncId::RxRelease => Self::RxRelease {
1103 vm_id: regs[1] as u16,
1104 },
1105 FuncId::RxTxMap32 => {
1106 let addr = RxTxAddr::Addr32 {
1107 rx: regs[2] as u32,
1108 tx: regs[1] as u32,
1109 };
1110 let page_cnt = regs[3] as u32;
1111
1112 Self::RxTxMap { addr, page_cnt }
1113 }
1114 FuncId::RxTxMap64 => {
1115 let addr = RxTxAddr::Addr64 {
1116 rx: regs[2],
1117 tx: regs[1],
1118 };
1119 let page_cnt = regs[3] as u32;
1120
1121 Self::RxTxMap { addr, page_cnt }
1122 }
1123 FuncId::RxTxUnmap => Self::RxTxUnmap { id: regs[1] as u16 },
1124 FuncId::PartitionInfoGet => {
1125 let uuid_words = [
1126 regs[1] as u32,
1127 regs[2] as u32,
1128 regs[3] as u32,
1129 regs[4] as u32,
1130 ];
1131 let mut bytes: [u8; 16] = [0; 16];
1132 for (i, b) in uuid_words.iter().flat_map(|w| w.to_le_bytes()).enumerate() {
1133 bytes[i] = b;
1134 }
1135 Self::PartitionInfoGet {
1136 uuid: Uuid::from_bytes(bytes),
Imre Kise295adb2025-04-10 13:26:28 +02001137 flags: PartitionInfoGetFlags::try_from(regs[5] as u32)?,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001138 }
1139 }
1140 FuncId::IdGet => Self::IdGet,
1141 FuncId::SpmIdGet => Self::SpmIdGet,
Tomás González092202a2025-03-05 11:56:45 +00001142 FuncId::MsgWait => Self::MsgWait {
1143 flags: if version >= Version(1, 2) {
1144 Some(MsgWaitFlags::try_from(regs[2] as u32)?)
1145 } else {
1146 None
1147 },
1148 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001149 FuncId::Yield => Self::Yield,
1150 FuncId::Run => Self::Run {
1151 target_info: (regs[1] as u32).into(),
1152 },
1153 FuncId::NormalWorldResume => Self::NormalWorldResume,
Tomás González17b92442025-03-10 16:45:04 +00001154 FuncId::SecondaryEpRegister32 => Self::SecondaryEpRegister {
1155 entrypoint: SecondaryEpRegisterAddr::Addr32(regs[1] as u32),
1156 },
1157 FuncId::SecondaryEpRegister64 => Self::SecondaryEpRegister {
1158 entrypoint: SecondaryEpRegisterAddr::Addr64(regs[1]),
1159 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001160 FuncId::MsgSend2 => Self::MsgSend2 {
1161 sender_vm_id: regs[1] as u16,
1162 flags: regs[2] as u32,
1163 },
1164 FuncId::MsgSendDirectReq32 => Self::MsgSendDirectReq {
1165 src_id: (regs[1] >> 16) as u16,
1166 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001167 args: if (regs[2] as u32 & DirectMsgArgs::FWK_MSG_BITS) != 0 {
1168 match regs[2] as u32 {
1169 DirectMsgArgs::VERSION_REQ => DirectMsgArgs::VersionReq {
1170 version: Version::try_from(regs[3] as u32)?,
1171 },
1172 DirectMsgArgs::POWER_PSCI_REQ => DirectMsgArgs::PowerPsciReq32 {
Tomás González67f92c72025-03-20 16:50:42 +00001173 params: [
1174 regs[3] as u32,
1175 regs[4] as u32,
1176 regs[5] as u32,
1177 regs[6] as u32,
1178 ],
Tomás González4d5b0ba2025-03-03 17:15:55 +00001179 },
1180 DirectMsgArgs::POWER_WARM_BOOT_REQ => DirectMsgArgs::PowerWarmBootReq {
1181 boot_type: WarmBootType::try_from(regs[3] as u32)?,
1182 },
1183 DirectMsgArgs::VM_CREATED => DirectMsgArgs::VmCreated {
1184 handle: memory_management::Handle::from([
1185 regs[3] as u32,
1186 regs[4] as u32,
1187 ]),
1188 vm_id: regs[5] as u16,
1189 },
1190 DirectMsgArgs::VM_DESTRUCTED => DirectMsgArgs::VmDestructed {
1191 handle: memory_management::Handle::from([
1192 regs[3] as u32,
1193 regs[4] as u32,
1194 ]),
1195 vm_id: regs[5] as u16,
1196 },
1197 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1198 }
1199 } else {
1200 DirectMsgArgs::Args32([
1201 regs[3] as u32,
1202 regs[4] as u32,
1203 regs[5] as u32,
1204 regs[6] as u32,
1205 regs[7] as u32,
1206 ])
1207 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001208 },
1209 FuncId::MsgSendDirectReq64 => Self::MsgSendDirectReq {
1210 src_id: (regs[1] >> 16) as u16,
1211 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001212 args: if (regs[2] & DirectMsgArgs::FWK_MSG_BITS as u64) != 0 {
1213 match regs[2] as u32 {
1214 DirectMsgArgs::POWER_PSCI_REQ => DirectMsgArgs::PowerPsciReq64 {
Tomás González67f92c72025-03-20 16:50:42 +00001215 params: [regs[3], regs[4], regs[5], regs[6]],
Tomás González4d5b0ba2025-03-03 17:15:55 +00001216 },
1217 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1218 }
1219 } else {
1220 DirectMsgArgs::Args64([regs[3], regs[4], regs[5], regs[6], regs[7]])
1221 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001222 },
1223 FuncId::MsgSendDirectResp32 => Self::MsgSendDirectResp {
1224 src_id: (regs[1] >> 16) as u16,
1225 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001226 args: if (regs[2] as u32 & DirectMsgArgs::FWK_MSG_BITS) != 0 {
1227 match regs[2] as u32 {
1228 DirectMsgArgs::VERSION_RESP => {
1229 if regs[3] as i32 == FfaError::NotSupported.into() {
1230 DirectMsgArgs::VersionResp { version: None }
1231 } else {
1232 DirectMsgArgs::VersionResp {
1233 version: Some(Version::try_from(regs[3] as u32)?),
1234 }
1235 }
1236 }
1237 DirectMsgArgs::POWER_PSCI_RESP => DirectMsgArgs::PowerPsciResp {
1238 psci_status: regs[3] as i32,
1239 },
1240 DirectMsgArgs::VM_CREATED_ACK => DirectMsgArgs::VmCreatedAck {
1241 sp_status: (regs[3] as i32).try_into()?,
1242 },
1243 DirectMsgArgs::VM_DESTRUCTED_ACK => DirectMsgArgs::VmDestructedAck {
1244 sp_status: (regs[3] as i32).try_into()?,
1245 },
1246 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1247 }
1248 } else {
1249 DirectMsgArgs::Args32([
1250 regs[3] as u32,
1251 regs[4] as u32,
1252 regs[5] as u32,
1253 regs[6] as u32,
1254 regs[7] as u32,
1255 ])
1256 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001257 },
1258 FuncId::MsgSendDirectResp64 => Self::MsgSendDirectResp {
1259 src_id: (regs[1] >> 16) as u16,
1260 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001261 args: if (regs[2] & DirectMsgArgs::FWK_MSG_BITS as u64) != 0 {
1262 return Err(Error::UnrecognisedFwkMsg(regs[2] as u32));
1263 } else {
1264 DirectMsgArgs::Args64([regs[3], regs[4], regs[5], regs[6], regs[7]])
1265 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001266 },
1267 FuncId::MemDonate32 => Self::MemDonate {
1268 total_len: regs[1] as u32,
1269 frag_len: regs[2] as u32,
1270 buf: if regs[3] != 0 && regs[4] != 0 {
1271 Some(MemOpBuf::Buf32 {
1272 addr: regs[3] as u32,
1273 page_cnt: regs[4] as u32,
1274 })
1275 } else {
1276 None
1277 },
1278 },
1279 FuncId::MemDonate64 => Self::MemDonate {
1280 total_len: regs[1] as u32,
1281 frag_len: regs[2] as u32,
1282 buf: if regs[3] != 0 && regs[4] != 0 {
1283 Some(MemOpBuf::Buf64 {
1284 addr: regs[3],
1285 page_cnt: regs[4] as u32,
1286 })
1287 } else {
1288 None
1289 },
1290 },
1291 FuncId::MemLend32 => Self::MemLend {
1292 total_len: regs[1] as u32,
1293 frag_len: regs[2] as u32,
1294 buf: if regs[3] != 0 && regs[4] != 0 {
1295 Some(MemOpBuf::Buf32 {
1296 addr: regs[3] as u32,
1297 page_cnt: regs[4] as u32,
1298 })
1299 } else {
1300 None
1301 },
1302 },
1303 FuncId::MemLend64 => Self::MemLend {
1304 total_len: regs[1] as u32,
1305 frag_len: regs[2] as u32,
1306 buf: if regs[3] != 0 && regs[4] != 0 {
1307 Some(MemOpBuf::Buf64 {
1308 addr: regs[3],
1309 page_cnt: regs[4] as u32,
1310 })
1311 } else {
1312 None
1313 },
1314 },
1315 FuncId::MemShare32 => Self::MemShare {
1316 total_len: regs[1] as u32,
1317 frag_len: regs[2] as u32,
1318 buf: if regs[3] != 0 && regs[4] != 0 {
1319 Some(MemOpBuf::Buf32 {
1320 addr: regs[3] as u32,
1321 page_cnt: regs[4] as u32,
1322 })
1323 } else {
1324 None
1325 },
1326 },
1327 FuncId::MemShare64 => Self::MemShare {
1328 total_len: regs[1] as u32,
1329 frag_len: regs[2] as u32,
1330 buf: if regs[3] != 0 && regs[4] != 0 {
1331 Some(MemOpBuf::Buf64 {
1332 addr: regs[3],
1333 page_cnt: regs[4] as u32,
1334 })
1335 } else {
1336 None
1337 },
1338 },
1339 FuncId::MemRetrieveReq32 => Self::MemRetrieveReq {
1340 total_len: regs[1] as u32,
1341 frag_len: regs[2] as u32,
1342 buf: if regs[3] != 0 && regs[4] != 0 {
1343 Some(MemOpBuf::Buf32 {
1344 addr: regs[3] as u32,
1345 page_cnt: regs[4] as u32,
1346 })
1347 } else {
1348 None
1349 },
1350 },
1351 FuncId::MemRetrieveReq64 => Self::MemRetrieveReq {
1352 total_len: regs[1] as u32,
1353 frag_len: regs[2] as u32,
1354 buf: if regs[3] != 0 && regs[4] != 0 {
1355 Some(MemOpBuf::Buf64 {
1356 addr: regs[3],
1357 page_cnt: regs[4] as u32,
1358 })
1359 } else {
1360 None
1361 },
1362 },
1363 FuncId::MemRetrieveResp => Self::MemRetrieveResp {
1364 total_len: regs[1] as u32,
1365 frag_len: regs[2] as u32,
1366 },
1367 FuncId::MemRelinquish => Self::MemRelinquish,
1368 FuncId::MemReclaim => Self::MemReclaim {
1369 handle: memory_management::Handle::from([regs[1] as u32, regs[2] as u32]),
1370 flags: regs[3] as u32,
1371 },
1372 FuncId::MemPermGet32 => Self::MemPermGet {
1373 addr: MemAddr::Addr32(regs[1] as u32),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001374 page_cnt: if version >= Version(1, 3) {
1375 Some(regs[2] as u32)
1376 } else {
1377 None
1378 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001379 },
1380 FuncId::MemPermGet64 => Self::MemPermGet {
1381 addr: MemAddr::Addr64(regs[1]),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001382 page_cnt: if version >= Version(1, 3) {
1383 Some(regs[2] as u32)
1384 } else {
1385 None
1386 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001387 },
1388 FuncId::MemPermSet32 => Self::MemPermSet {
1389 addr: MemAddr::Addr32(regs[1] as u32),
1390 page_cnt: regs[2] as u32,
1391 mem_perm: regs[3] as u32,
1392 },
1393 FuncId::MemPermSet64 => Self::MemPermSet {
1394 addr: MemAddr::Addr64(regs[1]),
1395 page_cnt: regs[2] as u32,
1396 mem_perm: regs[3] as u32,
1397 },
1398 FuncId::ConsoleLog32 => Self::ConsoleLog {
1399 char_cnt: regs[1] as u8,
1400 char_lists: ConsoleLogChars::Reg32([
1401 regs[2] as u32,
1402 regs[3] as u32,
1403 regs[4] as u32,
1404 regs[5] as u32,
1405 regs[6] as u32,
1406 regs[7] as u32,
1407 ]),
1408 },
Tomás González7ffb6132025-04-03 12:28:58 +01001409 FuncId::NotificationBitmapCreate => {
1410 let tentative_vm_id = regs[1] as u32;
1411 if (tentative_vm_id >> 16) != 0 {
1412 return Err(Error::InvalidVmId(tentative_vm_id));
1413 }
1414 Self::NotificationBitmapCreate {
1415 vm_id: tentative_vm_id as u16,
1416 vcpu_cnt: regs[2] as u32,
1417 }
1418 }
1419 FuncId::NotificationBitmapDestroy => {
1420 let tentative_vm_id = regs[1] as u32;
1421 if (tentative_vm_id >> 16) != 0 {
1422 return Err(Error::InvalidVmId(tentative_vm_id));
1423 }
1424 Self::NotificationBitmapDestroy {
1425 vm_id: tentative_vm_id as u16,
1426 }
1427 }
1428 FuncId::NotificationBind => Self::NotificationBind {
1429 sender_id: (regs[1] >> 16) as u16,
1430 receiver_id: regs[1] as u16,
1431 flags: (regs[2] as u32).into(),
1432 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1433 },
1434 FuncId::NotificationUnbind => Self::NotificationUnBind {
1435 sender_id: (regs[1] >> 16) as u16,
1436 receiver_id: regs[1] as u16,
1437 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1438 },
1439 FuncId::NotificationSet => Self::NotificationSet {
1440 sender_id: (regs[1] >> 16) as u16,
1441 receiver_id: regs[1] as u16,
1442 flags: (regs[2] as u32).try_into()?,
1443 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1444 },
1445 FuncId::NotificationGet => Self::NotificationGet {
1446 vcpu_id: (regs[1] >> 16) as u16,
1447 endpoint_id: regs[1] as u16,
1448 flags: (regs[2] as u32).into(),
1449 },
1450 FuncId::NotificationInfoGet32 => Self::NotificationInfoGet { is_32bit: true },
1451 FuncId::NotificationInfoGet64 => Self::NotificationInfoGet { is_32bit: false },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01001452 FuncId::El3IntrHandle => Self::El3IntrHandle,
Balint Dobszayde0dc802025-02-28 14:16:52 +01001453 _ => panic!("Invalid number of registers (8) for function {:#x?}", fid),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001454 };
1455
1456 Ok(msg)
1457 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01001458
Balint Dobszayde0dc802025-02-28 14:16:52 +01001459 fn unpack_regs18(version: Version, regs: &[u64; 18]) -> Result<Self, Error> {
1460 assert!(version >= Version(1, 2));
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001461
Balint Dobszayde0dc802025-02-28 14:16:52 +01001462 let fid = FuncId::try_from(regs[0] as u32)?;
1463
1464 let msg = match fid {
1465 FuncId::Success64 => Self::Success {
1466 target_info: regs[1] as u32,
Imre Kis54773b62025-04-10 13:47:39 +02001467 args: SuccessArgs::Args64_2(regs[2..18].try_into().unwrap()),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001468 },
1469 FuncId::MsgSendDirectReq64_2 => Self::MsgSendDirectReq2 {
1470 src_id: (regs[1] >> 16) as u16,
1471 dst_id: regs[1] as u16,
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00001472 uuid: Uuid::from_u64_pair(regs[2].swap_bytes(), regs[3].swap_bytes()),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001473 args: DirectMsg2Args(regs[4..18].try_into().unwrap()),
1474 },
1475 FuncId::MsgSendDirectResp64_2 => Self::MsgSendDirectResp2 {
1476 src_id: (regs[1] >> 16) as u16,
1477 dst_id: regs[1] as u16,
1478 args: DirectMsg2Args(regs[4..18].try_into().unwrap()),
1479 },
1480 FuncId::ConsoleLog64 => Self::ConsoleLog {
1481 char_cnt: regs[1] as u8,
1482 char_lists: ConsoleLogChars::Reg64(regs[2..18].try_into().unwrap()),
1483 },
Tomás González0a058bc2025-03-11 11:20:55 +00001484 FuncId::PartitionInfoGetRegs => {
1485 // Bits[15:0]: Start index
1486 let start_index = (regs[3] & 0xffff) as u16;
1487 let info_tag = ((regs[3] >> 16) & 0xffff) as u16;
1488 Self::PartitionInfoGetRegs {
1489 uuid: Uuid::from_u64_pair(regs[1].swap_bytes(), regs[2].swap_bytes()),
1490 start_index,
1491 info_tag: if start_index == 0 && info_tag != 0 {
1492 return Err(Error::InvalidInformationTag(info_tag));
1493 } else {
1494 info_tag
1495 },
1496 }
1497 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001498 _ => panic!("Invalid number of registers (18) for function {:#x?}", fid),
1499 };
1500
1501 Ok(msg)
Balint Dobszay3aad9572025-01-17 16:54:11 +01001502 }
1503
Balint Dobszaya5846852025-02-26 15:38:53 +01001504 /// Create register contents for an interface.
Balint Dobszayde0dc802025-02-28 14:16:52 +01001505 pub fn to_regs(&self, version: Version, regs: &mut [u64]) {
1506 let reg_cnt = regs.len();
1507
1508 match reg_cnt {
1509 8 => {
1510 assert!(version <= Version(1, 1));
Balint Dobszay91bea9b2025-04-09 13:16:06 +02001511 regs.fill(0);
1512
Balint Dobszayde0dc802025-02-28 14:16:52 +01001513 self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
1514 }
1515 18 => {
1516 assert!(version >= Version(1, 2));
Balint Dobszay91bea9b2025-04-09 13:16:06 +02001517 regs.fill(0);
Balint Dobszayde0dc802025-02-28 14:16:52 +01001518
1519 match self {
1520 Interface::ConsoleLog {
1521 char_lists: ConsoleLogChars::Reg64(_),
1522 ..
1523 }
1524 | Interface::Success {
Imre Kis54773b62025-04-10 13:47:39 +02001525 args: SuccessArgs::Args64_2(_),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001526 ..
1527 }
1528 | Interface::MsgSendDirectReq2 { .. }
Tomás González0a058bc2025-03-11 11:20:55 +00001529 | Interface::MsgSendDirectResp2 { .. }
1530 | Interface::PartitionInfoGetRegs { .. } => {
Balint Dobszayde0dc802025-02-28 14:16:52 +01001531 self.pack_regs18(version, regs.try_into().unwrap());
1532 }
1533 _ => {
1534 self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
1535 }
1536 }
1537 }
1538 _ => panic!("Invalid number of registers {}", reg_cnt),
1539 }
1540 }
1541
1542 fn pack_regs8(&self, version: Version, a: &mut [u64; 8]) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001543 if let Some(function_id) = self.function_id() {
1544 a[0] = function_id as u64;
1545 }
1546
1547 match *self {
1548 Interface::Error {
1549 target_info,
1550 error_code,
Balint Dobszayb727aab2025-04-07 10:24:59 +02001551 error_arg,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001552 } => {
1553 a[1] = u32::from(target_info).into();
1554 a[2] = (error_code as u32).into();
Balint Dobszayb727aab2025-04-07 10:24:59 +02001555 a[3] = error_arg.into();
Balint Dobszay3aad9572025-01-17 16:54:11 +01001556 }
1557 Interface::Success { target_info, args } => {
1558 a[1] = target_info.into();
1559 match args {
Imre Kis54773b62025-04-10 13:47:39 +02001560 SuccessArgs::Args32(regs) => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001561 a[2] = regs[0].into();
1562 a[3] = regs[1].into();
1563 a[4] = regs[2].into();
1564 a[5] = regs[3].into();
1565 a[6] = regs[4].into();
1566 a[7] = regs[5].into();
1567 }
Imre Kis54773b62025-04-10 13:47:39 +02001568 SuccessArgs::Args64(regs) => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001569 a[2] = regs[0];
1570 a[3] = regs[1];
1571 a[4] = regs[2];
1572 a[5] = regs[3];
1573 a[6] = regs[4];
1574 a[7] = regs[5];
1575 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001576 _ => panic!("{:#x?} requires 18 registers", args),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001577 }
1578 }
1579 Interface::Interrupt {
1580 target_info,
1581 interrupt_id,
1582 } => {
1583 a[1] = u32::from(target_info).into();
1584 a[2] = interrupt_id.into();
1585 }
1586 Interface::Version { input_version } => {
1587 a[1] = u32::from(input_version).into();
1588 }
1589 Interface::VersionOut { output_version } => {
1590 a[0] = u32::from(output_version).into();
1591 }
1592 Interface::Features {
1593 feat_id,
1594 input_properties,
1595 } => {
1596 a[1] = u32::from(feat_id).into();
1597 a[2] = input_properties.into();
1598 }
1599 Interface::RxAcquire { vm_id } => {
1600 a[1] = vm_id.into();
1601 }
1602 Interface::RxRelease { vm_id } => {
1603 a[1] = vm_id.into();
1604 }
1605 Interface::RxTxMap { addr, page_cnt } => {
1606 match addr {
1607 RxTxAddr::Addr32 { rx, tx } => {
1608 a[1] = tx.into();
1609 a[2] = rx.into();
1610 }
1611 RxTxAddr::Addr64 { rx, tx } => {
1612 a[1] = tx;
1613 a[2] = rx;
1614 }
1615 }
1616 a[3] = page_cnt.into();
1617 }
1618 Interface::RxTxUnmap { id } => {
1619 a[1] = id.into();
1620 }
1621 Interface::PartitionInfoGet { uuid, flags } => {
1622 let bytes = uuid.into_bytes();
1623 a[1] = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]).into();
1624 a[2] = u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]).into();
1625 a[3] = u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]).into();
1626 a[4] = u32::from_le_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]).into();
Imre Kise295adb2025-04-10 13:26:28 +02001627 a[5] = u32::from(flags).into();
Balint Dobszay3aad9572025-01-17 16:54:11 +01001628 }
Tomás González092202a2025-03-05 11:56:45 +00001629 Interface::MsgWait { flags } => {
1630 if version >= Version(1, 2) {
1631 if let Some(flags) = flags {
1632 a[2] = u32::from(flags).into();
1633 }
1634 }
1635 }
1636 Interface::IdGet | Interface::SpmIdGet | Interface::Yield => {}
Balint Dobszay3aad9572025-01-17 16:54:11 +01001637 Interface::Run { target_info } => {
1638 a[1] = u32::from(target_info).into();
1639 }
1640 Interface::NormalWorldResume => {}
Tomás González17b92442025-03-10 16:45:04 +00001641 Interface::SecondaryEpRegister { entrypoint } => match entrypoint {
1642 SecondaryEpRegisterAddr::Addr32(addr) => a[1] = addr as u64,
1643 SecondaryEpRegisterAddr::Addr64(addr) => a[1] = addr,
1644 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001645 Interface::MsgSend2 {
1646 sender_vm_id,
1647 flags,
1648 } => {
1649 a[1] = sender_vm_id.into();
1650 a[2] = flags.into();
1651 }
1652 Interface::MsgSendDirectReq {
1653 src_id,
1654 dst_id,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001655 args,
1656 } => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +01001657 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Balint Dobszay3aad9572025-01-17 16:54:11 +01001658 match args {
1659 DirectMsgArgs::Args32(args) => {
1660 a[3] = args[0].into();
1661 a[4] = args[1].into();
1662 a[5] = args[2].into();
1663 a[6] = args[3].into();
1664 a[7] = args[4].into();
1665 }
1666 DirectMsgArgs::Args64(args) => {
1667 a[3] = args[0];
1668 a[4] = args[1];
1669 a[5] = args[2];
1670 a[6] = args[3];
1671 a[7] = args[4];
1672 }
Tomás González4d5b0ba2025-03-03 17:15:55 +00001673 DirectMsgArgs::VersionReq { version } => {
1674 a[2] = DirectMsgArgs::VERSION_REQ.into();
1675 a[3] = u32::from(version).into();
1676 }
Tomás González67f92c72025-03-20 16:50:42 +00001677 DirectMsgArgs::PowerPsciReq32 { params } => {
Tomás González4d5b0ba2025-03-03 17:15:55 +00001678 a[2] = DirectMsgArgs::POWER_PSCI_REQ.into();
Tomás González67f92c72025-03-20 16:50:42 +00001679 a[3] = params[0].into();
1680 a[4] = params[1].into();
1681 a[5] = params[2].into();
1682 a[6] = params[3].into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00001683 }
Tomás González67f92c72025-03-20 16:50:42 +00001684 DirectMsgArgs::PowerPsciReq64 { params } => {
Tomás González4d5b0ba2025-03-03 17:15:55 +00001685 a[2] = DirectMsgArgs::POWER_PSCI_REQ.into();
Tomás González67f92c72025-03-20 16:50:42 +00001686 a[3] = params[0];
1687 a[4] = params[1];
1688 a[5] = params[2];
1689 a[6] = params[3];
Tomás González4d5b0ba2025-03-03 17:15:55 +00001690 }
1691 DirectMsgArgs::PowerWarmBootReq { boot_type } => {
1692 a[2] = DirectMsgArgs::POWER_WARM_BOOT_REQ.into();
1693 a[3] = u32::from(boot_type).into();
1694 }
1695 DirectMsgArgs::VmCreated { handle, vm_id } => {
1696 a[2] = DirectMsgArgs::VM_CREATED.into();
1697 let handle_regs: [u32; 2] = handle.into();
1698 a[3] = handle_regs[0].into();
1699 a[4] = handle_regs[1].into();
1700 a[5] = vm_id.into();
1701 }
1702 DirectMsgArgs::VmDestructed { handle, vm_id } => {
1703 a[2] = DirectMsgArgs::VM_DESTRUCTED.into();
1704 let handle_regs: [u32; 2] = handle.into();
1705 a[3] = handle_regs[0].into();
1706 a[4] = handle_regs[1].into();
1707 a[5] = vm_id.into();
1708 }
1709 _ => panic!("Malformed MsgSendDirectReq interface"),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001710 }
1711 }
1712 Interface::MsgSendDirectResp {
1713 src_id,
1714 dst_id,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001715 args,
1716 } => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +01001717 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Balint Dobszay3aad9572025-01-17 16:54:11 +01001718 match args {
1719 DirectMsgArgs::Args32(args) => {
1720 a[3] = args[0].into();
1721 a[4] = args[1].into();
1722 a[5] = args[2].into();
1723 a[6] = args[3].into();
1724 a[7] = args[4].into();
1725 }
1726 DirectMsgArgs::Args64(args) => {
1727 a[3] = args[0];
1728 a[4] = args[1];
1729 a[5] = args[2];
1730 a[6] = args[3];
1731 a[7] = args[4];
1732 }
Tomás González4d5b0ba2025-03-03 17:15:55 +00001733 DirectMsgArgs::VersionResp { version } => {
1734 a[2] = DirectMsgArgs::VERSION_RESP.into();
1735 match version {
Tomás González67f92c72025-03-20 16:50:42 +00001736 None => a[3] = (i32::from(FfaError::NotSupported) as u32).into(),
Tomás González4d5b0ba2025-03-03 17:15:55 +00001737 Some(ver) => a[3] = u32::from(ver).into(),
1738 }
1739 }
1740 DirectMsgArgs::PowerPsciResp { psci_status } => {
1741 a[2] = DirectMsgArgs::POWER_PSCI_RESP.into();
1742 a[3] = psci_status as u64;
1743 }
1744 DirectMsgArgs::VmCreatedAck { sp_status } => {
1745 a[2] = DirectMsgArgs::VM_CREATED_ACK.into();
Tomás González67f92c72025-03-20 16:50:42 +00001746 a[3] = (i32::from(sp_status) as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00001747 }
1748 DirectMsgArgs::VmDestructedAck { sp_status } => {
1749 a[2] = DirectMsgArgs::VM_DESTRUCTED_ACK.into();
Tomás González67f92c72025-03-20 16:50:42 +00001750 a[3] = (i32::from(sp_status) as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00001751 }
1752 _ => panic!("Malformed MsgSendDirectResp interface"),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001753 }
1754 }
1755 Interface::MemDonate {
1756 total_len,
1757 frag_len,
1758 buf,
1759 } => {
1760 a[1] = total_len.into();
1761 a[2] = frag_len.into();
1762 (a[3], a[4]) = match buf {
1763 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1764 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1765 None => (0, 0),
1766 };
1767 }
1768 Interface::MemLend {
1769 total_len,
1770 frag_len,
1771 buf,
1772 } => {
1773 a[1] = total_len.into();
1774 a[2] = frag_len.into();
1775 (a[3], a[4]) = match buf {
1776 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1777 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1778 None => (0, 0),
1779 };
1780 }
1781 Interface::MemShare {
1782 total_len,
1783 frag_len,
1784 buf,
1785 } => {
1786 a[1] = total_len.into();
1787 a[2] = frag_len.into();
1788 (a[3], a[4]) = match buf {
1789 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1790 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1791 None => (0, 0),
1792 };
1793 }
1794 Interface::MemRetrieveReq {
1795 total_len,
1796 frag_len,
1797 buf,
1798 } => {
1799 a[1] = total_len.into();
1800 a[2] = frag_len.into();
1801 (a[3], a[4]) = match buf {
1802 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1803 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1804 None => (0, 0),
1805 };
1806 }
1807 Interface::MemRetrieveResp {
1808 total_len,
1809 frag_len,
1810 } => {
1811 a[1] = total_len.into();
1812 a[2] = frag_len.into();
1813 }
1814 Interface::MemRelinquish => {}
1815 Interface::MemReclaim { handle, flags } => {
1816 let handle_regs: [u32; 2] = handle.into();
1817 a[1] = handle_regs[0].into();
1818 a[2] = handle_regs[1].into();
1819 a[3] = flags.into();
1820 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001821 Interface::MemPermGet { addr, page_cnt } => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001822 a[1] = match addr {
1823 MemAddr::Addr32(addr) => addr.into(),
1824 MemAddr::Addr64(addr) => addr,
1825 };
Balint Dobszayde0dc802025-02-28 14:16:52 +01001826 a[2] = if version >= Version(1, 3) {
1827 page_cnt.unwrap().into()
1828 } else {
1829 assert!(page_cnt.is_none());
1830 0
1831 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01001832 }
1833 Interface::MemPermSet {
1834 addr,
1835 page_cnt,
1836 mem_perm,
1837 } => {
1838 a[1] = match addr {
1839 MemAddr::Addr32(addr) => addr.into(),
1840 MemAddr::Addr64(addr) => addr,
1841 };
1842 a[2] = page_cnt.into();
1843 a[3] = mem_perm.into();
1844 }
1845 Interface::ConsoleLog {
1846 char_cnt,
1847 char_lists,
1848 } => {
1849 a[1] = char_cnt.into();
1850 match char_lists {
1851 ConsoleLogChars::Reg32(regs) => {
1852 a[2] = regs[0].into();
1853 a[3] = regs[1].into();
1854 a[4] = regs[2].into();
1855 a[5] = regs[3].into();
1856 a[6] = regs[4].into();
1857 a[7] = regs[5].into();
1858 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001859 _ => panic!("{:#x?} requires 18 registers", char_lists),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001860 }
1861 }
Tomás González7ffb6132025-04-03 12:28:58 +01001862 Interface::NotificationBitmapCreate { vm_id, vcpu_cnt } => {
1863 a[1] = vm_id.into();
1864 a[2] = vcpu_cnt.into();
1865 }
1866 Interface::NotificationBitmapDestroy { vm_id } => {
1867 a[1] = vm_id.into();
1868 }
1869 Interface::NotificationBind {
1870 sender_id,
1871 receiver_id,
1872 flags,
1873 bitmap,
1874 } => {
1875 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
1876 a[2] = u32::from(flags).into();
1877 a[3] = bitmap & 0xffff_ffff;
1878 a[4] = bitmap >> 32;
1879 }
1880 Interface::NotificationUnBind {
1881 sender_id,
1882 receiver_id,
1883 bitmap,
1884 } => {
1885 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
1886 a[3] = bitmap & 0xffff_ffff;
1887 a[4] = bitmap >> 32;
1888 }
1889 Interface::NotificationSet {
1890 sender_id,
1891 receiver_id,
1892 flags,
1893 bitmap,
1894 } => {
1895 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
1896 a[2] = u32::from(flags).into();
1897 a[3] = bitmap & 0xffff_ffff;
1898 a[4] = bitmap >> 32;
1899 }
1900 Interface::NotificationGet {
1901 vcpu_id,
1902 endpoint_id,
1903 flags,
1904 } => {
1905 a[1] = (u64::from(vcpu_id) << 16) | u64::from(endpoint_id);
1906 a[2] = u32::from(flags).into();
1907 }
1908 Interface::NotificationInfoGet { .. } => {}
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01001909 Interface::El3IntrHandle => {}
Balint Dobszayde0dc802025-02-28 14:16:52 +01001910 _ => panic!("{:#x?} requires 18 registers", self),
1911 }
1912 }
1913
1914 fn pack_regs18(&self, version: Version, a: &mut [u64; 18]) {
1915 assert!(version >= Version(1, 2));
1916
Balint Dobszayde0dc802025-02-28 14:16:52 +01001917 if let Some(function_id) = self.function_id() {
1918 a[0] = function_id as u64;
1919 }
1920
1921 match *self {
1922 Interface::Success { target_info, args } => {
1923 a[1] = target_info.into();
1924 match args {
Imre Kis54773b62025-04-10 13:47:39 +02001925 SuccessArgs::Args64_2(regs) => a[2..18].copy_from_slice(&regs[..16]),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001926 _ => panic!("{:#x?} requires 8 registers", args),
1927 }
1928 }
1929 Interface::MsgSendDirectReq2 {
1930 src_id,
1931 dst_id,
1932 uuid,
1933 args,
1934 } => {
1935 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00001936 let (uuid_msb, uuid_lsb) = uuid.as_u64_pair();
1937 (a[2], a[3]) = (uuid_msb.swap_bytes(), uuid_lsb.swap_bytes());
Balint Dobszayde0dc802025-02-28 14:16:52 +01001938 a[4..18].copy_from_slice(&args.0[..14]);
1939 }
1940 Interface::MsgSendDirectResp2 {
1941 src_id,
1942 dst_id,
1943 args,
1944 } => {
1945 a[1] = ((src_id as u64) << 16) | dst_id as u64;
1946 a[2] = 0;
1947 a[3] = 0;
1948 a[4..18].copy_from_slice(&args.0[..14]);
1949 }
1950 Interface::ConsoleLog {
1951 char_cnt,
1952 char_lists,
1953 } => {
1954 a[1] = char_cnt.into();
1955 match char_lists {
1956 ConsoleLogChars::Reg64(regs) => a[2..18].copy_from_slice(&regs[..16]),
1957 _ => panic!("{:#x?} requires 8 registers", char_lists),
1958 }
1959 }
Tomás González0a058bc2025-03-11 11:20:55 +00001960 Interface::PartitionInfoGetRegs {
1961 uuid,
1962 start_index,
1963 info_tag,
1964 } => {
1965 if start_index == 0 && info_tag != 0 {
1966 panic!("Information Tag MBZ if start index is 0: {:#x?}", self);
1967 }
1968 let (uuid_msb, uuid_lsb) = uuid.as_u64_pair();
1969 (a[1], a[2]) = (uuid_msb.swap_bytes(), uuid_lsb.swap_bytes());
1970 a[3] = (u64::from(info_tag) << 16) | u64::from(start_index);
1971 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001972 _ => panic!("{:#x?} requires 8 registers", self),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001973 }
1974 }
1975
Balint Dobszaya5846852025-02-26 15:38:53 +01001976 /// Helper function to create an `FFA_SUCCESS` interface without any arguments.
Balint Dobszay3aad9572025-01-17 16:54:11 +01001977 pub fn success32_noargs() -> Self {
1978 Self::Success {
1979 target_info: 0,
Imre Kis54773b62025-04-10 13:47:39 +02001980 args: SuccessArgs::Args32([0; 6]),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001981 }
1982 }
1983
Tomás González4c8c7d22025-03-10 17:14:57 +00001984 /// Helper function to create an `FFA_SUCCESS` interface without any arguments.
1985 pub fn success64_noargs() -> Self {
1986 Self::Success {
1987 target_info: 0,
Imre Kis54773b62025-04-10 13:47:39 +02001988 args: SuccessArgs::Args64([0; 6]),
Tomás González4c8c7d22025-03-10 17:14:57 +00001989 }
1990 }
1991
Balint Dobszaya5846852025-02-26 15:38:53 +01001992 /// Helper function to create an `FFA_ERROR` interface with an error code.
Balint Dobszay3aad9572025-01-17 16:54:11 +01001993 pub fn error(error_code: FfaError) -> Self {
1994 Self::Error {
1995 target_info: TargetInfo {
1996 endpoint_id: 0,
1997 vcpu_id: 0,
1998 },
1999 error_code,
Balint Dobszayb727aab2025-04-07 10:24:59 +02002000 error_arg: 0,
Balint Dobszay3aad9572025-01-17 16:54:11 +01002001 }
2002 }
2003}
2004
Balint Dobszaya5846852025-02-26 15:38:53 +01002005/// Maximum number of characters transmitted in a single `FFA_CONSOLE_LOG32` message.
2006pub const CONSOLE_LOG_32_MAX_CHAR_CNT: u8 = 24;
Balint Dobszayde0dc802025-02-28 14:16:52 +01002007/// Maximum number of characters transmitted in a single `FFA_CONSOLE_LOG64` message.
2008pub const CONSOLE_LOG_64_MAX_CHAR_CNT: u8 = 128;
Balint Dobszay3aad9572025-01-17 16:54:11 +01002009
Balint Dobszaya5846852025-02-26 15:38:53 +01002010/// Helper function to convert the "Tightly packed list of characters" format used by the
2011/// `FFA_CONSOLE_LOG` interface into a byte slice.
Balint Dobszay3aad9572025-01-17 16:54:11 +01002012pub fn parse_console_log(
2013 char_cnt: u8,
2014 char_lists: &ConsoleLogChars,
2015 log_bytes: &mut [u8],
2016) -> Result<(), FfaError> {
2017 match char_lists {
2018 ConsoleLogChars::Reg32(regs) => {
Balint Dobszaya5846852025-02-26 15:38:53 +01002019 if !(1..=CONSOLE_LOG_32_MAX_CHAR_CNT).contains(&char_cnt) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01002020 return Err(FfaError::InvalidParameters);
2021 }
2022 for (i, reg) in regs.iter().enumerate() {
2023 log_bytes[4 * i..4 * (i + 1)].copy_from_slice(&reg.to_le_bytes());
2024 }
2025 }
2026 ConsoleLogChars::Reg64(regs) => {
Balint Dobszaya5846852025-02-26 15:38:53 +01002027 if !(1..=CONSOLE_LOG_64_MAX_CHAR_CNT).contains(&char_cnt) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01002028 return Err(FfaError::InvalidParameters);
2029 }
2030 for (i, reg) in regs.iter().enumerate() {
2031 log_bytes[8 * i..8 * (i + 1)].copy_from_slice(&reg.to_le_bytes());
2032 }
2033 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +02002034 }
2035
Balint Dobszayc8802492025-01-15 18:11:39 +01002036 Ok(())
Balint Dobszay5bf492f2024-07-29 17:21:32 +02002037}
Tomás González0a058bc2025-03-11 11:20:55 +00002038
2039#[cfg(test)]
2040mod tests {
2041 use super::*;
2042
2043 #[test]
2044 fn part_info_get_regs() {
2045 let uuid = Uuid::parse_str("a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8").unwrap();
2046 let uuid_bytes = uuid.as_bytes();
2047 let test_info_tag = 0b1101_1101;
2048 let test_start_index = 0b1101;
2049 let start_index_and_tag = (test_info_tag << 16) | test_start_index;
2050 let version = Version(1, 2);
2051
2052 // From spec:
2053 // Bytes[0...7] of UUID with byte 0 in the low-order bits.
2054 let reg_x1 = (uuid_bytes[7] as u64) << 56
2055 | (uuid_bytes[6] as u64) << 48
2056 | (uuid_bytes[5] as u64) << 40
2057 | (uuid_bytes[4] as u64) << 32
2058 | (uuid_bytes[3] as u64) << 24
2059 | (uuid_bytes[2] as u64) << 16
2060 | (uuid_bytes[1] as u64) << 8
2061 | (uuid_bytes[0] as u64);
2062
2063 // From spec:
2064 // Bytes[8...15] of UUID with byte 8 in the low-order bits.
2065 let reg_x2 = (uuid_bytes[15] as u64) << 56
2066 | (uuid_bytes[14] as u64) << 48
2067 | (uuid_bytes[13] as u64) << 40
2068 | (uuid_bytes[12] as u64) << 32
2069 | (uuid_bytes[11] as u64) << 24
2070 | (uuid_bytes[10] as u64) << 16
2071 | (uuid_bytes[9] as u64) << 8
2072 | (uuid_bytes[8] as u64);
2073
2074 // First, test for wrong tag:
2075 {
2076 let mut regs = [0u64; 18];
2077 regs[0] = FuncId::PartitionInfoGetRegs as u64;
2078 regs[1] = reg_x1;
2079 regs[2] = reg_x2;
2080 regs[3] = test_info_tag << 16;
2081
2082 assert!(Interface::from_regs(version, &regs).is_err_and(
2083 |e| e == Error::InvalidInformationTag(test_info_tag.try_into().unwrap())
2084 ));
2085 }
2086
2087 // Test for regs -> Interface -> regs
2088 {
2089 let mut orig_regs = [0u64; 18];
2090 orig_regs[0] = FuncId::PartitionInfoGetRegs as u64;
2091 orig_regs[1] = reg_x1;
2092 orig_regs[2] = reg_x2;
2093 orig_regs[3] = start_index_and_tag;
2094
2095 let mut test_regs = orig_regs.clone();
2096 let interface = Interface::from_regs(version, &mut test_regs).unwrap();
2097 match &interface {
2098 Interface::PartitionInfoGetRegs {
2099 info_tag,
2100 start_index,
2101 uuid: int_uuid,
2102 } => {
2103 assert_eq!(u64::from(*info_tag), test_info_tag);
2104 assert_eq!(u64::from(*start_index), test_start_index);
2105 assert_eq!(*int_uuid, uuid);
2106 }
2107 _ => panic!("Expecting Interface::PartitionInfoGetRegs!"),
2108 }
2109 test_regs.fill(0);
2110 interface.to_regs(version, &mut test_regs);
2111 assert_eq!(orig_regs, test_regs);
2112 }
2113
2114 // Test for Interface -> regs -> Interface
2115 {
2116 let interface = Interface::PartitionInfoGetRegs {
2117 info_tag: test_info_tag.try_into().unwrap(),
2118 start_index: test_start_index.try_into().unwrap(),
2119 uuid,
2120 };
2121
2122 let mut regs: [u64; 18] = [0; 18];
2123 interface.to_regs(version, &mut regs);
2124
2125 assert_eq!(Some(FuncId::PartitionInfoGetRegs), interface.function_id());
2126 assert_eq!(regs[0], interface.function_id().unwrap() as u64);
2127 assert_eq!(regs[1], reg_x1);
2128 assert_eq!(regs[2], reg_x2);
2129 assert_eq!(regs[3], (test_info_tag << 16) | test_start_index);
2130
2131 assert_eq!(Interface::from_regs(version, &regs).unwrap(), interface);
2132 }
2133 }
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00002134
2135 #[test]
2136 fn msg_send_direct_req2() {
2137 let uuid = Uuid::parse_str("a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8").unwrap();
2138 let uuid_bytes = uuid.as_bytes();
2139
2140 // From spec:
2141 // Bytes[0...7] of UUID with byte 0 in the low-order bits.
2142 let reg_x2 = (uuid_bytes[7] as u64) << 56
2143 | (uuid_bytes[6] as u64) << 48
2144 | (uuid_bytes[5] as u64) << 40
2145 | (uuid_bytes[4] as u64) << 32
2146 | (uuid_bytes[3] as u64) << 24
2147 | (uuid_bytes[2] as u64) << 16
2148 | (uuid_bytes[1] as u64) << 8
2149 | (uuid_bytes[0] as u64);
2150
2151 // From spec:
2152 // Bytes[8...15] of UUID with byte 8 in the low-order bits.
2153 let reg_x3 = (uuid_bytes[15] as u64) << 56
2154 | (uuid_bytes[14] as u64) << 48
2155 | (uuid_bytes[13] as u64) << 40
2156 | (uuid_bytes[12] as u64) << 32
2157 | (uuid_bytes[11] as u64) << 24
2158 | (uuid_bytes[10] as u64) << 16
2159 | (uuid_bytes[9] as u64) << 8
2160 | (uuid_bytes[8] as u64);
2161
2162 let test_sender = 0b1101_1101;
2163 let test_receiver = 0b1101;
2164 let test_sender_receiver = (test_sender << 16) | test_receiver;
2165 let version = Version(1, 2);
2166
2167 // Test for regs -> Interface -> regs
2168 {
2169 let mut orig_regs = [0u64; 18];
2170 orig_regs[0] = FuncId::MsgSendDirectReq64_2 as u64;
2171 orig_regs[1] = test_sender_receiver;
2172 orig_regs[2] = reg_x2;
2173 orig_regs[3] = reg_x3;
2174
2175 let mut test_regs = orig_regs.clone();
2176 let interface = Interface::from_regs(version, &mut test_regs).unwrap();
2177 match &interface {
2178 Interface::MsgSendDirectReq2 {
2179 dst_id,
2180 src_id,
2181 args: _,
2182 uuid: int_uuid,
2183 } => {
2184 assert_eq!(u64::from(*src_id), test_sender);
2185 assert_eq!(u64::from(*dst_id), test_receiver);
2186 assert_eq!(*int_uuid, uuid);
2187 }
2188 _ => panic!("Expecting Interface::MsgSendDirectReq2!"),
2189 }
2190 test_regs.fill(0);
2191 interface.to_regs(version, &mut test_regs);
2192 assert_eq!(orig_regs, test_regs);
2193 }
2194
2195 // Test for Interface -> regs -> Interface
2196 {
2197 let rest_of_regs: [u64; 14] = [0; 14];
2198
2199 let interface = Interface::MsgSendDirectReq2 {
2200 src_id: test_sender.try_into().unwrap(),
2201 dst_id: test_receiver.try_into().unwrap(),
2202 uuid,
2203 args: DirectMsg2Args(rest_of_regs),
2204 };
2205
2206 let mut regs: [u64; 18] = [0; 18];
2207 interface.to_regs(version, &mut regs);
2208
2209 assert_eq!(Some(FuncId::MsgSendDirectReq64_2), interface.function_id());
2210 assert_eq!(regs[0], interface.function_id().unwrap() as u64);
2211 assert_eq!(regs[1], test_sender_receiver);
2212 assert_eq!(regs[2], reg_x2);
2213 assert_eq!(regs[3], reg_x3);
2214 assert_eq!(regs[4], 0);
2215
2216 assert_eq!(Interface::from_regs(version, &regs).unwrap(), interface);
2217 }
2218 }
Tomás González6ccba0a2025-04-09 13:31:29 +01002219
2220 #[test]
2221 fn is_32bit() {
2222 let interface_64 = Interface::MsgSendDirectReq {
2223 src_id: 0,
2224 dst_id: 1,
2225 args: DirectMsgArgs::Args64([0, 0, 0, 0, 0]),
2226 };
2227 assert!(!interface_64.is_32bit());
2228
2229 let interface_32 = Interface::MsgSendDirectReq {
2230 src_id: 0,
2231 dst_id: 1,
2232 args: DirectMsgArgs::Args32([0, 0, 0, 0, 0]),
2233 };
2234 assert!(interface_32.is_32bit());
2235 }
Tomás González0a058bc2025-03-11 11:20:55 +00002236}