blob: c87fa907555b5527e1a247d9806ff9e82d4e7643 [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,
Balint Dobszayb727aab2025-04-07 10:24:59 +0200624 error_arg: u32,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100625 },
626 Success {
627 target_info: u32,
628 args: SuccessArgs,
629 },
630 Interrupt {
631 target_info: TargetInfo,
632 interrupt_id: u32,
633 },
634 Version {
635 input_version: Version,
636 },
637 VersionOut {
638 output_version: Version,
639 },
640 Features {
641 feat_id: Feature,
642 input_properties: u32,
643 },
644 RxAcquire {
645 vm_id: u16,
646 },
647 RxRelease {
648 vm_id: u16,
649 },
650 RxTxMap {
651 addr: RxTxAddr,
652 page_cnt: u32,
653 },
654 RxTxUnmap {
655 id: u16,
656 },
657 PartitionInfoGet {
658 uuid: Uuid,
659 flags: u32,
660 },
Tomás González0a058bc2025-03-11 11:20:55 +0000661 PartitionInfoGetRegs {
662 uuid: Uuid,
663 start_index: u16,
664 info_tag: u16,
665 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100666 IdGet,
667 SpmIdGet,
Tomás González092202a2025-03-05 11:56:45 +0000668 MsgWait {
669 flags: Option<MsgWaitFlags>,
670 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100671 Yield,
672 Run {
673 target_info: TargetInfo,
674 },
675 NormalWorldResume,
Tomás González17b92442025-03-10 16:45:04 +0000676 SecondaryEpRegister {
677 entrypoint: SecondaryEpRegisterAddr,
678 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100679 MsgSend2 {
680 sender_vm_id: u16,
681 flags: u32,
682 },
683 MsgSendDirectReq {
684 src_id: u16,
685 dst_id: u16,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100686 args: DirectMsgArgs,
687 },
688 MsgSendDirectResp {
689 src_id: u16,
690 dst_id: u16,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100691 args: DirectMsgArgs,
692 },
Balint Dobszayde0dc802025-02-28 14:16:52 +0100693 MsgSendDirectReq2 {
694 src_id: u16,
695 dst_id: u16,
696 uuid: Uuid,
697 args: DirectMsg2Args,
698 },
699 MsgSendDirectResp2 {
700 src_id: u16,
701 dst_id: u16,
702 args: DirectMsg2Args,
703 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100704 MemDonate {
705 total_len: u32,
706 frag_len: u32,
707 buf: Option<MemOpBuf>,
708 },
709 MemLend {
710 total_len: u32,
711 frag_len: u32,
712 buf: Option<MemOpBuf>,
713 },
714 MemShare {
715 total_len: u32,
716 frag_len: u32,
717 buf: Option<MemOpBuf>,
718 },
719 MemRetrieveReq {
720 total_len: u32,
721 frag_len: u32,
722 buf: Option<MemOpBuf>,
723 },
724 MemRetrieveResp {
725 total_len: u32,
726 frag_len: u32,
727 },
728 MemRelinquish,
729 MemReclaim {
730 handle: memory_management::Handle,
731 flags: u32,
732 },
733 MemPermGet {
734 addr: MemAddr,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100735 page_cnt: Option<u32>,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100736 },
737 MemPermSet {
738 addr: MemAddr,
739 page_cnt: u32,
740 mem_perm: u32,
741 },
742 ConsoleLog {
743 char_cnt: u8,
744 char_lists: ConsoleLogChars,
745 },
Tomás González7ffb6132025-04-03 12:28:58 +0100746 NotificationBitmapCreate {
747 vm_id: u16,
748 vcpu_cnt: u32,
749 },
750 NotificationBitmapDestroy {
751 vm_id: u16,
752 },
753 NotificationBind {
754 sender_id: u16,
755 receiver_id: u16,
756 flags: NotificationBindFlags,
757 bitmap: u64,
758 },
759 NotificationUnBind {
760 sender_id: u16,
761 receiver_id: u16,
762 bitmap: u64,
763 },
764 NotificationSet {
765 sender_id: u16,
766 receiver_id: u16,
767 flags: NotificationSetFlags,
768 bitmap: u64,
769 },
770 NotificationGet {
771 vcpu_id: u16,
772 endpoint_id: u16,
773 flags: NotificationGetFlags,
774 },
775 NotificationInfoGet {
776 is_32bit: bool,
777 },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +0100778 El3IntrHandle,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100779}
780
Balint Dobszayde0dc802025-02-28 14:16:52 +0100781impl Interface {
782 /// Returns the function ID for the call, if it has one.
783 pub fn function_id(&self) -> Option<FuncId> {
784 match self {
785 Interface::Error { .. } => Some(FuncId::Error),
786 Interface::Success { args, .. } => match args {
787 SuccessArgs::Result32(..) => Some(FuncId::Success32),
788 SuccessArgs::Result64(..) | SuccessArgs::Result64_2(..) => Some(FuncId::Success64),
789 },
790 Interface::Interrupt { .. } => Some(FuncId::Interrupt),
791 Interface::Version { .. } => Some(FuncId::Version),
792 Interface::VersionOut { .. } => None,
793 Interface::Features { .. } => Some(FuncId::Features),
794 Interface::RxAcquire { .. } => Some(FuncId::RxAcquire),
795 Interface::RxRelease { .. } => Some(FuncId::RxRelease),
796 Interface::RxTxMap { addr, .. } => match addr {
797 RxTxAddr::Addr32 { .. } => Some(FuncId::RxTxMap32),
798 RxTxAddr::Addr64 { .. } => Some(FuncId::RxTxMap64),
799 },
800 Interface::RxTxUnmap { .. } => Some(FuncId::RxTxUnmap),
801 Interface::PartitionInfoGet { .. } => Some(FuncId::PartitionInfoGet),
Tomás González0a058bc2025-03-11 11:20:55 +0000802 Interface::PartitionInfoGetRegs { .. } => Some(FuncId::PartitionInfoGetRegs),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100803 Interface::IdGet => Some(FuncId::IdGet),
804 Interface::SpmIdGet => Some(FuncId::SpmIdGet),
Tomás González092202a2025-03-05 11:56:45 +0000805 Interface::MsgWait { .. } => Some(FuncId::MsgWait),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100806 Interface::Yield => Some(FuncId::Yield),
807 Interface::Run { .. } => Some(FuncId::Run),
808 Interface::NormalWorldResume => Some(FuncId::NormalWorldResume),
Tomás González17b92442025-03-10 16:45:04 +0000809 Interface::SecondaryEpRegister { entrypoint } => match entrypoint {
810 SecondaryEpRegisterAddr::Addr32 { .. } => Some(FuncId::SecondaryEpRegister32),
811 SecondaryEpRegisterAddr::Addr64 { .. } => Some(FuncId::SecondaryEpRegister64),
812 },
Balint Dobszayde0dc802025-02-28 14:16:52 +0100813 Interface::MsgSend2 { .. } => Some(FuncId::MsgSend2),
814 Interface::MsgSendDirectReq { args, .. } => match args {
815 DirectMsgArgs::Args32(_) => Some(FuncId::MsgSendDirectReq32),
816 DirectMsgArgs::Args64(_) => Some(FuncId::MsgSendDirectReq64),
Tomás González4d5b0ba2025-03-03 17:15:55 +0000817 DirectMsgArgs::VersionReq { .. } => Some(FuncId::MsgSendDirectReq32),
818 DirectMsgArgs::PowerPsciReq32 { .. } => Some(FuncId::MsgSendDirectReq32),
819 DirectMsgArgs::PowerPsciReq64 { .. } => Some(FuncId::MsgSendDirectReq64),
820 DirectMsgArgs::PowerWarmBootReq { .. } => Some(FuncId::MsgSendDirectReq32),
821 DirectMsgArgs::VmCreated { .. } => Some(FuncId::MsgSendDirectReq32),
822 DirectMsgArgs::VmDestructed { .. } => Some(FuncId::MsgSendDirectReq32),
823 _ => None,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100824 },
825 Interface::MsgSendDirectResp { args, .. } => match args {
826 DirectMsgArgs::Args32(_) => Some(FuncId::MsgSendDirectResp32),
827 DirectMsgArgs::Args64(_) => Some(FuncId::MsgSendDirectResp64),
Tomás González4d5b0ba2025-03-03 17:15:55 +0000828 DirectMsgArgs::VersionResp { .. } => Some(FuncId::MsgSendDirectResp32),
829 DirectMsgArgs::PowerPsciResp { .. } => Some(FuncId::MsgSendDirectResp32),
830 DirectMsgArgs::VmCreatedAck { .. } => Some(FuncId::MsgSendDirectResp32),
831 DirectMsgArgs::VmDestructedAck { .. } => Some(FuncId::MsgSendDirectResp32),
832 _ => None,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100833 },
834 Interface::MsgSendDirectReq2 { .. } => Some(FuncId::MsgSendDirectReq64_2),
835 Interface::MsgSendDirectResp2 { .. } => Some(FuncId::MsgSendDirectResp64_2),
836 Interface::MemDonate { buf, .. } => match buf {
837 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemDonate64),
838 _ => Some(FuncId::MemDonate32),
839 },
840 Interface::MemLend { buf, .. } => match buf {
841 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemLend64),
842 _ => Some(FuncId::MemLend32),
843 },
844 Interface::MemShare { buf, .. } => match buf {
845 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemShare64),
846 _ => Some(FuncId::MemShare32),
847 },
848 Interface::MemRetrieveReq { buf, .. } => match buf {
849 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemRetrieveReq64),
850 _ => Some(FuncId::MemRetrieveReq32),
851 },
852 Interface::MemRetrieveResp { .. } => Some(FuncId::MemRetrieveResp),
853 Interface::MemRelinquish => Some(FuncId::MemRelinquish),
854 Interface::MemReclaim { .. } => Some(FuncId::MemReclaim),
855 Interface::MemPermGet { addr, .. } => match addr {
856 MemAddr::Addr32(_) => Some(FuncId::MemPermGet32),
857 MemAddr::Addr64(_) => Some(FuncId::MemPermGet64),
858 },
859 Interface::MemPermSet { addr, .. } => match addr {
860 MemAddr::Addr32(_) => Some(FuncId::MemPermSet32),
861 MemAddr::Addr64(_) => Some(FuncId::MemPermSet64),
862 },
863 Interface::ConsoleLog { char_lists, .. } => match char_lists {
864 ConsoleLogChars::Reg32(_) => Some(FuncId::ConsoleLog32),
865 ConsoleLogChars::Reg64(_) => Some(FuncId::ConsoleLog64),
866 },
Tomás González7ffb6132025-04-03 12:28:58 +0100867 Interface::NotificationBitmapCreate { .. } => Some(FuncId::NotificationBitmapCreate),
868 Interface::NotificationBitmapDestroy { .. } => Some(FuncId::NotificationBitmapDestroy),
869 Interface::NotificationBind { .. } => Some(FuncId::NotificationBind),
870 Interface::NotificationUnBind { .. } => Some(FuncId::NotificationUnbind),
871 Interface::NotificationSet { .. } => Some(FuncId::NotificationSet),
872 Interface::NotificationGet { .. } => Some(FuncId::NotificationGet),
873 Interface::NotificationInfoGet { is_32bit } => match is_32bit {
874 true => Some(FuncId::NotificationInfoGet32),
875 false => Some(FuncId::NotificationInfoGet64),
876 },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +0100877 Interface::El3IntrHandle => Some(FuncId::El3IntrHandle),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100878 }
879 }
Balint Dobszay3aad9572025-01-17 16:54:11 +0100880
Balint Dobszayde0dc802025-02-28 14:16:52 +0100881 /// Returns true if this is a 32-bit call, or false if it is a 64-bit call.
882 pub fn is_32bit(&self) -> bool {
883 // TODO: self should always have a function ID?
884 self.function_id().unwrap().is_32bit()
885 }
886
887 /// Parse interface from register contents. The caller must ensure that the `regs` argument has
888 /// the correct length: 8 registers for FF-A v1.1 and lower, 18 registers for v1.2 and higher.
889 pub fn from_regs(version: Version, regs: &[u64]) -> Result<Self, Error> {
890 let reg_cnt = regs.len();
891
892 let msg = match reg_cnt {
893 8 => {
894 assert!(version <= Version(1, 1));
895 Interface::unpack_regs8(version, regs.try_into().unwrap())?
896 }
897 18 => {
898 assert!(version >= Version(1, 2));
899 match FuncId::try_from(regs[0] as u32)? {
900 FuncId::ConsoleLog64
901 | FuncId::Success64
902 | FuncId::MsgSendDirectReq64_2
Tomás González0a058bc2025-03-11 11:20:55 +0000903 | FuncId::MsgSendDirectResp64_2
904 | FuncId::PartitionInfoGetRegs => {
Balint Dobszayde0dc802025-02-28 14:16:52 +0100905 Interface::unpack_regs18(version, regs.try_into().unwrap())?
906 }
907 _ => Interface::unpack_regs8(version, regs[..8].try_into().unwrap())?,
908 }
909 }
910 _ => panic!(
911 "Invalid number of registers ({}) for FF-A version {}",
912 reg_cnt, version
913 ),
914 };
915
916 Ok(msg)
917 }
918
919 fn unpack_regs8(version: Version, regs: &[u64; 8]) -> Result<Self, Error> {
Balint Dobszay3aad9572025-01-17 16:54:11 +0100920 let fid = FuncId::try_from(regs[0] as u32)?;
921
922 let msg = match fid {
923 FuncId::Error => Self::Error {
924 target_info: (regs[1] as u32).into(),
925 error_code: FfaError::try_from(regs[2] as i32)?,
Balint Dobszayb727aab2025-04-07 10:24:59 +0200926 error_arg: regs[3] as u32,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100927 },
928 FuncId::Success32 => Self::Success {
929 target_info: regs[1] as u32,
930 args: SuccessArgs::Result32([
931 regs[2] as u32,
932 regs[3] as u32,
933 regs[4] as u32,
934 regs[5] as u32,
935 regs[6] as u32,
936 regs[7] as u32,
937 ]),
938 },
939 FuncId::Success64 => Self::Success {
940 target_info: regs[1] as u32,
941 args: SuccessArgs::Result64([regs[2], regs[3], regs[4], regs[5], regs[6], regs[7]]),
942 },
943 FuncId::Interrupt => Self::Interrupt {
944 target_info: (regs[1] as u32).into(),
945 interrupt_id: regs[2] as u32,
946 },
947 FuncId::Version => Self::Version {
Tomás González83146af2025-03-04 11:32:41 +0000948 input_version: (regs[1] as u32).try_into()?,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100949 },
950 FuncId::Features => Self::Features {
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100951 feat_id: (regs[1] as u32).into(),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100952 input_properties: regs[2] as u32,
953 },
954 FuncId::RxAcquire => Self::RxAcquire {
955 vm_id: regs[1] as u16,
956 },
957 FuncId::RxRelease => Self::RxRelease {
958 vm_id: regs[1] as u16,
959 },
960 FuncId::RxTxMap32 => {
961 let addr = RxTxAddr::Addr32 {
962 rx: regs[2] as u32,
963 tx: regs[1] as u32,
964 };
965 let page_cnt = regs[3] as u32;
966
967 Self::RxTxMap { addr, page_cnt }
968 }
969 FuncId::RxTxMap64 => {
970 let addr = RxTxAddr::Addr64 {
971 rx: regs[2],
972 tx: regs[1],
973 };
974 let page_cnt = regs[3] as u32;
975
976 Self::RxTxMap { addr, page_cnt }
977 }
978 FuncId::RxTxUnmap => Self::RxTxUnmap { id: regs[1] as u16 },
979 FuncId::PartitionInfoGet => {
980 let uuid_words = [
981 regs[1] as u32,
982 regs[2] as u32,
983 regs[3] as u32,
984 regs[4] as u32,
985 ];
986 let mut bytes: [u8; 16] = [0; 16];
987 for (i, b) in uuid_words.iter().flat_map(|w| w.to_le_bytes()).enumerate() {
988 bytes[i] = b;
989 }
990 Self::PartitionInfoGet {
991 uuid: Uuid::from_bytes(bytes),
992 flags: regs[5] as u32,
993 }
994 }
995 FuncId::IdGet => Self::IdGet,
996 FuncId::SpmIdGet => Self::SpmIdGet,
Tomás González092202a2025-03-05 11:56:45 +0000997 FuncId::MsgWait => Self::MsgWait {
998 flags: if version >= Version(1, 2) {
999 Some(MsgWaitFlags::try_from(regs[2] as u32)?)
1000 } else {
1001 None
1002 },
1003 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001004 FuncId::Yield => Self::Yield,
1005 FuncId::Run => Self::Run {
1006 target_info: (regs[1] as u32).into(),
1007 },
1008 FuncId::NormalWorldResume => Self::NormalWorldResume,
Tomás González17b92442025-03-10 16:45:04 +00001009 FuncId::SecondaryEpRegister32 => Self::SecondaryEpRegister {
1010 entrypoint: SecondaryEpRegisterAddr::Addr32(regs[1] as u32),
1011 },
1012 FuncId::SecondaryEpRegister64 => Self::SecondaryEpRegister {
1013 entrypoint: SecondaryEpRegisterAddr::Addr64(regs[1]),
1014 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001015 FuncId::MsgSend2 => Self::MsgSend2 {
1016 sender_vm_id: regs[1] as u16,
1017 flags: regs[2] as u32,
1018 },
1019 FuncId::MsgSendDirectReq32 => Self::MsgSendDirectReq {
1020 src_id: (regs[1] >> 16) as u16,
1021 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001022 args: if (regs[2] as u32 & DirectMsgArgs::FWK_MSG_BITS) != 0 {
1023 match regs[2] as u32 {
1024 DirectMsgArgs::VERSION_REQ => DirectMsgArgs::VersionReq {
1025 version: Version::try_from(regs[3] as u32)?,
1026 },
1027 DirectMsgArgs::POWER_PSCI_REQ => DirectMsgArgs::PowerPsciReq32 {
Tomás González67f92c72025-03-20 16:50:42 +00001028 params: [
1029 regs[3] as u32,
1030 regs[4] as u32,
1031 regs[5] as u32,
1032 regs[6] as u32,
1033 ],
Tomás González4d5b0ba2025-03-03 17:15:55 +00001034 },
1035 DirectMsgArgs::POWER_WARM_BOOT_REQ => DirectMsgArgs::PowerWarmBootReq {
1036 boot_type: WarmBootType::try_from(regs[3] as u32)?,
1037 },
1038 DirectMsgArgs::VM_CREATED => DirectMsgArgs::VmCreated {
1039 handle: memory_management::Handle::from([
1040 regs[3] as u32,
1041 regs[4] as u32,
1042 ]),
1043 vm_id: regs[5] as u16,
1044 },
1045 DirectMsgArgs::VM_DESTRUCTED => DirectMsgArgs::VmDestructed {
1046 handle: memory_management::Handle::from([
1047 regs[3] as u32,
1048 regs[4] as u32,
1049 ]),
1050 vm_id: regs[5] as u16,
1051 },
1052 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1053 }
1054 } else {
1055 DirectMsgArgs::Args32([
1056 regs[3] as u32,
1057 regs[4] as u32,
1058 regs[5] as u32,
1059 regs[6] as u32,
1060 regs[7] as u32,
1061 ])
1062 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001063 },
1064 FuncId::MsgSendDirectReq64 => Self::MsgSendDirectReq {
1065 src_id: (regs[1] >> 16) as u16,
1066 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001067 args: if (regs[2] & DirectMsgArgs::FWK_MSG_BITS as u64) != 0 {
1068 match regs[2] as u32 {
1069 DirectMsgArgs::POWER_PSCI_REQ => DirectMsgArgs::PowerPsciReq64 {
Tomás González67f92c72025-03-20 16:50:42 +00001070 params: [regs[3], regs[4], regs[5], regs[6]],
Tomás González4d5b0ba2025-03-03 17:15:55 +00001071 },
1072 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1073 }
1074 } else {
1075 DirectMsgArgs::Args64([regs[3], regs[4], regs[5], regs[6], regs[7]])
1076 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001077 },
1078 FuncId::MsgSendDirectResp32 => Self::MsgSendDirectResp {
1079 src_id: (regs[1] >> 16) as u16,
1080 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001081 args: if (regs[2] as u32 & DirectMsgArgs::FWK_MSG_BITS) != 0 {
1082 match regs[2] as u32 {
1083 DirectMsgArgs::VERSION_RESP => {
1084 if regs[3] as i32 == FfaError::NotSupported.into() {
1085 DirectMsgArgs::VersionResp { version: None }
1086 } else {
1087 DirectMsgArgs::VersionResp {
1088 version: Some(Version::try_from(regs[3] as u32)?),
1089 }
1090 }
1091 }
1092 DirectMsgArgs::POWER_PSCI_RESP => DirectMsgArgs::PowerPsciResp {
1093 psci_status: regs[3] as i32,
1094 },
1095 DirectMsgArgs::VM_CREATED_ACK => DirectMsgArgs::VmCreatedAck {
1096 sp_status: (regs[3] as i32).try_into()?,
1097 },
1098 DirectMsgArgs::VM_DESTRUCTED_ACK => DirectMsgArgs::VmDestructedAck {
1099 sp_status: (regs[3] as i32).try_into()?,
1100 },
1101 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1102 }
1103 } else {
1104 DirectMsgArgs::Args32([
1105 regs[3] as u32,
1106 regs[4] as u32,
1107 regs[5] as u32,
1108 regs[6] as u32,
1109 regs[7] as u32,
1110 ])
1111 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001112 },
1113 FuncId::MsgSendDirectResp64 => Self::MsgSendDirectResp {
1114 src_id: (regs[1] >> 16) as u16,
1115 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001116 args: if (regs[2] & DirectMsgArgs::FWK_MSG_BITS as u64) != 0 {
1117 return Err(Error::UnrecognisedFwkMsg(regs[2] as u32));
1118 } else {
1119 DirectMsgArgs::Args64([regs[3], regs[4], regs[5], regs[6], regs[7]])
1120 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001121 },
1122 FuncId::MemDonate32 => Self::MemDonate {
1123 total_len: regs[1] as u32,
1124 frag_len: regs[2] as u32,
1125 buf: if regs[3] != 0 && regs[4] != 0 {
1126 Some(MemOpBuf::Buf32 {
1127 addr: regs[3] as u32,
1128 page_cnt: regs[4] as u32,
1129 })
1130 } else {
1131 None
1132 },
1133 },
1134 FuncId::MemDonate64 => Self::MemDonate {
1135 total_len: regs[1] as u32,
1136 frag_len: regs[2] as u32,
1137 buf: if regs[3] != 0 && regs[4] != 0 {
1138 Some(MemOpBuf::Buf64 {
1139 addr: regs[3],
1140 page_cnt: regs[4] as u32,
1141 })
1142 } else {
1143 None
1144 },
1145 },
1146 FuncId::MemLend32 => Self::MemLend {
1147 total_len: regs[1] as u32,
1148 frag_len: regs[2] as u32,
1149 buf: if regs[3] != 0 && regs[4] != 0 {
1150 Some(MemOpBuf::Buf32 {
1151 addr: regs[3] as u32,
1152 page_cnt: regs[4] as u32,
1153 })
1154 } else {
1155 None
1156 },
1157 },
1158 FuncId::MemLend64 => Self::MemLend {
1159 total_len: regs[1] as u32,
1160 frag_len: regs[2] as u32,
1161 buf: if regs[3] != 0 && regs[4] != 0 {
1162 Some(MemOpBuf::Buf64 {
1163 addr: regs[3],
1164 page_cnt: regs[4] as u32,
1165 })
1166 } else {
1167 None
1168 },
1169 },
1170 FuncId::MemShare32 => Self::MemShare {
1171 total_len: regs[1] as u32,
1172 frag_len: regs[2] as u32,
1173 buf: if regs[3] != 0 && regs[4] != 0 {
1174 Some(MemOpBuf::Buf32 {
1175 addr: regs[3] as u32,
1176 page_cnt: regs[4] as u32,
1177 })
1178 } else {
1179 None
1180 },
1181 },
1182 FuncId::MemShare64 => Self::MemShare {
1183 total_len: regs[1] as u32,
1184 frag_len: regs[2] as u32,
1185 buf: if regs[3] != 0 && regs[4] != 0 {
1186 Some(MemOpBuf::Buf64 {
1187 addr: regs[3],
1188 page_cnt: regs[4] as u32,
1189 })
1190 } else {
1191 None
1192 },
1193 },
1194 FuncId::MemRetrieveReq32 => Self::MemRetrieveReq {
1195 total_len: regs[1] as u32,
1196 frag_len: regs[2] as u32,
1197 buf: if regs[3] != 0 && regs[4] != 0 {
1198 Some(MemOpBuf::Buf32 {
1199 addr: regs[3] as u32,
1200 page_cnt: regs[4] as u32,
1201 })
1202 } else {
1203 None
1204 },
1205 },
1206 FuncId::MemRetrieveReq64 => Self::MemRetrieveReq {
1207 total_len: regs[1] as u32,
1208 frag_len: regs[2] as u32,
1209 buf: if regs[3] != 0 && regs[4] != 0 {
1210 Some(MemOpBuf::Buf64 {
1211 addr: regs[3],
1212 page_cnt: regs[4] as u32,
1213 })
1214 } else {
1215 None
1216 },
1217 },
1218 FuncId::MemRetrieveResp => Self::MemRetrieveResp {
1219 total_len: regs[1] as u32,
1220 frag_len: regs[2] as u32,
1221 },
1222 FuncId::MemRelinquish => Self::MemRelinquish,
1223 FuncId::MemReclaim => Self::MemReclaim {
1224 handle: memory_management::Handle::from([regs[1] as u32, regs[2] as u32]),
1225 flags: regs[3] as u32,
1226 },
1227 FuncId::MemPermGet32 => Self::MemPermGet {
1228 addr: MemAddr::Addr32(regs[1] as u32),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001229 page_cnt: if version >= Version(1, 3) {
1230 Some(regs[2] as u32)
1231 } else {
1232 None
1233 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001234 },
1235 FuncId::MemPermGet64 => Self::MemPermGet {
1236 addr: MemAddr::Addr64(regs[1]),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001237 page_cnt: if version >= Version(1, 3) {
1238 Some(regs[2] as u32)
1239 } else {
1240 None
1241 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001242 },
1243 FuncId::MemPermSet32 => Self::MemPermSet {
1244 addr: MemAddr::Addr32(regs[1] as u32),
1245 page_cnt: regs[2] as u32,
1246 mem_perm: regs[3] as u32,
1247 },
1248 FuncId::MemPermSet64 => Self::MemPermSet {
1249 addr: MemAddr::Addr64(regs[1]),
1250 page_cnt: regs[2] as u32,
1251 mem_perm: regs[3] as u32,
1252 },
1253 FuncId::ConsoleLog32 => Self::ConsoleLog {
1254 char_cnt: regs[1] as u8,
1255 char_lists: ConsoleLogChars::Reg32([
1256 regs[2] as u32,
1257 regs[3] as u32,
1258 regs[4] as u32,
1259 regs[5] as u32,
1260 regs[6] as u32,
1261 regs[7] as u32,
1262 ]),
1263 },
Tomás González7ffb6132025-04-03 12:28:58 +01001264 FuncId::NotificationBitmapCreate => {
1265 let tentative_vm_id = regs[1] as u32;
1266 if (tentative_vm_id >> 16) != 0 {
1267 return Err(Error::InvalidVmId(tentative_vm_id));
1268 }
1269 Self::NotificationBitmapCreate {
1270 vm_id: tentative_vm_id as u16,
1271 vcpu_cnt: regs[2] as u32,
1272 }
1273 }
1274 FuncId::NotificationBitmapDestroy => {
1275 let tentative_vm_id = regs[1] as u32;
1276 if (tentative_vm_id >> 16) != 0 {
1277 return Err(Error::InvalidVmId(tentative_vm_id));
1278 }
1279 Self::NotificationBitmapDestroy {
1280 vm_id: tentative_vm_id as u16,
1281 }
1282 }
1283 FuncId::NotificationBind => Self::NotificationBind {
1284 sender_id: (regs[1] >> 16) as u16,
1285 receiver_id: regs[1] as u16,
1286 flags: (regs[2] as u32).into(),
1287 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1288 },
1289 FuncId::NotificationUnbind => Self::NotificationUnBind {
1290 sender_id: (regs[1] >> 16) as u16,
1291 receiver_id: regs[1] as u16,
1292 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1293 },
1294 FuncId::NotificationSet => Self::NotificationSet {
1295 sender_id: (regs[1] >> 16) as u16,
1296 receiver_id: regs[1] as u16,
1297 flags: (regs[2] as u32).try_into()?,
1298 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1299 },
1300 FuncId::NotificationGet => Self::NotificationGet {
1301 vcpu_id: (regs[1] >> 16) as u16,
1302 endpoint_id: regs[1] as u16,
1303 flags: (regs[2] as u32).into(),
1304 },
1305 FuncId::NotificationInfoGet32 => Self::NotificationInfoGet { is_32bit: true },
1306 FuncId::NotificationInfoGet64 => Self::NotificationInfoGet { is_32bit: false },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01001307 FuncId::El3IntrHandle => Self::El3IntrHandle,
Balint Dobszayde0dc802025-02-28 14:16:52 +01001308 _ => panic!("Invalid number of registers (8) for function {:#x?}", fid),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001309 };
1310
1311 Ok(msg)
1312 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01001313
Balint Dobszayde0dc802025-02-28 14:16:52 +01001314 fn unpack_regs18(version: Version, regs: &[u64; 18]) -> Result<Self, Error> {
1315 assert!(version >= Version(1, 2));
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001316
Balint Dobszayde0dc802025-02-28 14:16:52 +01001317 let fid = FuncId::try_from(regs[0] as u32)?;
1318
1319 let msg = match fid {
1320 FuncId::Success64 => Self::Success {
1321 target_info: regs[1] as u32,
1322 args: SuccessArgs::Result64_2(regs[2..18].try_into().unwrap()),
1323 },
1324 FuncId::MsgSendDirectReq64_2 => Self::MsgSendDirectReq2 {
1325 src_id: (regs[1] >> 16) as u16,
1326 dst_id: regs[1] as u16,
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00001327 uuid: Uuid::from_u64_pair(regs[2].swap_bytes(), regs[3].swap_bytes()),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001328 args: DirectMsg2Args(regs[4..18].try_into().unwrap()),
1329 },
1330 FuncId::MsgSendDirectResp64_2 => Self::MsgSendDirectResp2 {
1331 src_id: (regs[1] >> 16) as u16,
1332 dst_id: regs[1] as u16,
1333 args: DirectMsg2Args(regs[4..18].try_into().unwrap()),
1334 },
1335 FuncId::ConsoleLog64 => Self::ConsoleLog {
1336 char_cnt: regs[1] as u8,
1337 char_lists: ConsoleLogChars::Reg64(regs[2..18].try_into().unwrap()),
1338 },
Tomás González0a058bc2025-03-11 11:20:55 +00001339 FuncId::PartitionInfoGetRegs => {
1340 // Bits[15:0]: Start index
1341 let start_index = (regs[3] & 0xffff) as u16;
1342 let info_tag = ((regs[3] >> 16) & 0xffff) as u16;
1343 Self::PartitionInfoGetRegs {
1344 uuid: Uuid::from_u64_pair(regs[1].swap_bytes(), regs[2].swap_bytes()),
1345 start_index,
1346 info_tag: if start_index == 0 && info_tag != 0 {
1347 return Err(Error::InvalidInformationTag(info_tag));
1348 } else {
1349 info_tag
1350 },
1351 }
1352 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001353 _ => panic!("Invalid number of registers (18) for function {:#x?}", fid),
1354 };
1355
1356 Ok(msg)
Balint Dobszay3aad9572025-01-17 16:54:11 +01001357 }
1358
Balint Dobszaya5846852025-02-26 15:38:53 +01001359 /// Create register contents for an interface.
Balint Dobszayde0dc802025-02-28 14:16:52 +01001360 pub fn to_regs(&self, version: Version, regs: &mut [u64]) {
1361 let reg_cnt = regs.len();
1362
1363 match reg_cnt {
1364 8 => {
1365 assert!(version <= Version(1, 1));
1366 self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
1367 }
1368 18 => {
1369 assert!(version >= Version(1, 2));
1370
1371 match self {
1372 Interface::ConsoleLog {
1373 char_lists: ConsoleLogChars::Reg64(_),
1374 ..
1375 }
1376 | Interface::Success {
1377 args: SuccessArgs::Result64_2(_),
1378 ..
1379 }
1380 | Interface::MsgSendDirectReq2 { .. }
Tomás González0a058bc2025-03-11 11:20:55 +00001381 | Interface::MsgSendDirectResp2 { .. }
1382 | Interface::PartitionInfoGetRegs { .. } => {
Balint Dobszayde0dc802025-02-28 14:16:52 +01001383 self.pack_regs18(version, regs.try_into().unwrap());
1384 }
1385 _ => {
1386 self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
1387 }
1388 }
1389 }
1390 _ => panic!("Invalid number of registers {}", reg_cnt),
1391 }
1392 }
1393
1394 fn pack_regs8(&self, version: Version, a: &mut [u64; 8]) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001395 a.fill(0);
1396 if let Some(function_id) = self.function_id() {
1397 a[0] = function_id as u64;
1398 }
1399
1400 match *self {
1401 Interface::Error {
1402 target_info,
1403 error_code,
Balint Dobszayb727aab2025-04-07 10:24:59 +02001404 error_arg,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001405 } => {
1406 a[1] = u32::from(target_info).into();
1407 a[2] = (error_code as u32).into();
Balint Dobszayb727aab2025-04-07 10:24:59 +02001408 a[3] = error_arg.into();
Balint Dobszay3aad9572025-01-17 16:54:11 +01001409 }
1410 Interface::Success { target_info, args } => {
1411 a[1] = target_info.into();
1412 match args {
1413 SuccessArgs::Result32(regs) => {
1414 a[2] = regs[0].into();
1415 a[3] = regs[1].into();
1416 a[4] = regs[2].into();
1417 a[5] = regs[3].into();
1418 a[6] = regs[4].into();
1419 a[7] = regs[5].into();
1420 }
1421 SuccessArgs::Result64(regs) => {
1422 a[2] = regs[0];
1423 a[3] = regs[1];
1424 a[4] = regs[2];
1425 a[5] = regs[3];
1426 a[6] = regs[4];
1427 a[7] = regs[5];
1428 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001429 _ => panic!("{:#x?} requires 18 registers", args),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001430 }
1431 }
1432 Interface::Interrupt {
1433 target_info,
1434 interrupt_id,
1435 } => {
1436 a[1] = u32::from(target_info).into();
1437 a[2] = interrupt_id.into();
1438 }
1439 Interface::Version { input_version } => {
1440 a[1] = u32::from(input_version).into();
1441 }
1442 Interface::VersionOut { output_version } => {
1443 a[0] = u32::from(output_version).into();
1444 }
1445 Interface::Features {
1446 feat_id,
1447 input_properties,
1448 } => {
1449 a[1] = u32::from(feat_id).into();
1450 a[2] = input_properties.into();
1451 }
1452 Interface::RxAcquire { vm_id } => {
1453 a[1] = vm_id.into();
1454 }
1455 Interface::RxRelease { vm_id } => {
1456 a[1] = vm_id.into();
1457 }
1458 Interface::RxTxMap { addr, page_cnt } => {
1459 match addr {
1460 RxTxAddr::Addr32 { rx, tx } => {
1461 a[1] = tx.into();
1462 a[2] = rx.into();
1463 }
1464 RxTxAddr::Addr64 { rx, tx } => {
1465 a[1] = tx;
1466 a[2] = rx;
1467 }
1468 }
1469 a[3] = page_cnt.into();
1470 }
1471 Interface::RxTxUnmap { id } => {
1472 a[1] = id.into();
1473 }
1474 Interface::PartitionInfoGet { uuid, flags } => {
1475 let bytes = uuid.into_bytes();
1476 a[1] = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]).into();
1477 a[2] = u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]).into();
1478 a[3] = u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]).into();
1479 a[4] = u32::from_le_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]).into();
1480 a[5] = flags.into();
1481 }
Tomás González092202a2025-03-05 11:56:45 +00001482 Interface::MsgWait { flags } => {
1483 if version >= Version(1, 2) {
1484 if let Some(flags) = flags {
1485 a[2] = u32::from(flags).into();
1486 }
1487 }
1488 }
1489 Interface::IdGet | Interface::SpmIdGet | Interface::Yield => {}
Balint Dobszay3aad9572025-01-17 16:54:11 +01001490 Interface::Run { target_info } => {
1491 a[1] = u32::from(target_info).into();
1492 }
1493 Interface::NormalWorldResume => {}
Tomás González17b92442025-03-10 16:45:04 +00001494 Interface::SecondaryEpRegister { entrypoint } => match entrypoint {
1495 SecondaryEpRegisterAddr::Addr32(addr) => a[1] = addr as u64,
1496 SecondaryEpRegisterAddr::Addr64(addr) => a[1] = addr,
1497 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001498 Interface::MsgSend2 {
1499 sender_vm_id,
1500 flags,
1501 } => {
1502 a[1] = sender_vm_id.into();
1503 a[2] = flags.into();
1504 }
1505 Interface::MsgSendDirectReq {
1506 src_id,
1507 dst_id,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001508 args,
1509 } => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +01001510 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Balint Dobszay3aad9572025-01-17 16:54:11 +01001511 match args {
1512 DirectMsgArgs::Args32(args) => {
1513 a[3] = args[0].into();
1514 a[4] = args[1].into();
1515 a[5] = args[2].into();
1516 a[6] = args[3].into();
1517 a[7] = args[4].into();
1518 }
1519 DirectMsgArgs::Args64(args) => {
1520 a[3] = args[0];
1521 a[4] = args[1];
1522 a[5] = args[2];
1523 a[6] = args[3];
1524 a[7] = args[4];
1525 }
Tomás González4d5b0ba2025-03-03 17:15:55 +00001526 DirectMsgArgs::VersionReq { version } => {
1527 a[2] = DirectMsgArgs::VERSION_REQ.into();
1528 a[3] = u32::from(version).into();
1529 }
Tomás González67f92c72025-03-20 16:50:42 +00001530 DirectMsgArgs::PowerPsciReq32 { 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].into();
1533 a[4] = params[1].into();
1534 a[5] = params[2].into();
1535 a[6] = params[3].into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00001536 }
Tomás González67f92c72025-03-20 16:50:42 +00001537 DirectMsgArgs::PowerPsciReq64 { params } => {
Tomás González4d5b0ba2025-03-03 17:15:55 +00001538 a[2] = DirectMsgArgs::POWER_PSCI_REQ.into();
Tomás González67f92c72025-03-20 16:50:42 +00001539 a[3] = params[0];
1540 a[4] = params[1];
1541 a[5] = params[2];
1542 a[6] = params[3];
Tomás González4d5b0ba2025-03-03 17:15:55 +00001543 }
1544 DirectMsgArgs::PowerWarmBootReq { boot_type } => {
1545 a[2] = DirectMsgArgs::POWER_WARM_BOOT_REQ.into();
1546 a[3] = u32::from(boot_type).into();
1547 }
1548 DirectMsgArgs::VmCreated { handle, vm_id } => {
1549 a[2] = DirectMsgArgs::VM_CREATED.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 DirectMsgArgs::VmDestructed { handle, vm_id } => {
1556 a[2] = DirectMsgArgs::VM_DESTRUCTED.into();
1557 let handle_regs: [u32; 2] = handle.into();
1558 a[3] = handle_regs[0].into();
1559 a[4] = handle_regs[1].into();
1560 a[5] = vm_id.into();
1561 }
1562 _ => panic!("Malformed MsgSendDirectReq interface"),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001563 }
1564 }
1565 Interface::MsgSendDirectResp {
1566 src_id,
1567 dst_id,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001568 args,
1569 } => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +01001570 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Balint Dobszay3aad9572025-01-17 16:54:11 +01001571 match args {
1572 DirectMsgArgs::Args32(args) => {
1573 a[3] = args[0].into();
1574 a[4] = args[1].into();
1575 a[5] = args[2].into();
1576 a[6] = args[3].into();
1577 a[7] = args[4].into();
1578 }
1579 DirectMsgArgs::Args64(args) => {
1580 a[3] = args[0];
1581 a[4] = args[1];
1582 a[5] = args[2];
1583 a[6] = args[3];
1584 a[7] = args[4];
1585 }
Tomás González4d5b0ba2025-03-03 17:15:55 +00001586 DirectMsgArgs::VersionResp { version } => {
1587 a[2] = DirectMsgArgs::VERSION_RESP.into();
1588 match version {
Tomás González67f92c72025-03-20 16:50:42 +00001589 None => a[3] = (i32::from(FfaError::NotSupported) as u32).into(),
Tomás González4d5b0ba2025-03-03 17:15:55 +00001590 Some(ver) => a[3] = u32::from(ver).into(),
1591 }
1592 }
1593 DirectMsgArgs::PowerPsciResp { psci_status } => {
1594 a[2] = DirectMsgArgs::POWER_PSCI_RESP.into();
1595 a[3] = psci_status as u64;
1596 }
1597 DirectMsgArgs::VmCreatedAck { sp_status } => {
1598 a[2] = DirectMsgArgs::VM_CREATED_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 DirectMsgArgs::VmDestructedAck { sp_status } => {
1602 a[2] = DirectMsgArgs::VM_DESTRUCTED_ACK.into();
Tomás González67f92c72025-03-20 16:50:42 +00001603 a[3] = (i32::from(sp_status) as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00001604 }
1605 _ => panic!("Malformed MsgSendDirectResp interface"),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001606 }
1607 }
1608 Interface::MemDonate {
1609 total_len,
1610 frag_len,
1611 buf,
1612 } => {
1613 a[1] = total_len.into();
1614 a[2] = frag_len.into();
1615 (a[3], a[4]) = match buf {
1616 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1617 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1618 None => (0, 0),
1619 };
1620 }
1621 Interface::MemLend {
1622 total_len,
1623 frag_len,
1624 buf,
1625 } => {
1626 a[1] = total_len.into();
1627 a[2] = frag_len.into();
1628 (a[3], a[4]) = match buf {
1629 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1630 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1631 None => (0, 0),
1632 };
1633 }
1634 Interface::MemShare {
1635 total_len,
1636 frag_len,
1637 buf,
1638 } => {
1639 a[1] = total_len.into();
1640 a[2] = frag_len.into();
1641 (a[3], a[4]) = match buf {
1642 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1643 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1644 None => (0, 0),
1645 };
1646 }
1647 Interface::MemRetrieveReq {
1648 total_len,
1649 frag_len,
1650 buf,
1651 } => {
1652 a[1] = total_len.into();
1653 a[2] = frag_len.into();
1654 (a[3], a[4]) = match buf {
1655 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1656 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1657 None => (0, 0),
1658 };
1659 }
1660 Interface::MemRetrieveResp {
1661 total_len,
1662 frag_len,
1663 } => {
1664 a[1] = total_len.into();
1665 a[2] = frag_len.into();
1666 }
1667 Interface::MemRelinquish => {}
1668 Interface::MemReclaim { handle, flags } => {
1669 let handle_regs: [u32; 2] = handle.into();
1670 a[1] = handle_regs[0].into();
1671 a[2] = handle_regs[1].into();
1672 a[3] = flags.into();
1673 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001674 Interface::MemPermGet { addr, page_cnt } => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001675 a[1] = match addr {
1676 MemAddr::Addr32(addr) => addr.into(),
1677 MemAddr::Addr64(addr) => addr,
1678 };
Balint Dobszayde0dc802025-02-28 14:16:52 +01001679 a[2] = if version >= Version(1, 3) {
1680 page_cnt.unwrap().into()
1681 } else {
1682 assert!(page_cnt.is_none());
1683 0
1684 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01001685 }
1686 Interface::MemPermSet {
1687 addr,
1688 page_cnt,
1689 mem_perm,
1690 } => {
1691 a[1] = match addr {
1692 MemAddr::Addr32(addr) => addr.into(),
1693 MemAddr::Addr64(addr) => addr,
1694 };
1695 a[2] = page_cnt.into();
1696 a[3] = mem_perm.into();
1697 }
1698 Interface::ConsoleLog {
1699 char_cnt,
1700 char_lists,
1701 } => {
1702 a[1] = char_cnt.into();
1703 match char_lists {
1704 ConsoleLogChars::Reg32(regs) => {
1705 a[2] = regs[0].into();
1706 a[3] = regs[1].into();
1707 a[4] = regs[2].into();
1708 a[5] = regs[3].into();
1709 a[6] = regs[4].into();
1710 a[7] = regs[5].into();
1711 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001712 _ => panic!("{:#x?} requires 18 registers", char_lists),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001713 }
1714 }
Tomás González7ffb6132025-04-03 12:28:58 +01001715 Interface::NotificationBitmapCreate { vm_id, vcpu_cnt } => {
1716 a[1] = vm_id.into();
1717 a[2] = vcpu_cnt.into();
1718 }
1719 Interface::NotificationBitmapDestroy { vm_id } => {
1720 a[1] = vm_id.into();
1721 }
1722 Interface::NotificationBind {
1723 sender_id,
1724 receiver_id,
1725 flags,
1726 bitmap,
1727 } => {
1728 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
1729 a[2] = u32::from(flags).into();
1730 a[3] = bitmap & 0xffff_ffff;
1731 a[4] = bitmap >> 32;
1732 }
1733 Interface::NotificationUnBind {
1734 sender_id,
1735 receiver_id,
1736 bitmap,
1737 } => {
1738 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
1739 a[3] = bitmap & 0xffff_ffff;
1740 a[4] = bitmap >> 32;
1741 }
1742 Interface::NotificationSet {
1743 sender_id,
1744 receiver_id,
1745 flags,
1746 bitmap,
1747 } => {
1748 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
1749 a[2] = u32::from(flags).into();
1750 a[3] = bitmap & 0xffff_ffff;
1751 a[4] = bitmap >> 32;
1752 }
1753 Interface::NotificationGet {
1754 vcpu_id,
1755 endpoint_id,
1756 flags,
1757 } => {
1758 a[1] = (u64::from(vcpu_id) << 16) | u64::from(endpoint_id);
1759 a[2] = u32::from(flags).into();
1760 }
1761 Interface::NotificationInfoGet { .. } => {}
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01001762 Interface::El3IntrHandle => {}
Balint Dobszayde0dc802025-02-28 14:16:52 +01001763 _ => panic!("{:#x?} requires 18 registers", self),
1764 }
1765 }
1766
1767 fn pack_regs18(&self, version: Version, a: &mut [u64; 18]) {
1768 assert!(version >= Version(1, 2));
1769
1770 a.fill(0);
1771 if let Some(function_id) = self.function_id() {
1772 a[0] = function_id as u64;
1773 }
1774
1775 match *self {
1776 Interface::Success { target_info, args } => {
1777 a[1] = target_info.into();
1778 match args {
1779 SuccessArgs::Result64_2(regs) => a[2..18].copy_from_slice(&regs[..16]),
1780 _ => panic!("{:#x?} requires 8 registers", args),
1781 }
1782 }
1783 Interface::MsgSendDirectReq2 {
1784 src_id,
1785 dst_id,
1786 uuid,
1787 args,
1788 } => {
1789 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00001790 let (uuid_msb, uuid_lsb) = uuid.as_u64_pair();
1791 (a[2], a[3]) = (uuid_msb.swap_bytes(), uuid_lsb.swap_bytes());
Balint Dobszayde0dc802025-02-28 14:16:52 +01001792 a[4..18].copy_from_slice(&args.0[..14]);
1793 }
1794 Interface::MsgSendDirectResp2 {
1795 src_id,
1796 dst_id,
1797 args,
1798 } => {
1799 a[1] = ((src_id as u64) << 16) | dst_id as u64;
1800 a[2] = 0;
1801 a[3] = 0;
1802 a[4..18].copy_from_slice(&args.0[..14]);
1803 }
1804 Interface::ConsoleLog {
1805 char_cnt,
1806 char_lists,
1807 } => {
1808 a[1] = char_cnt.into();
1809 match char_lists {
1810 ConsoleLogChars::Reg64(regs) => a[2..18].copy_from_slice(&regs[..16]),
1811 _ => panic!("{:#x?} requires 8 registers", char_lists),
1812 }
1813 }
Tomás González0a058bc2025-03-11 11:20:55 +00001814 Interface::PartitionInfoGetRegs {
1815 uuid,
1816 start_index,
1817 info_tag,
1818 } => {
1819 if start_index == 0 && info_tag != 0 {
1820 panic!("Information Tag MBZ if start index is 0: {:#x?}", self);
1821 }
1822 let (uuid_msb, uuid_lsb) = uuid.as_u64_pair();
1823 (a[1], a[2]) = (uuid_msb.swap_bytes(), uuid_lsb.swap_bytes());
1824 a[3] = (u64::from(info_tag) << 16) | u64::from(start_index);
1825 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001826 _ => panic!("{:#x?} requires 8 registers", self),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001827 }
1828 }
1829
Balint Dobszaya5846852025-02-26 15:38:53 +01001830 /// Helper function to create an `FFA_SUCCESS` interface without any arguments.
Balint Dobszay3aad9572025-01-17 16:54:11 +01001831 pub fn success32_noargs() -> Self {
1832 Self::Success {
1833 target_info: 0,
1834 args: SuccessArgs::Result32([0, 0, 0, 0, 0, 0]),
1835 }
1836 }
1837
Tomás González4c8c7d22025-03-10 17:14:57 +00001838 /// Helper function to create an `FFA_SUCCESS` interface without any arguments.
1839 pub fn success64_noargs() -> Self {
1840 Self::Success {
1841 target_info: 0,
1842 args: SuccessArgs::Result64([0, 0, 0, 0, 0, 0]),
1843 }
1844 }
1845
Balint Dobszaya5846852025-02-26 15:38:53 +01001846 /// Helper function to create an `FFA_ERROR` interface with an error code.
Balint Dobszay3aad9572025-01-17 16:54:11 +01001847 pub fn error(error_code: FfaError) -> Self {
1848 Self::Error {
1849 target_info: TargetInfo {
1850 endpoint_id: 0,
1851 vcpu_id: 0,
1852 },
1853 error_code,
Balint Dobszayb727aab2025-04-07 10:24:59 +02001854 error_arg: 0,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001855 }
1856 }
1857}
1858
Balint Dobszaya5846852025-02-26 15:38:53 +01001859/// Maximum number of characters transmitted in a single `FFA_CONSOLE_LOG32` message.
1860pub const CONSOLE_LOG_32_MAX_CHAR_CNT: u8 = 24;
Balint Dobszayde0dc802025-02-28 14:16:52 +01001861/// Maximum number of characters transmitted in a single `FFA_CONSOLE_LOG64` message.
1862pub const CONSOLE_LOG_64_MAX_CHAR_CNT: u8 = 128;
Balint Dobszay3aad9572025-01-17 16:54:11 +01001863
Balint Dobszaya5846852025-02-26 15:38:53 +01001864/// Helper function to convert the "Tightly packed list of characters" format used by the
1865/// `FFA_CONSOLE_LOG` interface into a byte slice.
Balint Dobszay3aad9572025-01-17 16:54:11 +01001866pub fn parse_console_log(
1867 char_cnt: u8,
1868 char_lists: &ConsoleLogChars,
1869 log_bytes: &mut [u8],
1870) -> Result<(), FfaError> {
1871 match char_lists {
1872 ConsoleLogChars::Reg32(regs) => {
Balint Dobszaya5846852025-02-26 15:38:53 +01001873 if !(1..=CONSOLE_LOG_32_MAX_CHAR_CNT).contains(&char_cnt) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001874 return Err(FfaError::InvalidParameters);
1875 }
1876 for (i, reg) in regs.iter().enumerate() {
1877 log_bytes[4 * i..4 * (i + 1)].copy_from_slice(&reg.to_le_bytes());
1878 }
1879 }
1880 ConsoleLogChars::Reg64(regs) => {
Balint Dobszaya5846852025-02-26 15:38:53 +01001881 if !(1..=CONSOLE_LOG_64_MAX_CHAR_CNT).contains(&char_cnt) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001882 return Err(FfaError::InvalidParameters);
1883 }
1884 for (i, reg) in regs.iter().enumerate() {
1885 log_bytes[8 * i..8 * (i + 1)].copy_from_slice(&reg.to_le_bytes());
1886 }
1887 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001888 }
1889
Balint Dobszayc8802492025-01-15 18:11:39 +01001890 Ok(())
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001891}
Tomás González0a058bc2025-03-11 11:20:55 +00001892
1893#[cfg(test)]
1894mod tests {
1895 use super::*;
1896
1897 #[test]
1898 fn part_info_get_regs() {
1899 let uuid = Uuid::parse_str("a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8").unwrap();
1900 let uuid_bytes = uuid.as_bytes();
1901 let test_info_tag = 0b1101_1101;
1902 let test_start_index = 0b1101;
1903 let start_index_and_tag = (test_info_tag << 16) | test_start_index;
1904 let version = Version(1, 2);
1905
1906 // From spec:
1907 // Bytes[0...7] of UUID with byte 0 in the low-order bits.
1908 let reg_x1 = (uuid_bytes[7] as u64) << 56
1909 | (uuid_bytes[6] as u64) << 48
1910 | (uuid_bytes[5] as u64) << 40
1911 | (uuid_bytes[4] as u64) << 32
1912 | (uuid_bytes[3] as u64) << 24
1913 | (uuid_bytes[2] as u64) << 16
1914 | (uuid_bytes[1] as u64) << 8
1915 | (uuid_bytes[0] as u64);
1916
1917 // From spec:
1918 // Bytes[8...15] of UUID with byte 8 in the low-order bits.
1919 let reg_x2 = (uuid_bytes[15] as u64) << 56
1920 | (uuid_bytes[14] as u64) << 48
1921 | (uuid_bytes[13] as u64) << 40
1922 | (uuid_bytes[12] as u64) << 32
1923 | (uuid_bytes[11] as u64) << 24
1924 | (uuid_bytes[10] as u64) << 16
1925 | (uuid_bytes[9] as u64) << 8
1926 | (uuid_bytes[8] as u64);
1927
1928 // First, test for wrong tag:
1929 {
1930 let mut regs = [0u64; 18];
1931 regs[0] = FuncId::PartitionInfoGetRegs as u64;
1932 regs[1] = reg_x1;
1933 regs[2] = reg_x2;
1934 regs[3] = test_info_tag << 16;
1935
1936 assert!(Interface::from_regs(version, &regs).is_err_and(
1937 |e| e == Error::InvalidInformationTag(test_info_tag.try_into().unwrap())
1938 ));
1939 }
1940
1941 // Test for regs -> Interface -> regs
1942 {
1943 let mut orig_regs = [0u64; 18];
1944 orig_regs[0] = FuncId::PartitionInfoGetRegs as u64;
1945 orig_regs[1] = reg_x1;
1946 orig_regs[2] = reg_x2;
1947 orig_regs[3] = start_index_and_tag;
1948
1949 let mut test_regs = orig_regs.clone();
1950 let interface = Interface::from_regs(version, &mut test_regs).unwrap();
1951 match &interface {
1952 Interface::PartitionInfoGetRegs {
1953 info_tag,
1954 start_index,
1955 uuid: int_uuid,
1956 } => {
1957 assert_eq!(u64::from(*info_tag), test_info_tag);
1958 assert_eq!(u64::from(*start_index), test_start_index);
1959 assert_eq!(*int_uuid, uuid);
1960 }
1961 _ => panic!("Expecting Interface::PartitionInfoGetRegs!"),
1962 }
1963 test_regs.fill(0);
1964 interface.to_regs(version, &mut test_regs);
1965 assert_eq!(orig_regs, test_regs);
1966 }
1967
1968 // Test for Interface -> regs -> Interface
1969 {
1970 let interface = Interface::PartitionInfoGetRegs {
1971 info_tag: test_info_tag.try_into().unwrap(),
1972 start_index: test_start_index.try_into().unwrap(),
1973 uuid,
1974 };
1975
1976 let mut regs: [u64; 18] = [0; 18];
1977 interface.to_regs(version, &mut regs);
1978
1979 assert_eq!(Some(FuncId::PartitionInfoGetRegs), interface.function_id());
1980 assert_eq!(regs[0], interface.function_id().unwrap() as u64);
1981 assert_eq!(regs[1], reg_x1);
1982 assert_eq!(regs[2], reg_x2);
1983 assert_eq!(regs[3], (test_info_tag << 16) | test_start_index);
1984
1985 assert_eq!(Interface::from_regs(version, &regs).unwrap(), interface);
1986 }
1987 }
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00001988
1989 #[test]
1990 fn msg_send_direct_req2() {
1991 let uuid = Uuid::parse_str("a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8").unwrap();
1992 let uuid_bytes = uuid.as_bytes();
1993
1994 // From spec:
1995 // Bytes[0...7] of UUID with byte 0 in the low-order bits.
1996 let reg_x2 = (uuid_bytes[7] as u64) << 56
1997 | (uuid_bytes[6] as u64) << 48
1998 | (uuid_bytes[5] as u64) << 40
1999 | (uuid_bytes[4] as u64) << 32
2000 | (uuid_bytes[3] as u64) << 24
2001 | (uuid_bytes[2] as u64) << 16
2002 | (uuid_bytes[1] as u64) << 8
2003 | (uuid_bytes[0] as u64);
2004
2005 // From spec:
2006 // Bytes[8...15] of UUID with byte 8 in the low-order bits.
2007 let reg_x3 = (uuid_bytes[15] as u64) << 56
2008 | (uuid_bytes[14] as u64) << 48
2009 | (uuid_bytes[13] as u64) << 40
2010 | (uuid_bytes[12] as u64) << 32
2011 | (uuid_bytes[11] as u64) << 24
2012 | (uuid_bytes[10] as u64) << 16
2013 | (uuid_bytes[9] as u64) << 8
2014 | (uuid_bytes[8] as u64);
2015
2016 let test_sender = 0b1101_1101;
2017 let test_receiver = 0b1101;
2018 let test_sender_receiver = (test_sender << 16) | test_receiver;
2019 let version = Version(1, 2);
2020
2021 // Test for regs -> Interface -> regs
2022 {
2023 let mut orig_regs = [0u64; 18];
2024 orig_regs[0] = FuncId::MsgSendDirectReq64_2 as u64;
2025 orig_regs[1] = test_sender_receiver;
2026 orig_regs[2] = reg_x2;
2027 orig_regs[3] = reg_x3;
2028
2029 let mut test_regs = orig_regs.clone();
2030 let interface = Interface::from_regs(version, &mut test_regs).unwrap();
2031 match &interface {
2032 Interface::MsgSendDirectReq2 {
2033 dst_id,
2034 src_id,
2035 args: _,
2036 uuid: int_uuid,
2037 } => {
2038 assert_eq!(u64::from(*src_id), test_sender);
2039 assert_eq!(u64::from(*dst_id), test_receiver);
2040 assert_eq!(*int_uuid, uuid);
2041 }
2042 _ => panic!("Expecting Interface::MsgSendDirectReq2!"),
2043 }
2044 test_regs.fill(0);
2045 interface.to_regs(version, &mut test_regs);
2046 assert_eq!(orig_regs, test_regs);
2047 }
2048
2049 // Test for Interface -> regs -> Interface
2050 {
2051 let rest_of_regs: [u64; 14] = [0; 14];
2052
2053 let interface = Interface::MsgSendDirectReq2 {
2054 src_id: test_sender.try_into().unwrap(),
2055 dst_id: test_receiver.try_into().unwrap(),
2056 uuid,
2057 args: DirectMsg2Args(rest_of_regs),
2058 };
2059
2060 let mut regs: [u64; 18] = [0; 18];
2061 interface.to_regs(version, &mut regs);
2062
2063 assert_eq!(Some(FuncId::MsgSendDirectReq64_2), interface.function_id());
2064 assert_eq!(regs[0], interface.function_id().unwrap() as u64);
2065 assert_eq!(regs[1], test_sender_receiver);
2066 assert_eq!(regs[2], reg_x2);
2067 assert_eq!(regs[3], reg_x3);
2068 assert_eq!(regs[4], 0);
2069
2070 assert_eq!(Interface::from_regs(version, &regs).unwrap(), interface);
2071 }
2072 }
Tomás González0a058bc2025-03-11 11:20:55 +00002073}