blob: b3a8a53bcc9ebe9dae42f380589046fee066975d [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),
Balint Dobszay3aad9572025-01-17 16:54:11 +010050}
51
52impl From<Error> for FfaError {
53 fn from(value: Error) -> Self {
54 match value {
55 Error::UnrecognisedFunctionId(_) | Error::UnrecognisedFeatureId(_) => {
56 Self::NotSupported
57 }
Tomás González0a058bc2025-03-11 11:20:55 +000058 Error::InvalidInformationTag(_) => Self::Retry,
Tomás González4d5b0ba2025-03-03 17:15:55 +000059 Error::UnrecognisedErrorCode(_)
60 | Error::UnrecognisedFwkMsg(_)
61 | Error::InvalidVersion(_)
Tomás González092202a2025-03-05 11:56:45 +000062 | Error::InvalidMsgWaitFlag(_)
Tomás González4d5b0ba2025-03-03 17:15:55 +000063 | Error::UnrecognisedVmAvailabilityStatus(_)
Tomás González7ffb6132025-04-03 12:28:58 +010064 | Error::InvalidNotificationSetFlag(_)
65 | Error::InvalidVmId(_)
Tomás González4d5b0ba2025-03-03 17:15:55 +000066 | Error::UnrecognisedWarmBootType(_) => Self::InvalidParameters,
Balint Dobszay3aad9572025-01-17 16:54:11 +010067 }
68 }
69}
Balint Dobszay5bf492f2024-07-29 17:21:32 +020070
Balint Dobszaya5846852025-02-26 15:38:53 +010071/// 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 +020072#[derive(PartialEq, Clone, Copy)]
73pub enum Instance {
Balint Dobszaya5846852025-02-26 15:38:53 +010074 /// The instance between the SPMC and SPMD.
Balint Dobszay5bf492f2024-07-29 17:21:32 +020075 SecurePhysical,
Balint Dobszaya5846852025-02-26 15:38:53 +010076 /// The instance between the SPMC and a physical SP (contains the SP's endpoint ID).
Balint Dobszay5bf492f2024-07-29 17:21:32 +020077 SecureVirtual(u16),
78}
79
Balint Dobszaya5846852025-02-26 15:38:53 +010080/// Function IDs of the various FF-A interfaces.
Andrew Walbran969b9252024-11-25 15:35:42 +000081#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
Balint Dobszay3aad9572025-01-17 16:54:11 +010082#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedFunctionId))]
Balint Dobszay5bf492f2024-07-29 17:21:32 +020083#[repr(u32)]
84pub enum FuncId {
85 Error = 0x84000060,
86 Success32 = 0x84000061,
87 Success64 = 0xc4000061,
88 Interrupt = 0x84000062,
89 Version = 0x84000063,
90 Features = 0x84000064,
91 RxAcquire = 0x84000084,
92 RxRelease = 0x84000065,
93 RxTxMap32 = 0x84000066,
94 RxTxMap64 = 0xc4000066,
95 RxTxUnmap = 0x84000067,
96 PartitionInfoGet = 0x84000068,
Balint Dobszaye6aa4862025-02-28 16:37:56 +010097 PartitionInfoGetRegs = 0xc400008b,
Balint Dobszay5bf492f2024-07-29 17:21:32 +020098 IdGet = 0x84000069,
99 SpmIdGet = 0x84000085,
Balint Dobszaye6aa4862025-02-28 16:37:56 +0100100 ConsoleLog32 = 0x8400008a,
101 ConsoleLog64 = 0xc400008a,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200102 MsgWait = 0x8400006b,
103 Yield = 0x8400006c,
104 Run = 0x8400006d,
105 NormalWorldResume = 0x8400007c,
106 MsgSend2 = 0x84000086,
107 MsgSendDirectReq32 = 0x8400006f,
108 MsgSendDirectReq64 = 0xc400006f,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100109 MsgSendDirectReq64_2 = 0xc400008d,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200110 MsgSendDirectResp32 = 0x84000070,
111 MsgSendDirectResp64 = 0xc4000070,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100112 MsgSendDirectResp64_2 = 0xc400008e,
Balint Dobszaye6aa4862025-02-28 16:37:56 +0100113 NotificationBitmapCreate = 0x8400007d,
114 NotificationBitmapDestroy = 0x8400007e,
115 NotificationBind = 0x8400007f,
116 NotificationUnbind = 0x84000080,
117 NotificationSet = 0x84000081,
118 NotificationGet = 0x84000082,
119 NotificationInfoGet32 = 0x84000083,
120 NotificationInfoGet64 = 0xc4000083,
121 El3IntrHandle = 0x8400008c,
Tomás González17b92442025-03-10 16:45:04 +0000122 SecondaryEpRegister32 = 0x84000087,
123 SecondaryEpRegister64 = 0xc4000087,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200124 MemDonate32 = 0x84000071,
125 MemDonate64 = 0xc4000071,
126 MemLend32 = 0x84000072,
127 MemLend64 = 0xc4000072,
128 MemShare32 = 0x84000073,
129 MemShare64 = 0xc4000073,
130 MemRetrieveReq32 = 0x84000074,
131 MemRetrieveReq64 = 0xc4000074,
132 MemRetrieveResp = 0x84000075,
133 MemRelinquish = 0x84000076,
134 MemReclaim = 0x84000077,
135 MemPermGet32 = 0x84000088,
136 MemPermGet64 = 0xc4000088,
137 MemPermSet32 = 0x84000089,
138 MemPermSet64 = 0xc4000089,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200139}
140
Balint Dobszayde0dc802025-02-28 14:16:52 +0100141impl FuncId {
142 /// Returns true if this is a 32-bit call, or false if it is a 64-bit call.
143 pub fn is_32bit(&self) -> bool {
144 u32::from(*self) & (1 << 30) != 0
145 }
146}
147
Balint Dobszaya5846852025-02-26 15:38:53 +0100148/// Error status codes used by the `FFA_ERROR` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100149#[derive(Clone, Copy, Debug, Eq, Error, IntoPrimitive, PartialEq, TryFromPrimitive)]
150#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedErrorCode))]
151#[repr(i32)]
152pub enum FfaError {
153 #[error("Not supported")]
154 NotSupported = -1,
155 #[error("Invalid parameters")]
156 InvalidParameters = -2,
157 #[error("No memory")]
158 NoMemory = -3,
159 #[error("Busy")]
160 Busy = -4,
161 #[error("Interrupted")]
162 Interrupted = -5,
163 #[error("Denied")]
164 Denied = -6,
165 #[error("Retry")]
166 Retry = -7,
167 #[error("Aborted")]
168 Aborted = -8,
169 #[error("No data")]
170 NoData = -9,
171}
172
Balint Dobszaya5846852025-02-26 15:38:53 +0100173/// Endpoint ID and vCPU ID pair, used by `FFA_ERROR`, `FFA_INTERRUPT` and `FFA_RUN` interfaces.
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200174#[derive(Debug, Eq, PartialEq, Clone, Copy)]
Balint Dobszay3aad9572025-01-17 16:54:11 +0100175pub struct TargetInfo {
176 pub endpoint_id: u16,
177 pub vcpu_id: u16,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200178}
179
Balint Dobszay3aad9572025-01-17 16:54:11 +0100180impl From<u32> for TargetInfo {
181 fn from(value: u32) -> Self {
182 Self {
183 endpoint_id: (value >> 16) as u16,
184 vcpu_id: value as u16,
185 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200186 }
187}
188
Balint Dobszay3aad9572025-01-17 16:54:11 +0100189impl From<TargetInfo> for u32 {
190 fn from(value: TargetInfo) -> Self {
Balint Dobszaye9a3e762025-02-26 17:29:57 +0100191 ((value.endpoint_id as u32) << 16) | value.vcpu_id as u32
Andrew Walbran0d315812024-11-25 15:36:36 +0000192 }
Balint Dobszay3aad9572025-01-17 16:54:11 +0100193}
Andrew Walbran0d315812024-11-25 15:36:36 +0000194
Balint Dobszaya5846852025-02-26 15:38:53 +0100195/// Arguments for the `FFA_SUCCESS` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100196#[derive(Debug, Eq, PartialEq, Clone, Copy)]
197pub enum SuccessArgs {
198 Result32([u32; 6]),
199 Result64([u64; 6]),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100200 Result64_2([u64; 16]),
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200201}
202
Tomás González17b92442025-03-10 16:45:04 +0000203/// Entrypoint address argument for `FFA_SECONDARY_EP_REGISTER` interface.
204#[derive(Debug, Eq, PartialEq, Clone, Copy)]
205pub enum SecondaryEpRegisterAddr {
206 Addr32(u32),
207 Addr64(u64),
208}
209
Balint Dobszaya5846852025-02-26 15:38:53 +0100210/// Version number of the FF-A implementation, `.0` is the major, `.1` is minor the version.
Balint Dobszayde0dc802025-02-28 14:16:52 +0100211#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord)]
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200212pub struct Version(pub u16, pub u16);
213
Tomás González1f794352025-03-03 16:47:06 +0000214impl Version {
Tomás González83146af2025-03-04 11:32:41 +0000215 // The FF-A spec mandates that bit[31] of a version number must be 0
216 const MBZ_BITS: u32 = 1 << 31;
217
Tomás González1f794352025-03-03 16:47:06 +0000218 /// Returns whether the caller's version (self) is compatible with the callee's version (input
219 /// parameter)
220 pub fn is_compatible_to(&self, callee_version: &Version) -> bool {
221 self.0 == callee_version.0 && self.1 <= callee_version.1
222 }
223}
224
Tomás González83146af2025-03-04 11:32:41 +0000225impl TryFrom<u32> for Version {
226 type Error = Error;
227
228 fn try_from(val: u32) -> Result<Self, Self::Error> {
229 if (val & Self::MBZ_BITS) != 0 {
230 Err(Error::InvalidVersion(val))
231 } else {
232 Ok(Self((val >> 16) as u16, val as u16))
233 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200234 }
235}
236
237impl From<Version> for u32 {
238 fn from(v: Version) -> Self {
Tomás González83146af2025-03-04 11:32:41 +0000239 let v_u32 = ((v.0 as u32) << 16) | v.1 as u32;
240 assert!(v_u32 & Version::MBZ_BITS == 0);
241 v_u32
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200242 }
243}
244
Andrew Walbran19970ba2024-11-25 15:35:00 +0000245impl Display for Version {
246 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
247 write!(f, "{}.{}", self.0, self.1)
248 }
249}
250
251impl Debug for Version {
252 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
253 Display::fmt(self, f)
254 }
255}
256
Balint Dobszaya5846852025-02-26 15:38:53 +0100257/// Feature IDs used by the `FFA_FEATURES` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100258#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
259#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedFeatureId))]
260#[repr(u8)]
261pub enum FeatureId {
262 NotificationPendingInterrupt = 0x1,
263 ScheduleReceiverInterrupt = 0x2,
264 ManagedExitInterrupt = 0x3,
265}
Balint Dobszayc8802492025-01-15 18:11:39 +0100266
Balint Dobszaya5846852025-02-26 15:38:53 +0100267/// Arguments for the `FFA_FEATURES` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100268#[derive(Debug, Eq, PartialEq, Clone, Copy)]
269pub enum Feature {
270 FuncId(FuncId),
271 FeatureId(FeatureId),
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100272 Unknown(u32),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100273}
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200274
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100275impl From<u32> for Feature {
276 fn from(value: u32) -> Self {
277 // Bit[31] is set for all valid FF-A function IDs so we don't have to check it separately
278 if let Ok(func_id) = value.try_into() {
279 Self::FuncId(func_id)
280 } else if let Ok(feat_id) = (value as u8).try_into() {
281 Self::FeatureId(feat_id)
Balint Dobszay3aad9572025-01-17 16:54:11 +0100282 } else {
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100283 Self::Unknown(value)
284 }
Balint Dobszay3aad9572025-01-17 16:54:11 +0100285 }
286}
287
288impl From<Feature> for u32 {
289 fn from(value: Feature) -> Self {
290 match value {
291 Feature::FuncId(func_id) => (1 << 31) | func_id as u32,
292 Feature::FeatureId(feature_id) => feature_id as u32,
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100293 Feature::Unknown(id) => panic!("Unknown feature or function ID {:#x?}", id),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100294 }
295 }
296}
297
Balint Dobszaya5846852025-02-26 15:38:53 +0100298/// RXTX buffer descriptor, used by `FFA_RXTX_MAP`.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100299#[derive(Debug, Eq, PartialEq, Clone, Copy)]
300pub enum RxTxAddr {
301 Addr32 { rx: u32, tx: u32 },
302 Addr64 { rx: u64, tx: u64 },
303}
304
Tomás González4d5b0ba2025-03-03 17:15:55 +0000305/// Composite type for capturing success and error return codes for the VM availability messages.
306///
307/// Error codes are handled by the `FfaError` type. Having a separate type for errors helps using
308/// `Result<(), FfaError>`. If a single type would include both success and error values,
309/// then `Err(FfaError::Success)` would be incomprehensible.
310#[derive(Debug, Eq, PartialEq, Clone, Copy)]
311pub enum VmAvailabilityStatus {
312 Success,
313 Error(FfaError),
314}
315
316impl TryFrom<i32> for VmAvailabilityStatus {
317 type Error = Error;
318 fn try_from(value: i32) -> Result<Self, <Self as TryFrom<i32>>::Error> {
319 Ok(match value {
320 0 => Self::Success,
321 error_code => Self::Error(FfaError::try_from(error_code)?),
322 })
323 }
324}
325
326impl From<VmAvailabilityStatus> for i32 {
327 fn from(value: VmAvailabilityStatus) -> Self {
328 match value {
329 VmAvailabilityStatus::Success => 0,
330 VmAvailabilityStatus::Error(error_code) => error_code.into(),
331 }
332 }
333}
334
335/// Arguments for the Power Warm Boot `FFA_MSG_SEND_DIRECT_REQ` interface.
336#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
337#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedWarmBootType))]
338#[repr(u32)]
339pub enum WarmBootType {
340 ExitFromSuspend = 0,
341 ExitFromLowPower = 1,
342}
343
Balint Dobszaya5846852025-02-26 15:38:53 +0100344/// Arguments for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}` interfaces.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100345#[derive(Debug, Eq, PartialEq, Clone, Copy)]
346pub enum DirectMsgArgs {
347 Args32([u32; 5]),
348 Args64([u64; 5]),
Tomás González4d5b0ba2025-03-03 17:15:55 +0000349 /// Message for forwarding FFA_VERSION call from Normal world to the SPMC
350 VersionReq {
351 version: Version,
352 },
353 /// Response message to forwarded FFA_VERSION call from the Normal world
354 /// Contains the version returned by the SPMC or None
355 VersionResp {
356 version: Option<Version>,
357 },
358 /// Message for a power management operation initiated by a PSCI function
359 PowerPsciReq32 {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000360 // params[i]: Input parameter in w[i] in PSCI function invocation at EL3.
Tomás González67f92c72025-03-20 16:50:42 +0000361 // params[0]: Function ID.
362 params: [u32; 4],
Tomás González4d5b0ba2025-03-03 17:15:55 +0000363 },
364 /// Message for a power management operation initiated by a PSCI function
365 PowerPsciReq64 {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000366 // params[i]: Input parameter in x[i] in PSCI function invocation at EL3.
Tomás González67f92c72025-03-20 16:50:42 +0000367 // params[0]: Function ID.
368 params: [u64; 4],
Tomás González4d5b0ba2025-03-03 17:15:55 +0000369 },
370 /// Message for a warm boot
371 PowerWarmBootReq {
372 boot_type: WarmBootType,
373 },
374 /// Response message to indicate return status of the last power management request message
375 /// Return error code SUCCESS or DENIED as defined in PSCI spec. Caller is left to do the
376 /// parsing of the return status.
377 PowerPsciResp {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000378 psci_status: i32,
379 },
380 /// Message to signal creation of a VM
381 VmCreated {
382 // Globally unique Handle to identify a memory region that contains IMPLEMENTATION DEFINED
383 // information associated with the created VM.
384 // The invalid memory region handle must be specified by the Hypervisor if this field is not
385 // used.
386 handle: memory_management::Handle,
387 vm_id: u16,
388 },
389 /// Message to acknowledge creation of a VM
390 VmCreatedAck {
391 sp_status: VmAvailabilityStatus,
392 },
393 /// Message to signal destruction of a VM
394 VmDestructed {
395 // Globally unique Handle to identify a memory region that contains IMPLEMENTATION DEFINED
396 // information associated with the created VM.
397 // The invalid memory region handle must be specified by the Hypervisor if this field is not
398 // used.
399 handle: memory_management::Handle,
400 vm_id: u16,
401 },
402 /// Message to acknowledge destruction of a VM
403 VmDestructedAck {
404 sp_status: VmAvailabilityStatus,
405 },
406}
407
408impl DirectMsgArgs {
409 // Flags for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}` interfaces.
410
411 const FWK_MSG_BITS: u32 = 1 << 31;
412 const VERSION_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b1000;
413 const VERSION_RESP: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b1001;
414 const POWER_PSCI_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS;
415 const POWER_WARM_BOOT_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0001;
416 const POWER_PSCI_RESP: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0010;
417 const VM_CREATED: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0100;
418 const VM_CREATED_ACK: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0101;
419 const VM_DESTRUCTED: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0110;
420 const VM_DESTRUCTED_ACK: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0111;
Balint Dobszay3aad9572025-01-17 16:54:11 +0100421}
422
Balint Dobszayde0dc802025-02-28 14:16:52 +0100423/// Arguments for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}2` interfaces.
424#[derive(Debug, Eq, PartialEq, Clone, Copy)]
425pub struct DirectMsg2Args([u64; 14]);
426
Tomás González092202a2025-03-05 11:56:45 +0000427#[derive(Debug, Eq, PartialEq, Clone, Copy)]
428pub struct MsgWaitFlags {
429 retain_rx_buffer: bool,
430}
431
432impl MsgWaitFlags {
433 const RETAIN_RX_BUFFER: u32 = 0x01;
434 const MBZ_BITS: u32 = 0xfffe;
435}
436
437impl TryFrom<u32> for MsgWaitFlags {
438 type Error = Error;
439
440 fn try_from(val: u32) -> Result<Self, Self::Error> {
441 if (val & Self::MBZ_BITS) != 0 {
442 Err(Error::InvalidMsgWaitFlag(val))
443 } else {
444 Ok(MsgWaitFlags {
445 retain_rx_buffer: val & Self::RETAIN_RX_BUFFER != 0,
446 })
447 }
448 }
449}
450
451impl From<MsgWaitFlags> for u32 {
452 fn from(flags: MsgWaitFlags) -> Self {
453 let mut bits: u32 = 0;
454 if flags.retain_rx_buffer {
455 bits |= MsgWaitFlags::RETAIN_RX_BUFFER;
456 }
457 bits
458 }
459}
460
Balint Dobszaya5846852025-02-26 15:38:53 +0100461/// Descriptor for a dynamically allocated memory buffer that contains the memory transaction
Tomás Gonzálezf268e322025-03-05 11:18:11 +0000462/// descriptor.
463///
464/// Used by `FFA_MEM_{DONATE,LEND,SHARE,RETRIEVE_REQ}` interfaces, only when the TX buffer is not
465/// used to transmit the transaction descriptor.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100466#[derive(Debug, Eq, PartialEq, Clone, Copy)]
467pub enum MemOpBuf {
468 Buf32 { addr: u32, page_cnt: u32 },
469 Buf64 { addr: u64, page_cnt: u32 },
470}
471
Balint Dobszaya5846852025-02-26 15:38:53 +0100472/// Memory address argument for `FFA_MEM_PERM_{GET,SET}` interfaces.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100473#[derive(Debug, Eq, PartialEq, Clone, Copy)]
474pub enum MemAddr {
475 Addr32(u32),
476 Addr64(u64),
477}
478
Balint Dobszayde0dc802025-02-28 14:16:52 +0100479/// Argument for the `FFA_CONSOLE_LOG` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100480#[derive(Debug, Eq, PartialEq, Clone, Copy)]
481pub enum ConsoleLogChars {
482 Reg32([u32; 6]),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100483 Reg64([u64; 16]),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100484}
485
Tomás González7ffb6132025-04-03 12:28:58 +0100486#[derive(Debug, Eq, PartialEq, Clone, Copy)]
487pub struct NotificationBindFlags {
488 per_vcpu_notification: bool,
489}
490
491impl NotificationBindFlags {
492 const PER_VCPU_NOTIFICATION: u32 = 1;
493}
494
495impl From<NotificationBindFlags> for u32 {
496 fn from(flags: NotificationBindFlags) -> Self {
497 let mut bits: u32 = 0;
498 if flags.per_vcpu_notification {
499 bits |= NotificationBindFlags::PER_VCPU_NOTIFICATION;
500 }
501 bits
502 }
503}
504
505impl From<u32> for NotificationBindFlags {
506 fn from(flags: u32) -> Self {
507 Self {
508 per_vcpu_notification: flags & Self::PER_VCPU_NOTIFICATION != 0,
509 }
510 }
511}
512
513#[derive(Debug, Eq, PartialEq, Clone, Copy)]
514pub struct NotificationSetFlags {
515 delay_schedule_receiver: bool,
516 vcpu_id: Option<u16>,
517}
518
519impl NotificationSetFlags {
520 const PER_VCP_NOTIFICATION: u32 = 1 << 0;
521 const DELAY_SCHEDULE_RECEIVER: u32 = 1 << 1;
522 const VCPU_ID_SHIFT: u32 = 16;
523
524 const MBZ_BITS: u32 = 0xfffc;
525}
526
527impl From<NotificationSetFlags> for u32 {
528 fn from(flags: NotificationSetFlags) -> Self {
529 let mut bits: u32 = 0;
530
531 if flags.delay_schedule_receiver {
532 bits |= NotificationSetFlags::DELAY_SCHEDULE_RECEIVER;
533 }
534 if let Some(vcpu_id) = flags.vcpu_id {
535 bits |= NotificationSetFlags::PER_VCP_NOTIFICATION;
536 bits |= u32::from(vcpu_id) << NotificationSetFlags::VCPU_ID_SHIFT;
537 }
538
539 bits
540 }
541}
542
543impl TryFrom<u32> for NotificationSetFlags {
544 type Error = Error;
545
546 fn try_from(flags: u32) -> Result<Self, Self::Error> {
547 if (flags & Self::MBZ_BITS) != 0 {
548 return Err(Error::InvalidNotificationSetFlag(flags));
549 }
550
551 let tentative_vcpu_id = (flags >> Self::VCPU_ID_SHIFT) as u16;
552
553 let vcpu_id = if (flags & Self::PER_VCP_NOTIFICATION) != 0 {
554 Some(tentative_vcpu_id)
555 } else {
556 if tentative_vcpu_id != 0 {
557 return Err(Error::InvalidNotificationSetFlag(flags));
558 }
559 None
560 };
561
562 Ok(Self {
563 delay_schedule_receiver: (flags & Self::DELAY_SCHEDULE_RECEIVER) != 0,
564 vcpu_id,
565 })
566 }
567}
568
569#[derive(Debug, Eq, PartialEq, Clone, Copy)]
570pub struct NotificationGetFlags {
571 sp_bitmap_id: bool,
572 vm_bitmap_id: bool,
573 spm_bitmap_id: bool,
574 hyp_bitmap_id: bool,
575}
576
577impl NotificationGetFlags {
578 const SP_BITMAP_ID: u32 = 1;
579 const VM_BITMAP_ID: u32 = 1 << 1;
580 const SPM_BITMAP_ID: u32 = 1 << 2;
581 const HYP_BITMAP_ID: u32 = 1 << 3;
582}
583
584impl From<NotificationGetFlags> for u32 {
585 fn from(flags: NotificationGetFlags) -> Self {
586 let mut bits: u32 = 0;
587 if flags.sp_bitmap_id {
588 bits |= NotificationGetFlags::SP_BITMAP_ID;
589 }
590 if flags.vm_bitmap_id {
591 bits |= NotificationGetFlags::VM_BITMAP_ID;
592 }
593 if flags.spm_bitmap_id {
594 bits |= NotificationGetFlags::SPM_BITMAP_ID;
595 }
596 if flags.hyp_bitmap_id {
597 bits |= NotificationGetFlags::HYP_BITMAP_ID;
598 }
599 bits
600 }
601}
602
603impl From<u32> for NotificationGetFlags {
604 // This is a "from" instead of a "try_from" because Reserved Bits are SBZ, *not* MBZ.
605 fn from(flags: u32) -> Self {
606 Self {
607 sp_bitmap_id: (flags & Self::SP_BITMAP_ID) != 0,
608 vm_bitmap_id: (flags & Self::VM_BITMAP_ID) != 0,
609 spm_bitmap_id: (flags & Self::SPM_BITMAP_ID) != 0,
610 hyp_bitmap_id: (flags & Self::HYP_BITMAP_ID) != 0,
611 }
612 }
613}
614
Tomás Gonzálezf268e322025-03-05 11:18:11 +0000615/// FF-A "message types", the terminology used by the spec is "interfaces".
616///
617/// The interfaces are used by FF-A components for communication at an FF-A instance. The spec also
618/// describes the valid FF-A instances and conduits for each interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100619#[derive(Debug, Eq, PartialEq, Clone, Copy)]
620pub enum Interface {
621 Error {
622 target_info: TargetInfo,
623 error_code: FfaError,
624 },
625 Success {
626 target_info: u32,
627 args: SuccessArgs,
628 },
629 Interrupt {
630 target_info: TargetInfo,
631 interrupt_id: u32,
632 },
633 Version {
634 input_version: Version,
635 },
636 VersionOut {
637 output_version: Version,
638 },
639 Features {
640 feat_id: Feature,
641 input_properties: u32,
642 },
643 RxAcquire {
644 vm_id: u16,
645 },
646 RxRelease {
647 vm_id: u16,
648 },
649 RxTxMap {
650 addr: RxTxAddr,
651 page_cnt: u32,
652 },
653 RxTxUnmap {
654 id: u16,
655 },
656 PartitionInfoGet {
657 uuid: Uuid,
658 flags: u32,
659 },
Tomás González0a058bc2025-03-11 11:20:55 +0000660 PartitionInfoGetRegs {
661 uuid: Uuid,
662 start_index: u16,
663 info_tag: u16,
664 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100665 IdGet,
666 SpmIdGet,
Tomás González092202a2025-03-05 11:56:45 +0000667 MsgWait {
668 flags: Option<MsgWaitFlags>,
669 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100670 Yield,
671 Run {
672 target_info: TargetInfo,
673 },
674 NormalWorldResume,
Tomás González17b92442025-03-10 16:45:04 +0000675 SecondaryEpRegister {
676 entrypoint: SecondaryEpRegisterAddr,
677 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100678 MsgSend2 {
679 sender_vm_id: u16,
680 flags: u32,
681 },
682 MsgSendDirectReq {
683 src_id: u16,
684 dst_id: u16,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100685 args: DirectMsgArgs,
686 },
687 MsgSendDirectResp {
688 src_id: u16,
689 dst_id: u16,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100690 args: DirectMsgArgs,
691 },
Balint Dobszayde0dc802025-02-28 14:16:52 +0100692 MsgSendDirectReq2 {
693 src_id: u16,
694 dst_id: u16,
695 uuid: Uuid,
696 args: DirectMsg2Args,
697 },
698 MsgSendDirectResp2 {
699 src_id: u16,
700 dst_id: u16,
701 args: DirectMsg2Args,
702 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100703 MemDonate {
704 total_len: u32,
705 frag_len: u32,
706 buf: Option<MemOpBuf>,
707 },
708 MemLend {
709 total_len: u32,
710 frag_len: u32,
711 buf: Option<MemOpBuf>,
712 },
713 MemShare {
714 total_len: u32,
715 frag_len: u32,
716 buf: Option<MemOpBuf>,
717 },
718 MemRetrieveReq {
719 total_len: u32,
720 frag_len: u32,
721 buf: Option<MemOpBuf>,
722 },
723 MemRetrieveResp {
724 total_len: u32,
725 frag_len: u32,
726 },
727 MemRelinquish,
728 MemReclaim {
729 handle: memory_management::Handle,
730 flags: u32,
731 },
732 MemPermGet {
733 addr: MemAddr,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100734 page_cnt: Option<u32>,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100735 },
736 MemPermSet {
737 addr: MemAddr,
738 page_cnt: u32,
739 mem_perm: u32,
740 },
741 ConsoleLog {
742 char_cnt: u8,
743 char_lists: ConsoleLogChars,
744 },
Tomás González7ffb6132025-04-03 12:28:58 +0100745 NotificationBitmapCreate {
746 vm_id: u16,
747 vcpu_cnt: u32,
748 },
749 NotificationBitmapDestroy {
750 vm_id: u16,
751 },
752 NotificationBind {
753 sender_id: u16,
754 receiver_id: u16,
755 flags: NotificationBindFlags,
756 bitmap: u64,
757 },
758 NotificationUnBind {
759 sender_id: u16,
760 receiver_id: u16,
761 bitmap: u64,
762 },
763 NotificationSet {
764 sender_id: u16,
765 receiver_id: u16,
766 flags: NotificationSetFlags,
767 bitmap: u64,
768 },
769 NotificationGet {
770 vcpu_id: u16,
771 endpoint_id: u16,
772 flags: NotificationGetFlags,
773 },
774 NotificationInfoGet {
775 is_32bit: bool,
776 },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +0100777 El3IntrHandle,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100778}
779
Balint Dobszayde0dc802025-02-28 14:16:52 +0100780impl Interface {
781 /// Returns the function ID for the call, if it has one.
782 pub fn function_id(&self) -> Option<FuncId> {
783 match self {
784 Interface::Error { .. } => Some(FuncId::Error),
785 Interface::Success { args, .. } => match args {
786 SuccessArgs::Result32(..) => Some(FuncId::Success32),
787 SuccessArgs::Result64(..) | SuccessArgs::Result64_2(..) => Some(FuncId::Success64),
788 },
789 Interface::Interrupt { .. } => Some(FuncId::Interrupt),
790 Interface::Version { .. } => Some(FuncId::Version),
791 Interface::VersionOut { .. } => None,
792 Interface::Features { .. } => Some(FuncId::Features),
793 Interface::RxAcquire { .. } => Some(FuncId::RxAcquire),
794 Interface::RxRelease { .. } => Some(FuncId::RxRelease),
795 Interface::RxTxMap { addr, .. } => match addr {
796 RxTxAddr::Addr32 { .. } => Some(FuncId::RxTxMap32),
797 RxTxAddr::Addr64 { .. } => Some(FuncId::RxTxMap64),
798 },
799 Interface::RxTxUnmap { .. } => Some(FuncId::RxTxUnmap),
800 Interface::PartitionInfoGet { .. } => Some(FuncId::PartitionInfoGet),
Tomás González0a058bc2025-03-11 11:20:55 +0000801 Interface::PartitionInfoGetRegs { .. } => Some(FuncId::PartitionInfoGetRegs),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100802 Interface::IdGet => Some(FuncId::IdGet),
803 Interface::SpmIdGet => Some(FuncId::SpmIdGet),
Tomás González092202a2025-03-05 11:56:45 +0000804 Interface::MsgWait { .. } => Some(FuncId::MsgWait),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100805 Interface::Yield => Some(FuncId::Yield),
806 Interface::Run { .. } => Some(FuncId::Run),
807 Interface::NormalWorldResume => Some(FuncId::NormalWorldResume),
Tomás González17b92442025-03-10 16:45:04 +0000808 Interface::SecondaryEpRegister { entrypoint } => match entrypoint {
809 SecondaryEpRegisterAddr::Addr32 { .. } => Some(FuncId::SecondaryEpRegister32),
810 SecondaryEpRegisterAddr::Addr64 { .. } => Some(FuncId::SecondaryEpRegister64),
811 },
Balint Dobszayde0dc802025-02-28 14:16:52 +0100812 Interface::MsgSend2 { .. } => Some(FuncId::MsgSend2),
813 Interface::MsgSendDirectReq { args, .. } => match args {
814 DirectMsgArgs::Args32(_) => Some(FuncId::MsgSendDirectReq32),
815 DirectMsgArgs::Args64(_) => Some(FuncId::MsgSendDirectReq64),
Tomás González4d5b0ba2025-03-03 17:15:55 +0000816 DirectMsgArgs::VersionReq { .. } => Some(FuncId::MsgSendDirectReq32),
817 DirectMsgArgs::PowerPsciReq32 { .. } => Some(FuncId::MsgSendDirectReq32),
818 DirectMsgArgs::PowerPsciReq64 { .. } => Some(FuncId::MsgSendDirectReq64),
819 DirectMsgArgs::PowerWarmBootReq { .. } => Some(FuncId::MsgSendDirectReq32),
820 DirectMsgArgs::VmCreated { .. } => Some(FuncId::MsgSendDirectReq32),
821 DirectMsgArgs::VmDestructed { .. } => Some(FuncId::MsgSendDirectReq32),
822 _ => None,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100823 },
824 Interface::MsgSendDirectResp { args, .. } => match args {
825 DirectMsgArgs::Args32(_) => Some(FuncId::MsgSendDirectResp32),
826 DirectMsgArgs::Args64(_) => Some(FuncId::MsgSendDirectResp64),
Tomás González4d5b0ba2025-03-03 17:15:55 +0000827 DirectMsgArgs::VersionResp { .. } => Some(FuncId::MsgSendDirectResp32),
828 DirectMsgArgs::PowerPsciResp { .. } => Some(FuncId::MsgSendDirectResp32),
829 DirectMsgArgs::VmCreatedAck { .. } => Some(FuncId::MsgSendDirectResp32),
830 DirectMsgArgs::VmDestructedAck { .. } => Some(FuncId::MsgSendDirectResp32),
831 _ => None,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100832 },
833 Interface::MsgSendDirectReq2 { .. } => Some(FuncId::MsgSendDirectReq64_2),
834 Interface::MsgSendDirectResp2 { .. } => Some(FuncId::MsgSendDirectResp64_2),
835 Interface::MemDonate { buf, .. } => match buf {
836 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemDonate64),
837 _ => Some(FuncId::MemDonate32),
838 },
839 Interface::MemLend { buf, .. } => match buf {
840 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemLend64),
841 _ => Some(FuncId::MemLend32),
842 },
843 Interface::MemShare { buf, .. } => match buf {
844 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemShare64),
845 _ => Some(FuncId::MemShare32),
846 },
847 Interface::MemRetrieveReq { buf, .. } => match buf {
848 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemRetrieveReq64),
849 _ => Some(FuncId::MemRetrieveReq32),
850 },
851 Interface::MemRetrieveResp { .. } => Some(FuncId::MemRetrieveResp),
852 Interface::MemRelinquish => Some(FuncId::MemRelinquish),
853 Interface::MemReclaim { .. } => Some(FuncId::MemReclaim),
854 Interface::MemPermGet { addr, .. } => match addr {
855 MemAddr::Addr32(_) => Some(FuncId::MemPermGet32),
856 MemAddr::Addr64(_) => Some(FuncId::MemPermGet64),
857 },
858 Interface::MemPermSet { addr, .. } => match addr {
859 MemAddr::Addr32(_) => Some(FuncId::MemPermSet32),
860 MemAddr::Addr64(_) => Some(FuncId::MemPermSet64),
861 },
862 Interface::ConsoleLog { char_lists, .. } => match char_lists {
863 ConsoleLogChars::Reg32(_) => Some(FuncId::ConsoleLog32),
864 ConsoleLogChars::Reg64(_) => Some(FuncId::ConsoleLog64),
865 },
Tomás González7ffb6132025-04-03 12:28:58 +0100866 Interface::NotificationBitmapCreate { .. } => Some(FuncId::NotificationBitmapCreate),
867 Interface::NotificationBitmapDestroy { .. } => Some(FuncId::NotificationBitmapDestroy),
868 Interface::NotificationBind { .. } => Some(FuncId::NotificationBind),
869 Interface::NotificationUnBind { .. } => Some(FuncId::NotificationUnbind),
870 Interface::NotificationSet { .. } => Some(FuncId::NotificationSet),
871 Interface::NotificationGet { .. } => Some(FuncId::NotificationGet),
872 Interface::NotificationInfoGet { is_32bit } => match is_32bit {
873 true => Some(FuncId::NotificationInfoGet32),
874 false => Some(FuncId::NotificationInfoGet64),
875 },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +0100876 Interface::El3IntrHandle => Some(FuncId::El3IntrHandle),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100877 }
878 }
Balint Dobszay3aad9572025-01-17 16:54:11 +0100879
Balint Dobszayde0dc802025-02-28 14:16:52 +0100880 /// Returns true if this is a 32-bit call, or false if it is a 64-bit call.
881 pub fn is_32bit(&self) -> bool {
882 // TODO: self should always have a function ID?
883 self.function_id().unwrap().is_32bit()
884 }
885
886 /// Parse interface from register contents. The caller must ensure that the `regs` argument has
887 /// the correct length: 8 registers for FF-A v1.1 and lower, 18 registers for v1.2 and higher.
888 pub fn from_regs(version: Version, regs: &[u64]) -> Result<Self, Error> {
889 let reg_cnt = regs.len();
890
891 let msg = match reg_cnt {
892 8 => {
893 assert!(version <= Version(1, 1));
894 Interface::unpack_regs8(version, regs.try_into().unwrap())?
895 }
896 18 => {
897 assert!(version >= Version(1, 2));
898 match FuncId::try_from(regs[0] as u32)? {
899 FuncId::ConsoleLog64
900 | FuncId::Success64
901 | FuncId::MsgSendDirectReq64_2
Tomás González0a058bc2025-03-11 11:20:55 +0000902 | FuncId::MsgSendDirectResp64_2
903 | FuncId::PartitionInfoGetRegs => {
Balint Dobszayde0dc802025-02-28 14:16:52 +0100904 Interface::unpack_regs18(version, regs.try_into().unwrap())?
905 }
906 _ => Interface::unpack_regs8(version, regs[..8].try_into().unwrap())?,
907 }
908 }
909 _ => panic!(
910 "Invalid number of registers ({}) for FF-A version {}",
911 reg_cnt, version
912 ),
913 };
914
915 Ok(msg)
916 }
917
918 fn unpack_regs8(version: Version, regs: &[u64; 8]) -> Result<Self, Error> {
Balint Dobszay3aad9572025-01-17 16:54:11 +0100919 let fid = FuncId::try_from(regs[0] as u32)?;
920
921 let msg = match fid {
922 FuncId::Error => Self::Error {
923 target_info: (regs[1] as u32).into(),
924 error_code: FfaError::try_from(regs[2] as i32)?,
925 },
926 FuncId::Success32 => Self::Success {
927 target_info: regs[1] as u32,
928 args: SuccessArgs::Result32([
929 regs[2] as u32,
930 regs[3] as u32,
931 regs[4] as u32,
932 regs[5] as u32,
933 regs[6] as u32,
934 regs[7] as u32,
935 ]),
936 },
937 FuncId::Success64 => Self::Success {
938 target_info: regs[1] as u32,
939 args: SuccessArgs::Result64([regs[2], regs[3], regs[4], regs[5], regs[6], regs[7]]),
940 },
941 FuncId::Interrupt => Self::Interrupt {
942 target_info: (regs[1] as u32).into(),
943 interrupt_id: regs[2] as u32,
944 },
945 FuncId::Version => Self::Version {
Tomás González83146af2025-03-04 11:32:41 +0000946 input_version: (regs[1] as u32).try_into()?,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100947 },
948 FuncId::Features => Self::Features {
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100949 feat_id: (regs[1] as u32).into(),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100950 input_properties: regs[2] as u32,
951 },
952 FuncId::RxAcquire => Self::RxAcquire {
953 vm_id: regs[1] as u16,
954 },
955 FuncId::RxRelease => Self::RxRelease {
956 vm_id: regs[1] as u16,
957 },
958 FuncId::RxTxMap32 => {
959 let addr = RxTxAddr::Addr32 {
960 rx: regs[2] as u32,
961 tx: regs[1] as u32,
962 };
963 let page_cnt = regs[3] as u32;
964
965 Self::RxTxMap { addr, page_cnt }
966 }
967 FuncId::RxTxMap64 => {
968 let addr = RxTxAddr::Addr64 {
969 rx: regs[2],
970 tx: regs[1],
971 };
972 let page_cnt = regs[3] as u32;
973
974 Self::RxTxMap { addr, page_cnt }
975 }
976 FuncId::RxTxUnmap => Self::RxTxUnmap { id: regs[1] as u16 },
977 FuncId::PartitionInfoGet => {
978 let uuid_words = [
979 regs[1] as u32,
980 regs[2] as u32,
981 regs[3] as u32,
982 regs[4] as u32,
983 ];
984 let mut bytes: [u8; 16] = [0; 16];
985 for (i, b) in uuid_words.iter().flat_map(|w| w.to_le_bytes()).enumerate() {
986 bytes[i] = b;
987 }
988 Self::PartitionInfoGet {
989 uuid: Uuid::from_bytes(bytes),
990 flags: regs[5] as u32,
991 }
992 }
993 FuncId::IdGet => Self::IdGet,
994 FuncId::SpmIdGet => Self::SpmIdGet,
Tomás González092202a2025-03-05 11:56:45 +0000995 FuncId::MsgWait => Self::MsgWait {
996 flags: if version >= Version(1, 2) {
997 Some(MsgWaitFlags::try_from(regs[2] as u32)?)
998 } else {
999 None
1000 },
1001 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001002 FuncId::Yield => Self::Yield,
1003 FuncId::Run => Self::Run {
1004 target_info: (regs[1] as u32).into(),
1005 },
1006 FuncId::NormalWorldResume => Self::NormalWorldResume,
Tomás González17b92442025-03-10 16:45:04 +00001007 FuncId::SecondaryEpRegister32 => Self::SecondaryEpRegister {
1008 entrypoint: SecondaryEpRegisterAddr::Addr32(regs[1] as u32),
1009 },
1010 FuncId::SecondaryEpRegister64 => Self::SecondaryEpRegister {
1011 entrypoint: SecondaryEpRegisterAddr::Addr64(regs[1]),
1012 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001013 FuncId::MsgSend2 => Self::MsgSend2 {
1014 sender_vm_id: regs[1] as u16,
1015 flags: regs[2] as u32,
1016 },
1017 FuncId::MsgSendDirectReq32 => Self::MsgSendDirectReq {
1018 src_id: (regs[1] >> 16) as u16,
1019 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001020 args: if (regs[2] as u32 & DirectMsgArgs::FWK_MSG_BITS) != 0 {
1021 match regs[2] as u32 {
1022 DirectMsgArgs::VERSION_REQ => DirectMsgArgs::VersionReq {
1023 version: Version::try_from(regs[3] as u32)?,
1024 },
1025 DirectMsgArgs::POWER_PSCI_REQ => DirectMsgArgs::PowerPsciReq32 {
Tomás González67f92c72025-03-20 16:50:42 +00001026 params: [
1027 regs[3] as u32,
1028 regs[4] as u32,
1029 regs[5] as u32,
1030 regs[6] as u32,
1031 ],
Tomás González4d5b0ba2025-03-03 17:15:55 +00001032 },
1033 DirectMsgArgs::POWER_WARM_BOOT_REQ => DirectMsgArgs::PowerWarmBootReq {
1034 boot_type: WarmBootType::try_from(regs[3] as u32)?,
1035 },
1036 DirectMsgArgs::VM_CREATED => DirectMsgArgs::VmCreated {
1037 handle: memory_management::Handle::from([
1038 regs[3] as u32,
1039 regs[4] as u32,
1040 ]),
1041 vm_id: regs[5] as u16,
1042 },
1043 DirectMsgArgs::VM_DESTRUCTED => DirectMsgArgs::VmDestructed {
1044 handle: memory_management::Handle::from([
1045 regs[3] as u32,
1046 regs[4] as u32,
1047 ]),
1048 vm_id: regs[5] as u16,
1049 },
1050 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1051 }
1052 } else {
1053 DirectMsgArgs::Args32([
1054 regs[3] as u32,
1055 regs[4] as u32,
1056 regs[5] as u32,
1057 regs[6] as u32,
1058 regs[7] as u32,
1059 ])
1060 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001061 },
1062 FuncId::MsgSendDirectReq64 => Self::MsgSendDirectReq {
1063 src_id: (regs[1] >> 16) as u16,
1064 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001065 args: if (regs[2] & DirectMsgArgs::FWK_MSG_BITS as u64) != 0 {
1066 match regs[2] as u32 {
1067 DirectMsgArgs::POWER_PSCI_REQ => DirectMsgArgs::PowerPsciReq64 {
Tomás González67f92c72025-03-20 16:50:42 +00001068 params: [regs[3], regs[4], regs[5], regs[6]],
Tomás González4d5b0ba2025-03-03 17:15:55 +00001069 },
1070 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1071 }
1072 } else {
1073 DirectMsgArgs::Args64([regs[3], regs[4], regs[5], regs[6], regs[7]])
1074 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001075 },
1076 FuncId::MsgSendDirectResp32 => Self::MsgSendDirectResp {
1077 src_id: (regs[1] >> 16) as u16,
1078 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001079 args: if (regs[2] as u32 & DirectMsgArgs::FWK_MSG_BITS) != 0 {
1080 match regs[2] as u32 {
1081 DirectMsgArgs::VERSION_RESP => {
1082 if regs[3] as i32 == FfaError::NotSupported.into() {
1083 DirectMsgArgs::VersionResp { version: None }
1084 } else {
1085 DirectMsgArgs::VersionResp {
1086 version: Some(Version::try_from(regs[3] as u32)?),
1087 }
1088 }
1089 }
1090 DirectMsgArgs::POWER_PSCI_RESP => DirectMsgArgs::PowerPsciResp {
1091 psci_status: regs[3] as i32,
1092 },
1093 DirectMsgArgs::VM_CREATED_ACK => DirectMsgArgs::VmCreatedAck {
1094 sp_status: (regs[3] as i32).try_into()?,
1095 },
1096 DirectMsgArgs::VM_DESTRUCTED_ACK => DirectMsgArgs::VmDestructedAck {
1097 sp_status: (regs[3] as i32).try_into()?,
1098 },
1099 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1100 }
1101 } else {
1102 DirectMsgArgs::Args32([
1103 regs[3] as u32,
1104 regs[4] as u32,
1105 regs[5] as u32,
1106 regs[6] as u32,
1107 regs[7] as u32,
1108 ])
1109 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001110 },
1111 FuncId::MsgSendDirectResp64 => Self::MsgSendDirectResp {
1112 src_id: (regs[1] >> 16) as u16,
1113 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001114 args: if (regs[2] & DirectMsgArgs::FWK_MSG_BITS as u64) != 0 {
1115 return Err(Error::UnrecognisedFwkMsg(regs[2] as u32));
1116 } else {
1117 DirectMsgArgs::Args64([regs[3], regs[4], regs[5], regs[6], regs[7]])
1118 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001119 },
1120 FuncId::MemDonate32 => Self::MemDonate {
1121 total_len: regs[1] as u32,
1122 frag_len: regs[2] as u32,
1123 buf: if regs[3] != 0 && regs[4] != 0 {
1124 Some(MemOpBuf::Buf32 {
1125 addr: regs[3] as u32,
1126 page_cnt: regs[4] as u32,
1127 })
1128 } else {
1129 None
1130 },
1131 },
1132 FuncId::MemDonate64 => Self::MemDonate {
1133 total_len: regs[1] as u32,
1134 frag_len: regs[2] as u32,
1135 buf: if regs[3] != 0 && regs[4] != 0 {
1136 Some(MemOpBuf::Buf64 {
1137 addr: regs[3],
1138 page_cnt: regs[4] as u32,
1139 })
1140 } else {
1141 None
1142 },
1143 },
1144 FuncId::MemLend32 => Self::MemLend {
1145 total_len: regs[1] as u32,
1146 frag_len: regs[2] as u32,
1147 buf: if regs[3] != 0 && regs[4] != 0 {
1148 Some(MemOpBuf::Buf32 {
1149 addr: regs[3] as u32,
1150 page_cnt: regs[4] as u32,
1151 })
1152 } else {
1153 None
1154 },
1155 },
1156 FuncId::MemLend64 => Self::MemLend {
1157 total_len: regs[1] as u32,
1158 frag_len: regs[2] as u32,
1159 buf: if regs[3] != 0 && regs[4] != 0 {
1160 Some(MemOpBuf::Buf64 {
1161 addr: regs[3],
1162 page_cnt: regs[4] as u32,
1163 })
1164 } else {
1165 None
1166 },
1167 },
1168 FuncId::MemShare32 => Self::MemShare {
1169 total_len: regs[1] as u32,
1170 frag_len: regs[2] as u32,
1171 buf: if regs[3] != 0 && regs[4] != 0 {
1172 Some(MemOpBuf::Buf32 {
1173 addr: regs[3] as u32,
1174 page_cnt: regs[4] as u32,
1175 })
1176 } else {
1177 None
1178 },
1179 },
1180 FuncId::MemShare64 => Self::MemShare {
1181 total_len: regs[1] as u32,
1182 frag_len: regs[2] as u32,
1183 buf: if regs[3] != 0 && regs[4] != 0 {
1184 Some(MemOpBuf::Buf64 {
1185 addr: regs[3],
1186 page_cnt: regs[4] as u32,
1187 })
1188 } else {
1189 None
1190 },
1191 },
1192 FuncId::MemRetrieveReq32 => Self::MemRetrieveReq {
1193 total_len: regs[1] as u32,
1194 frag_len: regs[2] as u32,
1195 buf: if regs[3] != 0 && regs[4] != 0 {
1196 Some(MemOpBuf::Buf32 {
1197 addr: regs[3] as u32,
1198 page_cnt: regs[4] as u32,
1199 })
1200 } else {
1201 None
1202 },
1203 },
1204 FuncId::MemRetrieveReq64 => Self::MemRetrieveReq {
1205 total_len: regs[1] as u32,
1206 frag_len: regs[2] as u32,
1207 buf: if regs[3] != 0 && regs[4] != 0 {
1208 Some(MemOpBuf::Buf64 {
1209 addr: regs[3],
1210 page_cnt: regs[4] as u32,
1211 })
1212 } else {
1213 None
1214 },
1215 },
1216 FuncId::MemRetrieveResp => Self::MemRetrieveResp {
1217 total_len: regs[1] as u32,
1218 frag_len: regs[2] as u32,
1219 },
1220 FuncId::MemRelinquish => Self::MemRelinquish,
1221 FuncId::MemReclaim => Self::MemReclaim {
1222 handle: memory_management::Handle::from([regs[1] as u32, regs[2] as u32]),
1223 flags: regs[3] as u32,
1224 },
1225 FuncId::MemPermGet32 => Self::MemPermGet {
1226 addr: MemAddr::Addr32(regs[1] as u32),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001227 page_cnt: if version >= Version(1, 3) {
1228 Some(regs[2] as u32)
1229 } else {
1230 None
1231 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001232 },
1233 FuncId::MemPermGet64 => Self::MemPermGet {
1234 addr: MemAddr::Addr64(regs[1]),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001235 page_cnt: if version >= Version(1, 3) {
1236 Some(regs[2] as u32)
1237 } else {
1238 None
1239 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001240 },
1241 FuncId::MemPermSet32 => Self::MemPermSet {
1242 addr: MemAddr::Addr32(regs[1] as u32),
1243 page_cnt: regs[2] as u32,
1244 mem_perm: regs[3] as u32,
1245 },
1246 FuncId::MemPermSet64 => Self::MemPermSet {
1247 addr: MemAddr::Addr64(regs[1]),
1248 page_cnt: regs[2] as u32,
1249 mem_perm: regs[3] as u32,
1250 },
1251 FuncId::ConsoleLog32 => Self::ConsoleLog {
1252 char_cnt: regs[1] as u8,
1253 char_lists: ConsoleLogChars::Reg32([
1254 regs[2] as u32,
1255 regs[3] as u32,
1256 regs[4] as u32,
1257 regs[5] as u32,
1258 regs[6] as u32,
1259 regs[7] as u32,
1260 ]),
1261 },
Tomás González7ffb6132025-04-03 12:28:58 +01001262 FuncId::NotificationBitmapCreate => {
1263 let tentative_vm_id = regs[1] as u32;
1264 if (tentative_vm_id >> 16) != 0 {
1265 return Err(Error::InvalidVmId(tentative_vm_id));
1266 }
1267 Self::NotificationBitmapCreate {
1268 vm_id: tentative_vm_id as u16,
1269 vcpu_cnt: regs[2] as u32,
1270 }
1271 }
1272 FuncId::NotificationBitmapDestroy => {
1273 let tentative_vm_id = regs[1] as u32;
1274 if (tentative_vm_id >> 16) != 0 {
1275 return Err(Error::InvalidVmId(tentative_vm_id));
1276 }
1277 Self::NotificationBitmapDestroy {
1278 vm_id: tentative_vm_id as u16,
1279 }
1280 }
1281 FuncId::NotificationBind => Self::NotificationBind {
1282 sender_id: (regs[1] >> 16) as u16,
1283 receiver_id: regs[1] as u16,
1284 flags: (regs[2] as u32).into(),
1285 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1286 },
1287 FuncId::NotificationUnbind => Self::NotificationUnBind {
1288 sender_id: (regs[1] >> 16) as u16,
1289 receiver_id: regs[1] as u16,
1290 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1291 },
1292 FuncId::NotificationSet => Self::NotificationSet {
1293 sender_id: (regs[1] >> 16) as u16,
1294 receiver_id: regs[1] as u16,
1295 flags: (regs[2] as u32).try_into()?,
1296 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1297 },
1298 FuncId::NotificationGet => Self::NotificationGet {
1299 vcpu_id: (regs[1] >> 16) as u16,
1300 endpoint_id: regs[1] as u16,
1301 flags: (regs[2] as u32).into(),
1302 },
1303 FuncId::NotificationInfoGet32 => Self::NotificationInfoGet { is_32bit: true },
1304 FuncId::NotificationInfoGet64 => Self::NotificationInfoGet { is_32bit: false },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01001305 FuncId::El3IntrHandle => Self::El3IntrHandle,
Balint Dobszayde0dc802025-02-28 14:16:52 +01001306 _ => panic!("Invalid number of registers (8) for function {:#x?}", fid),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001307 };
1308
1309 Ok(msg)
1310 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01001311
Balint Dobszayde0dc802025-02-28 14:16:52 +01001312 fn unpack_regs18(version: Version, regs: &[u64; 18]) -> Result<Self, Error> {
1313 assert!(version >= Version(1, 2));
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001314
Balint Dobszayde0dc802025-02-28 14:16:52 +01001315 let fid = FuncId::try_from(regs[0] as u32)?;
1316
1317 let msg = match fid {
1318 FuncId::Success64 => Self::Success {
1319 target_info: regs[1] as u32,
1320 args: SuccessArgs::Result64_2(regs[2..18].try_into().unwrap()),
1321 },
1322 FuncId::MsgSendDirectReq64_2 => Self::MsgSendDirectReq2 {
1323 src_id: (regs[1] >> 16) as u16,
1324 dst_id: regs[1] as u16,
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00001325 uuid: Uuid::from_u64_pair(regs[2].swap_bytes(), regs[3].swap_bytes()),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001326 args: DirectMsg2Args(regs[4..18].try_into().unwrap()),
1327 },
1328 FuncId::MsgSendDirectResp64_2 => Self::MsgSendDirectResp2 {
1329 src_id: (regs[1] >> 16) as u16,
1330 dst_id: regs[1] as u16,
1331 args: DirectMsg2Args(regs[4..18].try_into().unwrap()),
1332 },
1333 FuncId::ConsoleLog64 => Self::ConsoleLog {
1334 char_cnt: regs[1] as u8,
1335 char_lists: ConsoleLogChars::Reg64(regs[2..18].try_into().unwrap()),
1336 },
Tomás González0a058bc2025-03-11 11:20:55 +00001337 FuncId::PartitionInfoGetRegs => {
1338 // Bits[15:0]: Start index
1339 let start_index = (regs[3] & 0xffff) as u16;
1340 let info_tag = ((regs[3] >> 16) & 0xffff) as u16;
1341 Self::PartitionInfoGetRegs {
1342 uuid: Uuid::from_u64_pair(regs[1].swap_bytes(), regs[2].swap_bytes()),
1343 start_index,
1344 info_tag: if start_index == 0 && info_tag != 0 {
1345 return Err(Error::InvalidInformationTag(info_tag));
1346 } else {
1347 info_tag
1348 },
1349 }
1350 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001351 _ => panic!("Invalid number of registers (18) for function {:#x?}", fid),
1352 };
1353
1354 Ok(msg)
Balint Dobszay3aad9572025-01-17 16:54:11 +01001355 }
1356
Balint Dobszaya5846852025-02-26 15:38:53 +01001357 /// Create register contents for an interface.
Balint Dobszayde0dc802025-02-28 14:16:52 +01001358 pub fn to_regs(&self, version: Version, regs: &mut [u64]) {
1359 let reg_cnt = regs.len();
1360
1361 match reg_cnt {
1362 8 => {
1363 assert!(version <= Version(1, 1));
1364 self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
1365 }
1366 18 => {
1367 assert!(version >= Version(1, 2));
1368
1369 match self {
1370 Interface::ConsoleLog {
1371 char_lists: ConsoleLogChars::Reg64(_),
1372 ..
1373 }
1374 | Interface::Success {
1375 args: SuccessArgs::Result64_2(_),
1376 ..
1377 }
1378 | Interface::MsgSendDirectReq2 { .. }
Tomás González0a058bc2025-03-11 11:20:55 +00001379 | Interface::MsgSendDirectResp2 { .. }
1380 | Interface::PartitionInfoGetRegs { .. } => {
Balint Dobszayde0dc802025-02-28 14:16:52 +01001381 self.pack_regs18(version, regs.try_into().unwrap());
1382 }
1383 _ => {
1384 self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
1385 }
1386 }
1387 }
1388 _ => panic!("Invalid number of registers {}", reg_cnt),
1389 }
1390 }
1391
1392 fn pack_regs8(&self, version: Version, a: &mut [u64; 8]) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001393 a.fill(0);
1394 if let Some(function_id) = self.function_id() {
1395 a[0] = function_id as u64;
1396 }
1397
1398 match *self {
1399 Interface::Error {
1400 target_info,
1401 error_code,
1402 } => {
1403 a[1] = u32::from(target_info).into();
1404 a[2] = (error_code as u32).into();
1405 }
1406 Interface::Success { target_info, args } => {
1407 a[1] = target_info.into();
1408 match args {
1409 SuccessArgs::Result32(regs) => {
1410 a[2] = regs[0].into();
1411 a[3] = regs[1].into();
1412 a[4] = regs[2].into();
1413 a[5] = regs[3].into();
1414 a[6] = regs[4].into();
1415 a[7] = regs[5].into();
1416 }
1417 SuccessArgs::Result64(regs) => {
1418 a[2] = regs[0];
1419 a[3] = regs[1];
1420 a[4] = regs[2];
1421 a[5] = regs[3];
1422 a[6] = regs[4];
1423 a[7] = regs[5];
1424 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001425 _ => panic!("{:#x?} requires 18 registers", args),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001426 }
1427 }
1428 Interface::Interrupt {
1429 target_info,
1430 interrupt_id,
1431 } => {
1432 a[1] = u32::from(target_info).into();
1433 a[2] = interrupt_id.into();
1434 }
1435 Interface::Version { input_version } => {
1436 a[1] = u32::from(input_version).into();
1437 }
1438 Interface::VersionOut { output_version } => {
1439 a[0] = u32::from(output_version).into();
1440 }
1441 Interface::Features {
1442 feat_id,
1443 input_properties,
1444 } => {
1445 a[1] = u32::from(feat_id).into();
1446 a[2] = input_properties.into();
1447 }
1448 Interface::RxAcquire { vm_id } => {
1449 a[1] = vm_id.into();
1450 }
1451 Interface::RxRelease { vm_id } => {
1452 a[1] = vm_id.into();
1453 }
1454 Interface::RxTxMap { addr, page_cnt } => {
1455 match addr {
1456 RxTxAddr::Addr32 { rx, tx } => {
1457 a[1] = tx.into();
1458 a[2] = rx.into();
1459 }
1460 RxTxAddr::Addr64 { rx, tx } => {
1461 a[1] = tx;
1462 a[2] = rx;
1463 }
1464 }
1465 a[3] = page_cnt.into();
1466 }
1467 Interface::RxTxUnmap { id } => {
1468 a[1] = id.into();
1469 }
1470 Interface::PartitionInfoGet { uuid, flags } => {
1471 let bytes = uuid.into_bytes();
1472 a[1] = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]).into();
1473 a[2] = u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]).into();
1474 a[3] = u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]).into();
1475 a[4] = u32::from_le_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]).into();
1476 a[5] = flags.into();
1477 }
Tomás González092202a2025-03-05 11:56:45 +00001478 Interface::MsgWait { flags } => {
1479 if version >= Version(1, 2) {
1480 if let Some(flags) = flags {
1481 a[2] = u32::from(flags).into();
1482 }
1483 }
1484 }
1485 Interface::IdGet | Interface::SpmIdGet | Interface::Yield => {}
Balint Dobszay3aad9572025-01-17 16:54:11 +01001486 Interface::Run { target_info } => {
1487 a[1] = u32::from(target_info).into();
1488 }
1489 Interface::NormalWorldResume => {}
Tomás González17b92442025-03-10 16:45:04 +00001490 Interface::SecondaryEpRegister { entrypoint } => match entrypoint {
1491 SecondaryEpRegisterAddr::Addr32(addr) => a[1] = addr as u64,
1492 SecondaryEpRegisterAddr::Addr64(addr) => a[1] = addr,
1493 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001494 Interface::MsgSend2 {
1495 sender_vm_id,
1496 flags,
1497 } => {
1498 a[1] = sender_vm_id.into();
1499 a[2] = flags.into();
1500 }
1501 Interface::MsgSendDirectReq {
1502 src_id,
1503 dst_id,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001504 args,
1505 } => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +01001506 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Balint Dobszay3aad9572025-01-17 16:54:11 +01001507 match args {
1508 DirectMsgArgs::Args32(args) => {
1509 a[3] = args[0].into();
1510 a[4] = args[1].into();
1511 a[5] = args[2].into();
1512 a[6] = args[3].into();
1513 a[7] = args[4].into();
1514 }
1515 DirectMsgArgs::Args64(args) => {
1516 a[3] = args[0];
1517 a[4] = args[1];
1518 a[5] = args[2];
1519 a[6] = args[3];
1520 a[7] = args[4];
1521 }
Tomás González4d5b0ba2025-03-03 17:15:55 +00001522 DirectMsgArgs::VersionReq { version } => {
1523 a[2] = DirectMsgArgs::VERSION_REQ.into();
1524 a[3] = u32::from(version).into();
1525 }
Tomás González67f92c72025-03-20 16:50:42 +00001526 DirectMsgArgs::PowerPsciReq32 { params } => {
Tomás González4d5b0ba2025-03-03 17:15:55 +00001527 a[2] = DirectMsgArgs::POWER_PSCI_REQ.into();
Tomás González67f92c72025-03-20 16:50:42 +00001528 a[3] = params[0].into();
1529 a[4] = params[1].into();
1530 a[5] = params[2].into();
1531 a[6] = params[3].into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00001532 }
Tomás González67f92c72025-03-20 16:50:42 +00001533 DirectMsgArgs::PowerPsciReq64 { params } => {
Tomás González4d5b0ba2025-03-03 17:15:55 +00001534 a[2] = DirectMsgArgs::POWER_PSCI_REQ.into();
Tomás González67f92c72025-03-20 16:50:42 +00001535 a[3] = params[0];
1536 a[4] = params[1];
1537 a[5] = params[2];
1538 a[6] = params[3];
Tomás González4d5b0ba2025-03-03 17:15:55 +00001539 }
1540 DirectMsgArgs::PowerWarmBootReq { boot_type } => {
1541 a[2] = DirectMsgArgs::POWER_WARM_BOOT_REQ.into();
1542 a[3] = u32::from(boot_type).into();
1543 }
1544 DirectMsgArgs::VmCreated { handle, vm_id } => {
1545 a[2] = DirectMsgArgs::VM_CREATED.into();
1546 let handle_regs: [u32; 2] = handle.into();
1547 a[3] = handle_regs[0].into();
1548 a[4] = handle_regs[1].into();
1549 a[5] = vm_id.into();
1550 }
1551 DirectMsgArgs::VmDestructed { handle, vm_id } => {
1552 a[2] = DirectMsgArgs::VM_DESTRUCTED.into();
1553 let handle_regs: [u32; 2] = handle.into();
1554 a[3] = handle_regs[0].into();
1555 a[4] = handle_regs[1].into();
1556 a[5] = vm_id.into();
1557 }
1558 _ => panic!("Malformed MsgSendDirectReq interface"),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001559 }
1560 }
1561 Interface::MsgSendDirectResp {
1562 src_id,
1563 dst_id,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001564 args,
1565 } => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +01001566 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Balint Dobszay3aad9572025-01-17 16:54:11 +01001567 match args {
1568 DirectMsgArgs::Args32(args) => {
1569 a[3] = args[0].into();
1570 a[4] = args[1].into();
1571 a[5] = args[2].into();
1572 a[6] = args[3].into();
1573 a[7] = args[4].into();
1574 }
1575 DirectMsgArgs::Args64(args) => {
1576 a[3] = args[0];
1577 a[4] = args[1];
1578 a[5] = args[2];
1579 a[6] = args[3];
1580 a[7] = args[4];
1581 }
Tomás González4d5b0ba2025-03-03 17:15:55 +00001582 DirectMsgArgs::VersionResp { version } => {
1583 a[2] = DirectMsgArgs::VERSION_RESP.into();
1584 match version {
Tomás González67f92c72025-03-20 16:50:42 +00001585 None => a[3] = (i32::from(FfaError::NotSupported) as u32).into(),
Tomás González4d5b0ba2025-03-03 17:15:55 +00001586 Some(ver) => a[3] = u32::from(ver).into(),
1587 }
1588 }
1589 DirectMsgArgs::PowerPsciResp { psci_status } => {
1590 a[2] = DirectMsgArgs::POWER_PSCI_RESP.into();
1591 a[3] = psci_status as u64;
1592 }
1593 DirectMsgArgs::VmCreatedAck { sp_status } => {
1594 a[2] = DirectMsgArgs::VM_CREATED_ACK.into();
Tomás González67f92c72025-03-20 16:50:42 +00001595 a[3] = (i32::from(sp_status) as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00001596 }
1597 DirectMsgArgs::VmDestructedAck { sp_status } => {
1598 a[2] = DirectMsgArgs::VM_DESTRUCTED_ACK.into();
Tomás González67f92c72025-03-20 16:50:42 +00001599 a[3] = (i32::from(sp_status) as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00001600 }
1601 _ => panic!("Malformed MsgSendDirectResp interface"),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001602 }
1603 }
1604 Interface::MemDonate {
1605 total_len,
1606 frag_len,
1607 buf,
1608 } => {
1609 a[1] = total_len.into();
1610 a[2] = frag_len.into();
1611 (a[3], a[4]) = match buf {
1612 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1613 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1614 None => (0, 0),
1615 };
1616 }
1617 Interface::MemLend {
1618 total_len,
1619 frag_len,
1620 buf,
1621 } => {
1622 a[1] = total_len.into();
1623 a[2] = frag_len.into();
1624 (a[3], a[4]) = match buf {
1625 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1626 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1627 None => (0, 0),
1628 };
1629 }
1630 Interface::MemShare {
1631 total_len,
1632 frag_len,
1633 buf,
1634 } => {
1635 a[1] = total_len.into();
1636 a[2] = frag_len.into();
1637 (a[3], a[4]) = match buf {
1638 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1639 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1640 None => (0, 0),
1641 };
1642 }
1643 Interface::MemRetrieveReq {
1644 total_len,
1645 frag_len,
1646 buf,
1647 } => {
1648 a[1] = total_len.into();
1649 a[2] = frag_len.into();
1650 (a[3], a[4]) = match buf {
1651 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1652 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1653 None => (0, 0),
1654 };
1655 }
1656 Interface::MemRetrieveResp {
1657 total_len,
1658 frag_len,
1659 } => {
1660 a[1] = total_len.into();
1661 a[2] = frag_len.into();
1662 }
1663 Interface::MemRelinquish => {}
1664 Interface::MemReclaim { handle, flags } => {
1665 let handle_regs: [u32; 2] = handle.into();
1666 a[1] = handle_regs[0].into();
1667 a[2] = handle_regs[1].into();
1668 a[3] = flags.into();
1669 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001670 Interface::MemPermGet { addr, page_cnt } => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001671 a[1] = match addr {
1672 MemAddr::Addr32(addr) => addr.into(),
1673 MemAddr::Addr64(addr) => addr,
1674 };
Balint Dobszayde0dc802025-02-28 14:16:52 +01001675 a[2] = if version >= Version(1, 3) {
1676 page_cnt.unwrap().into()
1677 } else {
1678 assert!(page_cnt.is_none());
1679 0
1680 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01001681 }
1682 Interface::MemPermSet {
1683 addr,
1684 page_cnt,
1685 mem_perm,
1686 } => {
1687 a[1] = match addr {
1688 MemAddr::Addr32(addr) => addr.into(),
1689 MemAddr::Addr64(addr) => addr,
1690 };
1691 a[2] = page_cnt.into();
1692 a[3] = mem_perm.into();
1693 }
1694 Interface::ConsoleLog {
1695 char_cnt,
1696 char_lists,
1697 } => {
1698 a[1] = char_cnt.into();
1699 match char_lists {
1700 ConsoleLogChars::Reg32(regs) => {
1701 a[2] = regs[0].into();
1702 a[3] = regs[1].into();
1703 a[4] = regs[2].into();
1704 a[5] = regs[3].into();
1705 a[6] = regs[4].into();
1706 a[7] = regs[5].into();
1707 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001708 _ => panic!("{:#x?} requires 18 registers", char_lists),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001709 }
1710 }
Tomás González7ffb6132025-04-03 12:28:58 +01001711 Interface::NotificationBitmapCreate { vm_id, vcpu_cnt } => {
1712 a[1] = vm_id.into();
1713 a[2] = vcpu_cnt.into();
1714 }
1715 Interface::NotificationBitmapDestroy { vm_id } => {
1716 a[1] = vm_id.into();
1717 }
1718 Interface::NotificationBind {
1719 sender_id,
1720 receiver_id,
1721 flags,
1722 bitmap,
1723 } => {
1724 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
1725 a[2] = u32::from(flags).into();
1726 a[3] = bitmap & 0xffff_ffff;
1727 a[4] = bitmap >> 32;
1728 }
1729 Interface::NotificationUnBind {
1730 sender_id,
1731 receiver_id,
1732 bitmap,
1733 } => {
1734 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
1735 a[3] = bitmap & 0xffff_ffff;
1736 a[4] = bitmap >> 32;
1737 }
1738 Interface::NotificationSet {
1739 sender_id,
1740 receiver_id,
1741 flags,
1742 bitmap,
1743 } => {
1744 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
1745 a[2] = u32::from(flags).into();
1746 a[3] = bitmap & 0xffff_ffff;
1747 a[4] = bitmap >> 32;
1748 }
1749 Interface::NotificationGet {
1750 vcpu_id,
1751 endpoint_id,
1752 flags,
1753 } => {
1754 a[1] = (u64::from(vcpu_id) << 16) | u64::from(endpoint_id);
1755 a[2] = u32::from(flags).into();
1756 }
1757 Interface::NotificationInfoGet { .. } => {}
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01001758 Interface::El3IntrHandle => {}
Balint Dobszayde0dc802025-02-28 14:16:52 +01001759 _ => panic!("{:#x?} requires 18 registers", self),
1760 }
1761 }
1762
1763 fn pack_regs18(&self, version: Version, a: &mut [u64; 18]) {
1764 assert!(version >= Version(1, 2));
1765
1766 a.fill(0);
1767 if let Some(function_id) = self.function_id() {
1768 a[0] = function_id as u64;
1769 }
1770
1771 match *self {
1772 Interface::Success { target_info, args } => {
1773 a[1] = target_info.into();
1774 match args {
1775 SuccessArgs::Result64_2(regs) => a[2..18].copy_from_slice(&regs[..16]),
1776 _ => panic!("{:#x?} requires 8 registers", args),
1777 }
1778 }
1779 Interface::MsgSendDirectReq2 {
1780 src_id,
1781 dst_id,
1782 uuid,
1783 args,
1784 } => {
1785 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00001786 let (uuid_msb, uuid_lsb) = uuid.as_u64_pair();
1787 (a[2], a[3]) = (uuid_msb.swap_bytes(), uuid_lsb.swap_bytes());
Balint Dobszayde0dc802025-02-28 14:16:52 +01001788 a[4..18].copy_from_slice(&args.0[..14]);
1789 }
1790 Interface::MsgSendDirectResp2 {
1791 src_id,
1792 dst_id,
1793 args,
1794 } => {
1795 a[1] = ((src_id as u64) << 16) | dst_id as u64;
1796 a[2] = 0;
1797 a[3] = 0;
1798 a[4..18].copy_from_slice(&args.0[..14]);
1799 }
1800 Interface::ConsoleLog {
1801 char_cnt,
1802 char_lists,
1803 } => {
1804 a[1] = char_cnt.into();
1805 match char_lists {
1806 ConsoleLogChars::Reg64(regs) => a[2..18].copy_from_slice(&regs[..16]),
1807 _ => panic!("{:#x?} requires 8 registers", char_lists),
1808 }
1809 }
Tomás González0a058bc2025-03-11 11:20:55 +00001810 Interface::PartitionInfoGetRegs {
1811 uuid,
1812 start_index,
1813 info_tag,
1814 } => {
1815 if start_index == 0 && info_tag != 0 {
1816 panic!("Information Tag MBZ if start index is 0: {:#x?}", self);
1817 }
1818 let (uuid_msb, uuid_lsb) = uuid.as_u64_pair();
1819 (a[1], a[2]) = (uuid_msb.swap_bytes(), uuid_lsb.swap_bytes());
1820 a[3] = (u64::from(info_tag) << 16) | u64::from(start_index);
1821 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001822 _ => panic!("{:#x?} requires 8 registers", self),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001823 }
1824 }
1825
Balint Dobszaya5846852025-02-26 15:38:53 +01001826 /// Helper function to create an `FFA_SUCCESS` interface without any arguments.
Balint Dobszay3aad9572025-01-17 16:54:11 +01001827 pub fn success32_noargs() -> Self {
1828 Self::Success {
1829 target_info: 0,
1830 args: SuccessArgs::Result32([0, 0, 0, 0, 0, 0]),
1831 }
1832 }
1833
Tomás González4c8c7d22025-03-10 17:14:57 +00001834 /// Helper function to create an `FFA_SUCCESS` interface without any arguments.
1835 pub fn success64_noargs() -> Self {
1836 Self::Success {
1837 target_info: 0,
1838 args: SuccessArgs::Result64([0, 0, 0, 0, 0, 0]),
1839 }
1840 }
1841
Balint Dobszaya5846852025-02-26 15:38:53 +01001842 /// Helper function to create an `FFA_ERROR` interface with an error code.
Balint Dobszay3aad9572025-01-17 16:54:11 +01001843 pub fn error(error_code: FfaError) -> Self {
1844 Self::Error {
1845 target_info: TargetInfo {
1846 endpoint_id: 0,
1847 vcpu_id: 0,
1848 },
1849 error_code,
1850 }
1851 }
1852}
1853
Balint Dobszaya5846852025-02-26 15:38:53 +01001854/// Maximum number of characters transmitted in a single `FFA_CONSOLE_LOG32` message.
1855pub const CONSOLE_LOG_32_MAX_CHAR_CNT: u8 = 24;
Balint Dobszayde0dc802025-02-28 14:16:52 +01001856/// Maximum number of characters transmitted in a single `FFA_CONSOLE_LOG64` message.
1857pub const CONSOLE_LOG_64_MAX_CHAR_CNT: u8 = 128;
Balint Dobszay3aad9572025-01-17 16:54:11 +01001858
Balint Dobszaya5846852025-02-26 15:38:53 +01001859/// Helper function to convert the "Tightly packed list of characters" format used by the
1860/// `FFA_CONSOLE_LOG` interface into a byte slice.
Balint Dobszay3aad9572025-01-17 16:54:11 +01001861pub fn parse_console_log(
1862 char_cnt: u8,
1863 char_lists: &ConsoleLogChars,
1864 log_bytes: &mut [u8],
1865) -> Result<(), FfaError> {
1866 match char_lists {
1867 ConsoleLogChars::Reg32(regs) => {
Balint Dobszaya5846852025-02-26 15:38:53 +01001868 if !(1..=CONSOLE_LOG_32_MAX_CHAR_CNT).contains(&char_cnt) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001869 return Err(FfaError::InvalidParameters);
1870 }
1871 for (i, reg) in regs.iter().enumerate() {
1872 log_bytes[4 * i..4 * (i + 1)].copy_from_slice(&reg.to_le_bytes());
1873 }
1874 }
1875 ConsoleLogChars::Reg64(regs) => {
Balint Dobszaya5846852025-02-26 15:38:53 +01001876 if !(1..=CONSOLE_LOG_64_MAX_CHAR_CNT).contains(&char_cnt) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001877 return Err(FfaError::InvalidParameters);
1878 }
1879 for (i, reg) in regs.iter().enumerate() {
1880 log_bytes[8 * i..8 * (i + 1)].copy_from_slice(&reg.to_le_bytes());
1881 }
1882 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001883 }
1884
Balint Dobszayc8802492025-01-15 18:11:39 +01001885 Ok(())
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001886}
Tomás González0a058bc2025-03-11 11:20:55 +00001887
1888#[cfg(test)]
1889mod tests {
1890 use super::*;
1891
1892 #[test]
1893 fn part_info_get_regs() {
1894 let uuid = Uuid::parse_str("a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8").unwrap();
1895 let uuid_bytes = uuid.as_bytes();
1896 let test_info_tag = 0b1101_1101;
1897 let test_start_index = 0b1101;
1898 let start_index_and_tag = (test_info_tag << 16) | test_start_index;
1899 let version = Version(1, 2);
1900
1901 // From spec:
1902 // Bytes[0...7] of UUID with byte 0 in the low-order bits.
1903 let reg_x1 = (uuid_bytes[7] as u64) << 56
1904 | (uuid_bytes[6] as u64) << 48
1905 | (uuid_bytes[5] as u64) << 40
1906 | (uuid_bytes[4] as u64) << 32
1907 | (uuid_bytes[3] as u64) << 24
1908 | (uuid_bytes[2] as u64) << 16
1909 | (uuid_bytes[1] as u64) << 8
1910 | (uuid_bytes[0] as u64);
1911
1912 // From spec:
1913 // Bytes[8...15] of UUID with byte 8 in the low-order bits.
1914 let reg_x2 = (uuid_bytes[15] as u64) << 56
1915 | (uuid_bytes[14] as u64) << 48
1916 | (uuid_bytes[13] as u64) << 40
1917 | (uuid_bytes[12] as u64) << 32
1918 | (uuid_bytes[11] as u64) << 24
1919 | (uuid_bytes[10] as u64) << 16
1920 | (uuid_bytes[9] as u64) << 8
1921 | (uuid_bytes[8] as u64);
1922
1923 // First, test for wrong tag:
1924 {
1925 let mut regs = [0u64; 18];
1926 regs[0] = FuncId::PartitionInfoGetRegs as u64;
1927 regs[1] = reg_x1;
1928 regs[2] = reg_x2;
1929 regs[3] = test_info_tag << 16;
1930
1931 assert!(Interface::from_regs(version, &regs).is_err_and(
1932 |e| e == Error::InvalidInformationTag(test_info_tag.try_into().unwrap())
1933 ));
1934 }
1935
1936 // Test for regs -> Interface -> regs
1937 {
1938 let mut orig_regs = [0u64; 18];
1939 orig_regs[0] = FuncId::PartitionInfoGetRegs as u64;
1940 orig_regs[1] = reg_x1;
1941 orig_regs[2] = reg_x2;
1942 orig_regs[3] = start_index_and_tag;
1943
1944 let mut test_regs = orig_regs.clone();
1945 let interface = Interface::from_regs(version, &mut test_regs).unwrap();
1946 match &interface {
1947 Interface::PartitionInfoGetRegs {
1948 info_tag,
1949 start_index,
1950 uuid: int_uuid,
1951 } => {
1952 assert_eq!(u64::from(*info_tag), test_info_tag);
1953 assert_eq!(u64::from(*start_index), test_start_index);
1954 assert_eq!(*int_uuid, uuid);
1955 }
1956 _ => panic!("Expecting Interface::PartitionInfoGetRegs!"),
1957 }
1958 test_regs.fill(0);
1959 interface.to_regs(version, &mut test_regs);
1960 assert_eq!(orig_regs, test_regs);
1961 }
1962
1963 // Test for Interface -> regs -> Interface
1964 {
1965 let interface = Interface::PartitionInfoGetRegs {
1966 info_tag: test_info_tag.try_into().unwrap(),
1967 start_index: test_start_index.try_into().unwrap(),
1968 uuid,
1969 };
1970
1971 let mut regs: [u64; 18] = [0; 18];
1972 interface.to_regs(version, &mut regs);
1973
1974 assert_eq!(Some(FuncId::PartitionInfoGetRegs), interface.function_id());
1975 assert_eq!(regs[0], interface.function_id().unwrap() as u64);
1976 assert_eq!(regs[1], reg_x1);
1977 assert_eq!(regs[2], reg_x2);
1978 assert_eq!(regs[3], (test_info_tag << 16) | test_start_index);
1979
1980 assert_eq!(Interface::from_regs(version, &regs).unwrap(), interface);
1981 }
1982 }
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00001983
1984 #[test]
1985 fn msg_send_direct_req2() {
1986 let uuid = Uuid::parse_str("a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8").unwrap();
1987 let uuid_bytes = uuid.as_bytes();
1988
1989 // From spec:
1990 // Bytes[0...7] of UUID with byte 0 in the low-order bits.
1991 let reg_x2 = (uuid_bytes[7] as u64) << 56
1992 | (uuid_bytes[6] as u64) << 48
1993 | (uuid_bytes[5] as u64) << 40
1994 | (uuid_bytes[4] as u64) << 32
1995 | (uuid_bytes[3] as u64) << 24
1996 | (uuid_bytes[2] as u64) << 16
1997 | (uuid_bytes[1] as u64) << 8
1998 | (uuid_bytes[0] as u64);
1999
2000 // From spec:
2001 // Bytes[8...15] of UUID with byte 8 in the low-order bits.
2002 let reg_x3 = (uuid_bytes[15] as u64) << 56
2003 | (uuid_bytes[14] as u64) << 48
2004 | (uuid_bytes[13] as u64) << 40
2005 | (uuid_bytes[12] as u64) << 32
2006 | (uuid_bytes[11] as u64) << 24
2007 | (uuid_bytes[10] as u64) << 16
2008 | (uuid_bytes[9] as u64) << 8
2009 | (uuid_bytes[8] as u64);
2010
2011 let test_sender = 0b1101_1101;
2012 let test_receiver = 0b1101;
2013 let test_sender_receiver = (test_sender << 16) | test_receiver;
2014 let version = Version(1, 2);
2015
2016 // Test for regs -> Interface -> regs
2017 {
2018 let mut orig_regs = [0u64; 18];
2019 orig_regs[0] = FuncId::MsgSendDirectReq64_2 as u64;
2020 orig_regs[1] = test_sender_receiver;
2021 orig_regs[2] = reg_x2;
2022 orig_regs[3] = reg_x3;
2023
2024 let mut test_regs = orig_regs.clone();
2025 let interface = Interface::from_regs(version, &mut test_regs).unwrap();
2026 match &interface {
2027 Interface::MsgSendDirectReq2 {
2028 dst_id,
2029 src_id,
2030 args: _,
2031 uuid: int_uuid,
2032 } => {
2033 assert_eq!(u64::from(*src_id), test_sender);
2034 assert_eq!(u64::from(*dst_id), test_receiver);
2035 assert_eq!(*int_uuid, uuid);
2036 }
2037 _ => panic!("Expecting Interface::MsgSendDirectReq2!"),
2038 }
2039 test_regs.fill(0);
2040 interface.to_regs(version, &mut test_regs);
2041 assert_eq!(orig_regs, test_regs);
2042 }
2043
2044 // Test for Interface -> regs -> Interface
2045 {
2046 let rest_of_regs: [u64; 14] = [0; 14];
2047
2048 let interface = Interface::MsgSendDirectReq2 {
2049 src_id: test_sender.try_into().unwrap(),
2050 dst_id: test_receiver.try_into().unwrap(),
2051 uuid,
2052 args: DirectMsg2Args(rest_of_regs),
2053 };
2054
2055 let mut regs: [u64; 18] = [0; 18];
2056 interface.to_regs(version, &mut regs);
2057
2058 assert_eq!(Some(FuncId::MsgSendDirectReq64_2), interface.function_id());
2059 assert_eq!(regs[0], interface.function_id().unwrap() as u64);
2060 assert_eq!(regs[1], test_sender_receiver);
2061 assert_eq!(regs[2], reg_x2);
2062 assert_eq!(regs[3], reg_x3);
2063 assert_eq!(regs[4], 0);
2064
2065 assert_eq!(Interface::from_regs(version, &regs).unwrap(), interface);
2066 }
2067 }
Tomás González0a058bc2025-03-11 11:20:55 +00002068}