blob: 308760a0701e9d9cfa4eb0403ba452aa000c5be0 [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 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100777}
778
Balint Dobszayde0dc802025-02-28 14:16:52 +0100779impl Interface {
780 /// Returns the function ID for the call, if it has one.
781 pub fn function_id(&self) -> Option<FuncId> {
782 match self {
783 Interface::Error { .. } => Some(FuncId::Error),
784 Interface::Success { args, .. } => match args {
785 SuccessArgs::Result32(..) => Some(FuncId::Success32),
786 SuccessArgs::Result64(..) | SuccessArgs::Result64_2(..) => Some(FuncId::Success64),
787 },
788 Interface::Interrupt { .. } => Some(FuncId::Interrupt),
789 Interface::Version { .. } => Some(FuncId::Version),
790 Interface::VersionOut { .. } => None,
791 Interface::Features { .. } => Some(FuncId::Features),
792 Interface::RxAcquire { .. } => Some(FuncId::RxAcquire),
793 Interface::RxRelease { .. } => Some(FuncId::RxRelease),
794 Interface::RxTxMap { addr, .. } => match addr {
795 RxTxAddr::Addr32 { .. } => Some(FuncId::RxTxMap32),
796 RxTxAddr::Addr64 { .. } => Some(FuncId::RxTxMap64),
797 },
798 Interface::RxTxUnmap { .. } => Some(FuncId::RxTxUnmap),
799 Interface::PartitionInfoGet { .. } => Some(FuncId::PartitionInfoGet),
Tomás González0a058bc2025-03-11 11:20:55 +0000800 Interface::PartitionInfoGetRegs { .. } => Some(FuncId::PartitionInfoGetRegs),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100801 Interface::IdGet => Some(FuncId::IdGet),
802 Interface::SpmIdGet => Some(FuncId::SpmIdGet),
Tomás González092202a2025-03-05 11:56:45 +0000803 Interface::MsgWait { .. } => Some(FuncId::MsgWait),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100804 Interface::Yield => Some(FuncId::Yield),
805 Interface::Run { .. } => Some(FuncId::Run),
806 Interface::NormalWorldResume => Some(FuncId::NormalWorldResume),
Tomás González17b92442025-03-10 16:45:04 +0000807 Interface::SecondaryEpRegister { entrypoint } => match entrypoint {
808 SecondaryEpRegisterAddr::Addr32 { .. } => Some(FuncId::SecondaryEpRegister32),
809 SecondaryEpRegisterAddr::Addr64 { .. } => Some(FuncId::SecondaryEpRegister64),
810 },
Balint Dobszayde0dc802025-02-28 14:16:52 +0100811 Interface::MsgSend2 { .. } => Some(FuncId::MsgSend2),
812 Interface::MsgSendDirectReq { args, .. } => match args {
813 DirectMsgArgs::Args32(_) => Some(FuncId::MsgSendDirectReq32),
814 DirectMsgArgs::Args64(_) => Some(FuncId::MsgSendDirectReq64),
Tomás González4d5b0ba2025-03-03 17:15:55 +0000815 DirectMsgArgs::VersionReq { .. } => Some(FuncId::MsgSendDirectReq32),
816 DirectMsgArgs::PowerPsciReq32 { .. } => Some(FuncId::MsgSendDirectReq32),
817 DirectMsgArgs::PowerPsciReq64 { .. } => Some(FuncId::MsgSendDirectReq64),
818 DirectMsgArgs::PowerWarmBootReq { .. } => Some(FuncId::MsgSendDirectReq32),
819 DirectMsgArgs::VmCreated { .. } => Some(FuncId::MsgSendDirectReq32),
820 DirectMsgArgs::VmDestructed { .. } => Some(FuncId::MsgSendDirectReq32),
821 _ => None,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100822 },
823 Interface::MsgSendDirectResp { args, .. } => match args {
824 DirectMsgArgs::Args32(_) => Some(FuncId::MsgSendDirectResp32),
825 DirectMsgArgs::Args64(_) => Some(FuncId::MsgSendDirectResp64),
Tomás González4d5b0ba2025-03-03 17:15:55 +0000826 DirectMsgArgs::VersionResp { .. } => Some(FuncId::MsgSendDirectResp32),
827 DirectMsgArgs::PowerPsciResp { .. } => Some(FuncId::MsgSendDirectResp32),
828 DirectMsgArgs::VmCreatedAck { .. } => Some(FuncId::MsgSendDirectResp32),
829 DirectMsgArgs::VmDestructedAck { .. } => Some(FuncId::MsgSendDirectResp32),
830 _ => None,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100831 },
832 Interface::MsgSendDirectReq2 { .. } => Some(FuncId::MsgSendDirectReq64_2),
833 Interface::MsgSendDirectResp2 { .. } => Some(FuncId::MsgSendDirectResp64_2),
834 Interface::MemDonate { buf, .. } => match buf {
835 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemDonate64),
836 _ => Some(FuncId::MemDonate32),
837 },
838 Interface::MemLend { buf, .. } => match buf {
839 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemLend64),
840 _ => Some(FuncId::MemLend32),
841 },
842 Interface::MemShare { buf, .. } => match buf {
843 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemShare64),
844 _ => Some(FuncId::MemShare32),
845 },
846 Interface::MemRetrieveReq { buf, .. } => match buf {
847 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemRetrieveReq64),
848 _ => Some(FuncId::MemRetrieveReq32),
849 },
850 Interface::MemRetrieveResp { .. } => Some(FuncId::MemRetrieveResp),
851 Interface::MemRelinquish => Some(FuncId::MemRelinquish),
852 Interface::MemReclaim { .. } => Some(FuncId::MemReclaim),
853 Interface::MemPermGet { addr, .. } => match addr {
854 MemAddr::Addr32(_) => Some(FuncId::MemPermGet32),
855 MemAddr::Addr64(_) => Some(FuncId::MemPermGet64),
856 },
857 Interface::MemPermSet { addr, .. } => match addr {
858 MemAddr::Addr32(_) => Some(FuncId::MemPermSet32),
859 MemAddr::Addr64(_) => Some(FuncId::MemPermSet64),
860 },
861 Interface::ConsoleLog { char_lists, .. } => match char_lists {
862 ConsoleLogChars::Reg32(_) => Some(FuncId::ConsoleLog32),
863 ConsoleLogChars::Reg64(_) => Some(FuncId::ConsoleLog64),
864 },
Tomás González7ffb6132025-04-03 12:28:58 +0100865 Interface::NotificationBitmapCreate { .. } => Some(FuncId::NotificationBitmapCreate),
866 Interface::NotificationBitmapDestroy { .. } => Some(FuncId::NotificationBitmapDestroy),
867 Interface::NotificationBind { .. } => Some(FuncId::NotificationBind),
868 Interface::NotificationUnBind { .. } => Some(FuncId::NotificationUnbind),
869 Interface::NotificationSet { .. } => Some(FuncId::NotificationSet),
870 Interface::NotificationGet { .. } => Some(FuncId::NotificationGet),
871 Interface::NotificationInfoGet { is_32bit } => match is_32bit {
872 true => Some(FuncId::NotificationInfoGet32),
873 false => Some(FuncId::NotificationInfoGet64),
874 },
Balint Dobszayde0dc802025-02-28 14:16:52 +0100875 }
876 }
Balint Dobszay3aad9572025-01-17 16:54:11 +0100877
Balint Dobszayde0dc802025-02-28 14:16:52 +0100878 /// Returns true if this is a 32-bit call, or false if it is a 64-bit call.
879 pub fn is_32bit(&self) -> bool {
880 // TODO: self should always have a function ID?
881 self.function_id().unwrap().is_32bit()
882 }
883
884 /// Parse interface from register contents. The caller must ensure that the `regs` argument has
885 /// the correct length: 8 registers for FF-A v1.1 and lower, 18 registers for v1.2 and higher.
886 pub fn from_regs(version: Version, regs: &[u64]) -> Result<Self, Error> {
887 let reg_cnt = regs.len();
888
889 let msg = match reg_cnt {
890 8 => {
891 assert!(version <= Version(1, 1));
892 Interface::unpack_regs8(version, regs.try_into().unwrap())?
893 }
894 18 => {
895 assert!(version >= Version(1, 2));
896 match FuncId::try_from(regs[0] as u32)? {
897 FuncId::ConsoleLog64
898 | FuncId::Success64
899 | FuncId::MsgSendDirectReq64_2
Tomás González0a058bc2025-03-11 11:20:55 +0000900 | FuncId::MsgSendDirectResp64_2
901 | FuncId::PartitionInfoGetRegs => {
Balint Dobszayde0dc802025-02-28 14:16:52 +0100902 Interface::unpack_regs18(version, regs.try_into().unwrap())?
903 }
904 _ => Interface::unpack_regs8(version, regs[..8].try_into().unwrap())?,
905 }
906 }
907 _ => panic!(
908 "Invalid number of registers ({}) for FF-A version {}",
909 reg_cnt, version
910 ),
911 };
912
913 Ok(msg)
914 }
915
916 fn unpack_regs8(version: Version, regs: &[u64; 8]) -> Result<Self, Error> {
Balint Dobszay3aad9572025-01-17 16:54:11 +0100917 let fid = FuncId::try_from(regs[0] as u32)?;
918
919 let msg = match fid {
920 FuncId::Error => Self::Error {
921 target_info: (regs[1] as u32).into(),
922 error_code: FfaError::try_from(regs[2] as i32)?,
923 },
924 FuncId::Success32 => Self::Success {
925 target_info: regs[1] as u32,
926 args: SuccessArgs::Result32([
927 regs[2] as u32,
928 regs[3] as u32,
929 regs[4] as u32,
930 regs[5] as u32,
931 regs[6] as u32,
932 regs[7] as u32,
933 ]),
934 },
935 FuncId::Success64 => Self::Success {
936 target_info: regs[1] as u32,
937 args: SuccessArgs::Result64([regs[2], regs[3], regs[4], regs[5], regs[6], regs[7]]),
938 },
939 FuncId::Interrupt => Self::Interrupt {
940 target_info: (regs[1] as u32).into(),
941 interrupt_id: regs[2] as u32,
942 },
943 FuncId::Version => Self::Version {
Tomás González83146af2025-03-04 11:32:41 +0000944 input_version: (regs[1] as u32).try_into()?,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100945 },
946 FuncId::Features => Self::Features {
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100947 feat_id: (regs[1] as u32).into(),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100948 input_properties: regs[2] as u32,
949 },
950 FuncId::RxAcquire => Self::RxAcquire {
951 vm_id: regs[1] as u16,
952 },
953 FuncId::RxRelease => Self::RxRelease {
954 vm_id: regs[1] as u16,
955 },
956 FuncId::RxTxMap32 => {
957 let addr = RxTxAddr::Addr32 {
958 rx: regs[2] as u32,
959 tx: regs[1] as u32,
960 };
961 let page_cnt = regs[3] as u32;
962
963 Self::RxTxMap { addr, page_cnt }
964 }
965 FuncId::RxTxMap64 => {
966 let addr = RxTxAddr::Addr64 {
967 rx: regs[2],
968 tx: regs[1],
969 };
970 let page_cnt = regs[3] as u32;
971
972 Self::RxTxMap { addr, page_cnt }
973 }
974 FuncId::RxTxUnmap => Self::RxTxUnmap { id: regs[1] as u16 },
975 FuncId::PartitionInfoGet => {
976 let uuid_words = [
977 regs[1] as u32,
978 regs[2] as u32,
979 regs[3] as u32,
980 regs[4] as u32,
981 ];
982 let mut bytes: [u8; 16] = [0; 16];
983 for (i, b) in uuid_words.iter().flat_map(|w| w.to_le_bytes()).enumerate() {
984 bytes[i] = b;
985 }
986 Self::PartitionInfoGet {
987 uuid: Uuid::from_bytes(bytes),
988 flags: regs[5] as u32,
989 }
990 }
991 FuncId::IdGet => Self::IdGet,
992 FuncId::SpmIdGet => Self::SpmIdGet,
Tomás González092202a2025-03-05 11:56:45 +0000993 FuncId::MsgWait => Self::MsgWait {
994 flags: if version >= Version(1, 2) {
995 Some(MsgWaitFlags::try_from(regs[2] as u32)?)
996 } else {
997 None
998 },
999 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001000 FuncId::Yield => Self::Yield,
1001 FuncId::Run => Self::Run {
1002 target_info: (regs[1] as u32).into(),
1003 },
1004 FuncId::NormalWorldResume => Self::NormalWorldResume,
Tomás González17b92442025-03-10 16:45:04 +00001005 FuncId::SecondaryEpRegister32 => Self::SecondaryEpRegister {
1006 entrypoint: SecondaryEpRegisterAddr::Addr32(regs[1] as u32),
1007 },
1008 FuncId::SecondaryEpRegister64 => Self::SecondaryEpRegister {
1009 entrypoint: SecondaryEpRegisterAddr::Addr64(regs[1]),
1010 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001011 FuncId::MsgSend2 => Self::MsgSend2 {
1012 sender_vm_id: regs[1] as u16,
1013 flags: regs[2] as u32,
1014 },
1015 FuncId::MsgSendDirectReq32 => Self::MsgSendDirectReq {
1016 src_id: (regs[1] >> 16) as u16,
1017 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001018 args: if (regs[2] as u32 & DirectMsgArgs::FWK_MSG_BITS) != 0 {
1019 match regs[2] as u32 {
1020 DirectMsgArgs::VERSION_REQ => DirectMsgArgs::VersionReq {
1021 version: Version::try_from(regs[3] as u32)?,
1022 },
1023 DirectMsgArgs::POWER_PSCI_REQ => DirectMsgArgs::PowerPsciReq32 {
Tomás González67f92c72025-03-20 16:50:42 +00001024 params: [
1025 regs[3] as u32,
1026 regs[4] as u32,
1027 regs[5] as u32,
1028 regs[6] as u32,
1029 ],
Tomás González4d5b0ba2025-03-03 17:15:55 +00001030 },
1031 DirectMsgArgs::POWER_WARM_BOOT_REQ => DirectMsgArgs::PowerWarmBootReq {
1032 boot_type: WarmBootType::try_from(regs[3] as u32)?,
1033 },
1034 DirectMsgArgs::VM_CREATED => DirectMsgArgs::VmCreated {
1035 handle: memory_management::Handle::from([
1036 regs[3] as u32,
1037 regs[4] as u32,
1038 ]),
1039 vm_id: regs[5] as u16,
1040 },
1041 DirectMsgArgs::VM_DESTRUCTED => DirectMsgArgs::VmDestructed {
1042 handle: memory_management::Handle::from([
1043 regs[3] as u32,
1044 regs[4] as u32,
1045 ]),
1046 vm_id: regs[5] as u16,
1047 },
1048 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1049 }
1050 } else {
1051 DirectMsgArgs::Args32([
1052 regs[3] as u32,
1053 regs[4] as u32,
1054 regs[5] as u32,
1055 regs[6] as u32,
1056 regs[7] as u32,
1057 ])
1058 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001059 },
1060 FuncId::MsgSendDirectReq64 => Self::MsgSendDirectReq {
1061 src_id: (regs[1] >> 16) as u16,
1062 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001063 args: if (regs[2] & DirectMsgArgs::FWK_MSG_BITS as u64) != 0 {
1064 match regs[2] as u32 {
1065 DirectMsgArgs::POWER_PSCI_REQ => DirectMsgArgs::PowerPsciReq64 {
Tomás González67f92c72025-03-20 16:50:42 +00001066 params: [regs[3], regs[4], regs[5], regs[6]],
Tomás González4d5b0ba2025-03-03 17:15:55 +00001067 },
1068 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1069 }
1070 } else {
1071 DirectMsgArgs::Args64([regs[3], regs[4], regs[5], regs[6], regs[7]])
1072 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001073 },
1074 FuncId::MsgSendDirectResp32 => Self::MsgSendDirectResp {
1075 src_id: (regs[1] >> 16) as u16,
1076 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001077 args: if (regs[2] as u32 & DirectMsgArgs::FWK_MSG_BITS) != 0 {
1078 match regs[2] as u32 {
1079 DirectMsgArgs::VERSION_RESP => {
1080 if regs[3] as i32 == FfaError::NotSupported.into() {
1081 DirectMsgArgs::VersionResp { version: None }
1082 } else {
1083 DirectMsgArgs::VersionResp {
1084 version: Some(Version::try_from(regs[3] as u32)?),
1085 }
1086 }
1087 }
1088 DirectMsgArgs::POWER_PSCI_RESP => DirectMsgArgs::PowerPsciResp {
1089 psci_status: regs[3] as i32,
1090 },
1091 DirectMsgArgs::VM_CREATED_ACK => DirectMsgArgs::VmCreatedAck {
1092 sp_status: (regs[3] as i32).try_into()?,
1093 },
1094 DirectMsgArgs::VM_DESTRUCTED_ACK => DirectMsgArgs::VmDestructedAck {
1095 sp_status: (regs[3] as i32).try_into()?,
1096 },
1097 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1098 }
1099 } else {
1100 DirectMsgArgs::Args32([
1101 regs[3] as u32,
1102 regs[4] as u32,
1103 regs[5] as u32,
1104 regs[6] as u32,
1105 regs[7] as u32,
1106 ])
1107 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001108 },
1109 FuncId::MsgSendDirectResp64 => Self::MsgSendDirectResp {
1110 src_id: (regs[1] >> 16) as u16,
1111 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001112 args: if (regs[2] & DirectMsgArgs::FWK_MSG_BITS as u64) != 0 {
1113 return Err(Error::UnrecognisedFwkMsg(regs[2] as u32));
1114 } else {
1115 DirectMsgArgs::Args64([regs[3], regs[4], regs[5], regs[6], regs[7]])
1116 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001117 },
1118 FuncId::MemDonate32 => Self::MemDonate {
1119 total_len: regs[1] as u32,
1120 frag_len: regs[2] as u32,
1121 buf: if regs[3] != 0 && regs[4] != 0 {
1122 Some(MemOpBuf::Buf32 {
1123 addr: regs[3] as u32,
1124 page_cnt: regs[4] as u32,
1125 })
1126 } else {
1127 None
1128 },
1129 },
1130 FuncId::MemDonate64 => Self::MemDonate {
1131 total_len: regs[1] as u32,
1132 frag_len: regs[2] as u32,
1133 buf: if regs[3] != 0 && regs[4] != 0 {
1134 Some(MemOpBuf::Buf64 {
1135 addr: regs[3],
1136 page_cnt: regs[4] as u32,
1137 })
1138 } else {
1139 None
1140 },
1141 },
1142 FuncId::MemLend32 => Self::MemLend {
1143 total_len: regs[1] as u32,
1144 frag_len: regs[2] as u32,
1145 buf: if regs[3] != 0 && regs[4] != 0 {
1146 Some(MemOpBuf::Buf32 {
1147 addr: regs[3] as u32,
1148 page_cnt: regs[4] as u32,
1149 })
1150 } else {
1151 None
1152 },
1153 },
1154 FuncId::MemLend64 => Self::MemLend {
1155 total_len: regs[1] as u32,
1156 frag_len: regs[2] as u32,
1157 buf: if regs[3] != 0 && regs[4] != 0 {
1158 Some(MemOpBuf::Buf64 {
1159 addr: regs[3],
1160 page_cnt: regs[4] as u32,
1161 })
1162 } else {
1163 None
1164 },
1165 },
1166 FuncId::MemShare32 => Self::MemShare {
1167 total_len: regs[1] as u32,
1168 frag_len: regs[2] as u32,
1169 buf: if regs[3] != 0 && regs[4] != 0 {
1170 Some(MemOpBuf::Buf32 {
1171 addr: regs[3] as u32,
1172 page_cnt: regs[4] as u32,
1173 })
1174 } else {
1175 None
1176 },
1177 },
1178 FuncId::MemShare64 => Self::MemShare {
1179 total_len: regs[1] as u32,
1180 frag_len: regs[2] as u32,
1181 buf: if regs[3] != 0 && regs[4] != 0 {
1182 Some(MemOpBuf::Buf64 {
1183 addr: regs[3],
1184 page_cnt: regs[4] as u32,
1185 })
1186 } else {
1187 None
1188 },
1189 },
1190 FuncId::MemRetrieveReq32 => Self::MemRetrieveReq {
1191 total_len: regs[1] as u32,
1192 frag_len: regs[2] as u32,
1193 buf: if regs[3] != 0 && regs[4] != 0 {
1194 Some(MemOpBuf::Buf32 {
1195 addr: regs[3] as u32,
1196 page_cnt: regs[4] as u32,
1197 })
1198 } else {
1199 None
1200 },
1201 },
1202 FuncId::MemRetrieveReq64 => Self::MemRetrieveReq {
1203 total_len: regs[1] as u32,
1204 frag_len: regs[2] as u32,
1205 buf: if regs[3] != 0 && regs[4] != 0 {
1206 Some(MemOpBuf::Buf64 {
1207 addr: regs[3],
1208 page_cnt: regs[4] as u32,
1209 })
1210 } else {
1211 None
1212 },
1213 },
1214 FuncId::MemRetrieveResp => Self::MemRetrieveResp {
1215 total_len: regs[1] as u32,
1216 frag_len: regs[2] as u32,
1217 },
1218 FuncId::MemRelinquish => Self::MemRelinquish,
1219 FuncId::MemReclaim => Self::MemReclaim {
1220 handle: memory_management::Handle::from([regs[1] as u32, regs[2] as u32]),
1221 flags: regs[3] as u32,
1222 },
1223 FuncId::MemPermGet32 => Self::MemPermGet {
1224 addr: MemAddr::Addr32(regs[1] as u32),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001225 page_cnt: if version >= Version(1, 3) {
1226 Some(regs[2] as u32)
1227 } else {
1228 None
1229 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001230 },
1231 FuncId::MemPermGet64 => Self::MemPermGet {
1232 addr: MemAddr::Addr64(regs[1]),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001233 page_cnt: if version >= Version(1, 3) {
1234 Some(regs[2] as u32)
1235 } else {
1236 None
1237 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001238 },
1239 FuncId::MemPermSet32 => Self::MemPermSet {
1240 addr: MemAddr::Addr32(regs[1] as u32),
1241 page_cnt: regs[2] as u32,
1242 mem_perm: regs[3] as u32,
1243 },
1244 FuncId::MemPermSet64 => Self::MemPermSet {
1245 addr: MemAddr::Addr64(regs[1]),
1246 page_cnt: regs[2] as u32,
1247 mem_perm: regs[3] as u32,
1248 },
1249 FuncId::ConsoleLog32 => Self::ConsoleLog {
1250 char_cnt: regs[1] as u8,
1251 char_lists: ConsoleLogChars::Reg32([
1252 regs[2] as u32,
1253 regs[3] as u32,
1254 regs[4] as u32,
1255 regs[5] as u32,
1256 regs[6] as u32,
1257 regs[7] as u32,
1258 ]),
1259 },
Tomás González7ffb6132025-04-03 12:28:58 +01001260 FuncId::NotificationBitmapCreate => {
1261 let tentative_vm_id = regs[1] as u32;
1262 if (tentative_vm_id >> 16) != 0 {
1263 return Err(Error::InvalidVmId(tentative_vm_id));
1264 }
1265 Self::NotificationBitmapCreate {
1266 vm_id: tentative_vm_id as u16,
1267 vcpu_cnt: regs[2] as u32,
1268 }
1269 }
1270 FuncId::NotificationBitmapDestroy => {
1271 let tentative_vm_id = regs[1] as u32;
1272 if (tentative_vm_id >> 16) != 0 {
1273 return Err(Error::InvalidVmId(tentative_vm_id));
1274 }
1275 Self::NotificationBitmapDestroy {
1276 vm_id: tentative_vm_id as u16,
1277 }
1278 }
1279 FuncId::NotificationBind => Self::NotificationBind {
1280 sender_id: (regs[1] >> 16) as u16,
1281 receiver_id: regs[1] as u16,
1282 flags: (regs[2] as u32).into(),
1283 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1284 },
1285 FuncId::NotificationUnbind => Self::NotificationUnBind {
1286 sender_id: (regs[1] >> 16) as u16,
1287 receiver_id: regs[1] as u16,
1288 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1289 },
1290 FuncId::NotificationSet => Self::NotificationSet {
1291 sender_id: (regs[1] >> 16) as u16,
1292 receiver_id: regs[1] as u16,
1293 flags: (regs[2] as u32).try_into()?,
1294 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1295 },
1296 FuncId::NotificationGet => Self::NotificationGet {
1297 vcpu_id: (regs[1] >> 16) as u16,
1298 endpoint_id: regs[1] as u16,
1299 flags: (regs[2] as u32).into(),
1300 },
1301 FuncId::NotificationInfoGet32 => Self::NotificationInfoGet { is_32bit: true },
1302 FuncId::NotificationInfoGet64 => Self::NotificationInfoGet { is_32bit: false },
Balint Dobszayde0dc802025-02-28 14:16:52 +01001303 _ => panic!("Invalid number of registers (8) for function {:#x?}", fid),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001304 };
1305
1306 Ok(msg)
1307 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01001308
Balint Dobszayde0dc802025-02-28 14:16:52 +01001309 fn unpack_regs18(version: Version, regs: &[u64; 18]) -> Result<Self, Error> {
1310 assert!(version >= Version(1, 2));
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001311
Balint Dobszayde0dc802025-02-28 14:16:52 +01001312 let fid = FuncId::try_from(regs[0] as u32)?;
1313
1314 let msg = match fid {
1315 FuncId::Success64 => Self::Success {
1316 target_info: regs[1] as u32,
1317 args: SuccessArgs::Result64_2(regs[2..18].try_into().unwrap()),
1318 },
1319 FuncId::MsgSendDirectReq64_2 => Self::MsgSendDirectReq2 {
1320 src_id: (regs[1] >> 16) as u16,
1321 dst_id: regs[1] as u16,
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00001322 uuid: Uuid::from_u64_pair(regs[2].swap_bytes(), regs[3].swap_bytes()),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001323 args: DirectMsg2Args(regs[4..18].try_into().unwrap()),
1324 },
1325 FuncId::MsgSendDirectResp64_2 => Self::MsgSendDirectResp2 {
1326 src_id: (regs[1] >> 16) as u16,
1327 dst_id: regs[1] as u16,
1328 args: DirectMsg2Args(regs[4..18].try_into().unwrap()),
1329 },
1330 FuncId::ConsoleLog64 => Self::ConsoleLog {
1331 char_cnt: regs[1] as u8,
1332 char_lists: ConsoleLogChars::Reg64(regs[2..18].try_into().unwrap()),
1333 },
Tomás González0a058bc2025-03-11 11:20:55 +00001334 FuncId::PartitionInfoGetRegs => {
1335 // Bits[15:0]: Start index
1336 let start_index = (regs[3] & 0xffff) as u16;
1337 let info_tag = ((regs[3] >> 16) & 0xffff) as u16;
1338 Self::PartitionInfoGetRegs {
1339 uuid: Uuid::from_u64_pair(regs[1].swap_bytes(), regs[2].swap_bytes()),
1340 start_index,
1341 info_tag: if start_index == 0 && info_tag != 0 {
1342 return Err(Error::InvalidInformationTag(info_tag));
1343 } else {
1344 info_tag
1345 },
1346 }
1347 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001348 _ => panic!("Invalid number of registers (18) for function {:#x?}", fid),
1349 };
1350
1351 Ok(msg)
Balint Dobszay3aad9572025-01-17 16:54:11 +01001352 }
1353
Balint Dobszaya5846852025-02-26 15:38:53 +01001354 /// Create register contents for an interface.
Balint Dobszayde0dc802025-02-28 14:16:52 +01001355 pub fn to_regs(&self, version: Version, regs: &mut [u64]) {
1356 let reg_cnt = regs.len();
1357
1358 match reg_cnt {
1359 8 => {
1360 assert!(version <= Version(1, 1));
1361 self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
1362 }
1363 18 => {
1364 assert!(version >= Version(1, 2));
1365
1366 match self {
1367 Interface::ConsoleLog {
1368 char_lists: ConsoleLogChars::Reg64(_),
1369 ..
1370 }
1371 | Interface::Success {
1372 args: SuccessArgs::Result64_2(_),
1373 ..
1374 }
1375 | Interface::MsgSendDirectReq2 { .. }
Tomás González0a058bc2025-03-11 11:20:55 +00001376 | Interface::MsgSendDirectResp2 { .. }
1377 | Interface::PartitionInfoGetRegs { .. } => {
Balint Dobszayde0dc802025-02-28 14:16:52 +01001378 self.pack_regs18(version, regs.try_into().unwrap());
1379 }
1380 _ => {
1381 self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
1382 }
1383 }
1384 }
1385 _ => panic!("Invalid number of registers {}", reg_cnt),
1386 }
1387 }
1388
1389 fn pack_regs8(&self, version: Version, a: &mut [u64; 8]) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001390 a.fill(0);
1391 if let Some(function_id) = self.function_id() {
1392 a[0] = function_id as u64;
1393 }
1394
1395 match *self {
1396 Interface::Error {
1397 target_info,
1398 error_code,
1399 } => {
1400 a[1] = u32::from(target_info).into();
1401 a[2] = (error_code as u32).into();
1402 }
1403 Interface::Success { target_info, args } => {
1404 a[1] = target_info.into();
1405 match args {
1406 SuccessArgs::Result32(regs) => {
1407 a[2] = regs[0].into();
1408 a[3] = regs[1].into();
1409 a[4] = regs[2].into();
1410 a[5] = regs[3].into();
1411 a[6] = regs[4].into();
1412 a[7] = regs[5].into();
1413 }
1414 SuccessArgs::Result64(regs) => {
1415 a[2] = regs[0];
1416 a[3] = regs[1];
1417 a[4] = regs[2];
1418 a[5] = regs[3];
1419 a[6] = regs[4];
1420 a[7] = regs[5];
1421 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001422 _ => panic!("{:#x?} requires 18 registers", args),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001423 }
1424 }
1425 Interface::Interrupt {
1426 target_info,
1427 interrupt_id,
1428 } => {
1429 a[1] = u32::from(target_info).into();
1430 a[2] = interrupt_id.into();
1431 }
1432 Interface::Version { input_version } => {
1433 a[1] = u32::from(input_version).into();
1434 }
1435 Interface::VersionOut { output_version } => {
1436 a[0] = u32::from(output_version).into();
1437 }
1438 Interface::Features {
1439 feat_id,
1440 input_properties,
1441 } => {
1442 a[1] = u32::from(feat_id).into();
1443 a[2] = input_properties.into();
1444 }
1445 Interface::RxAcquire { vm_id } => {
1446 a[1] = vm_id.into();
1447 }
1448 Interface::RxRelease { vm_id } => {
1449 a[1] = vm_id.into();
1450 }
1451 Interface::RxTxMap { addr, page_cnt } => {
1452 match addr {
1453 RxTxAddr::Addr32 { rx, tx } => {
1454 a[1] = tx.into();
1455 a[2] = rx.into();
1456 }
1457 RxTxAddr::Addr64 { rx, tx } => {
1458 a[1] = tx;
1459 a[2] = rx;
1460 }
1461 }
1462 a[3] = page_cnt.into();
1463 }
1464 Interface::RxTxUnmap { id } => {
1465 a[1] = id.into();
1466 }
1467 Interface::PartitionInfoGet { uuid, flags } => {
1468 let bytes = uuid.into_bytes();
1469 a[1] = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]).into();
1470 a[2] = u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]).into();
1471 a[3] = u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]).into();
1472 a[4] = u32::from_le_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]).into();
1473 a[5] = flags.into();
1474 }
Tomás González092202a2025-03-05 11:56:45 +00001475 Interface::MsgWait { flags } => {
1476 if version >= Version(1, 2) {
1477 if let Some(flags) = flags {
1478 a[2] = u32::from(flags).into();
1479 }
1480 }
1481 }
1482 Interface::IdGet | Interface::SpmIdGet | Interface::Yield => {}
Balint Dobszay3aad9572025-01-17 16:54:11 +01001483 Interface::Run { target_info } => {
1484 a[1] = u32::from(target_info).into();
1485 }
1486 Interface::NormalWorldResume => {}
Tomás González17b92442025-03-10 16:45:04 +00001487 Interface::SecondaryEpRegister { entrypoint } => match entrypoint {
1488 SecondaryEpRegisterAddr::Addr32(addr) => a[1] = addr as u64,
1489 SecondaryEpRegisterAddr::Addr64(addr) => a[1] = addr,
1490 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001491 Interface::MsgSend2 {
1492 sender_vm_id,
1493 flags,
1494 } => {
1495 a[1] = sender_vm_id.into();
1496 a[2] = flags.into();
1497 }
1498 Interface::MsgSendDirectReq {
1499 src_id,
1500 dst_id,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001501 args,
1502 } => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +01001503 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Balint Dobszay3aad9572025-01-17 16:54:11 +01001504 match args {
1505 DirectMsgArgs::Args32(args) => {
1506 a[3] = args[0].into();
1507 a[4] = args[1].into();
1508 a[5] = args[2].into();
1509 a[6] = args[3].into();
1510 a[7] = args[4].into();
1511 }
1512 DirectMsgArgs::Args64(args) => {
1513 a[3] = args[0];
1514 a[4] = args[1];
1515 a[5] = args[2];
1516 a[6] = args[3];
1517 a[7] = args[4];
1518 }
Tomás González4d5b0ba2025-03-03 17:15:55 +00001519 DirectMsgArgs::VersionReq { version } => {
1520 a[2] = DirectMsgArgs::VERSION_REQ.into();
1521 a[3] = u32::from(version).into();
1522 }
Tomás González67f92c72025-03-20 16:50:42 +00001523 DirectMsgArgs::PowerPsciReq32 { params } => {
Tomás González4d5b0ba2025-03-03 17:15:55 +00001524 a[2] = DirectMsgArgs::POWER_PSCI_REQ.into();
Tomás González67f92c72025-03-20 16:50:42 +00001525 a[3] = params[0].into();
1526 a[4] = params[1].into();
1527 a[5] = params[2].into();
1528 a[6] = params[3].into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00001529 }
Tomás González67f92c72025-03-20 16:50:42 +00001530 DirectMsgArgs::PowerPsciReq64 { params } => {
Tomás González4d5b0ba2025-03-03 17:15:55 +00001531 a[2] = DirectMsgArgs::POWER_PSCI_REQ.into();
Tomás González67f92c72025-03-20 16:50:42 +00001532 a[3] = params[0];
1533 a[4] = params[1];
1534 a[5] = params[2];
1535 a[6] = params[3];
Tomás González4d5b0ba2025-03-03 17:15:55 +00001536 }
1537 DirectMsgArgs::PowerWarmBootReq { boot_type } => {
1538 a[2] = DirectMsgArgs::POWER_WARM_BOOT_REQ.into();
1539 a[3] = u32::from(boot_type).into();
1540 }
1541 DirectMsgArgs::VmCreated { handle, vm_id } => {
1542 a[2] = DirectMsgArgs::VM_CREATED.into();
1543 let handle_regs: [u32; 2] = handle.into();
1544 a[3] = handle_regs[0].into();
1545 a[4] = handle_regs[1].into();
1546 a[5] = vm_id.into();
1547 }
1548 DirectMsgArgs::VmDestructed { handle, vm_id } => {
1549 a[2] = DirectMsgArgs::VM_DESTRUCTED.into();
1550 let handle_regs: [u32; 2] = handle.into();
1551 a[3] = handle_regs[0].into();
1552 a[4] = handle_regs[1].into();
1553 a[5] = vm_id.into();
1554 }
1555 _ => panic!("Malformed MsgSendDirectReq interface"),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001556 }
1557 }
1558 Interface::MsgSendDirectResp {
1559 src_id,
1560 dst_id,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001561 args,
1562 } => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +01001563 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Balint Dobszay3aad9572025-01-17 16:54:11 +01001564 match args {
1565 DirectMsgArgs::Args32(args) => {
1566 a[3] = args[0].into();
1567 a[4] = args[1].into();
1568 a[5] = args[2].into();
1569 a[6] = args[3].into();
1570 a[7] = args[4].into();
1571 }
1572 DirectMsgArgs::Args64(args) => {
1573 a[3] = args[0];
1574 a[4] = args[1];
1575 a[5] = args[2];
1576 a[6] = args[3];
1577 a[7] = args[4];
1578 }
Tomás González4d5b0ba2025-03-03 17:15:55 +00001579 DirectMsgArgs::VersionResp { version } => {
1580 a[2] = DirectMsgArgs::VERSION_RESP.into();
1581 match version {
Tomás González67f92c72025-03-20 16:50:42 +00001582 None => a[3] = (i32::from(FfaError::NotSupported) as u32).into(),
Tomás González4d5b0ba2025-03-03 17:15:55 +00001583 Some(ver) => a[3] = u32::from(ver).into(),
1584 }
1585 }
1586 DirectMsgArgs::PowerPsciResp { psci_status } => {
1587 a[2] = DirectMsgArgs::POWER_PSCI_RESP.into();
1588 a[3] = psci_status as u64;
1589 }
1590 DirectMsgArgs::VmCreatedAck { sp_status } => {
1591 a[2] = DirectMsgArgs::VM_CREATED_ACK.into();
Tomás González67f92c72025-03-20 16:50:42 +00001592 a[3] = (i32::from(sp_status) as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00001593 }
1594 DirectMsgArgs::VmDestructedAck { sp_status } => {
1595 a[2] = DirectMsgArgs::VM_DESTRUCTED_ACK.into();
Tomás González67f92c72025-03-20 16:50:42 +00001596 a[3] = (i32::from(sp_status) as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00001597 }
1598 _ => panic!("Malformed MsgSendDirectResp interface"),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001599 }
1600 }
1601 Interface::MemDonate {
1602 total_len,
1603 frag_len,
1604 buf,
1605 } => {
1606 a[1] = total_len.into();
1607 a[2] = frag_len.into();
1608 (a[3], a[4]) = match buf {
1609 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1610 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1611 None => (0, 0),
1612 };
1613 }
1614 Interface::MemLend {
1615 total_len,
1616 frag_len,
1617 buf,
1618 } => {
1619 a[1] = total_len.into();
1620 a[2] = frag_len.into();
1621 (a[3], a[4]) = match buf {
1622 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1623 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1624 None => (0, 0),
1625 };
1626 }
1627 Interface::MemShare {
1628 total_len,
1629 frag_len,
1630 buf,
1631 } => {
1632 a[1] = total_len.into();
1633 a[2] = frag_len.into();
1634 (a[3], a[4]) = match buf {
1635 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1636 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1637 None => (0, 0),
1638 };
1639 }
1640 Interface::MemRetrieveReq {
1641 total_len,
1642 frag_len,
1643 buf,
1644 } => {
1645 a[1] = total_len.into();
1646 a[2] = frag_len.into();
1647 (a[3], a[4]) = match buf {
1648 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1649 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1650 None => (0, 0),
1651 };
1652 }
1653 Interface::MemRetrieveResp {
1654 total_len,
1655 frag_len,
1656 } => {
1657 a[1] = total_len.into();
1658 a[2] = frag_len.into();
1659 }
1660 Interface::MemRelinquish => {}
1661 Interface::MemReclaim { handle, flags } => {
1662 let handle_regs: [u32; 2] = handle.into();
1663 a[1] = handle_regs[0].into();
1664 a[2] = handle_regs[1].into();
1665 a[3] = flags.into();
1666 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001667 Interface::MemPermGet { addr, page_cnt } => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001668 a[1] = match addr {
1669 MemAddr::Addr32(addr) => addr.into(),
1670 MemAddr::Addr64(addr) => addr,
1671 };
Balint Dobszayde0dc802025-02-28 14:16:52 +01001672 a[2] = if version >= Version(1, 3) {
1673 page_cnt.unwrap().into()
1674 } else {
1675 assert!(page_cnt.is_none());
1676 0
1677 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01001678 }
1679 Interface::MemPermSet {
1680 addr,
1681 page_cnt,
1682 mem_perm,
1683 } => {
1684 a[1] = match addr {
1685 MemAddr::Addr32(addr) => addr.into(),
1686 MemAddr::Addr64(addr) => addr,
1687 };
1688 a[2] = page_cnt.into();
1689 a[3] = mem_perm.into();
1690 }
1691 Interface::ConsoleLog {
1692 char_cnt,
1693 char_lists,
1694 } => {
1695 a[1] = char_cnt.into();
1696 match char_lists {
1697 ConsoleLogChars::Reg32(regs) => {
1698 a[2] = regs[0].into();
1699 a[3] = regs[1].into();
1700 a[4] = regs[2].into();
1701 a[5] = regs[3].into();
1702 a[6] = regs[4].into();
1703 a[7] = regs[5].into();
1704 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001705 _ => panic!("{:#x?} requires 18 registers", char_lists),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001706 }
1707 }
Tomás González7ffb6132025-04-03 12:28:58 +01001708 Interface::NotificationBitmapCreate { vm_id, vcpu_cnt } => {
1709 a[1] = vm_id.into();
1710 a[2] = vcpu_cnt.into();
1711 }
1712 Interface::NotificationBitmapDestroy { vm_id } => {
1713 a[1] = vm_id.into();
1714 }
1715 Interface::NotificationBind {
1716 sender_id,
1717 receiver_id,
1718 flags,
1719 bitmap,
1720 } => {
1721 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
1722 a[2] = u32::from(flags).into();
1723 a[3] = bitmap & 0xffff_ffff;
1724 a[4] = bitmap >> 32;
1725 }
1726 Interface::NotificationUnBind {
1727 sender_id,
1728 receiver_id,
1729 bitmap,
1730 } => {
1731 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
1732 a[3] = bitmap & 0xffff_ffff;
1733 a[4] = bitmap >> 32;
1734 }
1735 Interface::NotificationSet {
1736 sender_id,
1737 receiver_id,
1738 flags,
1739 bitmap,
1740 } => {
1741 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
1742 a[2] = u32::from(flags).into();
1743 a[3] = bitmap & 0xffff_ffff;
1744 a[4] = bitmap >> 32;
1745 }
1746 Interface::NotificationGet {
1747 vcpu_id,
1748 endpoint_id,
1749 flags,
1750 } => {
1751 a[1] = (u64::from(vcpu_id) << 16) | u64::from(endpoint_id);
1752 a[2] = u32::from(flags).into();
1753 }
1754 Interface::NotificationInfoGet { .. } => {}
Balint Dobszayde0dc802025-02-28 14:16:52 +01001755 _ => panic!("{:#x?} requires 18 registers", self),
1756 }
1757 }
1758
1759 fn pack_regs18(&self, version: Version, a: &mut [u64; 18]) {
1760 assert!(version >= Version(1, 2));
1761
1762 a.fill(0);
1763 if let Some(function_id) = self.function_id() {
1764 a[0] = function_id as u64;
1765 }
1766
1767 match *self {
1768 Interface::Success { target_info, args } => {
1769 a[1] = target_info.into();
1770 match args {
1771 SuccessArgs::Result64_2(regs) => a[2..18].copy_from_slice(&regs[..16]),
1772 _ => panic!("{:#x?} requires 8 registers", args),
1773 }
1774 }
1775 Interface::MsgSendDirectReq2 {
1776 src_id,
1777 dst_id,
1778 uuid,
1779 args,
1780 } => {
1781 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00001782 let (uuid_msb, uuid_lsb) = uuid.as_u64_pair();
1783 (a[2], a[3]) = (uuid_msb.swap_bytes(), uuid_lsb.swap_bytes());
Balint Dobszayde0dc802025-02-28 14:16:52 +01001784 a[4..18].copy_from_slice(&args.0[..14]);
1785 }
1786 Interface::MsgSendDirectResp2 {
1787 src_id,
1788 dst_id,
1789 args,
1790 } => {
1791 a[1] = ((src_id as u64) << 16) | dst_id as u64;
1792 a[2] = 0;
1793 a[3] = 0;
1794 a[4..18].copy_from_slice(&args.0[..14]);
1795 }
1796 Interface::ConsoleLog {
1797 char_cnt,
1798 char_lists,
1799 } => {
1800 a[1] = char_cnt.into();
1801 match char_lists {
1802 ConsoleLogChars::Reg64(regs) => a[2..18].copy_from_slice(&regs[..16]),
1803 _ => panic!("{:#x?} requires 8 registers", char_lists),
1804 }
1805 }
Tomás González0a058bc2025-03-11 11:20:55 +00001806 Interface::PartitionInfoGetRegs {
1807 uuid,
1808 start_index,
1809 info_tag,
1810 } => {
1811 if start_index == 0 && info_tag != 0 {
1812 panic!("Information Tag MBZ if start index is 0: {:#x?}", self);
1813 }
1814 let (uuid_msb, uuid_lsb) = uuid.as_u64_pair();
1815 (a[1], a[2]) = (uuid_msb.swap_bytes(), uuid_lsb.swap_bytes());
1816 a[3] = (u64::from(info_tag) << 16) | u64::from(start_index);
1817 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001818 _ => panic!("{:#x?} requires 8 registers", self),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001819 }
1820 }
1821
Balint Dobszaya5846852025-02-26 15:38:53 +01001822 /// Helper function to create an `FFA_SUCCESS` interface without any arguments.
Balint Dobszay3aad9572025-01-17 16:54:11 +01001823 pub fn success32_noargs() -> Self {
1824 Self::Success {
1825 target_info: 0,
1826 args: SuccessArgs::Result32([0, 0, 0, 0, 0, 0]),
1827 }
1828 }
1829
Tomás González4c8c7d22025-03-10 17:14:57 +00001830 /// Helper function to create an `FFA_SUCCESS` interface without any arguments.
1831 pub fn success64_noargs() -> Self {
1832 Self::Success {
1833 target_info: 0,
1834 args: SuccessArgs::Result64([0, 0, 0, 0, 0, 0]),
1835 }
1836 }
1837
Balint Dobszaya5846852025-02-26 15:38:53 +01001838 /// Helper function to create an `FFA_ERROR` interface with an error code.
Balint Dobszay3aad9572025-01-17 16:54:11 +01001839 pub fn error(error_code: FfaError) -> Self {
1840 Self::Error {
1841 target_info: TargetInfo {
1842 endpoint_id: 0,
1843 vcpu_id: 0,
1844 },
1845 error_code,
1846 }
1847 }
1848}
1849
Balint Dobszaya5846852025-02-26 15:38:53 +01001850/// Maximum number of characters transmitted in a single `FFA_CONSOLE_LOG32` message.
1851pub const CONSOLE_LOG_32_MAX_CHAR_CNT: u8 = 24;
Balint Dobszayde0dc802025-02-28 14:16:52 +01001852/// Maximum number of characters transmitted in a single `FFA_CONSOLE_LOG64` message.
1853pub const CONSOLE_LOG_64_MAX_CHAR_CNT: u8 = 128;
Balint Dobszay3aad9572025-01-17 16:54:11 +01001854
Balint Dobszaya5846852025-02-26 15:38:53 +01001855/// Helper function to convert the "Tightly packed list of characters" format used by the
1856/// `FFA_CONSOLE_LOG` interface into a byte slice.
Balint Dobszay3aad9572025-01-17 16:54:11 +01001857pub fn parse_console_log(
1858 char_cnt: u8,
1859 char_lists: &ConsoleLogChars,
1860 log_bytes: &mut [u8],
1861) -> Result<(), FfaError> {
1862 match char_lists {
1863 ConsoleLogChars::Reg32(regs) => {
Balint Dobszaya5846852025-02-26 15:38:53 +01001864 if !(1..=CONSOLE_LOG_32_MAX_CHAR_CNT).contains(&char_cnt) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001865 return Err(FfaError::InvalidParameters);
1866 }
1867 for (i, reg) in regs.iter().enumerate() {
1868 log_bytes[4 * i..4 * (i + 1)].copy_from_slice(&reg.to_le_bytes());
1869 }
1870 }
1871 ConsoleLogChars::Reg64(regs) => {
Balint Dobszaya5846852025-02-26 15:38:53 +01001872 if !(1..=CONSOLE_LOG_64_MAX_CHAR_CNT).contains(&char_cnt) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001873 return Err(FfaError::InvalidParameters);
1874 }
1875 for (i, reg) in regs.iter().enumerate() {
1876 log_bytes[8 * i..8 * (i + 1)].copy_from_slice(&reg.to_le_bytes());
1877 }
1878 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001879 }
1880
Balint Dobszayc8802492025-01-15 18:11:39 +01001881 Ok(())
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001882}
Tomás González0a058bc2025-03-11 11:20:55 +00001883
1884#[cfg(test)]
1885mod tests {
1886 use super::*;
1887
1888 #[test]
1889 fn part_info_get_regs() {
1890 let uuid = Uuid::parse_str("a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8").unwrap();
1891 let uuid_bytes = uuid.as_bytes();
1892 let test_info_tag = 0b1101_1101;
1893 let test_start_index = 0b1101;
1894 let start_index_and_tag = (test_info_tag << 16) | test_start_index;
1895 let version = Version(1, 2);
1896
1897 // From spec:
1898 // Bytes[0...7] of UUID with byte 0 in the low-order bits.
1899 let reg_x1 = (uuid_bytes[7] as u64) << 56
1900 | (uuid_bytes[6] as u64) << 48
1901 | (uuid_bytes[5] as u64) << 40
1902 | (uuid_bytes[4] as u64) << 32
1903 | (uuid_bytes[3] as u64) << 24
1904 | (uuid_bytes[2] as u64) << 16
1905 | (uuid_bytes[1] as u64) << 8
1906 | (uuid_bytes[0] as u64);
1907
1908 // From spec:
1909 // Bytes[8...15] of UUID with byte 8 in the low-order bits.
1910 let reg_x2 = (uuid_bytes[15] as u64) << 56
1911 | (uuid_bytes[14] as u64) << 48
1912 | (uuid_bytes[13] as u64) << 40
1913 | (uuid_bytes[12] as u64) << 32
1914 | (uuid_bytes[11] as u64) << 24
1915 | (uuid_bytes[10] as u64) << 16
1916 | (uuid_bytes[9] as u64) << 8
1917 | (uuid_bytes[8] as u64);
1918
1919 // First, test for wrong tag:
1920 {
1921 let mut regs = [0u64; 18];
1922 regs[0] = FuncId::PartitionInfoGetRegs as u64;
1923 regs[1] = reg_x1;
1924 regs[2] = reg_x2;
1925 regs[3] = test_info_tag << 16;
1926
1927 assert!(Interface::from_regs(version, &regs).is_err_and(
1928 |e| e == Error::InvalidInformationTag(test_info_tag.try_into().unwrap())
1929 ));
1930 }
1931
1932 // Test for regs -> Interface -> regs
1933 {
1934 let mut orig_regs = [0u64; 18];
1935 orig_regs[0] = FuncId::PartitionInfoGetRegs as u64;
1936 orig_regs[1] = reg_x1;
1937 orig_regs[2] = reg_x2;
1938 orig_regs[3] = start_index_and_tag;
1939
1940 let mut test_regs = orig_regs.clone();
1941 let interface = Interface::from_regs(version, &mut test_regs).unwrap();
1942 match &interface {
1943 Interface::PartitionInfoGetRegs {
1944 info_tag,
1945 start_index,
1946 uuid: int_uuid,
1947 } => {
1948 assert_eq!(u64::from(*info_tag), test_info_tag);
1949 assert_eq!(u64::from(*start_index), test_start_index);
1950 assert_eq!(*int_uuid, uuid);
1951 }
1952 _ => panic!("Expecting Interface::PartitionInfoGetRegs!"),
1953 }
1954 test_regs.fill(0);
1955 interface.to_regs(version, &mut test_regs);
1956 assert_eq!(orig_regs, test_regs);
1957 }
1958
1959 // Test for Interface -> regs -> Interface
1960 {
1961 let interface = Interface::PartitionInfoGetRegs {
1962 info_tag: test_info_tag.try_into().unwrap(),
1963 start_index: test_start_index.try_into().unwrap(),
1964 uuid,
1965 };
1966
1967 let mut regs: [u64; 18] = [0; 18];
1968 interface.to_regs(version, &mut regs);
1969
1970 assert_eq!(Some(FuncId::PartitionInfoGetRegs), interface.function_id());
1971 assert_eq!(regs[0], interface.function_id().unwrap() as u64);
1972 assert_eq!(regs[1], reg_x1);
1973 assert_eq!(regs[2], reg_x2);
1974 assert_eq!(regs[3], (test_info_tag << 16) | test_start_index);
1975
1976 assert_eq!(Interface::from_regs(version, &regs).unwrap(), interface);
1977 }
1978 }
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00001979
1980 #[test]
1981 fn msg_send_direct_req2() {
1982 let uuid = Uuid::parse_str("a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8").unwrap();
1983 let uuid_bytes = uuid.as_bytes();
1984
1985 // From spec:
1986 // Bytes[0...7] of UUID with byte 0 in the low-order bits.
1987 let reg_x2 = (uuid_bytes[7] as u64) << 56
1988 | (uuid_bytes[6] as u64) << 48
1989 | (uuid_bytes[5] as u64) << 40
1990 | (uuid_bytes[4] as u64) << 32
1991 | (uuid_bytes[3] as u64) << 24
1992 | (uuid_bytes[2] as u64) << 16
1993 | (uuid_bytes[1] as u64) << 8
1994 | (uuid_bytes[0] as u64);
1995
1996 // From spec:
1997 // Bytes[8...15] of UUID with byte 8 in the low-order bits.
1998 let reg_x3 = (uuid_bytes[15] as u64) << 56
1999 | (uuid_bytes[14] as u64) << 48
2000 | (uuid_bytes[13] as u64) << 40
2001 | (uuid_bytes[12] as u64) << 32
2002 | (uuid_bytes[11] as u64) << 24
2003 | (uuid_bytes[10] as u64) << 16
2004 | (uuid_bytes[9] as u64) << 8
2005 | (uuid_bytes[8] as u64);
2006
2007 let test_sender = 0b1101_1101;
2008 let test_receiver = 0b1101;
2009 let test_sender_receiver = (test_sender << 16) | test_receiver;
2010 let version = Version(1, 2);
2011
2012 // Test for regs -> Interface -> regs
2013 {
2014 let mut orig_regs = [0u64; 18];
2015 orig_regs[0] = FuncId::MsgSendDirectReq64_2 as u64;
2016 orig_regs[1] = test_sender_receiver;
2017 orig_regs[2] = reg_x2;
2018 orig_regs[3] = reg_x3;
2019
2020 let mut test_regs = orig_regs.clone();
2021 let interface = Interface::from_regs(version, &mut test_regs).unwrap();
2022 match &interface {
2023 Interface::MsgSendDirectReq2 {
2024 dst_id,
2025 src_id,
2026 args: _,
2027 uuid: int_uuid,
2028 } => {
2029 assert_eq!(u64::from(*src_id), test_sender);
2030 assert_eq!(u64::from(*dst_id), test_receiver);
2031 assert_eq!(*int_uuid, uuid);
2032 }
2033 _ => panic!("Expecting Interface::MsgSendDirectReq2!"),
2034 }
2035 test_regs.fill(0);
2036 interface.to_regs(version, &mut test_regs);
2037 assert_eq!(orig_regs, test_regs);
2038 }
2039
2040 // Test for Interface -> regs -> Interface
2041 {
2042 let rest_of_regs: [u64; 14] = [0; 14];
2043
2044 let interface = Interface::MsgSendDirectReq2 {
2045 src_id: test_sender.try_into().unwrap(),
2046 dst_id: test_receiver.try_into().unwrap(),
2047 uuid,
2048 args: DirectMsg2Args(rest_of_regs),
2049 };
2050
2051 let mut regs: [u64; 18] = [0; 18];
2052 interface.to_regs(version, &mut regs);
2053
2054 assert_eq!(Some(FuncId::MsgSendDirectReq64_2), interface.function_id());
2055 assert_eq!(regs[0], interface.function_id().unwrap() as u64);
2056 assert_eq!(regs[1], test_sender_receiver);
2057 assert_eq!(regs[2], reg_x2);
2058 assert_eq!(regs[3], reg_x3);
2059 assert_eq!(regs[4], 0);
2060
2061 assert_eq!(Interface::from_regs(version, &regs).unwrap(), interface);
2062 }
2063 }
Tomás González0a058bc2025-03-11 11:20:55 +00002064}