blob: e3c9778a1dac5c51d9904d60def915e89e42d2c4 [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`]
Balint Dobszay3aad9572025-01-17 16:54:11 +0100208#[derive(Debug, Eq, PartialEq, Clone, Copy)]
209pub enum SuccessArgs {
Imre Kis54773b62025-04-10 13:47:39 +0200210 Args32([u32; 6]),
211 Args64([u64; 6]),
212 Args64_2([u64; 16]),
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200213}
214
Imre Kis839eaef2025-04-11 17:38:36 +0200215impl SuccessArgs {
216 fn try_get_args32(self) -> Result<[u32; 6], Error> {
217 match self {
218 SuccessArgs::Args32(args) => Ok(args),
219 SuccessArgs::Args64(_) | SuccessArgs::Args64_2(_) => {
220 Err(Error::InvalidSuccessArgsVariant)
221 }
222 }
223 }
224
225 fn try_get_args64(self) -> Result<[u64; 6], Error> {
226 match self {
227 SuccessArgs::Args64(args) => Ok(args),
228 SuccessArgs::Args32(_) | SuccessArgs::Args64_2(_) => {
229 Err(Error::InvalidSuccessArgsVariant)
230 }
231 }
232 }
233
234 fn try_get_args64_2(self) -> Result<[u64; 16], Error> {
235 match self {
236 SuccessArgs::Args64_2(args) => Ok(args),
237 SuccessArgs::Args32(_) | SuccessArgs::Args64(_) => {
238 Err(Error::InvalidSuccessArgsVariant)
239 }
240 }
241 }
242}
243
Tomás González17b92442025-03-10 16:45:04 +0000244/// Entrypoint address argument for `FFA_SECONDARY_EP_REGISTER` interface.
245#[derive(Debug, Eq, PartialEq, Clone, Copy)]
246pub enum SecondaryEpRegisterAddr {
247 Addr32(u32),
248 Addr64(u64),
249}
250
Balint Dobszaya5846852025-02-26 15:38:53 +0100251/// Version number of the FF-A implementation, `.0` is the major, `.1` is minor the version.
Balint Dobszayde0dc802025-02-28 14:16:52 +0100252#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord)]
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200253pub struct Version(pub u16, pub u16);
254
Tomás González1f794352025-03-03 16:47:06 +0000255impl Version {
Tomás González83146af2025-03-04 11:32:41 +0000256 // The FF-A spec mandates that bit[31] of a version number must be 0
257 const MBZ_BITS: u32 = 1 << 31;
258
Tomás González1f794352025-03-03 16:47:06 +0000259 /// Returns whether the caller's version (self) is compatible with the callee's version (input
260 /// parameter)
261 pub fn is_compatible_to(&self, callee_version: &Version) -> bool {
262 self.0 == callee_version.0 && self.1 <= callee_version.1
263 }
264}
265
Tomás González83146af2025-03-04 11:32:41 +0000266impl TryFrom<u32> for Version {
267 type Error = Error;
268
269 fn try_from(val: u32) -> Result<Self, Self::Error> {
270 if (val & Self::MBZ_BITS) != 0 {
271 Err(Error::InvalidVersion(val))
272 } else {
273 Ok(Self((val >> 16) as u16, val as u16))
274 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200275 }
276}
277
278impl From<Version> for u32 {
279 fn from(v: Version) -> Self {
Tomás González83146af2025-03-04 11:32:41 +0000280 let v_u32 = ((v.0 as u32) << 16) | v.1 as u32;
281 assert!(v_u32 & Version::MBZ_BITS == 0);
282 v_u32
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200283 }
284}
285
Andrew Walbran19970ba2024-11-25 15:35:00 +0000286impl Display for Version {
287 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
288 write!(f, "{}.{}", self.0, self.1)
289 }
290}
291
292impl Debug for Version {
293 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
294 Display::fmt(self, f)
295 }
296}
297
Balint Dobszaya5846852025-02-26 15:38:53 +0100298/// Feature IDs used by the `FFA_FEATURES` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100299#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
300#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedFeatureId))]
301#[repr(u8)]
302pub enum FeatureId {
303 NotificationPendingInterrupt = 0x1,
304 ScheduleReceiverInterrupt = 0x2,
305 ManagedExitInterrupt = 0x3,
306}
Balint Dobszayc8802492025-01-15 18:11:39 +0100307
Balint Dobszaya5846852025-02-26 15:38:53 +0100308/// Arguments for the `FFA_FEATURES` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100309#[derive(Debug, Eq, PartialEq, Clone, Copy)]
310pub enum Feature {
311 FuncId(FuncId),
312 FeatureId(FeatureId),
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100313 Unknown(u32),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100314}
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200315
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100316impl From<u32> for Feature {
317 fn from(value: u32) -> Self {
318 // Bit[31] is set for all valid FF-A function IDs so we don't have to check it separately
319 if let Ok(func_id) = value.try_into() {
320 Self::FuncId(func_id)
321 } else if let Ok(feat_id) = (value as u8).try_into() {
322 Self::FeatureId(feat_id)
Balint Dobszay3aad9572025-01-17 16:54:11 +0100323 } else {
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100324 Self::Unknown(value)
325 }
Balint Dobszay3aad9572025-01-17 16:54:11 +0100326 }
327}
328
329impl From<Feature> for u32 {
330 fn from(value: Feature) -> Self {
331 match value {
332 Feature::FuncId(func_id) => (1 << 31) | func_id as u32,
333 Feature::FeatureId(feature_id) => feature_id as u32,
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100334 Feature::Unknown(id) => panic!("Unknown feature or function ID {:#x?}", id),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100335 }
336 }
337}
338
Imre Kis4e9d8bc2025-04-10 13:48:26 +0200339/// `FFA_FEATURES` specific success argument structure. This type needs further specialization based
340/// on 'FF-A function ID or Feature ID' field of the preceeding `FFA_FEATURES` request.
341#[derive(Debug, Eq, PartialEq, Clone, Copy)]
342pub struct SuccessArgsFeatures {
343 pub properties: [u32; 2],
344}
345
346impl From<SuccessArgsFeatures> for SuccessArgs {
347 fn from(value: SuccessArgsFeatures) -> Self {
348 Self::Args32([value.properties[0], value.properties[1], 0, 0, 0, 0])
349 }
350}
351
352impl TryFrom<SuccessArgs> for SuccessArgsFeatures {
353 type Error = Error;
354
355 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
356 let args = value.try_get_args32()?;
357
358 Ok(Self {
359 properties: [args[0], args[1]],
360 })
361 }
362}
363
Balint Dobszaya5846852025-02-26 15:38:53 +0100364/// RXTX buffer descriptor, used by `FFA_RXTX_MAP`.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100365#[derive(Debug, Eq, PartialEq, Clone, Copy)]
366pub enum RxTxAddr {
367 Addr32 { rx: u32, tx: u32 },
368 Addr64 { rx: u64, tx: u64 },
369}
370
Imre Kise295adb2025-04-10 13:26:28 +0200371/// Flags of the `FFA_PARTITION_INFO_GET` interface.
372#[derive(Debug, Eq, PartialEq, Clone, Copy)]
373pub struct PartitionInfoGetFlags {
374 pub count_only: bool,
375}
376
377impl PartitionInfoGetFlags {
378 const RETURN_INFORMATION_TYPE_FLAG: u32 = 1 << 0;
379 const MBZ_BITS: u32 = 0xffff_fffe;
380}
381
382impl TryFrom<u32> for PartitionInfoGetFlags {
383 type Error = Error;
384
385 fn try_from(val: u32) -> Result<Self, Self::Error> {
386 if (val & Self::MBZ_BITS) != 0 {
387 Err(Error::InvalidPartitionInfoGetFlag(val))
388 } else {
389 Ok(Self {
390 count_only: val & Self::RETURN_INFORMATION_TYPE_FLAG != 0,
391 })
392 }
393 }
394}
395
396impl From<PartitionInfoGetFlags> for u32 {
397 fn from(flags: PartitionInfoGetFlags) -> Self {
398 let mut bits: u32 = 0;
399 if flags.count_only {
400 bits |= PartitionInfoGetFlags::RETURN_INFORMATION_TYPE_FLAG;
401 }
402 bits
403 }
404}
405
Tomás González4d5b0ba2025-03-03 17:15:55 +0000406/// Composite type for capturing success and error return codes for the VM availability messages.
407///
408/// Error codes are handled by the `FfaError` type. Having a separate type for errors helps using
409/// `Result<(), FfaError>`. If a single type would include both success and error values,
410/// then `Err(FfaError::Success)` would be incomprehensible.
411#[derive(Debug, Eq, PartialEq, Clone, Copy)]
412pub enum VmAvailabilityStatus {
413 Success,
414 Error(FfaError),
415}
416
417impl TryFrom<i32> for VmAvailabilityStatus {
418 type Error = Error;
419 fn try_from(value: i32) -> Result<Self, <Self as TryFrom<i32>>::Error> {
420 Ok(match value {
421 0 => Self::Success,
422 error_code => Self::Error(FfaError::try_from(error_code)?),
423 })
424 }
425}
426
427impl From<VmAvailabilityStatus> for i32 {
428 fn from(value: VmAvailabilityStatus) -> Self {
429 match value {
430 VmAvailabilityStatus::Success => 0,
431 VmAvailabilityStatus::Error(error_code) => error_code.into(),
432 }
433 }
434}
435
436/// Arguments for the Power Warm Boot `FFA_MSG_SEND_DIRECT_REQ` interface.
437#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
438#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedWarmBootType))]
439#[repr(u32)]
440pub enum WarmBootType {
441 ExitFromSuspend = 0,
442 ExitFromLowPower = 1,
443}
444
Balint Dobszaya5846852025-02-26 15:38:53 +0100445/// Arguments for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}` interfaces.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100446#[derive(Debug, Eq, PartialEq, Clone, Copy)]
447pub enum DirectMsgArgs {
448 Args32([u32; 5]),
449 Args64([u64; 5]),
Tomás González4d5b0ba2025-03-03 17:15:55 +0000450 /// Message for forwarding FFA_VERSION call from Normal world to the SPMC
451 VersionReq {
452 version: Version,
453 },
454 /// Response message to forwarded FFA_VERSION call from the Normal world
455 /// Contains the version returned by the SPMC or None
456 VersionResp {
457 version: Option<Version>,
458 },
459 /// Message for a power management operation initiated by a PSCI function
460 PowerPsciReq32 {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000461 // params[i]: Input parameter in w[i] in PSCI function invocation at EL3.
Tomás González67f92c72025-03-20 16:50:42 +0000462 // params[0]: Function ID.
463 params: [u32; 4],
Tomás González4d5b0ba2025-03-03 17:15:55 +0000464 },
465 /// Message for a power management operation initiated by a PSCI function
466 PowerPsciReq64 {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000467 // params[i]: Input parameter in x[i] in PSCI function invocation at EL3.
Tomás González67f92c72025-03-20 16:50:42 +0000468 // params[0]: Function ID.
469 params: [u64; 4],
Tomás González4d5b0ba2025-03-03 17:15:55 +0000470 },
471 /// Message for a warm boot
472 PowerWarmBootReq {
473 boot_type: WarmBootType,
474 },
475 /// Response message to indicate return status of the last power management request message
476 /// Return error code SUCCESS or DENIED as defined in PSCI spec. Caller is left to do the
477 /// parsing of the return status.
478 PowerPsciResp {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000479 psci_status: i32,
480 },
481 /// Message to signal creation of a VM
482 VmCreated {
483 // Globally unique Handle to identify a memory region that contains IMPLEMENTATION DEFINED
484 // information associated with the created VM.
485 // The invalid memory region handle must be specified by the Hypervisor if this field is not
486 // used.
487 handle: memory_management::Handle,
488 vm_id: u16,
489 },
490 /// Message to acknowledge creation of a VM
491 VmCreatedAck {
492 sp_status: VmAvailabilityStatus,
493 },
494 /// Message to signal destruction of a VM
495 VmDestructed {
496 // Globally unique Handle to identify a memory region that contains IMPLEMENTATION DEFINED
497 // information associated with the created VM.
498 // The invalid memory region handle must be specified by the Hypervisor if this field is not
499 // used.
500 handle: memory_management::Handle,
501 vm_id: u16,
502 },
503 /// Message to acknowledge destruction of a VM
504 VmDestructedAck {
505 sp_status: VmAvailabilityStatus,
506 },
507}
508
509impl DirectMsgArgs {
510 // Flags for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}` interfaces.
511
512 const FWK_MSG_BITS: u32 = 1 << 31;
513 const VERSION_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b1000;
514 const VERSION_RESP: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b1001;
515 const POWER_PSCI_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS;
516 const POWER_WARM_BOOT_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0001;
517 const POWER_PSCI_RESP: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0010;
518 const VM_CREATED: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0100;
519 const VM_CREATED_ACK: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0101;
520 const VM_DESTRUCTED: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0110;
521 const VM_DESTRUCTED_ACK: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0111;
Balint Dobszay3aad9572025-01-17 16:54:11 +0100522}
523
Balint Dobszayde0dc802025-02-28 14:16:52 +0100524/// Arguments for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}2` interfaces.
525#[derive(Debug, Eq, PartialEq, Clone, Copy)]
526pub struct DirectMsg2Args([u64; 14]);
527
Tomás González092202a2025-03-05 11:56:45 +0000528#[derive(Debug, Eq, PartialEq, Clone, Copy)]
529pub struct MsgWaitFlags {
530 retain_rx_buffer: bool,
531}
532
533impl MsgWaitFlags {
534 const RETAIN_RX_BUFFER: u32 = 0x01;
535 const MBZ_BITS: u32 = 0xfffe;
536}
537
538impl TryFrom<u32> for MsgWaitFlags {
539 type Error = Error;
540
541 fn try_from(val: u32) -> Result<Self, Self::Error> {
542 if (val & Self::MBZ_BITS) != 0 {
543 Err(Error::InvalidMsgWaitFlag(val))
544 } else {
545 Ok(MsgWaitFlags {
546 retain_rx_buffer: val & Self::RETAIN_RX_BUFFER != 0,
547 })
548 }
549 }
550}
551
552impl From<MsgWaitFlags> for u32 {
553 fn from(flags: MsgWaitFlags) -> Self {
554 let mut bits: u32 = 0;
555 if flags.retain_rx_buffer {
556 bits |= MsgWaitFlags::RETAIN_RX_BUFFER;
557 }
558 bits
559 }
560}
561
Balint Dobszaya5846852025-02-26 15:38:53 +0100562/// Descriptor for a dynamically allocated memory buffer that contains the memory transaction
Tomás Gonzálezf268e322025-03-05 11:18:11 +0000563/// descriptor.
564///
565/// Used by `FFA_MEM_{DONATE,LEND,SHARE,RETRIEVE_REQ}` interfaces, only when the TX buffer is not
566/// used to transmit the transaction descriptor.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100567#[derive(Debug, Eq, PartialEq, Clone, Copy)]
568pub enum MemOpBuf {
569 Buf32 { addr: u32, page_cnt: u32 },
570 Buf64 { addr: u64, page_cnt: u32 },
571}
572
Balint Dobszaya5846852025-02-26 15:38:53 +0100573/// Memory address argument for `FFA_MEM_PERM_{GET,SET}` interfaces.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100574#[derive(Debug, Eq, PartialEq, Clone, Copy)]
575pub enum MemAddr {
576 Addr32(u32),
577 Addr64(u64),
578}
579
Balint Dobszayde0dc802025-02-28 14:16:52 +0100580/// Argument for the `FFA_CONSOLE_LOG` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100581#[derive(Debug, Eq, PartialEq, Clone, Copy)]
582pub enum ConsoleLogChars {
583 Reg32([u32; 6]),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100584 Reg64([u64; 16]),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100585}
586
Tomás González7ffb6132025-04-03 12:28:58 +0100587#[derive(Debug, Eq, PartialEq, Clone, Copy)]
588pub struct NotificationBindFlags {
589 per_vcpu_notification: bool,
590}
591
592impl NotificationBindFlags {
593 const PER_VCPU_NOTIFICATION: u32 = 1;
594}
595
596impl From<NotificationBindFlags> for u32 {
597 fn from(flags: NotificationBindFlags) -> Self {
598 let mut bits: u32 = 0;
599 if flags.per_vcpu_notification {
600 bits |= NotificationBindFlags::PER_VCPU_NOTIFICATION;
601 }
602 bits
603 }
604}
605
606impl From<u32> for NotificationBindFlags {
607 fn from(flags: u32) -> Self {
608 Self {
609 per_vcpu_notification: flags & Self::PER_VCPU_NOTIFICATION != 0,
610 }
611 }
612}
613
614#[derive(Debug, Eq, PartialEq, Clone, Copy)]
615pub struct NotificationSetFlags {
616 delay_schedule_receiver: bool,
617 vcpu_id: Option<u16>,
618}
619
620impl NotificationSetFlags {
621 const PER_VCP_NOTIFICATION: u32 = 1 << 0;
622 const DELAY_SCHEDULE_RECEIVER: u32 = 1 << 1;
623 const VCPU_ID_SHIFT: u32 = 16;
624
625 const MBZ_BITS: u32 = 0xfffc;
626}
627
628impl From<NotificationSetFlags> for u32 {
629 fn from(flags: NotificationSetFlags) -> Self {
630 let mut bits: u32 = 0;
631
632 if flags.delay_schedule_receiver {
633 bits |= NotificationSetFlags::DELAY_SCHEDULE_RECEIVER;
634 }
635 if let Some(vcpu_id) = flags.vcpu_id {
636 bits |= NotificationSetFlags::PER_VCP_NOTIFICATION;
637 bits |= u32::from(vcpu_id) << NotificationSetFlags::VCPU_ID_SHIFT;
638 }
639
640 bits
641 }
642}
643
644impl TryFrom<u32> for NotificationSetFlags {
645 type Error = Error;
646
647 fn try_from(flags: u32) -> Result<Self, Self::Error> {
648 if (flags & Self::MBZ_BITS) != 0 {
649 return Err(Error::InvalidNotificationSetFlag(flags));
650 }
651
652 let tentative_vcpu_id = (flags >> Self::VCPU_ID_SHIFT) as u16;
653
654 let vcpu_id = if (flags & Self::PER_VCP_NOTIFICATION) != 0 {
655 Some(tentative_vcpu_id)
656 } else {
657 if tentative_vcpu_id != 0 {
658 return Err(Error::InvalidNotificationSetFlag(flags));
659 }
660 None
661 };
662
663 Ok(Self {
664 delay_schedule_receiver: (flags & Self::DELAY_SCHEDULE_RECEIVER) != 0,
665 vcpu_id,
666 })
667 }
668}
669
670#[derive(Debug, Eq, PartialEq, Clone, Copy)]
671pub struct NotificationGetFlags {
672 sp_bitmap_id: bool,
673 vm_bitmap_id: bool,
674 spm_bitmap_id: bool,
675 hyp_bitmap_id: bool,
676}
677
678impl NotificationGetFlags {
679 const SP_BITMAP_ID: u32 = 1;
680 const VM_BITMAP_ID: u32 = 1 << 1;
681 const SPM_BITMAP_ID: u32 = 1 << 2;
682 const HYP_BITMAP_ID: u32 = 1 << 3;
683}
684
685impl From<NotificationGetFlags> for u32 {
686 fn from(flags: NotificationGetFlags) -> Self {
687 let mut bits: u32 = 0;
688 if flags.sp_bitmap_id {
689 bits |= NotificationGetFlags::SP_BITMAP_ID;
690 }
691 if flags.vm_bitmap_id {
692 bits |= NotificationGetFlags::VM_BITMAP_ID;
693 }
694 if flags.spm_bitmap_id {
695 bits |= NotificationGetFlags::SPM_BITMAP_ID;
696 }
697 if flags.hyp_bitmap_id {
698 bits |= NotificationGetFlags::HYP_BITMAP_ID;
699 }
700 bits
701 }
702}
703
704impl From<u32> for NotificationGetFlags {
705 // This is a "from" instead of a "try_from" because Reserved Bits are SBZ, *not* MBZ.
706 fn from(flags: u32) -> Self {
707 Self {
708 sp_bitmap_id: (flags & Self::SP_BITMAP_ID) != 0,
709 vm_bitmap_id: (flags & Self::VM_BITMAP_ID) != 0,
710 spm_bitmap_id: (flags & Self::SPM_BITMAP_ID) != 0,
711 hyp_bitmap_id: (flags & Self::HYP_BITMAP_ID) != 0,
712 }
713 }
714}
715
Tomás Gonzálezf268e322025-03-05 11:18:11 +0000716/// FF-A "message types", the terminology used by the spec is "interfaces".
717///
718/// The interfaces are used by FF-A components for communication at an FF-A instance. The spec also
719/// describes the valid FF-A instances and conduits for each interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100720#[derive(Debug, Eq, PartialEq, Clone, Copy)]
721pub enum Interface {
722 Error {
723 target_info: TargetInfo,
724 error_code: FfaError,
Balint Dobszayb727aab2025-04-07 10:24:59 +0200725 error_arg: u32,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100726 },
727 Success {
728 target_info: u32,
729 args: SuccessArgs,
730 },
731 Interrupt {
732 target_info: TargetInfo,
733 interrupt_id: u32,
734 },
735 Version {
736 input_version: Version,
737 },
738 VersionOut {
739 output_version: Version,
740 },
741 Features {
742 feat_id: Feature,
743 input_properties: u32,
744 },
745 RxAcquire {
746 vm_id: u16,
747 },
748 RxRelease {
749 vm_id: u16,
750 },
751 RxTxMap {
752 addr: RxTxAddr,
753 page_cnt: u32,
754 },
755 RxTxUnmap {
756 id: u16,
757 },
758 PartitionInfoGet {
759 uuid: Uuid,
Imre Kise295adb2025-04-10 13:26:28 +0200760 flags: PartitionInfoGetFlags,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100761 },
Tomás González0a058bc2025-03-11 11:20:55 +0000762 PartitionInfoGetRegs {
763 uuid: Uuid,
764 start_index: u16,
765 info_tag: u16,
766 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100767 IdGet,
768 SpmIdGet,
Tomás González092202a2025-03-05 11:56:45 +0000769 MsgWait {
770 flags: Option<MsgWaitFlags>,
771 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100772 Yield,
773 Run {
774 target_info: TargetInfo,
775 },
776 NormalWorldResume,
Tomás González17b92442025-03-10 16:45:04 +0000777 SecondaryEpRegister {
778 entrypoint: SecondaryEpRegisterAddr,
779 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100780 MsgSend2 {
781 sender_vm_id: u16,
782 flags: u32,
783 },
784 MsgSendDirectReq {
785 src_id: u16,
786 dst_id: u16,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100787 args: DirectMsgArgs,
788 },
789 MsgSendDirectResp {
790 src_id: u16,
791 dst_id: u16,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100792 args: DirectMsgArgs,
793 },
Balint Dobszayde0dc802025-02-28 14:16:52 +0100794 MsgSendDirectReq2 {
795 src_id: u16,
796 dst_id: u16,
797 uuid: Uuid,
798 args: DirectMsg2Args,
799 },
800 MsgSendDirectResp2 {
801 src_id: u16,
802 dst_id: u16,
803 args: DirectMsg2Args,
804 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100805 MemDonate {
806 total_len: u32,
807 frag_len: u32,
808 buf: Option<MemOpBuf>,
809 },
810 MemLend {
811 total_len: u32,
812 frag_len: u32,
813 buf: Option<MemOpBuf>,
814 },
815 MemShare {
816 total_len: u32,
817 frag_len: u32,
818 buf: Option<MemOpBuf>,
819 },
820 MemRetrieveReq {
821 total_len: u32,
822 frag_len: u32,
823 buf: Option<MemOpBuf>,
824 },
825 MemRetrieveResp {
826 total_len: u32,
827 frag_len: u32,
828 },
829 MemRelinquish,
830 MemReclaim {
831 handle: memory_management::Handle,
832 flags: u32,
833 },
834 MemPermGet {
835 addr: MemAddr,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100836 page_cnt: Option<u32>,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100837 },
838 MemPermSet {
839 addr: MemAddr,
840 page_cnt: u32,
841 mem_perm: u32,
842 },
843 ConsoleLog {
844 char_cnt: u8,
845 char_lists: ConsoleLogChars,
846 },
Tomás González7ffb6132025-04-03 12:28:58 +0100847 NotificationBitmapCreate {
848 vm_id: u16,
849 vcpu_cnt: u32,
850 },
851 NotificationBitmapDestroy {
852 vm_id: u16,
853 },
854 NotificationBind {
855 sender_id: u16,
856 receiver_id: u16,
857 flags: NotificationBindFlags,
858 bitmap: u64,
859 },
860 NotificationUnBind {
861 sender_id: u16,
862 receiver_id: u16,
863 bitmap: u64,
864 },
865 NotificationSet {
866 sender_id: u16,
867 receiver_id: u16,
868 flags: NotificationSetFlags,
869 bitmap: u64,
870 },
871 NotificationGet {
872 vcpu_id: u16,
873 endpoint_id: u16,
874 flags: NotificationGetFlags,
875 },
876 NotificationInfoGet {
877 is_32bit: bool,
878 },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +0100879 El3IntrHandle,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100880}
881
Balint Dobszayde0dc802025-02-28 14:16:52 +0100882impl Interface {
883 /// Returns the function ID for the call, if it has one.
884 pub fn function_id(&self) -> Option<FuncId> {
885 match self {
886 Interface::Error { .. } => Some(FuncId::Error),
887 Interface::Success { args, .. } => match args {
Imre Kis54773b62025-04-10 13:47:39 +0200888 SuccessArgs::Args32(..) => Some(FuncId::Success32),
889 SuccessArgs::Args64(..) | SuccessArgs::Args64_2(..) => Some(FuncId::Success64),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100890 },
891 Interface::Interrupt { .. } => Some(FuncId::Interrupt),
892 Interface::Version { .. } => Some(FuncId::Version),
893 Interface::VersionOut { .. } => None,
894 Interface::Features { .. } => Some(FuncId::Features),
895 Interface::RxAcquire { .. } => Some(FuncId::RxAcquire),
896 Interface::RxRelease { .. } => Some(FuncId::RxRelease),
897 Interface::RxTxMap { addr, .. } => match addr {
898 RxTxAddr::Addr32 { .. } => Some(FuncId::RxTxMap32),
899 RxTxAddr::Addr64 { .. } => Some(FuncId::RxTxMap64),
900 },
901 Interface::RxTxUnmap { .. } => Some(FuncId::RxTxUnmap),
902 Interface::PartitionInfoGet { .. } => Some(FuncId::PartitionInfoGet),
Tomás González0a058bc2025-03-11 11:20:55 +0000903 Interface::PartitionInfoGetRegs { .. } => Some(FuncId::PartitionInfoGetRegs),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100904 Interface::IdGet => Some(FuncId::IdGet),
905 Interface::SpmIdGet => Some(FuncId::SpmIdGet),
Tomás González092202a2025-03-05 11:56:45 +0000906 Interface::MsgWait { .. } => Some(FuncId::MsgWait),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100907 Interface::Yield => Some(FuncId::Yield),
908 Interface::Run { .. } => Some(FuncId::Run),
909 Interface::NormalWorldResume => Some(FuncId::NormalWorldResume),
Tomás González17b92442025-03-10 16:45:04 +0000910 Interface::SecondaryEpRegister { entrypoint } => match entrypoint {
911 SecondaryEpRegisterAddr::Addr32 { .. } => Some(FuncId::SecondaryEpRegister32),
912 SecondaryEpRegisterAddr::Addr64 { .. } => Some(FuncId::SecondaryEpRegister64),
913 },
Balint Dobszayde0dc802025-02-28 14:16:52 +0100914 Interface::MsgSend2 { .. } => Some(FuncId::MsgSend2),
915 Interface::MsgSendDirectReq { args, .. } => match args {
916 DirectMsgArgs::Args32(_) => Some(FuncId::MsgSendDirectReq32),
917 DirectMsgArgs::Args64(_) => Some(FuncId::MsgSendDirectReq64),
Tomás González4d5b0ba2025-03-03 17:15:55 +0000918 DirectMsgArgs::VersionReq { .. } => Some(FuncId::MsgSendDirectReq32),
919 DirectMsgArgs::PowerPsciReq32 { .. } => Some(FuncId::MsgSendDirectReq32),
920 DirectMsgArgs::PowerPsciReq64 { .. } => Some(FuncId::MsgSendDirectReq64),
921 DirectMsgArgs::PowerWarmBootReq { .. } => Some(FuncId::MsgSendDirectReq32),
922 DirectMsgArgs::VmCreated { .. } => Some(FuncId::MsgSendDirectReq32),
923 DirectMsgArgs::VmDestructed { .. } => Some(FuncId::MsgSendDirectReq32),
924 _ => None,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100925 },
926 Interface::MsgSendDirectResp { args, .. } => match args {
927 DirectMsgArgs::Args32(_) => Some(FuncId::MsgSendDirectResp32),
928 DirectMsgArgs::Args64(_) => Some(FuncId::MsgSendDirectResp64),
Tomás González4d5b0ba2025-03-03 17:15:55 +0000929 DirectMsgArgs::VersionResp { .. } => Some(FuncId::MsgSendDirectResp32),
930 DirectMsgArgs::PowerPsciResp { .. } => Some(FuncId::MsgSendDirectResp32),
931 DirectMsgArgs::VmCreatedAck { .. } => Some(FuncId::MsgSendDirectResp32),
932 DirectMsgArgs::VmDestructedAck { .. } => Some(FuncId::MsgSendDirectResp32),
933 _ => None,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100934 },
935 Interface::MsgSendDirectReq2 { .. } => Some(FuncId::MsgSendDirectReq64_2),
936 Interface::MsgSendDirectResp2 { .. } => Some(FuncId::MsgSendDirectResp64_2),
937 Interface::MemDonate { buf, .. } => match buf {
938 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemDonate64),
939 _ => Some(FuncId::MemDonate32),
940 },
941 Interface::MemLend { buf, .. } => match buf {
942 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemLend64),
943 _ => Some(FuncId::MemLend32),
944 },
945 Interface::MemShare { buf, .. } => match buf {
946 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemShare64),
947 _ => Some(FuncId::MemShare32),
948 },
949 Interface::MemRetrieveReq { buf, .. } => match buf {
950 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemRetrieveReq64),
951 _ => Some(FuncId::MemRetrieveReq32),
952 },
953 Interface::MemRetrieveResp { .. } => Some(FuncId::MemRetrieveResp),
954 Interface::MemRelinquish => Some(FuncId::MemRelinquish),
955 Interface::MemReclaim { .. } => Some(FuncId::MemReclaim),
956 Interface::MemPermGet { addr, .. } => match addr {
957 MemAddr::Addr32(_) => Some(FuncId::MemPermGet32),
958 MemAddr::Addr64(_) => Some(FuncId::MemPermGet64),
959 },
960 Interface::MemPermSet { addr, .. } => match addr {
961 MemAddr::Addr32(_) => Some(FuncId::MemPermSet32),
962 MemAddr::Addr64(_) => Some(FuncId::MemPermSet64),
963 },
964 Interface::ConsoleLog { char_lists, .. } => match char_lists {
965 ConsoleLogChars::Reg32(_) => Some(FuncId::ConsoleLog32),
966 ConsoleLogChars::Reg64(_) => Some(FuncId::ConsoleLog64),
967 },
Tomás González7ffb6132025-04-03 12:28:58 +0100968 Interface::NotificationBitmapCreate { .. } => Some(FuncId::NotificationBitmapCreate),
969 Interface::NotificationBitmapDestroy { .. } => Some(FuncId::NotificationBitmapDestroy),
970 Interface::NotificationBind { .. } => Some(FuncId::NotificationBind),
971 Interface::NotificationUnBind { .. } => Some(FuncId::NotificationUnbind),
972 Interface::NotificationSet { .. } => Some(FuncId::NotificationSet),
973 Interface::NotificationGet { .. } => Some(FuncId::NotificationGet),
974 Interface::NotificationInfoGet { is_32bit } => match is_32bit {
975 true => Some(FuncId::NotificationInfoGet32),
976 false => Some(FuncId::NotificationInfoGet64),
977 },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +0100978 Interface::El3IntrHandle => Some(FuncId::El3IntrHandle),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100979 }
980 }
Balint Dobszay3aad9572025-01-17 16:54:11 +0100981
Balint Dobszayde0dc802025-02-28 14:16:52 +0100982 /// Returns true if this is a 32-bit call, or false if it is a 64-bit call.
983 pub fn is_32bit(&self) -> bool {
984 // TODO: self should always have a function ID?
985 self.function_id().unwrap().is_32bit()
986 }
987
988 /// Parse interface from register contents. The caller must ensure that the `regs` argument has
989 /// the correct length: 8 registers for FF-A v1.1 and lower, 18 registers for v1.2 and higher.
990 pub fn from_regs(version: Version, regs: &[u64]) -> Result<Self, Error> {
991 let reg_cnt = regs.len();
992
993 let msg = match reg_cnt {
994 8 => {
995 assert!(version <= Version(1, 1));
996 Interface::unpack_regs8(version, regs.try_into().unwrap())?
997 }
998 18 => {
999 assert!(version >= Version(1, 2));
1000 match FuncId::try_from(regs[0] as u32)? {
1001 FuncId::ConsoleLog64
1002 | FuncId::Success64
1003 | FuncId::MsgSendDirectReq64_2
Tomás González0a058bc2025-03-11 11:20:55 +00001004 | FuncId::MsgSendDirectResp64_2
1005 | FuncId::PartitionInfoGetRegs => {
Balint Dobszayde0dc802025-02-28 14:16:52 +01001006 Interface::unpack_regs18(version, regs.try_into().unwrap())?
1007 }
1008 _ => Interface::unpack_regs8(version, regs[..8].try_into().unwrap())?,
1009 }
1010 }
1011 _ => panic!(
1012 "Invalid number of registers ({}) for FF-A version {}",
1013 reg_cnt, version
1014 ),
1015 };
1016
1017 Ok(msg)
1018 }
1019
1020 fn unpack_regs8(version: Version, regs: &[u64; 8]) -> Result<Self, Error> {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001021 let fid = FuncId::try_from(regs[0] as u32)?;
1022
1023 let msg = match fid {
1024 FuncId::Error => Self::Error {
1025 target_info: (regs[1] as u32).into(),
1026 error_code: FfaError::try_from(regs[2] as i32)?,
Balint Dobszayb727aab2025-04-07 10:24:59 +02001027 error_arg: regs[3] as u32,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001028 },
1029 FuncId::Success32 => Self::Success {
1030 target_info: regs[1] as u32,
Imre Kis54773b62025-04-10 13:47:39 +02001031 args: SuccessArgs::Args32([
Balint Dobszay3aad9572025-01-17 16:54:11 +01001032 regs[2] as u32,
1033 regs[3] as u32,
1034 regs[4] as u32,
1035 regs[5] as u32,
1036 regs[6] as u32,
1037 regs[7] as u32,
1038 ]),
1039 },
1040 FuncId::Success64 => Self::Success {
1041 target_info: regs[1] as u32,
Imre Kis54773b62025-04-10 13:47:39 +02001042 args: SuccessArgs::Args64([regs[2], regs[3], regs[4], regs[5], regs[6], regs[7]]),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001043 },
1044 FuncId::Interrupt => Self::Interrupt {
1045 target_info: (regs[1] as u32).into(),
1046 interrupt_id: regs[2] as u32,
1047 },
1048 FuncId::Version => Self::Version {
Tomás González83146af2025-03-04 11:32:41 +00001049 input_version: (regs[1] as u32).try_into()?,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001050 },
1051 FuncId::Features => Self::Features {
Balint Dobszayc31e0b92025-03-03 20:16:56 +01001052 feat_id: (regs[1] as u32).into(),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001053 input_properties: regs[2] as u32,
1054 },
1055 FuncId::RxAcquire => Self::RxAcquire {
1056 vm_id: regs[1] as u16,
1057 },
1058 FuncId::RxRelease => Self::RxRelease {
1059 vm_id: regs[1] as u16,
1060 },
1061 FuncId::RxTxMap32 => {
1062 let addr = RxTxAddr::Addr32 {
1063 rx: regs[2] as u32,
1064 tx: regs[1] as u32,
1065 };
1066 let page_cnt = regs[3] as u32;
1067
1068 Self::RxTxMap { addr, page_cnt }
1069 }
1070 FuncId::RxTxMap64 => {
1071 let addr = RxTxAddr::Addr64 {
1072 rx: regs[2],
1073 tx: regs[1],
1074 };
1075 let page_cnt = regs[3] as u32;
1076
1077 Self::RxTxMap { addr, page_cnt }
1078 }
1079 FuncId::RxTxUnmap => Self::RxTxUnmap { id: regs[1] as u16 },
1080 FuncId::PartitionInfoGet => {
1081 let uuid_words = [
1082 regs[1] as u32,
1083 regs[2] as u32,
1084 regs[3] as u32,
1085 regs[4] as u32,
1086 ];
1087 let mut bytes: [u8; 16] = [0; 16];
1088 for (i, b) in uuid_words.iter().flat_map(|w| w.to_le_bytes()).enumerate() {
1089 bytes[i] = b;
1090 }
1091 Self::PartitionInfoGet {
1092 uuid: Uuid::from_bytes(bytes),
Imre Kise295adb2025-04-10 13:26:28 +02001093 flags: PartitionInfoGetFlags::try_from(regs[5] as u32)?,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001094 }
1095 }
1096 FuncId::IdGet => Self::IdGet,
1097 FuncId::SpmIdGet => Self::SpmIdGet,
Tomás González092202a2025-03-05 11:56:45 +00001098 FuncId::MsgWait => Self::MsgWait {
1099 flags: if version >= Version(1, 2) {
1100 Some(MsgWaitFlags::try_from(regs[2] as u32)?)
1101 } else {
1102 None
1103 },
1104 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001105 FuncId::Yield => Self::Yield,
1106 FuncId::Run => Self::Run {
1107 target_info: (regs[1] as u32).into(),
1108 },
1109 FuncId::NormalWorldResume => Self::NormalWorldResume,
Tomás González17b92442025-03-10 16:45:04 +00001110 FuncId::SecondaryEpRegister32 => Self::SecondaryEpRegister {
1111 entrypoint: SecondaryEpRegisterAddr::Addr32(regs[1] as u32),
1112 },
1113 FuncId::SecondaryEpRegister64 => Self::SecondaryEpRegister {
1114 entrypoint: SecondaryEpRegisterAddr::Addr64(regs[1]),
1115 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001116 FuncId::MsgSend2 => Self::MsgSend2 {
1117 sender_vm_id: regs[1] as u16,
1118 flags: regs[2] as u32,
1119 },
1120 FuncId::MsgSendDirectReq32 => Self::MsgSendDirectReq {
1121 src_id: (regs[1] >> 16) as u16,
1122 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001123 args: if (regs[2] as u32 & DirectMsgArgs::FWK_MSG_BITS) != 0 {
1124 match regs[2] as u32 {
1125 DirectMsgArgs::VERSION_REQ => DirectMsgArgs::VersionReq {
1126 version: Version::try_from(regs[3] as u32)?,
1127 },
1128 DirectMsgArgs::POWER_PSCI_REQ => DirectMsgArgs::PowerPsciReq32 {
Tomás González67f92c72025-03-20 16:50:42 +00001129 params: [
1130 regs[3] as u32,
1131 regs[4] as u32,
1132 regs[5] as u32,
1133 regs[6] as u32,
1134 ],
Tomás González4d5b0ba2025-03-03 17:15:55 +00001135 },
1136 DirectMsgArgs::POWER_WARM_BOOT_REQ => DirectMsgArgs::PowerWarmBootReq {
1137 boot_type: WarmBootType::try_from(regs[3] as u32)?,
1138 },
1139 DirectMsgArgs::VM_CREATED => DirectMsgArgs::VmCreated {
1140 handle: memory_management::Handle::from([
1141 regs[3] as u32,
1142 regs[4] as u32,
1143 ]),
1144 vm_id: regs[5] as u16,
1145 },
1146 DirectMsgArgs::VM_DESTRUCTED => DirectMsgArgs::VmDestructed {
1147 handle: memory_management::Handle::from([
1148 regs[3] as u32,
1149 regs[4] as u32,
1150 ]),
1151 vm_id: regs[5] as u16,
1152 },
1153 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1154 }
1155 } else {
1156 DirectMsgArgs::Args32([
1157 regs[3] as u32,
1158 regs[4] as u32,
1159 regs[5] as u32,
1160 regs[6] as u32,
1161 regs[7] as u32,
1162 ])
1163 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001164 },
1165 FuncId::MsgSendDirectReq64 => Self::MsgSendDirectReq {
1166 src_id: (regs[1] >> 16) as u16,
1167 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001168 args: if (regs[2] & DirectMsgArgs::FWK_MSG_BITS as u64) != 0 {
1169 match regs[2] as u32 {
1170 DirectMsgArgs::POWER_PSCI_REQ => DirectMsgArgs::PowerPsciReq64 {
Tomás González67f92c72025-03-20 16:50:42 +00001171 params: [regs[3], regs[4], regs[5], regs[6]],
Tomás González4d5b0ba2025-03-03 17:15:55 +00001172 },
1173 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1174 }
1175 } else {
1176 DirectMsgArgs::Args64([regs[3], regs[4], regs[5], regs[6], regs[7]])
1177 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001178 },
1179 FuncId::MsgSendDirectResp32 => Self::MsgSendDirectResp {
1180 src_id: (regs[1] >> 16) as u16,
1181 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001182 args: if (regs[2] as u32 & DirectMsgArgs::FWK_MSG_BITS) != 0 {
1183 match regs[2] as u32 {
1184 DirectMsgArgs::VERSION_RESP => {
1185 if regs[3] as i32 == FfaError::NotSupported.into() {
1186 DirectMsgArgs::VersionResp { version: None }
1187 } else {
1188 DirectMsgArgs::VersionResp {
1189 version: Some(Version::try_from(regs[3] as u32)?),
1190 }
1191 }
1192 }
1193 DirectMsgArgs::POWER_PSCI_RESP => DirectMsgArgs::PowerPsciResp {
1194 psci_status: regs[3] as i32,
1195 },
1196 DirectMsgArgs::VM_CREATED_ACK => DirectMsgArgs::VmCreatedAck {
1197 sp_status: (regs[3] as i32).try_into()?,
1198 },
1199 DirectMsgArgs::VM_DESTRUCTED_ACK => DirectMsgArgs::VmDestructedAck {
1200 sp_status: (regs[3] as i32).try_into()?,
1201 },
1202 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1203 }
1204 } else {
1205 DirectMsgArgs::Args32([
1206 regs[3] as u32,
1207 regs[4] as u32,
1208 regs[5] as u32,
1209 regs[6] as u32,
1210 regs[7] as u32,
1211 ])
1212 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001213 },
1214 FuncId::MsgSendDirectResp64 => Self::MsgSendDirectResp {
1215 src_id: (regs[1] >> 16) as u16,
1216 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001217 args: if (regs[2] & DirectMsgArgs::FWK_MSG_BITS as u64) != 0 {
1218 return Err(Error::UnrecognisedFwkMsg(regs[2] as u32));
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::MemDonate32 => Self::MemDonate {
1224 total_len: regs[1] as u32,
1225 frag_len: regs[2] as u32,
1226 buf: if regs[3] != 0 && regs[4] != 0 {
1227 Some(MemOpBuf::Buf32 {
1228 addr: regs[3] as u32,
1229 page_cnt: regs[4] as u32,
1230 })
1231 } else {
1232 None
1233 },
1234 },
1235 FuncId::MemDonate64 => Self::MemDonate {
1236 total_len: regs[1] as u32,
1237 frag_len: regs[2] as u32,
1238 buf: if regs[3] != 0 && regs[4] != 0 {
1239 Some(MemOpBuf::Buf64 {
1240 addr: regs[3],
1241 page_cnt: regs[4] as u32,
1242 })
1243 } else {
1244 None
1245 },
1246 },
1247 FuncId::MemLend32 => Self::MemLend {
1248 total_len: regs[1] as u32,
1249 frag_len: regs[2] as u32,
1250 buf: if regs[3] != 0 && regs[4] != 0 {
1251 Some(MemOpBuf::Buf32 {
1252 addr: regs[3] as u32,
1253 page_cnt: regs[4] as u32,
1254 })
1255 } else {
1256 None
1257 },
1258 },
1259 FuncId::MemLend64 => Self::MemLend {
1260 total_len: regs[1] as u32,
1261 frag_len: regs[2] as u32,
1262 buf: if regs[3] != 0 && regs[4] != 0 {
1263 Some(MemOpBuf::Buf64 {
1264 addr: regs[3],
1265 page_cnt: regs[4] as u32,
1266 })
1267 } else {
1268 None
1269 },
1270 },
1271 FuncId::MemShare32 => Self::MemShare {
1272 total_len: regs[1] as u32,
1273 frag_len: regs[2] as u32,
1274 buf: if regs[3] != 0 && regs[4] != 0 {
1275 Some(MemOpBuf::Buf32 {
1276 addr: regs[3] as u32,
1277 page_cnt: regs[4] as u32,
1278 })
1279 } else {
1280 None
1281 },
1282 },
1283 FuncId::MemShare64 => Self::MemShare {
1284 total_len: regs[1] as u32,
1285 frag_len: regs[2] as u32,
1286 buf: if regs[3] != 0 && regs[4] != 0 {
1287 Some(MemOpBuf::Buf64 {
1288 addr: regs[3],
1289 page_cnt: regs[4] as u32,
1290 })
1291 } else {
1292 None
1293 },
1294 },
1295 FuncId::MemRetrieveReq32 => Self::MemRetrieveReq {
1296 total_len: regs[1] as u32,
1297 frag_len: regs[2] as u32,
1298 buf: if regs[3] != 0 && regs[4] != 0 {
1299 Some(MemOpBuf::Buf32 {
1300 addr: regs[3] as u32,
1301 page_cnt: regs[4] as u32,
1302 })
1303 } else {
1304 None
1305 },
1306 },
1307 FuncId::MemRetrieveReq64 => Self::MemRetrieveReq {
1308 total_len: regs[1] as u32,
1309 frag_len: regs[2] as u32,
1310 buf: if regs[3] != 0 && regs[4] != 0 {
1311 Some(MemOpBuf::Buf64 {
1312 addr: regs[3],
1313 page_cnt: regs[4] as u32,
1314 })
1315 } else {
1316 None
1317 },
1318 },
1319 FuncId::MemRetrieveResp => Self::MemRetrieveResp {
1320 total_len: regs[1] as u32,
1321 frag_len: regs[2] as u32,
1322 },
1323 FuncId::MemRelinquish => Self::MemRelinquish,
1324 FuncId::MemReclaim => Self::MemReclaim {
1325 handle: memory_management::Handle::from([regs[1] as u32, regs[2] as u32]),
1326 flags: regs[3] as u32,
1327 },
1328 FuncId::MemPermGet32 => Self::MemPermGet {
1329 addr: MemAddr::Addr32(regs[1] as u32),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001330 page_cnt: if version >= Version(1, 3) {
1331 Some(regs[2] as u32)
1332 } else {
1333 None
1334 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001335 },
1336 FuncId::MemPermGet64 => Self::MemPermGet {
1337 addr: MemAddr::Addr64(regs[1]),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001338 page_cnt: if version >= Version(1, 3) {
1339 Some(regs[2] as u32)
1340 } else {
1341 None
1342 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001343 },
1344 FuncId::MemPermSet32 => Self::MemPermSet {
1345 addr: MemAddr::Addr32(regs[1] as u32),
1346 page_cnt: regs[2] as u32,
1347 mem_perm: regs[3] as u32,
1348 },
1349 FuncId::MemPermSet64 => Self::MemPermSet {
1350 addr: MemAddr::Addr64(regs[1]),
1351 page_cnt: regs[2] as u32,
1352 mem_perm: regs[3] as u32,
1353 },
1354 FuncId::ConsoleLog32 => Self::ConsoleLog {
1355 char_cnt: regs[1] as u8,
1356 char_lists: ConsoleLogChars::Reg32([
1357 regs[2] as u32,
1358 regs[3] as u32,
1359 regs[4] as u32,
1360 regs[5] as u32,
1361 regs[6] as u32,
1362 regs[7] as u32,
1363 ]),
1364 },
Tomás González7ffb6132025-04-03 12:28:58 +01001365 FuncId::NotificationBitmapCreate => {
1366 let tentative_vm_id = regs[1] as u32;
1367 if (tentative_vm_id >> 16) != 0 {
1368 return Err(Error::InvalidVmId(tentative_vm_id));
1369 }
1370 Self::NotificationBitmapCreate {
1371 vm_id: tentative_vm_id as u16,
1372 vcpu_cnt: regs[2] as u32,
1373 }
1374 }
1375 FuncId::NotificationBitmapDestroy => {
1376 let tentative_vm_id = regs[1] as u32;
1377 if (tentative_vm_id >> 16) != 0 {
1378 return Err(Error::InvalidVmId(tentative_vm_id));
1379 }
1380 Self::NotificationBitmapDestroy {
1381 vm_id: tentative_vm_id as u16,
1382 }
1383 }
1384 FuncId::NotificationBind => Self::NotificationBind {
1385 sender_id: (regs[1] >> 16) as u16,
1386 receiver_id: regs[1] as u16,
1387 flags: (regs[2] as u32).into(),
1388 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1389 },
1390 FuncId::NotificationUnbind => Self::NotificationUnBind {
1391 sender_id: (regs[1] >> 16) as u16,
1392 receiver_id: regs[1] as u16,
1393 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1394 },
1395 FuncId::NotificationSet => Self::NotificationSet {
1396 sender_id: (regs[1] >> 16) as u16,
1397 receiver_id: regs[1] as u16,
1398 flags: (regs[2] as u32).try_into()?,
1399 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1400 },
1401 FuncId::NotificationGet => Self::NotificationGet {
1402 vcpu_id: (regs[1] >> 16) as u16,
1403 endpoint_id: regs[1] as u16,
1404 flags: (regs[2] as u32).into(),
1405 },
1406 FuncId::NotificationInfoGet32 => Self::NotificationInfoGet { is_32bit: true },
1407 FuncId::NotificationInfoGet64 => Self::NotificationInfoGet { is_32bit: false },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01001408 FuncId::El3IntrHandle => Self::El3IntrHandle,
Balint Dobszayde0dc802025-02-28 14:16:52 +01001409 _ => panic!("Invalid number of registers (8) for function {:#x?}", fid),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001410 };
1411
1412 Ok(msg)
1413 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01001414
Balint Dobszayde0dc802025-02-28 14:16:52 +01001415 fn unpack_regs18(version: Version, regs: &[u64; 18]) -> Result<Self, Error> {
1416 assert!(version >= Version(1, 2));
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001417
Balint Dobszayde0dc802025-02-28 14:16:52 +01001418 let fid = FuncId::try_from(regs[0] as u32)?;
1419
1420 let msg = match fid {
1421 FuncId::Success64 => Self::Success {
1422 target_info: regs[1] as u32,
Imre Kis54773b62025-04-10 13:47:39 +02001423 args: SuccessArgs::Args64_2(regs[2..18].try_into().unwrap()),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001424 },
1425 FuncId::MsgSendDirectReq64_2 => Self::MsgSendDirectReq2 {
1426 src_id: (regs[1] >> 16) as u16,
1427 dst_id: regs[1] as u16,
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00001428 uuid: Uuid::from_u64_pair(regs[2].swap_bytes(), regs[3].swap_bytes()),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001429 args: DirectMsg2Args(regs[4..18].try_into().unwrap()),
1430 },
1431 FuncId::MsgSendDirectResp64_2 => Self::MsgSendDirectResp2 {
1432 src_id: (regs[1] >> 16) as u16,
1433 dst_id: regs[1] as u16,
1434 args: DirectMsg2Args(regs[4..18].try_into().unwrap()),
1435 },
1436 FuncId::ConsoleLog64 => Self::ConsoleLog {
1437 char_cnt: regs[1] as u8,
1438 char_lists: ConsoleLogChars::Reg64(regs[2..18].try_into().unwrap()),
1439 },
Tomás González0a058bc2025-03-11 11:20:55 +00001440 FuncId::PartitionInfoGetRegs => {
1441 // Bits[15:0]: Start index
1442 let start_index = (regs[3] & 0xffff) as u16;
1443 let info_tag = ((regs[3] >> 16) & 0xffff) as u16;
1444 Self::PartitionInfoGetRegs {
1445 uuid: Uuid::from_u64_pair(regs[1].swap_bytes(), regs[2].swap_bytes()),
1446 start_index,
1447 info_tag: if start_index == 0 && info_tag != 0 {
1448 return Err(Error::InvalidInformationTag(info_tag));
1449 } else {
1450 info_tag
1451 },
1452 }
1453 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001454 _ => panic!("Invalid number of registers (18) for function {:#x?}", fid),
1455 };
1456
1457 Ok(msg)
Balint Dobszay3aad9572025-01-17 16:54:11 +01001458 }
1459
Balint Dobszaya5846852025-02-26 15:38:53 +01001460 /// Create register contents for an interface.
Balint Dobszayde0dc802025-02-28 14:16:52 +01001461 pub fn to_regs(&self, version: Version, regs: &mut [u64]) {
1462 let reg_cnt = regs.len();
1463
1464 match reg_cnt {
1465 8 => {
1466 assert!(version <= Version(1, 1));
Balint Dobszay91bea9b2025-04-09 13:16:06 +02001467 regs.fill(0);
1468
Balint Dobszayde0dc802025-02-28 14:16:52 +01001469 self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
1470 }
1471 18 => {
1472 assert!(version >= Version(1, 2));
Balint Dobszay91bea9b2025-04-09 13:16:06 +02001473 regs.fill(0);
Balint Dobszayde0dc802025-02-28 14:16:52 +01001474
1475 match self {
1476 Interface::ConsoleLog {
1477 char_lists: ConsoleLogChars::Reg64(_),
1478 ..
1479 }
1480 | Interface::Success {
Imre Kis54773b62025-04-10 13:47:39 +02001481 args: SuccessArgs::Args64_2(_),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001482 ..
1483 }
1484 | Interface::MsgSendDirectReq2 { .. }
Tomás González0a058bc2025-03-11 11:20:55 +00001485 | Interface::MsgSendDirectResp2 { .. }
1486 | Interface::PartitionInfoGetRegs { .. } => {
Balint Dobszayde0dc802025-02-28 14:16:52 +01001487 self.pack_regs18(version, regs.try_into().unwrap());
1488 }
1489 _ => {
1490 self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
1491 }
1492 }
1493 }
1494 _ => panic!("Invalid number of registers {}", reg_cnt),
1495 }
1496 }
1497
1498 fn pack_regs8(&self, version: Version, a: &mut [u64; 8]) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001499 if let Some(function_id) = self.function_id() {
1500 a[0] = function_id as u64;
1501 }
1502
1503 match *self {
1504 Interface::Error {
1505 target_info,
1506 error_code,
Balint Dobszayb727aab2025-04-07 10:24:59 +02001507 error_arg,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001508 } => {
1509 a[1] = u32::from(target_info).into();
1510 a[2] = (error_code as u32).into();
Balint Dobszayb727aab2025-04-07 10:24:59 +02001511 a[3] = error_arg.into();
Balint Dobszay3aad9572025-01-17 16:54:11 +01001512 }
1513 Interface::Success { target_info, args } => {
1514 a[1] = target_info.into();
1515 match args {
Imre Kis54773b62025-04-10 13:47:39 +02001516 SuccessArgs::Args32(regs) => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001517 a[2] = regs[0].into();
1518 a[3] = regs[1].into();
1519 a[4] = regs[2].into();
1520 a[5] = regs[3].into();
1521 a[6] = regs[4].into();
1522 a[7] = regs[5].into();
1523 }
Imre Kis54773b62025-04-10 13:47:39 +02001524 SuccessArgs::Args64(regs) => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001525 a[2] = regs[0];
1526 a[3] = regs[1];
1527 a[4] = regs[2];
1528 a[5] = regs[3];
1529 a[6] = regs[4];
1530 a[7] = regs[5];
1531 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001532 _ => panic!("{:#x?} requires 18 registers", args),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001533 }
1534 }
1535 Interface::Interrupt {
1536 target_info,
1537 interrupt_id,
1538 } => {
1539 a[1] = u32::from(target_info).into();
1540 a[2] = interrupt_id.into();
1541 }
1542 Interface::Version { input_version } => {
1543 a[1] = u32::from(input_version).into();
1544 }
1545 Interface::VersionOut { output_version } => {
1546 a[0] = u32::from(output_version).into();
1547 }
1548 Interface::Features {
1549 feat_id,
1550 input_properties,
1551 } => {
1552 a[1] = u32::from(feat_id).into();
1553 a[2] = input_properties.into();
1554 }
1555 Interface::RxAcquire { vm_id } => {
1556 a[1] = vm_id.into();
1557 }
1558 Interface::RxRelease { vm_id } => {
1559 a[1] = vm_id.into();
1560 }
1561 Interface::RxTxMap { addr, page_cnt } => {
1562 match addr {
1563 RxTxAddr::Addr32 { rx, tx } => {
1564 a[1] = tx.into();
1565 a[2] = rx.into();
1566 }
1567 RxTxAddr::Addr64 { rx, tx } => {
1568 a[1] = tx;
1569 a[2] = rx;
1570 }
1571 }
1572 a[3] = page_cnt.into();
1573 }
1574 Interface::RxTxUnmap { id } => {
1575 a[1] = id.into();
1576 }
1577 Interface::PartitionInfoGet { uuid, flags } => {
1578 let bytes = uuid.into_bytes();
1579 a[1] = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]).into();
1580 a[2] = u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]).into();
1581 a[3] = u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]).into();
1582 a[4] = u32::from_le_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]).into();
Imre Kise295adb2025-04-10 13:26:28 +02001583 a[5] = u32::from(flags).into();
Balint Dobszay3aad9572025-01-17 16:54:11 +01001584 }
Tomás González092202a2025-03-05 11:56:45 +00001585 Interface::MsgWait { flags } => {
1586 if version >= Version(1, 2) {
1587 if let Some(flags) = flags {
1588 a[2] = u32::from(flags).into();
1589 }
1590 }
1591 }
1592 Interface::IdGet | Interface::SpmIdGet | Interface::Yield => {}
Balint Dobszay3aad9572025-01-17 16:54:11 +01001593 Interface::Run { target_info } => {
1594 a[1] = u32::from(target_info).into();
1595 }
1596 Interface::NormalWorldResume => {}
Tomás González17b92442025-03-10 16:45:04 +00001597 Interface::SecondaryEpRegister { entrypoint } => match entrypoint {
1598 SecondaryEpRegisterAddr::Addr32(addr) => a[1] = addr as u64,
1599 SecondaryEpRegisterAddr::Addr64(addr) => a[1] = addr,
1600 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001601 Interface::MsgSend2 {
1602 sender_vm_id,
1603 flags,
1604 } => {
1605 a[1] = sender_vm_id.into();
1606 a[2] = flags.into();
1607 }
1608 Interface::MsgSendDirectReq {
1609 src_id,
1610 dst_id,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001611 args,
1612 } => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +01001613 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Balint Dobszay3aad9572025-01-17 16:54:11 +01001614 match args {
1615 DirectMsgArgs::Args32(args) => {
1616 a[3] = args[0].into();
1617 a[4] = args[1].into();
1618 a[5] = args[2].into();
1619 a[6] = args[3].into();
1620 a[7] = args[4].into();
1621 }
1622 DirectMsgArgs::Args64(args) => {
1623 a[3] = args[0];
1624 a[4] = args[1];
1625 a[5] = args[2];
1626 a[6] = args[3];
1627 a[7] = args[4];
1628 }
Tomás González4d5b0ba2025-03-03 17:15:55 +00001629 DirectMsgArgs::VersionReq { version } => {
1630 a[2] = DirectMsgArgs::VERSION_REQ.into();
1631 a[3] = u32::from(version).into();
1632 }
Tomás González67f92c72025-03-20 16:50:42 +00001633 DirectMsgArgs::PowerPsciReq32 { params } => {
Tomás González4d5b0ba2025-03-03 17:15:55 +00001634 a[2] = DirectMsgArgs::POWER_PSCI_REQ.into();
Tomás González67f92c72025-03-20 16:50:42 +00001635 a[3] = params[0].into();
1636 a[4] = params[1].into();
1637 a[5] = params[2].into();
1638 a[6] = params[3].into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00001639 }
Tomás González67f92c72025-03-20 16:50:42 +00001640 DirectMsgArgs::PowerPsciReq64 { params } => {
Tomás González4d5b0ba2025-03-03 17:15:55 +00001641 a[2] = DirectMsgArgs::POWER_PSCI_REQ.into();
Tomás González67f92c72025-03-20 16:50:42 +00001642 a[3] = params[0];
1643 a[4] = params[1];
1644 a[5] = params[2];
1645 a[6] = params[3];
Tomás González4d5b0ba2025-03-03 17:15:55 +00001646 }
1647 DirectMsgArgs::PowerWarmBootReq { boot_type } => {
1648 a[2] = DirectMsgArgs::POWER_WARM_BOOT_REQ.into();
1649 a[3] = u32::from(boot_type).into();
1650 }
1651 DirectMsgArgs::VmCreated { handle, vm_id } => {
1652 a[2] = DirectMsgArgs::VM_CREATED.into();
1653 let handle_regs: [u32; 2] = handle.into();
1654 a[3] = handle_regs[0].into();
1655 a[4] = handle_regs[1].into();
1656 a[5] = vm_id.into();
1657 }
1658 DirectMsgArgs::VmDestructed { handle, vm_id } => {
1659 a[2] = DirectMsgArgs::VM_DESTRUCTED.into();
1660 let handle_regs: [u32; 2] = handle.into();
1661 a[3] = handle_regs[0].into();
1662 a[4] = handle_regs[1].into();
1663 a[5] = vm_id.into();
1664 }
1665 _ => panic!("Malformed MsgSendDirectReq interface"),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001666 }
1667 }
1668 Interface::MsgSendDirectResp {
1669 src_id,
1670 dst_id,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001671 args,
1672 } => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +01001673 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Balint Dobszay3aad9572025-01-17 16:54:11 +01001674 match args {
1675 DirectMsgArgs::Args32(args) => {
1676 a[3] = args[0].into();
1677 a[4] = args[1].into();
1678 a[5] = args[2].into();
1679 a[6] = args[3].into();
1680 a[7] = args[4].into();
1681 }
1682 DirectMsgArgs::Args64(args) => {
1683 a[3] = args[0];
1684 a[4] = args[1];
1685 a[5] = args[2];
1686 a[6] = args[3];
1687 a[7] = args[4];
1688 }
Tomás González4d5b0ba2025-03-03 17:15:55 +00001689 DirectMsgArgs::VersionResp { version } => {
1690 a[2] = DirectMsgArgs::VERSION_RESP.into();
1691 match version {
Tomás González67f92c72025-03-20 16:50:42 +00001692 None => a[3] = (i32::from(FfaError::NotSupported) as u32).into(),
Tomás González4d5b0ba2025-03-03 17:15:55 +00001693 Some(ver) => a[3] = u32::from(ver).into(),
1694 }
1695 }
1696 DirectMsgArgs::PowerPsciResp { psci_status } => {
1697 a[2] = DirectMsgArgs::POWER_PSCI_RESP.into();
1698 a[3] = psci_status as u64;
1699 }
1700 DirectMsgArgs::VmCreatedAck { sp_status } => {
1701 a[2] = DirectMsgArgs::VM_CREATED_ACK.into();
Tomás González67f92c72025-03-20 16:50:42 +00001702 a[3] = (i32::from(sp_status) as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00001703 }
1704 DirectMsgArgs::VmDestructedAck { sp_status } => {
1705 a[2] = DirectMsgArgs::VM_DESTRUCTED_ACK.into();
Tomás González67f92c72025-03-20 16:50:42 +00001706 a[3] = (i32::from(sp_status) as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00001707 }
1708 _ => panic!("Malformed MsgSendDirectResp interface"),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001709 }
1710 }
1711 Interface::MemDonate {
1712 total_len,
1713 frag_len,
1714 buf,
1715 } => {
1716 a[1] = total_len.into();
1717 a[2] = frag_len.into();
1718 (a[3], a[4]) = match buf {
1719 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1720 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1721 None => (0, 0),
1722 };
1723 }
1724 Interface::MemLend {
1725 total_len,
1726 frag_len,
1727 buf,
1728 } => {
1729 a[1] = total_len.into();
1730 a[2] = frag_len.into();
1731 (a[3], a[4]) = match buf {
1732 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1733 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1734 None => (0, 0),
1735 };
1736 }
1737 Interface::MemShare {
1738 total_len,
1739 frag_len,
1740 buf,
1741 } => {
1742 a[1] = total_len.into();
1743 a[2] = frag_len.into();
1744 (a[3], a[4]) = match buf {
1745 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1746 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1747 None => (0, 0),
1748 };
1749 }
1750 Interface::MemRetrieveReq {
1751 total_len,
1752 frag_len,
1753 buf,
1754 } => {
1755 a[1] = total_len.into();
1756 a[2] = frag_len.into();
1757 (a[3], a[4]) = match buf {
1758 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1759 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1760 None => (0, 0),
1761 };
1762 }
1763 Interface::MemRetrieveResp {
1764 total_len,
1765 frag_len,
1766 } => {
1767 a[1] = total_len.into();
1768 a[2] = frag_len.into();
1769 }
1770 Interface::MemRelinquish => {}
1771 Interface::MemReclaim { handle, flags } => {
1772 let handle_regs: [u32; 2] = handle.into();
1773 a[1] = handle_regs[0].into();
1774 a[2] = handle_regs[1].into();
1775 a[3] = flags.into();
1776 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001777 Interface::MemPermGet { addr, page_cnt } => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001778 a[1] = match addr {
1779 MemAddr::Addr32(addr) => addr.into(),
1780 MemAddr::Addr64(addr) => addr,
1781 };
Balint Dobszayde0dc802025-02-28 14:16:52 +01001782 a[2] = if version >= Version(1, 3) {
1783 page_cnt.unwrap().into()
1784 } else {
1785 assert!(page_cnt.is_none());
1786 0
1787 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01001788 }
1789 Interface::MemPermSet {
1790 addr,
1791 page_cnt,
1792 mem_perm,
1793 } => {
1794 a[1] = match addr {
1795 MemAddr::Addr32(addr) => addr.into(),
1796 MemAddr::Addr64(addr) => addr,
1797 };
1798 a[2] = page_cnt.into();
1799 a[3] = mem_perm.into();
1800 }
1801 Interface::ConsoleLog {
1802 char_cnt,
1803 char_lists,
1804 } => {
1805 a[1] = char_cnt.into();
1806 match char_lists {
1807 ConsoleLogChars::Reg32(regs) => {
1808 a[2] = regs[0].into();
1809 a[3] = regs[1].into();
1810 a[4] = regs[2].into();
1811 a[5] = regs[3].into();
1812 a[6] = regs[4].into();
1813 a[7] = regs[5].into();
1814 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001815 _ => panic!("{:#x?} requires 18 registers", char_lists),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001816 }
1817 }
Tomás González7ffb6132025-04-03 12:28:58 +01001818 Interface::NotificationBitmapCreate { vm_id, vcpu_cnt } => {
1819 a[1] = vm_id.into();
1820 a[2] = vcpu_cnt.into();
1821 }
1822 Interface::NotificationBitmapDestroy { vm_id } => {
1823 a[1] = vm_id.into();
1824 }
1825 Interface::NotificationBind {
1826 sender_id,
1827 receiver_id,
1828 flags,
1829 bitmap,
1830 } => {
1831 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
1832 a[2] = u32::from(flags).into();
1833 a[3] = bitmap & 0xffff_ffff;
1834 a[4] = bitmap >> 32;
1835 }
1836 Interface::NotificationUnBind {
1837 sender_id,
1838 receiver_id,
1839 bitmap,
1840 } => {
1841 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
1842 a[3] = bitmap & 0xffff_ffff;
1843 a[4] = bitmap >> 32;
1844 }
1845 Interface::NotificationSet {
1846 sender_id,
1847 receiver_id,
1848 flags,
1849 bitmap,
1850 } => {
1851 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
1852 a[2] = u32::from(flags).into();
1853 a[3] = bitmap & 0xffff_ffff;
1854 a[4] = bitmap >> 32;
1855 }
1856 Interface::NotificationGet {
1857 vcpu_id,
1858 endpoint_id,
1859 flags,
1860 } => {
1861 a[1] = (u64::from(vcpu_id) << 16) | u64::from(endpoint_id);
1862 a[2] = u32::from(flags).into();
1863 }
1864 Interface::NotificationInfoGet { .. } => {}
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01001865 Interface::El3IntrHandle => {}
Balint Dobszayde0dc802025-02-28 14:16:52 +01001866 _ => panic!("{:#x?} requires 18 registers", self),
1867 }
1868 }
1869
1870 fn pack_regs18(&self, version: Version, a: &mut [u64; 18]) {
1871 assert!(version >= Version(1, 2));
1872
Balint Dobszayde0dc802025-02-28 14:16:52 +01001873 if let Some(function_id) = self.function_id() {
1874 a[0] = function_id as u64;
1875 }
1876
1877 match *self {
1878 Interface::Success { target_info, args } => {
1879 a[1] = target_info.into();
1880 match args {
Imre Kis54773b62025-04-10 13:47:39 +02001881 SuccessArgs::Args64_2(regs) => a[2..18].copy_from_slice(&regs[..16]),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001882 _ => panic!("{:#x?} requires 8 registers", args),
1883 }
1884 }
1885 Interface::MsgSendDirectReq2 {
1886 src_id,
1887 dst_id,
1888 uuid,
1889 args,
1890 } => {
1891 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00001892 let (uuid_msb, uuid_lsb) = uuid.as_u64_pair();
1893 (a[2], a[3]) = (uuid_msb.swap_bytes(), uuid_lsb.swap_bytes());
Balint Dobszayde0dc802025-02-28 14:16:52 +01001894 a[4..18].copy_from_slice(&args.0[..14]);
1895 }
1896 Interface::MsgSendDirectResp2 {
1897 src_id,
1898 dst_id,
1899 args,
1900 } => {
1901 a[1] = ((src_id as u64) << 16) | dst_id as u64;
1902 a[2] = 0;
1903 a[3] = 0;
1904 a[4..18].copy_from_slice(&args.0[..14]);
1905 }
1906 Interface::ConsoleLog {
1907 char_cnt,
1908 char_lists,
1909 } => {
1910 a[1] = char_cnt.into();
1911 match char_lists {
1912 ConsoleLogChars::Reg64(regs) => a[2..18].copy_from_slice(&regs[..16]),
1913 _ => panic!("{:#x?} requires 8 registers", char_lists),
1914 }
1915 }
Tomás González0a058bc2025-03-11 11:20:55 +00001916 Interface::PartitionInfoGetRegs {
1917 uuid,
1918 start_index,
1919 info_tag,
1920 } => {
1921 if start_index == 0 && info_tag != 0 {
1922 panic!("Information Tag MBZ if start index is 0: {:#x?}", self);
1923 }
1924 let (uuid_msb, uuid_lsb) = uuid.as_u64_pair();
1925 (a[1], a[2]) = (uuid_msb.swap_bytes(), uuid_lsb.swap_bytes());
1926 a[3] = (u64::from(info_tag) << 16) | u64::from(start_index);
1927 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001928 _ => panic!("{:#x?} requires 8 registers", self),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001929 }
1930 }
1931
Balint Dobszaya5846852025-02-26 15:38:53 +01001932 /// Helper function to create an `FFA_SUCCESS` interface without any arguments.
Balint Dobszay3aad9572025-01-17 16:54:11 +01001933 pub fn success32_noargs() -> Self {
1934 Self::Success {
1935 target_info: 0,
Imre Kis54773b62025-04-10 13:47:39 +02001936 args: SuccessArgs::Args32([0; 6]),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001937 }
1938 }
1939
Tomás González4c8c7d22025-03-10 17:14:57 +00001940 /// Helper function to create an `FFA_SUCCESS` interface without any arguments.
1941 pub fn success64_noargs() -> Self {
1942 Self::Success {
1943 target_info: 0,
Imre Kis54773b62025-04-10 13:47:39 +02001944 args: SuccessArgs::Args64([0; 6]),
Tomás González4c8c7d22025-03-10 17:14:57 +00001945 }
1946 }
1947
Balint Dobszaya5846852025-02-26 15:38:53 +01001948 /// Helper function to create an `FFA_ERROR` interface with an error code.
Balint Dobszay3aad9572025-01-17 16:54:11 +01001949 pub fn error(error_code: FfaError) -> Self {
1950 Self::Error {
1951 target_info: TargetInfo {
1952 endpoint_id: 0,
1953 vcpu_id: 0,
1954 },
1955 error_code,
Balint Dobszayb727aab2025-04-07 10:24:59 +02001956 error_arg: 0,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001957 }
1958 }
1959}
1960
Balint Dobszaya5846852025-02-26 15:38:53 +01001961/// Maximum number of characters transmitted in a single `FFA_CONSOLE_LOG32` message.
1962pub const CONSOLE_LOG_32_MAX_CHAR_CNT: u8 = 24;
Balint Dobszayde0dc802025-02-28 14:16:52 +01001963/// Maximum number of characters transmitted in a single `FFA_CONSOLE_LOG64` message.
1964pub const CONSOLE_LOG_64_MAX_CHAR_CNT: u8 = 128;
Balint Dobszay3aad9572025-01-17 16:54:11 +01001965
Balint Dobszaya5846852025-02-26 15:38:53 +01001966/// Helper function to convert the "Tightly packed list of characters" format used by the
1967/// `FFA_CONSOLE_LOG` interface into a byte slice.
Balint Dobszay3aad9572025-01-17 16:54:11 +01001968pub fn parse_console_log(
1969 char_cnt: u8,
1970 char_lists: &ConsoleLogChars,
1971 log_bytes: &mut [u8],
1972) -> Result<(), FfaError> {
1973 match char_lists {
1974 ConsoleLogChars::Reg32(regs) => {
Balint Dobszaya5846852025-02-26 15:38:53 +01001975 if !(1..=CONSOLE_LOG_32_MAX_CHAR_CNT).contains(&char_cnt) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001976 return Err(FfaError::InvalidParameters);
1977 }
1978 for (i, reg) in regs.iter().enumerate() {
1979 log_bytes[4 * i..4 * (i + 1)].copy_from_slice(&reg.to_le_bytes());
1980 }
1981 }
1982 ConsoleLogChars::Reg64(regs) => {
Balint Dobszaya5846852025-02-26 15:38:53 +01001983 if !(1..=CONSOLE_LOG_64_MAX_CHAR_CNT).contains(&char_cnt) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001984 return Err(FfaError::InvalidParameters);
1985 }
1986 for (i, reg) in regs.iter().enumerate() {
1987 log_bytes[8 * i..8 * (i + 1)].copy_from_slice(&reg.to_le_bytes());
1988 }
1989 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001990 }
1991
Balint Dobszayc8802492025-01-15 18:11:39 +01001992 Ok(())
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001993}
Tomás González0a058bc2025-03-11 11:20:55 +00001994
1995#[cfg(test)]
1996mod tests {
1997 use super::*;
1998
1999 #[test]
2000 fn part_info_get_regs() {
2001 let uuid = Uuid::parse_str("a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8").unwrap();
2002 let uuid_bytes = uuid.as_bytes();
2003 let test_info_tag = 0b1101_1101;
2004 let test_start_index = 0b1101;
2005 let start_index_and_tag = (test_info_tag << 16) | test_start_index;
2006 let version = Version(1, 2);
2007
2008 // From spec:
2009 // Bytes[0...7] of UUID with byte 0 in the low-order bits.
2010 let reg_x1 = (uuid_bytes[7] as u64) << 56
2011 | (uuid_bytes[6] as u64) << 48
2012 | (uuid_bytes[5] as u64) << 40
2013 | (uuid_bytes[4] as u64) << 32
2014 | (uuid_bytes[3] as u64) << 24
2015 | (uuid_bytes[2] as u64) << 16
2016 | (uuid_bytes[1] as u64) << 8
2017 | (uuid_bytes[0] as u64);
2018
2019 // From spec:
2020 // Bytes[8...15] of UUID with byte 8 in the low-order bits.
2021 let reg_x2 = (uuid_bytes[15] as u64) << 56
2022 | (uuid_bytes[14] as u64) << 48
2023 | (uuid_bytes[13] as u64) << 40
2024 | (uuid_bytes[12] as u64) << 32
2025 | (uuid_bytes[11] as u64) << 24
2026 | (uuid_bytes[10] as u64) << 16
2027 | (uuid_bytes[9] as u64) << 8
2028 | (uuid_bytes[8] as u64);
2029
2030 // First, test for wrong tag:
2031 {
2032 let mut regs = [0u64; 18];
2033 regs[0] = FuncId::PartitionInfoGetRegs as u64;
2034 regs[1] = reg_x1;
2035 regs[2] = reg_x2;
2036 regs[3] = test_info_tag << 16;
2037
2038 assert!(Interface::from_regs(version, &regs).is_err_and(
2039 |e| e == Error::InvalidInformationTag(test_info_tag.try_into().unwrap())
2040 ));
2041 }
2042
2043 // Test for regs -> Interface -> regs
2044 {
2045 let mut orig_regs = [0u64; 18];
2046 orig_regs[0] = FuncId::PartitionInfoGetRegs as u64;
2047 orig_regs[1] = reg_x1;
2048 orig_regs[2] = reg_x2;
2049 orig_regs[3] = start_index_and_tag;
2050
2051 let mut test_regs = orig_regs.clone();
2052 let interface = Interface::from_regs(version, &mut test_regs).unwrap();
2053 match &interface {
2054 Interface::PartitionInfoGetRegs {
2055 info_tag,
2056 start_index,
2057 uuid: int_uuid,
2058 } => {
2059 assert_eq!(u64::from(*info_tag), test_info_tag);
2060 assert_eq!(u64::from(*start_index), test_start_index);
2061 assert_eq!(*int_uuid, uuid);
2062 }
2063 _ => panic!("Expecting Interface::PartitionInfoGetRegs!"),
2064 }
2065 test_regs.fill(0);
2066 interface.to_regs(version, &mut test_regs);
2067 assert_eq!(orig_regs, test_regs);
2068 }
2069
2070 // Test for Interface -> regs -> Interface
2071 {
2072 let interface = Interface::PartitionInfoGetRegs {
2073 info_tag: test_info_tag.try_into().unwrap(),
2074 start_index: test_start_index.try_into().unwrap(),
2075 uuid,
2076 };
2077
2078 let mut regs: [u64; 18] = [0; 18];
2079 interface.to_regs(version, &mut regs);
2080
2081 assert_eq!(Some(FuncId::PartitionInfoGetRegs), interface.function_id());
2082 assert_eq!(regs[0], interface.function_id().unwrap() as u64);
2083 assert_eq!(regs[1], reg_x1);
2084 assert_eq!(regs[2], reg_x2);
2085 assert_eq!(regs[3], (test_info_tag << 16) | test_start_index);
2086
2087 assert_eq!(Interface::from_regs(version, &regs).unwrap(), interface);
2088 }
2089 }
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00002090
2091 #[test]
2092 fn msg_send_direct_req2() {
2093 let uuid = Uuid::parse_str("a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8").unwrap();
2094 let uuid_bytes = uuid.as_bytes();
2095
2096 // From spec:
2097 // Bytes[0...7] of UUID with byte 0 in the low-order bits.
2098 let reg_x2 = (uuid_bytes[7] as u64) << 56
2099 | (uuid_bytes[6] as u64) << 48
2100 | (uuid_bytes[5] as u64) << 40
2101 | (uuid_bytes[4] as u64) << 32
2102 | (uuid_bytes[3] as u64) << 24
2103 | (uuid_bytes[2] as u64) << 16
2104 | (uuid_bytes[1] as u64) << 8
2105 | (uuid_bytes[0] as u64);
2106
2107 // From spec:
2108 // Bytes[8...15] of UUID with byte 8 in the low-order bits.
2109 let reg_x3 = (uuid_bytes[15] as u64) << 56
2110 | (uuid_bytes[14] as u64) << 48
2111 | (uuid_bytes[13] as u64) << 40
2112 | (uuid_bytes[12] as u64) << 32
2113 | (uuid_bytes[11] as u64) << 24
2114 | (uuid_bytes[10] as u64) << 16
2115 | (uuid_bytes[9] as u64) << 8
2116 | (uuid_bytes[8] as u64);
2117
2118 let test_sender = 0b1101_1101;
2119 let test_receiver = 0b1101;
2120 let test_sender_receiver = (test_sender << 16) | test_receiver;
2121 let version = Version(1, 2);
2122
2123 // Test for regs -> Interface -> regs
2124 {
2125 let mut orig_regs = [0u64; 18];
2126 orig_regs[0] = FuncId::MsgSendDirectReq64_2 as u64;
2127 orig_regs[1] = test_sender_receiver;
2128 orig_regs[2] = reg_x2;
2129 orig_regs[3] = reg_x3;
2130
2131 let mut test_regs = orig_regs.clone();
2132 let interface = Interface::from_regs(version, &mut test_regs).unwrap();
2133 match &interface {
2134 Interface::MsgSendDirectReq2 {
2135 dst_id,
2136 src_id,
2137 args: _,
2138 uuid: int_uuid,
2139 } => {
2140 assert_eq!(u64::from(*src_id), test_sender);
2141 assert_eq!(u64::from(*dst_id), test_receiver);
2142 assert_eq!(*int_uuid, uuid);
2143 }
2144 _ => panic!("Expecting Interface::MsgSendDirectReq2!"),
2145 }
2146 test_regs.fill(0);
2147 interface.to_regs(version, &mut test_regs);
2148 assert_eq!(orig_regs, test_regs);
2149 }
2150
2151 // Test for Interface -> regs -> Interface
2152 {
2153 let rest_of_regs: [u64; 14] = [0; 14];
2154
2155 let interface = Interface::MsgSendDirectReq2 {
2156 src_id: test_sender.try_into().unwrap(),
2157 dst_id: test_receiver.try_into().unwrap(),
2158 uuid,
2159 args: DirectMsg2Args(rest_of_regs),
2160 };
2161
2162 let mut regs: [u64; 18] = [0; 18];
2163 interface.to_regs(version, &mut regs);
2164
2165 assert_eq!(Some(FuncId::MsgSendDirectReq64_2), interface.function_id());
2166 assert_eq!(regs[0], interface.function_id().unwrap() as u64);
2167 assert_eq!(regs[1], test_sender_receiver);
2168 assert_eq!(regs[2], reg_x2);
2169 assert_eq!(regs[3], reg_x3);
2170 assert_eq!(regs[4], 0);
2171
2172 assert_eq!(Interface::from_regs(version, &regs).unwrap(), interface);
2173 }
2174 }
Tomás González6ccba0a2025-04-09 13:31:29 +01002175
2176 #[test]
2177 fn is_32bit() {
2178 let interface_64 = Interface::MsgSendDirectReq {
2179 src_id: 0,
2180 dst_id: 1,
2181 args: DirectMsgArgs::Args64([0, 0, 0, 0, 0]),
2182 };
2183 assert!(!interface_64.is_32bit());
2184
2185 let interface_32 = Interface::MsgSendDirectReq {
2186 src_id: 0,
2187 dst_id: 1,
2188 args: DirectMsgArgs::Args32([0, 0, 0, 0, 0]),
2189 };
2190 assert!(interface_32.is_32bit());
2191 }
Tomás González0a058bc2025-03-11 11:20:55 +00002192}