blob: c6dbc59c49d6a7d7f4889408aba864b1b76a6a4e [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 {
Tomás González6ccba0a2025-04-09 13:31:29 +0100144 u32::from(*self) & (1 << 30) == 0
Balint Dobszayde0dc802025-02-28 14:16:52 +0100145 }
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));
Balint Dobszay91bea9b2025-04-09 13:16:06 +02001366 regs.fill(0);
1367
Balint Dobszayde0dc802025-02-28 14:16:52 +01001368 self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
1369 }
1370 18 => {
1371 assert!(version >= Version(1, 2));
Balint Dobszay91bea9b2025-04-09 13:16:06 +02001372 regs.fill(0);
Balint Dobszayde0dc802025-02-28 14:16:52 +01001373
1374 match self {
1375 Interface::ConsoleLog {
1376 char_lists: ConsoleLogChars::Reg64(_),
1377 ..
1378 }
1379 | Interface::Success {
1380 args: SuccessArgs::Result64_2(_),
1381 ..
1382 }
1383 | Interface::MsgSendDirectReq2 { .. }
Tomás González0a058bc2025-03-11 11:20:55 +00001384 | Interface::MsgSendDirectResp2 { .. }
1385 | Interface::PartitionInfoGetRegs { .. } => {
Balint Dobszayde0dc802025-02-28 14:16:52 +01001386 self.pack_regs18(version, regs.try_into().unwrap());
1387 }
1388 _ => {
1389 self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
1390 }
1391 }
1392 }
1393 _ => panic!("Invalid number of registers {}", reg_cnt),
1394 }
1395 }
1396
1397 fn pack_regs8(&self, version: Version, a: &mut [u64; 8]) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001398 if let Some(function_id) = self.function_id() {
1399 a[0] = function_id as u64;
1400 }
1401
1402 match *self {
1403 Interface::Error {
1404 target_info,
1405 error_code,
Balint Dobszayb727aab2025-04-07 10:24:59 +02001406 error_arg,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001407 } => {
1408 a[1] = u32::from(target_info).into();
1409 a[2] = (error_code as u32).into();
Balint Dobszayb727aab2025-04-07 10:24:59 +02001410 a[3] = error_arg.into();
Balint Dobszay3aad9572025-01-17 16:54:11 +01001411 }
1412 Interface::Success { target_info, args } => {
1413 a[1] = target_info.into();
1414 match args {
1415 SuccessArgs::Result32(regs) => {
1416 a[2] = regs[0].into();
1417 a[3] = regs[1].into();
1418 a[4] = regs[2].into();
1419 a[5] = regs[3].into();
1420 a[6] = regs[4].into();
1421 a[7] = regs[5].into();
1422 }
1423 SuccessArgs::Result64(regs) => {
1424 a[2] = regs[0];
1425 a[3] = regs[1];
1426 a[4] = regs[2];
1427 a[5] = regs[3];
1428 a[6] = regs[4];
1429 a[7] = regs[5];
1430 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001431 _ => panic!("{:#x?} requires 18 registers", args),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001432 }
1433 }
1434 Interface::Interrupt {
1435 target_info,
1436 interrupt_id,
1437 } => {
1438 a[1] = u32::from(target_info).into();
1439 a[2] = interrupt_id.into();
1440 }
1441 Interface::Version { input_version } => {
1442 a[1] = u32::from(input_version).into();
1443 }
1444 Interface::VersionOut { output_version } => {
1445 a[0] = u32::from(output_version).into();
1446 }
1447 Interface::Features {
1448 feat_id,
1449 input_properties,
1450 } => {
1451 a[1] = u32::from(feat_id).into();
1452 a[2] = input_properties.into();
1453 }
1454 Interface::RxAcquire { vm_id } => {
1455 a[1] = vm_id.into();
1456 }
1457 Interface::RxRelease { vm_id } => {
1458 a[1] = vm_id.into();
1459 }
1460 Interface::RxTxMap { addr, page_cnt } => {
1461 match addr {
1462 RxTxAddr::Addr32 { rx, tx } => {
1463 a[1] = tx.into();
1464 a[2] = rx.into();
1465 }
1466 RxTxAddr::Addr64 { rx, tx } => {
1467 a[1] = tx;
1468 a[2] = rx;
1469 }
1470 }
1471 a[3] = page_cnt.into();
1472 }
1473 Interface::RxTxUnmap { id } => {
1474 a[1] = id.into();
1475 }
1476 Interface::PartitionInfoGet { uuid, flags } => {
1477 let bytes = uuid.into_bytes();
1478 a[1] = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]).into();
1479 a[2] = u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]).into();
1480 a[3] = u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]).into();
1481 a[4] = u32::from_le_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]).into();
1482 a[5] = flags.into();
1483 }
Tomás González092202a2025-03-05 11:56:45 +00001484 Interface::MsgWait { flags } => {
1485 if version >= Version(1, 2) {
1486 if let Some(flags) = flags {
1487 a[2] = u32::from(flags).into();
1488 }
1489 }
1490 }
1491 Interface::IdGet | Interface::SpmIdGet | Interface::Yield => {}
Balint Dobszay3aad9572025-01-17 16:54:11 +01001492 Interface::Run { target_info } => {
1493 a[1] = u32::from(target_info).into();
1494 }
1495 Interface::NormalWorldResume => {}
Tomás González17b92442025-03-10 16:45:04 +00001496 Interface::SecondaryEpRegister { entrypoint } => match entrypoint {
1497 SecondaryEpRegisterAddr::Addr32(addr) => a[1] = addr as u64,
1498 SecondaryEpRegisterAddr::Addr64(addr) => a[1] = addr,
1499 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001500 Interface::MsgSend2 {
1501 sender_vm_id,
1502 flags,
1503 } => {
1504 a[1] = sender_vm_id.into();
1505 a[2] = flags.into();
1506 }
1507 Interface::MsgSendDirectReq {
1508 src_id,
1509 dst_id,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001510 args,
1511 } => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +01001512 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Balint Dobszay3aad9572025-01-17 16:54:11 +01001513 match args {
1514 DirectMsgArgs::Args32(args) => {
1515 a[3] = args[0].into();
1516 a[4] = args[1].into();
1517 a[5] = args[2].into();
1518 a[6] = args[3].into();
1519 a[7] = args[4].into();
1520 }
1521 DirectMsgArgs::Args64(args) => {
1522 a[3] = args[0];
1523 a[4] = args[1];
1524 a[5] = args[2];
1525 a[6] = args[3];
1526 a[7] = args[4];
1527 }
Tomás González4d5b0ba2025-03-03 17:15:55 +00001528 DirectMsgArgs::VersionReq { version } => {
1529 a[2] = DirectMsgArgs::VERSION_REQ.into();
1530 a[3] = u32::from(version).into();
1531 }
Tomás González67f92c72025-03-20 16:50:42 +00001532 DirectMsgArgs::PowerPsciReq32 { params } => {
Tomás González4d5b0ba2025-03-03 17:15:55 +00001533 a[2] = DirectMsgArgs::POWER_PSCI_REQ.into();
Tomás González67f92c72025-03-20 16:50:42 +00001534 a[3] = params[0].into();
1535 a[4] = params[1].into();
1536 a[5] = params[2].into();
1537 a[6] = params[3].into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00001538 }
Tomás González67f92c72025-03-20 16:50:42 +00001539 DirectMsgArgs::PowerPsciReq64 { params } => {
Tomás González4d5b0ba2025-03-03 17:15:55 +00001540 a[2] = DirectMsgArgs::POWER_PSCI_REQ.into();
Tomás González67f92c72025-03-20 16:50:42 +00001541 a[3] = params[0];
1542 a[4] = params[1];
1543 a[5] = params[2];
1544 a[6] = params[3];
Tomás González4d5b0ba2025-03-03 17:15:55 +00001545 }
1546 DirectMsgArgs::PowerWarmBootReq { boot_type } => {
1547 a[2] = DirectMsgArgs::POWER_WARM_BOOT_REQ.into();
1548 a[3] = u32::from(boot_type).into();
1549 }
1550 DirectMsgArgs::VmCreated { handle, vm_id } => {
1551 a[2] = DirectMsgArgs::VM_CREATED.into();
1552 let handle_regs: [u32; 2] = handle.into();
1553 a[3] = handle_regs[0].into();
1554 a[4] = handle_regs[1].into();
1555 a[5] = vm_id.into();
1556 }
1557 DirectMsgArgs::VmDestructed { handle, vm_id } => {
1558 a[2] = DirectMsgArgs::VM_DESTRUCTED.into();
1559 let handle_regs: [u32; 2] = handle.into();
1560 a[3] = handle_regs[0].into();
1561 a[4] = handle_regs[1].into();
1562 a[5] = vm_id.into();
1563 }
1564 _ => panic!("Malformed MsgSendDirectReq interface"),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001565 }
1566 }
1567 Interface::MsgSendDirectResp {
1568 src_id,
1569 dst_id,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001570 args,
1571 } => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +01001572 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Balint Dobszay3aad9572025-01-17 16:54:11 +01001573 match args {
1574 DirectMsgArgs::Args32(args) => {
1575 a[3] = args[0].into();
1576 a[4] = args[1].into();
1577 a[5] = args[2].into();
1578 a[6] = args[3].into();
1579 a[7] = args[4].into();
1580 }
1581 DirectMsgArgs::Args64(args) => {
1582 a[3] = args[0];
1583 a[4] = args[1];
1584 a[5] = args[2];
1585 a[6] = args[3];
1586 a[7] = args[4];
1587 }
Tomás González4d5b0ba2025-03-03 17:15:55 +00001588 DirectMsgArgs::VersionResp { version } => {
1589 a[2] = DirectMsgArgs::VERSION_RESP.into();
1590 match version {
Tomás González67f92c72025-03-20 16:50:42 +00001591 None => a[3] = (i32::from(FfaError::NotSupported) as u32).into(),
Tomás González4d5b0ba2025-03-03 17:15:55 +00001592 Some(ver) => a[3] = u32::from(ver).into(),
1593 }
1594 }
1595 DirectMsgArgs::PowerPsciResp { psci_status } => {
1596 a[2] = DirectMsgArgs::POWER_PSCI_RESP.into();
1597 a[3] = psci_status as u64;
1598 }
1599 DirectMsgArgs::VmCreatedAck { sp_status } => {
1600 a[2] = DirectMsgArgs::VM_CREATED_ACK.into();
Tomás González67f92c72025-03-20 16:50:42 +00001601 a[3] = (i32::from(sp_status) as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00001602 }
1603 DirectMsgArgs::VmDestructedAck { sp_status } => {
1604 a[2] = DirectMsgArgs::VM_DESTRUCTED_ACK.into();
Tomás González67f92c72025-03-20 16:50:42 +00001605 a[3] = (i32::from(sp_status) as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00001606 }
1607 _ => panic!("Malformed MsgSendDirectResp interface"),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001608 }
1609 }
1610 Interface::MemDonate {
1611 total_len,
1612 frag_len,
1613 buf,
1614 } => {
1615 a[1] = total_len.into();
1616 a[2] = frag_len.into();
1617 (a[3], a[4]) = match buf {
1618 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1619 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1620 None => (0, 0),
1621 };
1622 }
1623 Interface::MemLend {
1624 total_len,
1625 frag_len,
1626 buf,
1627 } => {
1628 a[1] = total_len.into();
1629 a[2] = frag_len.into();
1630 (a[3], a[4]) = match buf {
1631 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1632 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1633 None => (0, 0),
1634 };
1635 }
1636 Interface::MemShare {
1637 total_len,
1638 frag_len,
1639 buf,
1640 } => {
1641 a[1] = total_len.into();
1642 a[2] = frag_len.into();
1643 (a[3], a[4]) = match buf {
1644 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1645 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1646 None => (0, 0),
1647 };
1648 }
1649 Interface::MemRetrieveReq {
1650 total_len,
1651 frag_len,
1652 buf,
1653 } => {
1654 a[1] = total_len.into();
1655 a[2] = frag_len.into();
1656 (a[3], a[4]) = match buf {
1657 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1658 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1659 None => (0, 0),
1660 };
1661 }
1662 Interface::MemRetrieveResp {
1663 total_len,
1664 frag_len,
1665 } => {
1666 a[1] = total_len.into();
1667 a[2] = frag_len.into();
1668 }
1669 Interface::MemRelinquish => {}
1670 Interface::MemReclaim { handle, flags } => {
1671 let handle_regs: [u32; 2] = handle.into();
1672 a[1] = handle_regs[0].into();
1673 a[2] = handle_regs[1].into();
1674 a[3] = flags.into();
1675 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001676 Interface::MemPermGet { addr, page_cnt } => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001677 a[1] = match addr {
1678 MemAddr::Addr32(addr) => addr.into(),
1679 MemAddr::Addr64(addr) => addr,
1680 };
Balint Dobszayde0dc802025-02-28 14:16:52 +01001681 a[2] = if version >= Version(1, 3) {
1682 page_cnt.unwrap().into()
1683 } else {
1684 assert!(page_cnt.is_none());
1685 0
1686 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01001687 }
1688 Interface::MemPermSet {
1689 addr,
1690 page_cnt,
1691 mem_perm,
1692 } => {
1693 a[1] = match addr {
1694 MemAddr::Addr32(addr) => addr.into(),
1695 MemAddr::Addr64(addr) => addr,
1696 };
1697 a[2] = page_cnt.into();
1698 a[3] = mem_perm.into();
1699 }
1700 Interface::ConsoleLog {
1701 char_cnt,
1702 char_lists,
1703 } => {
1704 a[1] = char_cnt.into();
1705 match char_lists {
1706 ConsoleLogChars::Reg32(regs) => {
1707 a[2] = regs[0].into();
1708 a[3] = regs[1].into();
1709 a[4] = regs[2].into();
1710 a[5] = regs[3].into();
1711 a[6] = regs[4].into();
1712 a[7] = regs[5].into();
1713 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001714 _ => panic!("{:#x?} requires 18 registers", char_lists),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001715 }
1716 }
Tomás González7ffb6132025-04-03 12:28:58 +01001717 Interface::NotificationBitmapCreate { vm_id, vcpu_cnt } => {
1718 a[1] = vm_id.into();
1719 a[2] = vcpu_cnt.into();
1720 }
1721 Interface::NotificationBitmapDestroy { vm_id } => {
1722 a[1] = vm_id.into();
1723 }
1724 Interface::NotificationBind {
1725 sender_id,
1726 receiver_id,
1727 flags,
1728 bitmap,
1729 } => {
1730 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
1731 a[2] = u32::from(flags).into();
1732 a[3] = bitmap & 0xffff_ffff;
1733 a[4] = bitmap >> 32;
1734 }
1735 Interface::NotificationUnBind {
1736 sender_id,
1737 receiver_id,
1738 bitmap,
1739 } => {
1740 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
1741 a[3] = bitmap & 0xffff_ffff;
1742 a[4] = bitmap >> 32;
1743 }
1744 Interface::NotificationSet {
1745 sender_id,
1746 receiver_id,
1747 flags,
1748 bitmap,
1749 } => {
1750 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
1751 a[2] = u32::from(flags).into();
1752 a[3] = bitmap & 0xffff_ffff;
1753 a[4] = bitmap >> 32;
1754 }
1755 Interface::NotificationGet {
1756 vcpu_id,
1757 endpoint_id,
1758 flags,
1759 } => {
1760 a[1] = (u64::from(vcpu_id) << 16) | u64::from(endpoint_id);
1761 a[2] = u32::from(flags).into();
1762 }
1763 Interface::NotificationInfoGet { .. } => {}
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01001764 Interface::El3IntrHandle => {}
Balint Dobszayde0dc802025-02-28 14:16:52 +01001765 _ => panic!("{:#x?} requires 18 registers", self),
1766 }
1767 }
1768
1769 fn pack_regs18(&self, version: Version, a: &mut [u64; 18]) {
1770 assert!(version >= Version(1, 2));
1771
Balint Dobszayde0dc802025-02-28 14:16:52 +01001772 if let Some(function_id) = self.function_id() {
1773 a[0] = function_id as u64;
1774 }
1775
1776 match *self {
1777 Interface::Success { target_info, args } => {
1778 a[1] = target_info.into();
1779 match args {
1780 SuccessArgs::Result64_2(regs) => a[2..18].copy_from_slice(&regs[..16]),
1781 _ => panic!("{:#x?} requires 8 registers", args),
1782 }
1783 }
1784 Interface::MsgSendDirectReq2 {
1785 src_id,
1786 dst_id,
1787 uuid,
1788 args,
1789 } => {
1790 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00001791 let (uuid_msb, uuid_lsb) = uuid.as_u64_pair();
1792 (a[2], a[3]) = (uuid_msb.swap_bytes(), uuid_lsb.swap_bytes());
Balint Dobszayde0dc802025-02-28 14:16:52 +01001793 a[4..18].copy_from_slice(&args.0[..14]);
1794 }
1795 Interface::MsgSendDirectResp2 {
1796 src_id,
1797 dst_id,
1798 args,
1799 } => {
1800 a[1] = ((src_id as u64) << 16) | dst_id as u64;
1801 a[2] = 0;
1802 a[3] = 0;
1803 a[4..18].copy_from_slice(&args.0[..14]);
1804 }
1805 Interface::ConsoleLog {
1806 char_cnt,
1807 char_lists,
1808 } => {
1809 a[1] = char_cnt.into();
1810 match char_lists {
1811 ConsoleLogChars::Reg64(regs) => a[2..18].copy_from_slice(&regs[..16]),
1812 _ => panic!("{:#x?} requires 8 registers", char_lists),
1813 }
1814 }
Tomás González0a058bc2025-03-11 11:20:55 +00001815 Interface::PartitionInfoGetRegs {
1816 uuid,
1817 start_index,
1818 info_tag,
1819 } => {
1820 if start_index == 0 && info_tag != 0 {
1821 panic!("Information Tag MBZ if start index is 0: {:#x?}", self);
1822 }
1823 let (uuid_msb, uuid_lsb) = uuid.as_u64_pair();
1824 (a[1], a[2]) = (uuid_msb.swap_bytes(), uuid_lsb.swap_bytes());
1825 a[3] = (u64::from(info_tag) << 16) | u64::from(start_index);
1826 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001827 _ => panic!("{:#x?} requires 8 registers", self),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001828 }
1829 }
1830
Balint Dobszaya5846852025-02-26 15:38:53 +01001831 /// Helper function to create an `FFA_SUCCESS` interface without any arguments.
Balint Dobszay3aad9572025-01-17 16:54:11 +01001832 pub fn success32_noargs() -> Self {
1833 Self::Success {
1834 target_info: 0,
1835 args: SuccessArgs::Result32([0, 0, 0, 0, 0, 0]),
1836 }
1837 }
1838
Tomás González4c8c7d22025-03-10 17:14:57 +00001839 /// Helper function to create an `FFA_SUCCESS` interface without any arguments.
1840 pub fn success64_noargs() -> Self {
1841 Self::Success {
1842 target_info: 0,
1843 args: SuccessArgs::Result64([0, 0, 0, 0, 0, 0]),
1844 }
1845 }
1846
Balint Dobszaya5846852025-02-26 15:38:53 +01001847 /// Helper function to create an `FFA_ERROR` interface with an error code.
Balint Dobszay3aad9572025-01-17 16:54:11 +01001848 pub fn error(error_code: FfaError) -> Self {
1849 Self::Error {
1850 target_info: TargetInfo {
1851 endpoint_id: 0,
1852 vcpu_id: 0,
1853 },
1854 error_code,
Balint Dobszayb727aab2025-04-07 10:24:59 +02001855 error_arg: 0,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001856 }
1857 }
1858}
1859
Balint Dobszaya5846852025-02-26 15:38:53 +01001860/// Maximum number of characters transmitted in a single `FFA_CONSOLE_LOG32` message.
1861pub const CONSOLE_LOG_32_MAX_CHAR_CNT: u8 = 24;
Balint Dobszayde0dc802025-02-28 14:16:52 +01001862/// Maximum number of characters transmitted in a single `FFA_CONSOLE_LOG64` message.
1863pub const CONSOLE_LOG_64_MAX_CHAR_CNT: u8 = 128;
Balint Dobszay3aad9572025-01-17 16:54:11 +01001864
Balint Dobszaya5846852025-02-26 15:38:53 +01001865/// Helper function to convert the "Tightly packed list of characters" format used by the
1866/// `FFA_CONSOLE_LOG` interface into a byte slice.
Balint Dobszay3aad9572025-01-17 16:54:11 +01001867pub fn parse_console_log(
1868 char_cnt: u8,
1869 char_lists: &ConsoleLogChars,
1870 log_bytes: &mut [u8],
1871) -> Result<(), FfaError> {
1872 match char_lists {
1873 ConsoleLogChars::Reg32(regs) => {
Balint Dobszaya5846852025-02-26 15:38:53 +01001874 if !(1..=CONSOLE_LOG_32_MAX_CHAR_CNT).contains(&char_cnt) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001875 return Err(FfaError::InvalidParameters);
1876 }
1877 for (i, reg) in regs.iter().enumerate() {
1878 log_bytes[4 * i..4 * (i + 1)].copy_from_slice(&reg.to_le_bytes());
1879 }
1880 }
1881 ConsoleLogChars::Reg64(regs) => {
Balint Dobszaya5846852025-02-26 15:38:53 +01001882 if !(1..=CONSOLE_LOG_64_MAX_CHAR_CNT).contains(&char_cnt) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001883 return Err(FfaError::InvalidParameters);
1884 }
1885 for (i, reg) in regs.iter().enumerate() {
1886 log_bytes[8 * i..8 * (i + 1)].copy_from_slice(&reg.to_le_bytes());
1887 }
1888 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001889 }
1890
Balint Dobszayc8802492025-01-15 18:11:39 +01001891 Ok(())
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001892}
Tomás González0a058bc2025-03-11 11:20:55 +00001893
1894#[cfg(test)]
1895mod tests {
1896 use super::*;
1897
1898 #[test]
1899 fn part_info_get_regs() {
1900 let uuid = Uuid::parse_str("a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8").unwrap();
1901 let uuid_bytes = uuid.as_bytes();
1902 let test_info_tag = 0b1101_1101;
1903 let test_start_index = 0b1101;
1904 let start_index_and_tag = (test_info_tag << 16) | test_start_index;
1905 let version = Version(1, 2);
1906
1907 // From spec:
1908 // Bytes[0...7] of UUID with byte 0 in the low-order bits.
1909 let reg_x1 = (uuid_bytes[7] as u64) << 56
1910 | (uuid_bytes[6] as u64) << 48
1911 | (uuid_bytes[5] as u64) << 40
1912 | (uuid_bytes[4] as u64) << 32
1913 | (uuid_bytes[3] as u64) << 24
1914 | (uuid_bytes[2] as u64) << 16
1915 | (uuid_bytes[1] as u64) << 8
1916 | (uuid_bytes[0] as u64);
1917
1918 // From spec:
1919 // Bytes[8...15] of UUID with byte 8 in the low-order bits.
1920 let reg_x2 = (uuid_bytes[15] as u64) << 56
1921 | (uuid_bytes[14] as u64) << 48
1922 | (uuid_bytes[13] as u64) << 40
1923 | (uuid_bytes[12] as u64) << 32
1924 | (uuid_bytes[11] as u64) << 24
1925 | (uuid_bytes[10] as u64) << 16
1926 | (uuid_bytes[9] as u64) << 8
1927 | (uuid_bytes[8] as u64);
1928
1929 // First, test for wrong tag:
1930 {
1931 let mut regs = [0u64; 18];
1932 regs[0] = FuncId::PartitionInfoGetRegs as u64;
1933 regs[1] = reg_x1;
1934 regs[2] = reg_x2;
1935 regs[3] = test_info_tag << 16;
1936
1937 assert!(Interface::from_regs(version, &regs).is_err_and(
1938 |e| e == Error::InvalidInformationTag(test_info_tag.try_into().unwrap())
1939 ));
1940 }
1941
1942 // Test for regs -> Interface -> regs
1943 {
1944 let mut orig_regs = [0u64; 18];
1945 orig_regs[0] = FuncId::PartitionInfoGetRegs as u64;
1946 orig_regs[1] = reg_x1;
1947 orig_regs[2] = reg_x2;
1948 orig_regs[3] = start_index_and_tag;
1949
1950 let mut test_regs = orig_regs.clone();
1951 let interface = Interface::from_regs(version, &mut test_regs).unwrap();
1952 match &interface {
1953 Interface::PartitionInfoGetRegs {
1954 info_tag,
1955 start_index,
1956 uuid: int_uuid,
1957 } => {
1958 assert_eq!(u64::from(*info_tag), test_info_tag);
1959 assert_eq!(u64::from(*start_index), test_start_index);
1960 assert_eq!(*int_uuid, uuid);
1961 }
1962 _ => panic!("Expecting Interface::PartitionInfoGetRegs!"),
1963 }
1964 test_regs.fill(0);
1965 interface.to_regs(version, &mut test_regs);
1966 assert_eq!(orig_regs, test_regs);
1967 }
1968
1969 // Test for Interface -> regs -> Interface
1970 {
1971 let interface = Interface::PartitionInfoGetRegs {
1972 info_tag: test_info_tag.try_into().unwrap(),
1973 start_index: test_start_index.try_into().unwrap(),
1974 uuid,
1975 };
1976
1977 let mut regs: [u64; 18] = [0; 18];
1978 interface.to_regs(version, &mut regs);
1979
1980 assert_eq!(Some(FuncId::PartitionInfoGetRegs), interface.function_id());
1981 assert_eq!(regs[0], interface.function_id().unwrap() as u64);
1982 assert_eq!(regs[1], reg_x1);
1983 assert_eq!(regs[2], reg_x2);
1984 assert_eq!(regs[3], (test_info_tag << 16) | test_start_index);
1985
1986 assert_eq!(Interface::from_regs(version, &regs).unwrap(), interface);
1987 }
1988 }
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00001989
1990 #[test]
1991 fn msg_send_direct_req2() {
1992 let uuid = Uuid::parse_str("a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8").unwrap();
1993 let uuid_bytes = uuid.as_bytes();
1994
1995 // From spec:
1996 // Bytes[0...7] of UUID with byte 0 in the low-order bits.
1997 let reg_x2 = (uuid_bytes[7] as u64) << 56
1998 | (uuid_bytes[6] as u64) << 48
1999 | (uuid_bytes[5] as u64) << 40
2000 | (uuid_bytes[4] as u64) << 32
2001 | (uuid_bytes[3] as u64) << 24
2002 | (uuid_bytes[2] as u64) << 16
2003 | (uuid_bytes[1] as u64) << 8
2004 | (uuid_bytes[0] as u64);
2005
2006 // From spec:
2007 // Bytes[8...15] of UUID with byte 8 in the low-order bits.
2008 let reg_x3 = (uuid_bytes[15] as u64) << 56
2009 | (uuid_bytes[14] as u64) << 48
2010 | (uuid_bytes[13] as u64) << 40
2011 | (uuid_bytes[12] as u64) << 32
2012 | (uuid_bytes[11] as u64) << 24
2013 | (uuid_bytes[10] as u64) << 16
2014 | (uuid_bytes[9] as u64) << 8
2015 | (uuid_bytes[8] as u64);
2016
2017 let test_sender = 0b1101_1101;
2018 let test_receiver = 0b1101;
2019 let test_sender_receiver = (test_sender << 16) | test_receiver;
2020 let version = Version(1, 2);
2021
2022 // Test for regs -> Interface -> regs
2023 {
2024 let mut orig_regs = [0u64; 18];
2025 orig_regs[0] = FuncId::MsgSendDirectReq64_2 as u64;
2026 orig_regs[1] = test_sender_receiver;
2027 orig_regs[2] = reg_x2;
2028 orig_regs[3] = reg_x3;
2029
2030 let mut test_regs = orig_regs.clone();
2031 let interface = Interface::from_regs(version, &mut test_regs).unwrap();
2032 match &interface {
2033 Interface::MsgSendDirectReq2 {
2034 dst_id,
2035 src_id,
2036 args: _,
2037 uuid: int_uuid,
2038 } => {
2039 assert_eq!(u64::from(*src_id), test_sender);
2040 assert_eq!(u64::from(*dst_id), test_receiver);
2041 assert_eq!(*int_uuid, uuid);
2042 }
2043 _ => panic!("Expecting Interface::MsgSendDirectReq2!"),
2044 }
2045 test_regs.fill(0);
2046 interface.to_regs(version, &mut test_regs);
2047 assert_eq!(orig_regs, test_regs);
2048 }
2049
2050 // Test for Interface -> regs -> Interface
2051 {
2052 let rest_of_regs: [u64; 14] = [0; 14];
2053
2054 let interface = Interface::MsgSendDirectReq2 {
2055 src_id: test_sender.try_into().unwrap(),
2056 dst_id: test_receiver.try_into().unwrap(),
2057 uuid,
2058 args: DirectMsg2Args(rest_of_regs),
2059 };
2060
2061 let mut regs: [u64; 18] = [0; 18];
2062 interface.to_regs(version, &mut regs);
2063
2064 assert_eq!(Some(FuncId::MsgSendDirectReq64_2), interface.function_id());
2065 assert_eq!(regs[0], interface.function_id().unwrap() as u64);
2066 assert_eq!(regs[1], test_sender_receiver);
2067 assert_eq!(regs[2], reg_x2);
2068 assert_eq!(regs[3], reg_x3);
2069 assert_eq!(regs[4], 0);
2070
2071 assert_eq!(Interface::from_regs(version, &regs).unwrap(), interface);
2072 }
2073 }
Tomás González6ccba0a2025-04-09 13:31:29 +01002074
2075 #[test]
2076 fn is_32bit() {
2077 let interface_64 = Interface::MsgSendDirectReq {
2078 src_id: 0,
2079 dst_id: 1,
2080 args: DirectMsgArgs::Args64([0, 0, 0, 0, 0]),
2081 };
2082 assert!(!interface_64.is_32bit());
2083
2084 let interface_32 = Interface::MsgSendDirectReq {
2085 src_id: 0,
2086 dst_id: 1,
2087 args: DirectMsgArgs::Args32([0, 0, 0, 0, 0]),
2088 };
2089 assert!(interface_32.is_32bit());
2090 }
Tomás González0a058bc2025-03-11 11:20:55 +00002091}