blob: 4ef3560d20b08317162500be8eeafd3cb1fcce46 [file] [log] [blame]
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001// SPDX-FileCopyrightText: Copyright 2023 Arm Limited and/or its affiliates <open-source-office@arm.com>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4#![cfg_attr(not(test), no_std)]
Balint Dobszaya5846852025-02-26 15:38:53 +01005#![deny(clippy::undocumented_unsafe_blocks)]
6#![deny(unsafe_op_in_unsafe_fn)]
7#![doc = include_str!("../README.md")]
Balint Dobszay5bf492f2024-07-29 17:21:32 +02008
Andrew Walbran19970ba2024-11-25 15:35:00 +00009use core::fmt::{self, Debug, Display, Formatter};
Andrew Walbran44029a02024-11-25 15:34:31 +000010use num_enum::{IntoPrimitive, TryFromPrimitive};
11use thiserror::Error;
Balint Dobszay5bf492f2024-07-29 17:21:32 +020012use uuid::Uuid;
13
14pub mod boot_info;
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +010015mod ffa_v1_1;
Balint Dobszayde0dc802025-02-28 14:16:52 +010016mod ffa_v1_2;
Balint Dobszay5bf492f2024-07-29 17:21:32 +020017pub mod memory_management;
18pub mod partition_info;
19
Balint Dobszaya5846852025-02-26 15:38:53 +010020/// Constant for 4K page size. On many occasions the FF-A spec defines memory size as count of 4K
21/// pages, regardless of the current translation granule.
Balint Dobszay3aad9572025-01-17 16:54:11 +010022pub const FFA_PAGE_SIZE_4K: usize = 4096;
23
Balint Dobszaya5846852025-02-26 15:38:53 +010024/// Rich error types returned by this module. Should be converted to [`crate::FfaError`] when used
25/// with the `FFA_ERROR` interface.
Tomás González0a058bc2025-03-11 11:20:55 +000026#[derive(Debug, Error, PartialEq)]
Balint Dobszay3aad9572025-01-17 16:54:11 +010027pub enum Error {
28 #[error("Unrecognised FF-A function ID {0}")]
29 UnrecognisedFunctionId(u32),
30 #[error("Unrecognised FF-A feature ID {0}")]
31 UnrecognisedFeatureId(u8),
32 #[error("Unrecognised FF-A error code {0}")]
33 UnrecognisedErrorCode(i32),
Tomás González4d5b0ba2025-03-03 17:15:55 +000034 #[error("Unrecognised FF-A Framework Message {0}")]
35 UnrecognisedFwkMsg(u32),
Tomás González092202a2025-03-05 11:56:45 +000036 #[error("Invalid FF-A Msg Wait Flag {0}")]
37 InvalidMsgWaitFlag(u32),
Tomás González4d5b0ba2025-03-03 17:15:55 +000038 #[error("Unrecognised VM availability status {0}")]
39 UnrecognisedVmAvailabilityStatus(i32),
40 #[error("Unrecognised FF-A Warm Boot Type {0}")]
41 UnrecognisedWarmBootType(u32),
42 #[error("Invalid version {0}")]
43 InvalidVersion(u32),
Tomás González0a058bc2025-03-11 11:20:55 +000044 #[error("Invalid Information Tag {0}")]
45 InvalidInformationTag(u16),
Tomás González7ffb6132025-04-03 12:28:58 +010046 #[error("Invalid Flag for Notification Set")]
47 InvalidNotificationSetFlag(u32),
48 #[error("Invalid Vm ID")]
49 InvalidVmId(u32),
Imre Kise295adb2025-04-10 13:26:28 +020050 #[error("Invalid FF-A Partition Info Get Flag {0}")]
51 InvalidPartitionInfoGetFlag(u32),
Balint Dobszay3aad9572025-01-17 16:54:11 +010052}
53
54impl From<Error> for FfaError {
55 fn from(value: Error) -> Self {
56 match value {
57 Error::UnrecognisedFunctionId(_) | Error::UnrecognisedFeatureId(_) => {
58 Self::NotSupported
59 }
Tomás González0a058bc2025-03-11 11:20:55 +000060 Error::InvalidInformationTag(_) => Self::Retry,
Tomás González4d5b0ba2025-03-03 17:15:55 +000061 Error::UnrecognisedErrorCode(_)
62 | Error::UnrecognisedFwkMsg(_)
63 | Error::InvalidVersion(_)
Tomás González092202a2025-03-05 11:56:45 +000064 | Error::InvalidMsgWaitFlag(_)
Tomás González4d5b0ba2025-03-03 17:15:55 +000065 | Error::UnrecognisedVmAvailabilityStatus(_)
Tomás González7ffb6132025-04-03 12:28:58 +010066 | Error::InvalidNotificationSetFlag(_)
67 | Error::InvalidVmId(_)
Imre Kise295adb2025-04-10 13:26:28 +020068 | Error::UnrecognisedWarmBootType(_)
69 | Error::InvalidPartitionInfoGetFlag(_) => Self::InvalidParameters,
Balint Dobszay3aad9572025-01-17 16:54:11 +010070 }
71 }
72}
Balint Dobszay5bf492f2024-07-29 17:21:32 +020073
Balint Dobszaya5846852025-02-26 15:38:53 +010074/// 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 +020075#[derive(PartialEq, Clone, Copy)]
76pub enum Instance {
Balint Dobszaya5846852025-02-26 15:38:53 +010077 /// The instance between the SPMC and SPMD.
Balint Dobszay5bf492f2024-07-29 17:21:32 +020078 SecurePhysical,
Balint Dobszaya5846852025-02-26 15:38:53 +010079 /// The instance between the SPMC and a physical SP (contains the SP's endpoint ID).
Balint Dobszay5bf492f2024-07-29 17:21:32 +020080 SecureVirtual(u16),
81}
82
Balint Dobszaya5846852025-02-26 15:38:53 +010083/// Function IDs of the various FF-A interfaces.
Andrew Walbran969b9252024-11-25 15:35:42 +000084#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
Balint Dobszay3aad9572025-01-17 16:54:11 +010085#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedFunctionId))]
Balint Dobszay5bf492f2024-07-29 17:21:32 +020086#[repr(u32)]
87pub enum FuncId {
88 Error = 0x84000060,
89 Success32 = 0x84000061,
90 Success64 = 0xc4000061,
91 Interrupt = 0x84000062,
92 Version = 0x84000063,
93 Features = 0x84000064,
94 RxAcquire = 0x84000084,
95 RxRelease = 0x84000065,
96 RxTxMap32 = 0x84000066,
97 RxTxMap64 = 0xc4000066,
98 RxTxUnmap = 0x84000067,
99 PartitionInfoGet = 0x84000068,
Balint Dobszaye6aa4862025-02-28 16:37:56 +0100100 PartitionInfoGetRegs = 0xc400008b,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200101 IdGet = 0x84000069,
102 SpmIdGet = 0x84000085,
Balint Dobszaye6aa4862025-02-28 16:37:56 +0100103 ConsoleLog32 = 0x8400008a,
104 ConsoleLog64 = 0xc400008a,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200105 MsgWait = 0x8400006b,
106 Yield = 0x8400006c,
107 Run = 0x8400006d,
108 NormalWorldResume = 0x8400007c,
109 MsgSend2 = 0x84000086,
110 MsgSendDirectReq32 = 0x8400006f,
111 MsgSendDirectReq64 = 0xc400006f,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100112 MsgSendDirectReq64_2 = 0xc400008d,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200113 MsgSendDirectResp32 = 0x84000070,
114 MsgSendDirectResp64 = 0xc4000070,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100115 MsgSendDirectResp64_2 = 0xc400008e,
Balint Dobszaye6aa4862025-02-28 16:37:56 +0100116 NotificationBitmapCreate = 0x8400007d,
117 NotificationBitmapDestroy = 0x8400007e,
118 NotificationBind = 0x8400007f,
119 NotificationUnbind = 0x84000080,
120 NotificationSet = 0x84000081,
121 NotificationGet = 0x84000082,
122 NotificationInfoGet32 = 0x84000083,
123 NotificationInfoGet64 = 0xc4000083,
124 El3IntrHandle = 0x8400008c,
Tomás González17b92442025-03-10 16:45:04 +0000125 SecondaryEpRegister32 = 0x84000087,
126 SecondaryEpRegister64 = 0xc4000087,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200127 MemDonate32 = 0x84000071,
128 MemDonate64 = 0xc4000071,
129 MemLend32 = 0x84000072,
130 MemLend64 = 0xc4000072,
131 MemShare32 = 0x84000073,
132 MemShare64 = 0xc4000073,
133 MemRetrieveReq32 = 0x84000074,
134 MemRetrieveReq64 = 0xc4000074,
135 MemRetrieveResp = 0x84000075,
136 MemRelinquish = 0x84000076,
137 MemReclaim = 0x84000077,
138 MemPermGet32 = 0x84000088,
139 MemPermGet64 = 0xc4000088,
140 MemPermSet32 = 0x84000089,
141 MemPermSet64 = 0xc4000089,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200142}
143
Balint Dobszayde0dc802025-02-28 14:16:52 +0100144impl FuncId {
145 /// Returns true if this is a 32-bit call, or false if it is a 64-bit call.
146 pub fn is_32bit(&self) -> bool {
Tomás González6ccba0a2025-04-09 13:31:29 +0100147 u32::from(*self) & (1 << 30) == 0
Balint Dobszayde0dc802025-02-28 14:16:52 +0100148 }
149}
150
Balint Dobszaya5846852025-02-26 15:38:53 +0100151/// Error status codes used by the `FFA_ERROR` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100152#[derive(Clone, Copy, Debug, Eq, Error, IntoPrimitive, PartialEq, TryFromPrimitive)]
153#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedErrorCode))]
154#[repr(i32)]
155pub enum FfaError {
156 #[error("Not supported")]
157 NotSupported = -1,
158 #[error("Invalid parameters")]
159 InvalidParameters = -2,
160 #[error("No memory")]
161 NoMemory = -3,
162 #[error("Busy")]
163 Busy = -4,
164 #[error("Interrupted")]
165 Interrupted = -5,
166 #[error("Denied")]
167 Denied = -6,
168 #[error("Retry")]
169 Retry = -7,
170 #[error("Aborted")]
171 Aborted = -8,
172 #[error("No data")]
173 NoData = -9,
174}
175
Balint Dobszaya5846852025-02-26 15:38:53 +0100176/// Endpoint ID and vCPU ID pair, used by `FFA_ERROR`, `FFA_INTERRUPT` and `FFA_RUN` interfaces.
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200177#[derive(Debug, Eq, PartialEq, Clone, Copy)]
Balint Dobszay3aad9572025-01-17 16:54:11 +0100178pub struct TargetInfo {
179 pub endpoint_id: u16,
180 pub vcpu_id: u16,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200181}
182
Balint Dobszay3aad9572025-01-17 16:54:11 +0100183impl From<u32> for TargetInfo {
184 fn from(value: u32) -> Self {
185 Self {
186 endpoint_id: (value >> 16) as u16,
187 vcpu_id: value as u16,
188 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200189 }
190}
191
Balint Dobszay3aad9572025-01-17 16:54:11 +0100192impl From<TargetInfo> for u32 {
193 fn from(value: TargetInfo) -> Self {
Balint Dobszaye9a3e762025-02-26 17:29:57 +0100194 ((value.endpoint_id as u32) << 16) | value.vcpu_id as u32
Andrew Walbran0d315812024-11-25 15:36:36 +0000195 }
Balint Dobszay3aad9572025-01-17 16:54:11 +0100196}
Andrew Walbran0d315812024-11-25 15:36:36 +0000197
Balint Dobszaya5846852025-02-26 15:38:53 +0100198/// Arguments for the `FFA_SUCCESS` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100199#[derive(Debug, Eq, PartialEq, Clone, Copy)]
200pub enum SuccessArgs {
Imre Kis54773b62025-04-10 13:47:39 +0200201 Args32([u32; 6]),
202 Args64([u64; 6]),
203 Args64_2([u64; 16]),
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200204}
205
Tomás González17b92442025-03-10 16:45:04 +0000206/// Entrypoint address argument for `FFA_SECONDARY_EP_REGISTER` interface.
207#[derive(Debug, Eq, PartialEq, Clone, Copy)]
208pub enum SecondaryEpRegisterAddr {
209 Addr32(u32),
210 Addr64(u64),
211}
212
Balint Dobszaya5846852025-02-26 15:38:53 +0100213/// Version number of the FF-A implementation, `.0` is the major, `.1` is minor the version.
Balint Dobszayde0dc802025-02-28 14:16:52 +0100214#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord)]
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200215pub struct Version(pub u16, pub u16);
216
Tomás González1f794352025-03-03 16:47:06 +0000217impl Version {
Tomás González83146af2025-03-04 11:32:41 +0000218 // The FF-A spec mandates that bit[31] of a version number must be 0
219 const MBZ_BITS: u32 = 1 << 31;
220
Tomás González1f794352025-03-03 16:47:06 +0000221 /// Returns whether the caller's version (self) is compatible with the callee's version (input
222 /// parameter)
223 pub fn is_compatible_to(&self, callee_version: &Version) -> bool {
224 self.0 == callee_version.0 && self.1 <= callee_version.1
225 }
226}
227
Tomás González83146af2025-03-04 11:32:41 +0000228impl TryFrom<u32> for Version {
229 type Error = Error;
230
231 fn try_from(val: u32) -> Result<Self, Self::Error> {
232 if (val & Self::MBZ_BITS) != 0 {
233 Err(Error::InvalidVersion(val))
234 } else {
235 Ok(Self((val >> 16) as u16, val as u16))
236 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200237 }
238}
239
240impl From<Version> for u32 {
241 fn from(v: Version) -> Self {
Tomás González83146af2025-03-04 11:32:41 +0000242 let v_u32 = ((v.0 as u32) << 16) | v.1 as u32;
243 assert!(v_u32 & Version::MBZ_BITS == 0);
244 v_u32
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200245 }
246}
247
Andrew Walbran19970ba2024-11-25 15:35:00 +0000248impl Display for Version {
249 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
250 write!(f, "{}.{}", self.0, self.1)
251 }
252}
253
254impl Debug for Version {
255 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
256 Display::fmt(self, f)
257 }
258}
259
Balint Dobszaya5846852025-02-26 15:38:53 +0100260/// Feature IDs used by the `FFA_FEATURES` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100261#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
262#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedFeatureId))]
263#[repr(u8)]
264pub enum FeatureId {
265 NotificationPendingInterrupt = 0x1,
266 ScheduleReceiverInterrupt = 0x2,
267 ManagedExitInterrupt = 0x3,
268}
Balint Dobszayc8802492025-01-15 18:11:39 +0100269
Balint Dobszaya5846852025-02-26 15:38:53 +0100270/// Arguments for the `FFA_FEATURES` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100271#[derive(Debug, Eq, PartialEq, Clone, Copy)]
272pub enum Feature {
273 FuncId(FuncId),
274 FeatureId(FeatureId),
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100275 Unknown(u32),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100276}
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200277
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100278impl From<u32> for Feature {
279 fn from(value: u32) -> Self {
280 // Bit[31] is set for all valid FF-A function IDs so we don't have to check it separately
281 if let Ok(func_id) = value.try_into() {
282 Self::FuncId(func_id)
283 } else if let Ok(feat_id) = (value as u8).try_into() {
284 Self::FeatureId(feat_id)
Balint Dobszay3aad9572025-01-17 16:54:11 +0100285 } else {
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100286 Self::Unknown(value)
287 }
Balint Dobszay3aad9572025-01-17 16:54:11 +0100288 }
289}
290
291impl From<Feature> for u32 {
292 fn from(value: Feature) -> Self {
293 match value {
294 Feature::FuncId(func_id) => (1 << 31) | func_id as u32,
295 Feature::FeatureId(feature_id) => feature_id as u32,
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100296 Feature::Unknown(id) => panic!("Unknown feature or function ID {:#x?}", id),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100297 }
298 }
299}
300
Balint Dobszaya5846852025-02-26 15:38:53 +0100301/// RXTX buffer descriptor, used by `FFA_RXTX_MAP`.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100302#[derive(Debug, Eq, PartialEq, Clone, Copy)]
303pub enum RxTxAddr {
304 Addr32 { rx: u32, tx: u32 },
305 Addr64 { rx: u64, tx: u64 },
306}
307
Imre Kise295adb2025-04-10 13:26:28 +0200308/// Flags of the `FFA_PARTITION_INFO_GET` interface.
309#[derive(Debug, Eq, PartialEq, Clone, Copy)]
310pub struct PartitionInfoGetFlags {
311 pub count_only: bool,
312}
313
314impl PartitionInfoGetFlags {
315 const RETURN_INFORMATION_TYPE_FLAG: u32 = 1 << 0;
316 const MBZ_BITS: u32 = 0xffff_fffe;
317}
318
319impl TryFrom<u32> for PartitionInfoGetFlags {
320 type Error = Error;
321
322 fn try_from(val: u32) -> Result<Self, Self::Error> {
323 if (val & Self::MBZ_BITS) != 0 {
324 Err(Error::InvalidPartitionInfoGetFlag(val))
325 } else {
326 Ok(Self {
327 count_only: val & Self::RETURN_INFORMATION_TYPE_FLAG != 0,
328 })
329 }
330 }
331}
332
333impl From<PartitionInfoGetFlags> for u32 {
334 fn from(flags: PartitionInfoGetFlags) -> Self {
335 let mut bits: u32 = 0;
336 if flags.count_only {
337 bits |= PartitionInfoGetFlags::RETURN_INFORMATION_TYPE_FLAG;
338 }
339 bits
340 }
341}
342
Tomás González4d5b0ba2025-03-03 17:15:55 +0000343/// Composite type for capturing success and error return codes for the VM availability messages.
344///
345/// Error codes are handled by the `FfaError` type. Having a separate type for errors helps using
346/// `Result<(), FfaError>`. If a single type would include both success and error values,
347/// then `Err(FfaError::Success)` would be incomprehensible.
348#[derive(Debug, Eq, PartialEq, Clone, Copy)]
349pub enum VmAvailabilityStatus {
350 Success,
351 Error(FfaError),
352}
353
354impl TryFrom<i32> for VmAvailabilityStatus {
355 type Error = Error;
356 fn try_from(value: i32) -> Result<Self, <Self as TryFrom<i32>>::Error> {
357 Ok(match value {
358 0 => Self::Success,
359 error_code => Self::Error(FfaError::try_from(error_code)?),
360 })
361 }
362}
363
364impl From<VmAvailabilityStatus> for i32 {
365 fn from(value: VmAvailabilityStatus) -> Self {
366 match value {
367 VmAvailabilityStatus::Success => 0,
368 VmAvailabilityStatus::Error(error_code) => error_code.into(),
369 }
370 }
371}
372
373/// Arguments for the Power Warm Boot `FFA_MSG_SEND_DIRECT_REQ` interface.
374#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
375#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedWarmBootType))]
376#[repr(u32)]
377pub enum WarmBootType {
378 ExitFromSuspend = 0,
379 ExitFromLowPower = 1,
380}
381
Balint Dobszaya5846852025-02-26 15:38:53 +0100382/// Arguments for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}` interfaces.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100383#[derive(Debug, Eq, PartialEq, Clone, Copy)]
384pub enum DirectMsgArgs {
385 Args32([u32; 5]),
386 Args64([u64; 5]),
Tomás González4d5b0ba2025-03-03 17:15:55 +0000387 /// Message for forwarding FFA_VERSION call from Normal world to the SPMC
388 VersionReq {
389 version: Version,
390 },
391 /// Response message to forwarded FFA_VERSION call from the Normal world
392 /// Contains the version returned by the SPMC or None
393 VersionResp {
394 version: Option<Version>,
395 },
396 /// Message for a power management operation initiated by a PSCI function
397 PowerPsciReq32 {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000398 // params[i]: Input parameter in w[i] in PSCI function invocation at EL3.
Tomás González67f92c72025-03-20 16:50:42 +0000399 // params[0]: Function ID.
400 params: [u32; 4],
Tomás González4d5b0ba2025-03-03 17:15:55 +0000401 },
402 /// Message for a power management operation initiated by a PSCI function
403 PowerPsciReq64 {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000404 // params[i]: Input parameter in x[i] in PSCI function invocation at EL3.
Tomás González67f92c72025-03-20 16:50:42 +0000405 // params[0]: Function ID.
406 params: [u64; 4],
Tomás González4d5b0ba2025-03-03 17:15:55 +0000407 },
408 /// Message for a warm boot
409 PowerWarmBootReq {
410 boot_type: WarmBootType,
411 },
412 /// Response message to indicate return status of the last power management request message
413 /// Return error code SUCCESS or DENIED as defined in PSCI spec. Caller is left to do the
414 /// parsing of the return status.
415 PowerPsciResp {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000416 psci_status: i32,
417 },
418 /// Message to signal creation of a VM
419 VmCreated {
420 // Globally unique Handle to identify a memory region that contains IMPLEMENTATION DEFINED
421 // information associated with the created VM.
422 // The invalid memory region handle must be specified by the Hypervisor if this field is not
423 // used.
424 handle: memory_management::Handle,
425 vm_id: u16,
426 },
427 /// Message to acknowledge creation of a VM
428 VmCreatedAck {
429 sp_status: VmAvailabilityStatus,
430 },
431 /// Message to signal destruction of a VM
432 VmDestructed {
433 // Globally unique Handle to identify a memory region that contains IMPLEMENTATION DEFINED
434 // information associated with the created VM.
435 // The invalid memory region handle must be specified by the Hypervisor if this field is not
436 // used.
437 handle: memory_management::Handle,
438 vm_id: u16,
439 },
440 /// Message to acknowledge destruction of a VM
441 VmDestructedAck {
442 sp_status: VmAvailabilityStatus,
443 },
444}
445
446impl DirectMsgArgs {
447 // Flags for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}` interfaces.
448
449 const FWK_MSG_BITS: u32 = 1 << 31;
450 const VERSION_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b1000;
451 const VERSION_RESP: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b1001;
452 const POWER_PSCI_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS;
453 const POWER_WARM_BOOT_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0001;
454 const POWER_PSCI_RESP: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0010;
455 const VM_CREATED: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0100;
456 const VM_CREATED_ACK: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0101;
457 const VM_DESTRUCTED: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0110;
458 const VM_DESTRUCTED_ACK: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0111;
Balint Dobszay3aad9572025-01-17 16:54:11 +0100459}
460
Balint Dobszayde0dc802025-02-28 14:16:52 +0100461/// Arguments for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}2` interfaces.
462#[derive(Debug, Eq, PartialEq, Clone, Copy)]
463pub struct DirectMsg2Args([u64; 14]);
464
Tomás González092202a2025-03-05 11:56:45 +0000465#[derive(Debug, Eq, PartialEq, Clone, Copy)]
466pub struct MsgWaitFlags {
467 retain_rx_buffer: bool,
468}
469
470impl MsgWaitFlags {
471 const RETAIN_RX_BUFFER: u32 = 0x01;
472 const MBZ_BITS: u32 = 0xfffe;
473}
474
475impl TryFrom<u32> for MsgWaitFlags {
476 type Error = Error;
477
478 fn try_from(val: u32) -> Result<Self, Self::Error> {
479 if (val & Self::MBZ_BITS) != 0 {
480 Err(Error::InvalidMsgWaitFlag(val))
481 } else {
482 Ok(MsgWaitFlags {
483 retain_rx_buffer: val & Self::RETAIN_RX_BUFFER != 0,
484 })
485 }
486 }
487}
488
489impl From<MsgWaitFlags> for u32 {
490 fn from(flags: MsgWaitFlags) -> Self {
491 let mut bits: u32 = 0;
492 if flags.retain_rx_buffer {
493 bits |= MsgWaitFlags::RETAIN_RX_BUFFER;
494 }
495 bits
496 }
497}
498
Balint Dobszaya5846852025-02-26 15:38:53 +0100499/// Descriptor for a dynamically allocated memory buffer that contains the memory transaction
Tomás Gonzálezf268e322025-03-05 11:18:11 +0000500/// descriptor.
501///
502/// Used by `FFA_MEM_{DONATE,LEND,SHARE,RETRIEVE_REQ}` interfaces, only when the TX buffer is not
503/// used to transmit the transaction descriptor.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100504#[derive(Debug, Eq, PartialEq, Clone, Copy)]
505pub enum MemOpBuf {
506 Buf32 { addr: u32, page_cnt: u32 },
507 Buf64 { addr: u64, page_cnt: u32 },
508}
509
Balint Dobszaya5846852025-02-26 15:38:53 +0100510/// Memory address argument for `FFA_MEM_PERM_{GET,SET}` interfaces.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100511#[derive(Debug, Eq, PartialEq, Clone, Copy)]
512pub enum MemAddr {
513 Addr32(u32),
514 Addr64(u64),
515}
516
Balint Dobszayde0dc802025-02-28 14:16:52 +0100517/// Argument for the `FFA_CONSOLE_LOG` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100518#[derive(Debug, Eq, PartialEq, Clone, Copy)]
519pub enum ConsoleLogChars {
520 Reg32([u32; 6]),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100521 Reg64([u64; 16]),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100522}
523
Tomás González7ffb6132025-04-03 12:28:58 +0100524#[derive(Debug, Eq, PartialEq, Clone, Copy)]
525pub struct NotificationBindFlags {
526 per_vcpu_notification: bool,
527}
528
529impl NotificationBindFlags {
530 const PER_VCPU_NOTIFICATION: u32 = 1;
531}
532
533impl From<NotificationBindFlags> for u32 {
534 fn from(flags: NotificationBindFlags) -> Self {
535 let mut bits: u32 = 0;
536 if flags.per_vcpu_notification {
537 bits |= NotificationBindFlags::PER_VCPU_NOTIFICATION;
538 }
539 bits
540 }
541}
542
543impl From<u32> for NotificationBindFlags {
544 fn from(flags: u32) -> Self {
545 Self {
546 per_vcpu_notification: flags & Self::PER_VCPU_NOTIFICATION != 0,
547 }
548 }
549}
550
551#[derive(Debug, Eq, PartialEq, Clone, Copy)]
552pub struct NotificationSetFlags {
553 delay_schedule_receiver: bool,
554 vcpu_id: Option<u16>,
555}
556
557impl NotificationSetFlags {
558 const PER_VCP_NOTIFICATION: u32 = 1 << 0;
559 const DELAY_SCHEDULE_RECEIVER: u32 = 1 << 1;
560 const VCPU_ID_SHIFT: u32 = 16;
561
562 const MBZ_BITS: u32 = 0xfffc;
563}
564
565impl From<NotificationSetFlags> for u32 {
566 fn from(flags: NotificationSetFlags) -> Self {
567 let mut bits: u32 = 0;
568
569 if flags.delay_schedule_receiver {
570 bits |= NotificationSetFlags::DELAY_SCHEDULE_RECEIVER;
571 }
572 if let Some(vcpu_id) = flags.vcpu_id {
573 bits |= NotificationSetFlags::PER_VCP_NOTIFICATION;
574 bits |= u32::from(vcpu_id) << NotificationSetFlags::VCPU_ID_SHIFT;
575 }
576
577 bits
578 }
579}
580
581impl TryFrom<u32> for NotificationSetFlags {
582 type Error = Error;
583
584 fn try_from(flags: u32) -> Result<Self, Self::Error> {
585 if (flags & Self::MBZ_BITS) != 0 {
586 return Err(Error::InvalidNotificationSetFlag(flags));
587 }
588
589 let tentative_vcpu_id = (flags >> Self::VCPU_ID_SHIFT) as u16;
590
591 let vcpu_id = if (flags & Self::PER_VCP_NOTIFICATION) != 0 {
592 Some(tentative_vcpu_id)
593 } else {
594 if tentative_vcpu_id != 0 {
595 return Err(Error::InvalidNotificationSetFlag(flags));
596 }
597 None
598 };
599
600 Ok(Self {
601 delay_schedule_receiver: (flags & Self::DELAY_SCHEDULE_RECEIVER) != 0,
602 vcpu_id,
603 })
604 }
605}
606
607#[derive(Debug, Eq, PartialEq, Clone, Copy)]
608pub struct NotificationGetFlags {
609 sp_bitmap_id: bool,
610 vm_bitmap_id: bool,
611 spm_bitmap_id: bool,
612 hyp_bitmap_id: bool,
613}
614
615impl NotificationGetFlags {
616 const SP_BITMAP_ID: u32 = 1;
617 const VM_BITMAP_ID: u32 = 1 << 1;
618 const SPM_BITMAP_ID: u32 = 1 << 2;
619 const HYP_BITMAP_ID: u32 = 1 << 3;
620}
621
622impl From<NotificationGetFlags> for u32 {
623 fn from(flags: NotificationGetFlags) -> Self {
624 let mut bits: u32 = 0;
625 if flags.sp_bitmap_id {
626 bits |= NotificationGetFlags::SP_BITMAP_ID;
627 }
628 if flags.vm_bitmap_id {
629 bits |= NotificationGetFlags::VM_BITMAP_ID;
630 }
631 if flags.spm_bitmap_id {
632 bits |= NotificationGetFlags::SPM_BITMAP_ID;
633 }
634 if flags.hyp_bitmap_id {
635 bits |= NotificationGetFlags::HYP_BITMAP_ID;
636 }
637 bits
638 }
639}
640
641impl From<u32> for NotificationGetFlags {
642 // This is a "from" instead of a "try_from" because Reserved Bits are SBZ, *not* MBZ.
643 fn from(flags: u32) -> Self {
644 Self {
645 sp_bitmap_id: (flags & Self::SP_BITMAP_ID) != 0,
646 vm_bitmap_id: (flags & Self::VM_BITMAP_ID) != 0,
647 spm_bitmap_id: (flags & Self::SPM_BITMAP_ID) != 0,
648 hyp_bitmap_id: (flags & Self::HYP_BITMAP_ID) != 0,
649 }
650 }
651}
652
Tomás Gonzálezf268e322025-03-05 11:18:11 +0000653/// FF-A "message types", the terminology used by the spec is "interfaces".
654///
655/// The interfaces are used by FF-A components for communication at an FF-A instance. The spec also
656/// describes the valid FF-A instances and conduits for each interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100657#[derive(Debug, Eq, PartialEq, Clone, Copy)]
658pub enum Interface {
659 Error {
660 target_info: TargetInfo,
661 error_code: FfaError,
Balint Dobszayb727aab2025-04-07 10:24:59 +0200662 error_arg: u32,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100663 },
664 Success {
665 target_info: u32,
666 args: SuccessArgs,
667 },
668 Interrupt {
669 target_info: TargetInfo,
670 interrupt_id: u32,
671 },
672 Version {
673 input_version: Version,
674 },
675 VersionOut {
676 output_version: Version,
677 },
678 Features {
679 feat_id: Feature,
680 input_properties: u32,
681 },
682 RxAcquire {
683 vm_id: u16,
684 },
685 RxRelease {
686 vm_id: u16,
687 },
688 RxTxMap {
689 addr: RxTxAddr,
690 page_cnt: u32,
691 },
692 RxTxUnmap {
693 id: u16,
694 },
695 PartitionInfoGet {
696 uuid: Uuid,
Imre Kise295adb2025-04-10 13:26:28 +0200697 flags: PartitionInfoGetFlags,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100698 },
Tomás González0a058bc2025-03-11 11:20:55 +0000699 PartitionInfoGetRegs {
700 uuid: Uuid,
701 start_index: u16,
702 info_tag: u16,
703 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100704 IdGet,
705 SpmIdGet,
Tomás González092202a2025-03-05 11:56:45 +0000706 MsgWait {
707 flags: Option<MsgWaitFlags>,
708 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100709 Yield,
710 Run {
711 target_info: TargetInfo,
712 },
713 NormalWorldResume,
Tomás González17b92442025-03-10 16:45:04 +0000714 SecondaryEpRegister {
715 entrypoint: SecondaryEpRegisterAddr,
716 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100717 MsgSend2 {
718 sender_vm_id: u16,
719 flags: u32,
720 },
721 MsgSendDirectReq {
722 src_id: u16,
723 dst_id: u16,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100724 args: DirectMsgArgs,
725 },
726 MsgSendDirectResp {
727 src_id: u16,
728 dst_id: u16,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100729 args: DirectMsgArgs,
730 },
Balint Dobszayde0dc802025-02-28 14:16:52 +0100731 MsgSendDirectReq2 {
732 src_id: u16,
733 dst_id: u16,
734 uuid: Uuid,
735 args: DirectMsg2Args,
736 },
737 MsgSendDirectResp2 {
738 src_id: u16,
739 dst_id: u16,
740 args: DirectMsg2Args,
741 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100742 MemDonate {
743 total_len: u32,
744 frag_len: u32,
745 buf: Option<MemOpBuf>,
746 },
747 MemLend {
748 total_len: u32,
749 frag_len: u32,
750 buf: Option<MemOpBuf>,
751 },
752 MemShare {
753 total_len: u32,
754 frag_len: u32,
755 buf: Option<MemOpBuf>,
756 },
757 MemRetrieveReq {
758 total_len: u32,
759 frag_len: u32,
760 buf: Option<MemOpBuf>,
761 },
762 MemRetrieveResp {
763 total_len: u32,
764 frag_len: u32,
765 },
766 MemRelinquish,
767 MemReclaim {
768 handle: memory_management::Handle,
769 flags: u32,
770 },
771 MemPermGet {
772 addr: MemAddr,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100773 page_cnt: Option<u32>,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100774 },
775 MemPermSet {
776 addr: MemAddr,
777 page_cnt: u32,
778 mem_perm: u32,
779 },
780 ConsoleLog {
781 char_cnt: u8,
782 char_lists: ConsoleLogChars,
783 },
Tomás González7ffb6132025-04-03 12:28:58 +0100784 NotificationBitmapCreate {
785 vm_id: u16,
786 vcpu_cnt: u32,
787 },
788 NotificationBitmapDestroy {
789 vm_id: u16,
790 },
791 NotificationBind {
792 sender_id: u16,
793 receiver_id: u16,
794 flags: NotificationBindFlags,
795 bitmap: u64,
796 },
797 NotificationUnBind {
798 sender_id: u16,
799 receiver_id: u16,
800 bitmap: u64,
801 },
802 NotificationSet {
803 sender_id: u16,
804 receiver_id: u16,
805 flags: NotificationSetFlags,
806 bitmap: u64,
807 },
808 NotificationGet {
809 vcpu_id: u16,
810 endpoint_id: u16,
811 flags: NotificationGetFlags,
812 },
813 NotificationInfoGet {
814 is_32bit: bool,
815 },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +0100816 El3IntrHandle,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100817}
818
Balint Dobszayde0dc802025-02-28 14:16:52 +0100819impl Interface {
820 /// Returns the function ID for the call, if it has one.
821 pub fn function_id(&self) -> Option<FuncId> {
822 match self {
823 Interface::Error { .. } => Some(FuncId::Error),
824 Interface::Success { args, .. } => match args {
Imre Kis54773b62025-04-10 13:47:39 +0200825 SuccessArgs::Args32(..) => Some(FuncId::Success32),
826 SuccessArgs::Args64(..) | SuccessArgs::Args64_2(..) => Some(FuncId::Success64),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100827 },
828 Interface::Interrupt { .. } => Some(FuncId::Interrupt),
829 Interface::Version { .. } => Some(FuncId::Version),
830 Interface::VersionOut { .. } => None,
831 Interface::Features { .. } => Some(FuncId::Features),
832 Interface::RxAcquire { .. } => Some(FuncId::RxAcquire),
833 Interface::RxRelease { .. } => Some(FuncId::RxRelease),
834 Interface::RxTxMap { addr, .. } => match addr {
835 RxTxAddr::Addr32 { .. } => Some(FuncId::RxTxMap32),
836 RxTxAddr::Addr64 { .. } => Some(FuncId::RxTxMap64),
837 },
838 Interface::RxTxUnmap { .. } => Some(FuncId::RxTxUnmap),
839 Interface::PartitionInfoGet { .. } => Some(FuncId::PartitionInfoGet),
Tomás González0a058bc2025-03-11 11:20:55 +0000840 Interface::PartitionInfoGetRegs { .. } => Some(FuncId::PartitionInfoGetRegs),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100841 Interface::IdGet => Some(FuncId::IdGet),
842 Interface::SpmIdGet => Some(FuncId::SpmIdGet),
Tomás González092202a2025-03-05 11:56:45 +0000843 Interface::MsgWait { .. } => Some(FuncId::MsgWait),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100844 Interface::Yield => Some(FuncId::Yield),
845 Interface::Run { .. } => Some(FuncId::Run),
846 Interface::NormalWorldResume => Some(FuncId::NormalWorldResume),
Tomás González17b92442025-03-10 16:45:04 +0000847 Interface::SecondaryEpRegister { entrypoint } => match entrypoint {
848 SecondaryEpRegisterAddr::Addr32 { .. } => Some(FuncId::SecondaryEpRegister32),
849 SecondaryEpRegisterAddr::Addr64 { .. } => Some(FuncId::SecondaryEpRegister64),
850 },
Balint Dobszayde0dc802025-02-28 14:16:52 +0100851 Interface::MsgSend2 { .. } => Some(FuncId::MsgSend2),
852 Interface::MsgSendDirectReq { args, .. } => match args {
853 DirectMsgArgs::Args32(_) => Some(FuncId::MsgSendDirectReq32),
854 DirectMsgArgs::Args64(_) => Some(FuncId::MsgSendDirectReq64),
Tomás González4d5b0ba2025-03-03 17:15:55 +0000855 DirectMsgArgs::VersionReq { .. } => Some(FuncId::MsgSendDirectReq32),
856 DirectMsgArgs::PowerPsciReq32 { .. } => Some(FuncId::MsgSendDirectReq32),
857 DirectMsgArgs::PowerPsciReq64 { .. } => Some(FuncId::MsgSendDirectReq64),
858 DirectMsgArgs::PowerWarmBootReq { .. } => Some(FuncId::MsgSendDirectReq32),
859 DirectMsgArgs::VmCreated { .. } => Some(FuncId::MsgSendDirectReq32),
860 DirectMsgArgs::VmDestructed { .. } => Some(FuncId::MsgSendDirectReq32),
861 _ => None,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100862 },
863 Interface::MsgSendDirectResp { args, .. } => match args {
864 DirectMsgArgs::Args32(_) => Some(FuncId::MsgSendDirectResp32),
865 DirectMsgArgs::Args64(_) => Some(FuncId::MsgSendDirectResp64),
Tomás González4d5b0ba2025-03-03 17:15:55 +0000866 DirectMsgArgs::VersionResp { .. } => Some(FuncId::MsgSendDirectResp32),
867 DirectMsgArgs::PowerPsciResp { .. } => Some(FuncId::MsgSendDirectResp32),
868 DirectMsgArgs::VmCreatedAck { .. } => Some(FuncId::MsgSendDirectResp32),
869 DirectMsgArgs::VmDestructedAck { .. } => Some(FuncId::MsgSendDirectResp32),
870 _ => None,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100871 },
872 Interface::MsgSendDirectReq2 { .. } => Some(FuncId::MsgSendDirectReq64_2),
873 Interface::MsgSendDirectResp2 { .. } => Some(FuncId::MsgSendDirectResp64_2),
874 Interface::MemDonate { buf, .. } => match buf {
875 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemDonate64),
876 _ => Some(FuncId::MemDonate32),
877 },
878 Interface::MemLend { buf, .. } => match buf {
879 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemLend64),
880 _ => Some(FuncId::MemLend32),
881 },
882 Interface::MemShare { buf, .. } => match buf {
883 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemShare64),
884 _ => Some(FuncId::MemShare32),
885 },
886 Interface::MemRetrieveReq { buf, .. } => match buf {
887 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemRetrieveReq64),
888 _ => Some(FuncId::MemRetrieveReq32),
889 },
890 Interface::MemRetrieveResp { .. } => Some(FuncId::MemRetrieveResp),
891 Interface::MemRelinquish => Some(FuncId::MemRelinquish),
892 Interface::MemReclaim { .. } => Some(FuncId::MemReclaim),
893 Interface::MemPermGet { addr, .. } => match addr {
894 MemAddr::Addr32(_) => Some(FuncId::MemPermGet32),
895 MemAddr::Addr64(_) => Some(FuncId::MemPermGet64),
896 },
897 Interface::MemPermSet { addr, .. } => match addr {
898 MemAddr::Addr32(_) => Some(FuncId::MemPermSet32),
899 MemAddr::Addr64(_) => Some(FuncId::MemPermSet64),
900 },
901 Interface::ConsoleLog { char_lists, .. } => match char_lists {
902 ConsoleLogChars::Reg32(_) => Some(FuncId::ConsoleLog32),
903 ConsoleLogChars::Reg64(_) => Some(FuncId::ConsoleLog64),
904 },
Tomás González7ffb6132025-04-03 12:28:58 +0100905 Interface::NotificationBitmapCreate { .. } => Some(FuncId::NotificationBitmapCreate),
906 Interface::NotificationBitmapDestroy { .. } => Some(FuncId::NotificationBitmapDestroy),
907 Interface::NotificationBind { .. } => Some(FuncId::NotificationBind),
908 Interface::NotificationUnBind { .. } => Some(FuncId::NotificationUnbind),
909 Interface::NotificationSet { .. } => Some(FuncId::NotificationSet),
910 Interface::NotificationGet { .. } => Some(FuncId::NotificationGet),
911 Interface::NotificationInfoGet { is_32bit } => match is_32bit {
912 true => Some(FuncId::NotificationInfoGet32),
913 false => Some(FuncId::NotificationInfoGet64),
914 },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +0100915 Interface::El3IntrHandle => Some(FuncId::El3IntrHandle),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100916 }
917 }
Balint Dobszay3aad9572025-01-17 16:54:11 +0100918
Balint Dobszayde0dc802025-02-28 14:16:52 +0100919 /// Returns true if this is a 32-bit call, or false if it is a 64-bit call.
920 pub fn is_32bit(&self) -> bool {
921 // TODO: self should always have a function ID?
922 self.function_id().unwrap().is_32bit()
923 }
924
925 /// Parse interface from register contents. The caller must ensure that the `regs` argument has
926 /// the correct length: 8 registers for FF-A v1.1 and lower, 18 registers for v1.2 and higher.
927 pub fn from_regs(version: Version, regs: &[u64]) -> Result<Self, Error> {
928 let reg_cnt = regs.len();
929
930 let msg = match reg_cnt {
931 8 => {
932 assert!(version <= Version(1, 1));
933 Interface::unpack_regs8(version, regs.try_into().unwrap())?
934 }
935 18 => {
936 assert!(version >= Version(1, 2));
937 match FuncId::try_from(regs[0] as u32)? {
938 FuncId::ConsoleLog64
939 | FuncId::Success64
940 | FuncId::MsgSendDirectReq64_2
Tomás González0a058bc2025-03-11 11:20:55 +0000941 | FuncId::MsgSendDirectResp64_2
942 | FuncId::PartitionInfoGetRegs => {
Balint Dobszayde0dc802025-02-28 14:16:52 +0100943 Interface::unpack_regs18(version, regs.try_into().unwrap())?
944 }
945 _ => Interface::unpack_regs8(version, regs[..8].try_into().unwrap())?,
946 }
947 }
948 _ => panic!(
949 "Invalid number of registers ({}) for FF-A version {}",
950 reg_cnt, version
951 ),
952 };
953
954 Ok(msg)
955 }
956
957 fn unpack_regs8(version: Version, regs: &[u64; 8]) -> Result<Self, Error> {
Balint Dobszay3aad9572025-01-17 16:54:11 +0100958 let fid = FuncId::try_from(regs[0] as u32)?;
959
960 let msg = match fid {
961 FuncId::Error => Self::Error {
962 target_info: (regs[1] as u32).into(),
963 error_code: FfaError::try_from(regs[2] as i32)?,
Balint Dobszayb727aab2025-04-07 10:24:59 +0200964 error_arg: regs[3] as u32,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100965 },
966 FuncId::Success32 => Self::Success {
967 target_info: regs[1] as u32,
Imre Kis54773b62025-04-10 13:47:39 +0200968 args: SuccessArgs::Args32([
Balint Dobszay3aad9572025-01-17 16:54:11 +0100969 regs[2] as u32,
970 regs[3] as u32,
971 regs[4] as u32,
972 regs[5] as u32,
973 regs[6] as u32,
974 regs[7] as u32,
975 ]),
976 },
977 FuncId::Success64 => Self::Success {
978 target_info: regs[1] as u32,
Imre Kis54773b62025-04-10 13:47:39 +0200979 args: SuccessArgs::Args64([regs[2], regs[3], regs[4], regs[5], regs[6], regs[7]]),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100980 },
981 FuncId::Interrupt => Self::Interrupt {
982 target_info: (regs[1] as u32).into(),
983 interrupt_id: regs[2] as u32,
984 },
985 FuncId::Version => Self::Version {
Tomás González83146af2025-03-04 11:32:41 +0000986 input_version: (regs[1] as u32).try_into()?,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100987 },
988 FuncId::Features => Self::Features {
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100989 feat_id: (regs[1] as u32).into(),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100990 input_properties: regs[2] as u32,
991 },
992 FuncId::RxAcquire => Self::RxAcquire {
993 vm_id: regs[1] as u16,
994 },
995 FuncId::RxRelease => Self::RxRelease {
996 vm_id: regs[1] as u16,
997 },
998 FuncId::RxTxMap32 => {
999 let addr = RxTxAddr::Addr32 {
1000 rx: regs[2] as u32,
1001 tx: regs[1] as u32,
1002 };
1003 let page_cnt = regs[3] as u32;
1004
1005 Self::RxTxMap { addr, page_cnt }
1006 }
1007 FuncId::RxTxMap64 => {
1008 let addr = RxTxAddr::Addr64 {
1009 rx: regs[2],
1010 tx: regs[1],
1011 };
1012 let page_cnt = regs[3] as u32;
1013
1014 Self::RxTxMap { addr, page_cnt }
1015 }
1016 FuncId::RxTxUnmap => Self::RxTxUnmap { id: regs[1] as u16 },
1017 FuncId::PartitionInfoGet => {
1018 let uuid_words = [
1019 regs[1] as u32,
1020 regs[2] as u32,
1021 regs[3] as u32,
1022 regs[4] as u32,
1023 ];
1024 let mut bytes: [u8; 16] = [0; 16];
1025 for (i, b) in uuid_words.iter().flat_map(|w| w.to_le_bytes()).enumerate() {
1026 bytes[i] = b;
1027 }
1028 Self::PartitionInfoGet {
1029 uuid: Uuid::from_bytes(bytes),
Imre Kise295adb2025-04-10 13:26:28 +02001030 flags: PartitionInfoGetFlags::try_from(regs[5] as u32)?,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001031 }
1032 }
1033 FuncId::IdGet => Self::IdGet,
1034 FuncId::SpmIdGet => Self::SpmIdGet,
Tomás González092202a2025-03-05 11:56:45 +00001035 FuncId::MsgWait => Self::MsgWait {
1036 flags: if version >= Version(1, 2) {
1037 Some(MsgWaitFlags::try_from(regs[2] as u32)?)
1038 } else {
1039 None
1040 },
1041 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001042 FuncId::Yield => Self::Yield,
1043 FuncId::Run => Self::Run {
1044 target_info: (regs[1] as u32).into(),
1045 },
1046 FuncId::NormalWorldResume => Self::NormalWorldResume,
Tomás González17b92442025-03-10 16:45:04 +00001047 FuncId::SecondaryEpRegister32 => Self::SecondaryEpRegister {
1048 entrypoint: SecondaryEpRegisterAddr::Addr32(regs[1] as u32),
1049 },
1050 FuncId::SecondaryEpRegister64 => Self::SecondaryEpRegister {
1051 entrypoint: SecondaryEpRegisterAddr::Addr64(regs[1]),
1052 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001053 FuncId::MsgSend2 => Self::MsgSend2 {
1054 sender_vm_id: regs[1] as u16,
1055 flags: regs[2] as u32,
1056 },
1057 FuncId::MsgSendDirectReq32 => Self::MsgSendDirectReq {
1058 src_id: (regs[1] >> 16) as u16,
1059 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001060 args: if (regs[2] as u32 & DirectMsgArgs::FWK_MSG_BITS) != 0 {
1061 match regs[2] as u32 {
1062 DirectMsgArgs::VERSION_REQ => DirectMsgArgs::VersionReq {
1063 version: Version::try_from(regs[3] as u32)?,
1064 },
1065 DirectMsgArgs::POWER_PSCI_REQ => DirectMsgArgs::PowerPsciReq32 {
Tomás González67f92c72025-03-20 16:50:42 +00001066 params: [
1067 regs[3] as u32,
1068 regs[4] as u32,
1069 regs[5] as u32,
1070 regs[6] as u32,
1071 ],
Tomás González4d5b0ba2025-03-03 17:15:55 +00001072 },
1073 DirectMsgArgs::POWER_WARM_BOOT_REQ => DirectMsgArgs::PowerWarmBootReq {
1074 boot_type: WarmBootType::try_from(regs[3] as u32)?,
1075 },
1076 DirectMsgArgs::VM_CREATED => DirectMsgArgs::VmCreated {
1077 handle: memory_management::Handle::from([
1078 regs[3] as u32,
1079 regs[4] as u32,
1080 ]),
1081 vm_id: regs[5] as u16,
1082 },
1083 DirectMsgArgs::VM_DESTRUCTED => DirectMsgArgs::VmDestructed {
1084 handle: memory_management::Handle::from([
1085 regs[3] as u32,
1086 regs[4] as u32,
1087 ]),
1088 vm_id: regs[5] as u16,
1089 },
1090 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1091 }
1092 } else {
1093 DirectMsgArgs::Args32([
1094 regs[3] as u32,
1095 regs[4] as u32,
1096 regs[5] as u32,
1097 regs[6] as u32,
1098 regs[7] as u32,
1099 ])
1100 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001101 },
1102 FuncId::MsgSendDirectReq64 => Self::MsgSendDirectReq {
1103 src_id: (regs[1] >> 16) as u16,
1104 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001105 args: if (regs[2] & DirectMsgArgs::FWK_MSG_BITS as u64) != 0 {
1106 match regs[2] as u32 {
1107 DirectMsgArgs::POWER_PSCI_REQ => DirectMsgArgs::PowerPsciReq64 {
Tomás González67f92c72025-03-20 16:50:42 +00001108 params: [regs[3], regs[4], regs[5], regs[6]],
Tomás González4d5b0ba2025-03-03 17:15:55 +00001109 },
1110 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1111 }
1112 } else {
1113 DirectMsgArgs::Args64([regs[3], regs[4], regs[5], regs[6], regs[7]])
1114 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001115 },
1116 FuncId::MsgSendDirectResp32 => Self::MsgSendDirectResp {
1117 src_id: (regs[1] >> 16) as u16,
1118 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001119 args: if (regs[2] as u32 & DirectMsgArgs::FWK_MSG_BITS) != 0 {
1120 match regs[2] as u32 {
1121 DirectMsgArgs::VERSION_RESP => {
1122 if regs[3] as i32 == FfaError::NotSupported.into() {
1123 DirectMsgArgs::VersionResp { version: None }
1124 } else {
1125 DirectMsgArgs::VersionResp {
1126 version: Some(Version::try_from(regs[3] as u32)?),
1127 }
1128 }
1129 }
1130 DirectMsgArgs::POWER_PSCI_RESP => DirectMsgArgs::PowerPsciResp {
1131 psci_status: regs[3] as i32,
1132 },
1133 DirectMsgArgs::VM_CREATED_ACK => DirectMsgArgs::VmCreatedAck {
1134 sp_status: (regs[3] as i32).try_into()?,
1135 },
1136 DirectMsgArgs::VM_DESTRUCTED_ACK => DirectMsgArgs::VmDestructedAck {
1137 sp_status: (regs[3] as i32).try_into()?,
1138 },
1139 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1140 }
1141 } else {
1142 DirectMsgArgs::Args32([
1143 regs[3] as u32,
1144 regs[4] as u32,
1145 regs[5] as u32,
1146 regs[6] as u32,
1147 regs[7] as u32,
1148 ])
1149 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001150 },
1151 FuncId::MsgSendDirectResp64 => Self::MsgSendDirectResp {
1152 src_id: (regs[1] >> 16) as u16,
1153 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001154 args: if (regs[2] & DirectMsgArgs::FWK_MSG_BITS as u64) != 0 {
1155 return Err(Error::UnrecognisedFwkMsg(regs[2] as u32));
1156 } else {
1157 DirectMsgArgs::Args64([regs[3], regs[4], regs[5], regs[6], regs[7]])
1158 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001159 },
1160 FuncId::MemDonate32 => Self::MemDonate {
1161 total_len: regs[1] as u32,
1162 frag_len: regs[2] as u32,
1163 buf: if regs[3] != 0 && regs[4] != 0 {
1164 Some(MemOpBuf::Buf32 {
1165 addr: regs[3] as u32,
1166 page_cnt: regs[4] as u32,
1167 })
1168 } else {
1169 None
1170 },
1171 },
1172 FuncId::MemDonate64 => Self::MemDonate {
1173 total_len: regs[1] as u32,
1174 frag_len: regs[2] as u32,
1175 buf: if regs[3] != 0 && regs[4] != 0 {
1176 Some(MemOpBuf::Buf64 {
1177 addr: regs[3],
1178 page_cnt: regs[4] as u32,
1179 })
1180 } else {
1181 None
1182 },
1183 },
1184 FuncId::MemLend32 => Self::MemLend {
1185 total_len: regs[1] as u32,
1186 frag_len: regs[2] as u32,
1187 buf: if regs[3] != 0 && regs[4] != 0 {
1188 Some(MemOpBuf::Buf32 {
1189 addr: regs[3] as u32,
1190 page_cnt: regs[4] as u32,
1191 })
1192 } else {
1193 None
1194 },
1195 },
1196 FuncId::MemLend64 => Self::MemLend {
1197 total_len: regs[1] as u32,
1198 frag_len: regs[2] as u32,
1199 buf: if regs[3] != 0 && regs[4] != 0 {
1200 Some(MemOpBuf::Buf64 {
1201 addr: regs[3],
1202 page_cnt: regs[4] as u32,
1203 })
1204 } else {
1205 None
1206 },
1207 },
1208 FuncId::MemShare32 => Self::MemShare {
1209 total_len: regs[1] as u32,
1210 frag_len: regs[2] as u32,
1211 buf: if regs[3] != 0 && regs[4] != 0 {
1212 Some(MemOpBuf::Buf32 {
1213 addr: regs[3] as u32,
1214 page_cnt: regs[4] as u32,
1215 })
1216 } else {
1217 None
1218 },
1219 },
1220 FuncId::MemShare64 => Self::MemShare {
1221 total_len: regs[1] as u32,
1222 frag_len: regs[2] as u32,
1223 buf: if regs[3] != 0 && regs[4] != 0 {
1224 Some(MemOpBuf::Buf64 {
1225 addr: regs[3],
1226 page_cnt: regs[4] as u32,
1227 })
1228 } else {
1229 None
1230 },
1231 },
1232 FuncId::MemRetrieveReq32 => Self::MemRetrieveReq {
1233 total_len: regs[1] as u32,
1234 frag_len: regs[2] as u32,
1235 buf: if regs[3] != 0 && regs[4] != 0 {
1236 Some(MemOpBuf::Buf32 {
1237 addr: regs[3] as u32,
1238 page_cnt: regs[4] as u32,
1239 })
1240 } else {
1241 None
1242 },
1243 },
1244 FuncId::MemRetrieveReq64 => Self::MemRetrieveReq {
1245 total_len: regs[1] as u32,
1246 frag_len: regs[2] as u32,
1247 buf: if regs[3] != 0 && regs[4] != 0 {
1248 Some(MemOpBuf::Buf64 {
1249 addr: regs[3],
1250 page_cnt: regs[4] as u32,
1251 })
1252 } else {
1253 None
1254 },
1255 },
1256 FuncId::MemRetrieveResp => Self::MemRetrieveResp {
1257 total_len: regs[1] as u32,
1258 frag_len: regs[2] as u32,
1259 },
1260 FuncId::MemRelinquish => Self::MemRelinquish,
1261 FuncId::MemReclaim => Self::MemReclaim {
1262 handle: memory_management::Handle::from([regs[1] as u32, regs[2] as u32]),
1263 flags: regs[3] as u32,
1264 },
1265 FuncId::MemPermGet32 => Self::MemPermGet {
1266 addr: MemAddr::Addr32(regs[1] as u32),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001267 page_cnt: if version >= Version(1, 3) {
1268 Some(regs[2] as u32)
1269 } else {
1270 None
1271 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001272 },
1273 FuncId::MemPermGet64 => Self::MemPermGet {
1274 addr: MemAddr::Addr64(regs[1]),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001275 page_cnt: if version >= Version(1, 3) {
1276 Some(regs[2] as u32)
1277 } else {
1278 None
1279 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001280 },
1281 FuncId::MemPermSet32 => Self::MemPermSet {
1282 addr: MemAddr::Addr32(regs[1] as u32),
1283 page_cnt: regs[2] as u32,
1284 mem_perm: regs[3] as u32,
1285 },
1286 FuncId::MemPermSet64 => Self::MemPermSet {
1287 addr: MemAddr::Addr64(regs[1]),
1288 page_cnt: regs[2] as u32,
1289 mem_perm: regs[3] as u32,
1290 },
1291 FuncId::ConsoleLog32 => Self::ConsoleLog {
1292 char_cnt: regs[1] as u8,
1293 char_lists: ConsoleLogChars::Reg32([
1294 regs[2] as u32,
1295 regs[3] as u32,
1296 regs[4] as u32,
1297 regs[5] as u32,
1298 regs[6] as u32,
1299 regs[7] as u32,
1300 ]),
1301 },
Tomás González7ffb6132025-04-03 12:28:58 +01001302 FuncId::NotificationBitmapCreate => {
1303 let tentative_vm_id = regs[1] as u32;
1304 if (tentative_vm_id >> 16) != 0 {
1305 return Err(Error::InvalidVmId(tentative_vm_id));
1306 }
1307 Self::NotificationBitmapCreate {
1308 vm_id: tentative_vm_id as u16,
1309 vcpu_cnt: regs[2] as u32,
1310 }
1311 }
1312 FuncId::NotificationBitmapDestroy => {
1313 let tentative_vm_id = regs[1] as u32;
1314 if (tentative_vm_id >> 16) != 0 {
1315 return Err(Error::InvalidVmId(tentative_vm_id));
1316 }
1317 Self::NotificationBitmapDestroy {
1318 vm_id: tentative_vm_id as u16,
1319 }
1320 }
1321 FuncId::NotificationBind => Self::NotificationBind {
1322 sender_id: (regs[1] >> 16) as u16,
1323 receiver_id: regs[1] as u16,
1324 flags: (regs[2] as u32).into(),
1325 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1326 },
1327 FuncId::NotificationUnbind => Self::NotificationUnBind {
1328 sender_id: (regs[1] >> 16) as u16,
1329 receiver_id: regs[1] as u16,
1330 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1331 },
1332 FuncId::NotificationSet => Self::NotificationSet {
1333 sender_id: (regs[1] >> 16) as u16,
1334 receiver_id: regs[1] as u16,
1335 flags: (regs[2] as u32).try_into()?,
1336 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1337 },
1338 FuncId::NotificationGet => Self::NotificationGet {
1339 vcpu_id: (regs[1] >> 16) as u16,
1340 endpoint_id: regs[1] as u16,
1341 flags: (regs[2] as u32).into(),
1342 },
1343 FuncId::NotificationInfoGet32 => Self::NotificationInfoGet { is_32bit: true },
1344 FuncId::NotificationInfoGet64 => Self::NotificationInfoGet { is_32bit: false },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01001345 FuncId::El3IntrHandle => Self::El3IntrHandle,
Balint Dobszayde0dc802025-02-28 14:16:52 +01001346 _ => panic!("Invalid number of registers (8) for function {:#x?}", fid),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001347 };
1348
1349 Ok(msg)
1350 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01001351
Balint Dobszayde0dc802025-02-28 14:16:52 +01001352 fn unpack_regs18(version: Version, regs: &[u64; 18]) -> Result<Self, Error> {
1353 assert!(version >= Version(1, 2));
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001354
Balint Dobszayde0dc802025-02-28 14:16:52 +01001355 let fid = FuncId::try_from(regs[0] as u32)?;
1356
1357 let msg = match fid {
1358 FuncId::Success64 => Self::Success {
1359 target_info: regs[1] as u32,
Imre Kis54773b62025-04-10 13:47:39 +02001360 args: SuccessArgs::Args64_2(regs[2..18].try_into().unwrap()),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001361 },
1362 FuncId::MsgSendDirectReq64_2 => Self::MsgSendDirectReq2 {
1363 src_id: (regs[1] >> 16) as u16,
1364 dst_id: regs[1] as u16,
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00001365 uuid: Uuid::from_u64_pair(regs[2].swap_bytes(), regs[3].swap_bytes()),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001366 args: DirectMsg2Args(regs[4..18].try_into().unwrap()),
1367 },
1368 FuncId::MsgSendDirectResp64_2 => Self::MsgSendDirectResp2 {
1369 src_id: (regs[1] >> 16) as u16,
1370 dst_id: regs[1] as u16,
1371 args: DirectMsg2Args(regs[4..18].try_into().unwrap()),
1372 },
1373 FuncId::ConsoleLog64 => Self::ConsoleLog {
1374 char_cnt: regs[1] as u8,
1375 char_lists: ConsoleLogChars::Reg64(regs[2..18].try_into().unwrap()),
1376 },
Tomás González0a058bc2025-03-11 11:20:55 +00001377 FuncId::PartitionInfoGetRegs => {
1378 // Bits[15:0]: Start index
1379 let start_index = (regs[3] & 0xffff) as u16;
1380 let info_tag = ((regs[3] >> 16) & 0xffff) as u16;
1381 Self::PartitionInfoGetRegs {
1382 uuid: Uuid::from_u64_pair(regs[1].swap_bytes(), regs[2].swap_bytes()),
1383 start_index,
1384 info_tag: if start_index == 0 && info_tag != 0 {
1385 return Err(Error::InvalidInformationTag(info_tag));
1386 } else {
1387 info_tag
1388 },
1389 }
1390 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001391 _ => panic!("Invalid number of registers (18) for function {:#x?}", fid),
1392 };
1393
1394 Ok(msg)
Balint Dobszay3aad9572025-01-17 16:54:11 +01001395 }
1396
Balint Dobszaya5846852025-02-26 15:38:53 +01001397 /// Create register contents for an interface.
Balint Dobszayde0dc802025-02-28 14:16:52 +01001398 pub fn to_regs(&self, version: Version, regs: &mut [u64]) {
1399 let reg_cnt = regs.len();
1400
1401 match reg_cnt {
1402 8 => {
1403 assert!(version <= Version(1, 1));
Balint Dobszay91bea9b2025-04-09 13:16:06 +02001404 regs.fill(0);
1405
Balint Dobszayde0dc802025-02-28 14:16:52 +01001406 self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
1407 }
1408 18 => {
1409 assert!(version >= Version(1, 2));
Balint Dobszay91bea9b2025-04-09 13:16:06 +02001410 regs.fill(0);
Balint Dobszayde0dc802025-02-28 14:16:52 +01001411
1412 match self {
1413 Interface::ConsoleLog {
1414 char_lists: ConsoleLogChars::Reg64(_),
1415 ..
1416 }
1417 | Interface::Success {
Imre Kis54773b62025-04-10 13:47:39 +02001418 args: SuccessArgs::Args64_2(_),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001419 ..
1420 }
1421 | Interface::MsgSendDirectReq2 { .. }
Tomás González0a058bc2025-03-11 11:20:55 +00001422 | Interface::MsgSendDirectResp2 { .. }
1423 | Interface::PartitionInfoGetRegs { .. } => {
Balint Dobszayde0dc802025-02-28 14:16:52 +01001424 self.pack_regs18(version, regs.try_into().unwrap());
1425 }
1426 _ => {
1427 self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
1428 }
1429 }
1430 }
1431 _ => panic!("Invalid number of registers {}", reg_cnt),
1432 }
1433 }
1434
1435 fn pack_regs8(&self, version: Version, a: &mut [u64; 8]) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001436 if let Some(function_id) = self.function_id() {
1437 a[0] = function_id as u64;
1438 }
1439
1440 match *self {
1441 Interface::Error {
1442 target_info,
1443 error_code,
Balint Dobszayb727aab2025-04-07 10:24:59 +02001444 error_arg,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001445 } => {
1446 a[1] = u32::from(target_info).into();
1447 a[2] = (error_code as u32).into();
Balint Dobszayb727aab2025-04-07 10:24:59 +02001448 a[3] = error_arg.into();
Balint Dobszay3aad9572025-01-17 16:54:11 +01001449 }
1450 Interface::Success { target_info, args } => {
1451 a[1] = target_info.into();
1452 match args {
Imre Kis54773b62025-04-10 13:47:39 +02001453 SuccessArgs::Args32(regs) => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001454 a[2] = regs[0].into();
1455 a[3] = regs[1].into();
1456 a[4] = regs[2].into();
1457 a[5] = regs[3].into();
1458 a[6] = regs[4].into();
1459 a[7] = regs[5].into();
1460 }
Imre Kis54773b62025-04-10 13:47:39 +02001461 SuccessArgs::Args64(regs) => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001462 a[2] = regs[0];
1463 a[3] = regs[1];
1464 a[4] = regs[2];
1465 a[5] = regs[3];
1466 a[6] = regs[4];
1467 a[7] = regs[5];
1468 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001469 _ => panic!("{:#x?} requires 18 registers", args),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001470 }
1471 }
1472 Interface::Interrupt {
1473 target_info,
1474 interrupt_id,
1475 } => {
1476 a[1] = u32::from(target_info).into();
1477 a[2] = interrupt_id.into();
1478 }
1479 Interface::Version { input_version } => {
1480 a[1] = u32::from(input_version).into();
1481 }
1482 Interface::VersionOut { output_version } => {
1483 a[0] = u32::from(output_version).into();
1484 }
1485 Interface::Features {
1486 feat_id,
1487 input_properties,
1488 } => {
1489 a[1] = u32::from(feat_id).into();
1490 a[2] = input_properties.into();
1491 }
1492 Interface::RxAcquire { vm_id } => {
1493 a[1] = vm_id.into();
1494 }
1495 Interface::RxRelease { vm_id } => {
1496 a[1] = vm_id.into();
1497 }
1498 Interface::RxTxMap { addr, page_cnt } => {
1499 match addr {
1500 RxTxAddr::Addr32 { rx, tx } => {
1501 a[1] = tx.into();
1502 a[2] = rx.into();
1503 }
1504 RxTxAddr::Addr64 { rx, tx } => {
1505 a[1] = tx;
1506 a[2] = rx;
1507 }
1508 }
1509 a[3] = page_cnt.into();
1510 }
1511 Interface::RxTxUnmap { id } => {
1512 a[1] = id.into();
1513 }
1514 Interface::PartitionInfoGet { uuid, flags } => {
1515 let bytes = uuid.into_bytes();
1516 a[1] = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]).into();
1517 a[2] = u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]).into();
1518 a[3] = u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]).into();
1519 a[4] = u32::from_le_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]).into();
Imre Kise295adb2025-04-10 13:26:28 +02001520 a[5] = u32::from(flags).into();
Balint Dobszay3aad9572025-01-17 16:54:11 +01001521 }
Tomás González092202a2025-03-05 11:56:45 +00001522 Interface::MsgWait { flags } => {
1523 if version >= Version(1, 2) {
1524 if let Some(flags) = flags {
1525 a[2] = u32::from(flags).into();
1526 }
1527 }
1528 }
1529 Interface::IdGet | Interface::SpmIdGet | Interface::Yield => {}
Balint Dobszay3aad9572025-01-17 16:54:11 +01001530 Interface::Run { target_info } => {
1531 a[1] = u32::from(target_info).into();
1532 }
1533 Interface::NormalWorldResume => {}
Tomás González17b92442025-03-10 16:45:04 +00001534 Interface::SecondaryEpRegister { entrypoint } => match entrypoint {
1535 SecondaryEpRegisterAddr::Addr32(addr) => a[1] = addr as u64,
1536 SecondaryEpRegisterAddr::Addr64(addr) => a[1] = addr,
1537 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001538 Interface::MsgSend2 {
1539 sender_vm_id,
1540 flags,
1541 } => {
1542 a[1] = sender_vm_id.into();
1543 a[2] = flags.into();
1544 }
1545 Interface::MsgSendDirectReq {
1546 src_id,
1547 dst_id,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001548 args,
1549 } => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +01001550 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Balint Dobszay3aad9572025-01-17 16:54:11 +01001551 match args {
1552 DirectMsgArgs::Args32(args) => {
1553 a[3] = args[0].into();
1554 a[4] = args[1].into();
1555 a[5] = args[2].into();
1556 a[6] = args[3].into();
1557 a[7] = args[4].into();
1558 }
1559 DirectMsgArgs::Args64(args) => {
1560 a[3] = args[0];
1561 a[4] = args[1];
1562 a[5] = args[2];
1563 a[6] = args[3];
1564 a[7] = args[4];
1565 }
Tomás González4d5b0ba2025-03-03 17:15:55 +00001566 DirectMsgArgs::VersionReq { version } => {
1567 a[2] = DirectMsgArgs::VERSION_REQ.into();
1568 a[3] = u32::from(version).into();
1569 }
Tomás González67f92c72025-03-20 16:50:42 +00001570 DirectMsgArgs::PowerPsciReq32 { params } => {
Tomás González4d5b0ba2025-03-03 17:15:55 +00001571 a[2] = DirectMsgArgs::POWER_PSCI_REQ.into();
Tomás González67f92c72025-03-20 16:50:42 +00001572 a[3] = params[0].into();
1573 a[4] = params[1].into();
1574 a[5] = params[2].into();
1575 a[6] = params[3].into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00001576 }
Tomás González67f92c72025-03-20 16:50:42 +00001577 DirectMsgArgs::PowerPsciReq64 { params } => {
Tomás González4d5b0ba2025-03-03 17:15:55 +00001578 a[2] = DirectMsgArgs::POWER_PSCI_REQ.into();
Tomás González67f92c72025-03-20 16:50:42 +00001579 a[3] = params[0];
1580 a[4] = params[1];
1581 a[5] = params[2];
1582 a[6] = params[3];
Tomás González4d5b0ba2025-03-03 17:15:55 +00001583 }
1584 DirectMsgArgs::PowerWarmBootReq { boot_type } => {
1585 a[2] = DirectMsgArgs::POWER_WARM_BOOT_REQ.into();
1586 a[3] = u32::from(boot_type).into();
1587 }
1588 DirectMsgArgs::VmCreated { handle, vm_id } => {
1589 a[2] = DirectMsgArgs::VM_CREATED.into();
1590 let handle_regs: [u32; 2] = handle.into();
1591 a[3] = handle_regs[0].into();
1592 a[4] = handle_regs[1].into();
1593 a[5] = vm_id.into();
1594 }
1595 DirectMsgArgs::VmDestructed { handle, vm_id } => {
1596 a[2] = DirectMsgArgs::VM_DESTRUCTED.into();
1597 let handle_regs: [u32; 2] = handle.into();
1598 a[3] = handle_regs[0].into();
1599 a[4] = handle_regs[1].into();
1600 a[5] = vm_id.into();
1601 }
1602 _ => panic!("Malformed MsgSendDirectReq interface"),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001603 }
1604 }
1605 Interface::MsgSendDirectResp {
1606 src_id,
1607 dst_id,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001608 args,
1609 } => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +01001610 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Balint Dobszay3aad9572025-01-17 16:54:11 +01001611 match args {
1612 DirectMsgArgs::Args32(args) => {
1613 a[3] = args[0].into();
1614 a[4] = args[1].into();
1615 a[5] = args[2].into();
1616 a[6] = args[3].into();
1617 a[7] = args[4].into();
1618 }
1619 DirectMsgArgs::Args64(args) => {
1620 a[3] = args[0];
1621 a[4] = args[1];
1622 a[5] = args[2];
1623 a[6] = args[3];
1624 a[7] = args[4];
1625 }
Tomás González4d5b0ba2025-03-03 17:15:55 +00001626 DirectMsgArgs::VersionResp { version } => {
1627 a[2] = DirectMsgArgs::VERSION_RESP.into();
1628 match version {
Tomás González67f92c72025-03-20 16:50:42 +00001629 None => a[3] = (i32::from(FfaError::NotSupported) as u32).into(),
Tomás González4d5b0ba2025-03-03 17:15:55 +00001630 Some(ver) => a[3] = u32::from(ver).into(),
1631 }
1632 }
1633 DirectMsgArgs::PowerPsciResp { psci_status } => {
1634 a[2] = DirectMsgArgs::POWER_PSCI_RESP.into();
1635 a[3] = psci_status as u64;
1636 }
1637 DirectMsgArgs::VmCreatedAck { sp_status } => {
1638 a[2] = DirectMsgArgs::VM_CREATED_ACK.into();
Tomás González67f92c72025-03-20 16:50:42 +00001639 a[3] = (i32::from(sp_status) as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00001640 }
1641 DirectMsgArgs::VmDestructedAck { sp_status } => {
1642 a[2] = DirectMsgArgs::VM_DESTRUCTED_ACK.into();
Tomás González67f92c72025-03-20 16:50:42 +00001643 a[3] = (i32::from(sp_status) as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00001644 }
1645 _ => panic!("Malformed MsgSendDirectResp interface"),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001646 }
1647 }
1648 Interface::MemDonate {
1649 total_len,
1650 frag_len,
1651 buf,
1652 } => {
1653 a[1] = total_len.into();
1654 a[2] = frag_len.into();
1655 (a[3], a[4]) = match buf {
1656 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1657 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1658 None => (0, 0),
1659 };
1660 }
1661 Interface::MemLend {
1662 total_len,
1663 frag_len,
1664 buf,
1665 } => {
1666 a[1] = total_len.into();
1667 a[2] = frag_len.into();
1668 (a[3], a[4]) = match buf {
1669 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1670 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1671 None => (0, 0),
1672 };
1673 }
1674 Interface::MemShare {
1675 total_len,
1676 frag_len,
1677 buf,
1678 } => {
1679 a[1] = total_len.into();
1680 a[2] = frag_len.into();
1681 (a[3], a[4]) = match buf {
1682 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1683 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1684 None => (0, 0),
1685 };
1686 }
1687 Interface::MemRetrieveReq {
1688 total_len,
1689 frag_len,
1690 buf,
1691 } => {
1692 a[1] = total_len.into();
1693 a[2] = frag_len.into();
1694 (a[3], a[4]) = match buf {
1695 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1696 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1697 None => (0, 0),
1698 };
1699 }
1700 Interface::MemRetrieveResp {
1701 total_len,
1702 frag_len,
1703 } => {
1704 a[1] = total_len.into();
1705 a[2] = frag_len.into();
1706 }
1707 Interface::MemRelinquish => {}
1708 Interface::MemReclaim { handle, flags } => {
1709 let handle_regs: [u32; 2] = handle.into();
1710 a[1] = handle_regs[0].into();
1711 a[2] = handle_regs[1].into();
1712 a[3] = flags.into();
1713 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001714 Interface::MemPermGet { addr, page_cnt } => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001715 a[1] = match addr {
1716 MemAddr::Addr32(addr) => addr.into(),
1717 MemAddr::Addr64(addr) => addr,
1718 };
Balint Dobszayde0dc802025-02-28 14:16:52 +01001719 a[2] = if version >= Version(1, 3) {
1720 page_cnt.unwrap().into()
1721 } else {
1722 assert!(page_cnt.is_none());
1723 0
1724 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01001725 }
1726 Interface::MemPermSet {
1727 addr,
1728 page_cnt,
1729 mem_perm,
1730 } => {
1731 a[1] = match addr {
1732 MemAddr::Addr32(addr) => addr.into(),
1733 MemAddr::Addr64(addr) => addr,
1734 };
1735 a[2] = page_cnt.into();
1736 a[3] = mem_perm.into();
1737 }
1738 Interface::ConsoleLog {
1739 char_cnt,
1740 char_lists,
1741 } => {
1742 a[1] = char_cnt.into();
1743 match char_lists {
1744 ConsoleLogChars::Reg32(regs) => {
1745 a[2] = regs[0].into();
1746 a[3] = regs[1].into();
1747 a[4] = regs[2].into();
1748 a[5] = regs[3].into();
1749 a[6] = regs[4].into();
1750 a[7] = regs[5].into();
1751 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001752 _ => panic!("{:#x?} requires 18 registers", char_lists),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001753 }
1754 }
Tomás González7ffb6132025-04-03 12:28:58 +01001755 Interface::NotificationBitmapCreate { vm_id, vcpu_cnt } => {
1756 a[1] = vm_id.into();
1757 a[2] = vcpu_cnt.into();
1758 }
1759 Interface::NotificationBitmapDestroy { vm_id } => {
1760 a[1] = vm_id.into();
1761 }
1762 Interface::NotificationBind {
1763 sender_id,
1764 receiver_id,
1765 flags,
1766 bitmap,
1767 } => {
1768 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
1769 a[2] = u32::from(flags).into();
1770 a[3] = bitmap & 0xffff_ffff;
1771 a[4] = bitmap >> 32;
1772 }
1773 Interface::NotificationUnBind {
1774 sender_id,
1775 receiver_id,
1776 bitmap,
1777 } => {
1778 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
1779 a[3] = bitmap & 0xffff_ffff;
1780 a[4] = bitmap >> 32;
1781 }
1782 Interface::NotificationSet {
1783 sender_id,
1784 receiver_id,
1785 flags,
1786 bitmap,
1787 } => {
1788 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
1789 a[2] = u32::from(flags).into();
1790 a[3] = bitmap & 0xffff_ffff;
1791 a[4] = bitmap >> 32;
1792 }
1793 Interface::NotificationGet {
1794 vcpu_id,
1795 endpoint_id,
1796 flags,
1797 } => {
1798 a[1] = (u64::from(vcpu_id) << 16) | u64::from(endpoint_id);
1799 a[2] = u32::from(flags).into();
1800 }
1801 Interface::NotificationInfoGet { .. } => {}
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01001802 Interface::El3IntrHandle => {}
Balint Dobszayde0dc802025-02-28 14:16:52 +01001803 _ => panic!("{:#x?} requires 18 registers", self),
1804 }
1805 }
1806
1807 fn pack_regs18(&self, version: Version, a: &mut [u64; 18]) {
1808 assert!(version >= Version(1, 2));
1809
Balint Dobszayde0dc802025-02-28 14:16:52 +01001810 if let Some(function_id) = self.function_id() {
1811 a[0] = function_id as u64;
1812 }
1813
1814 match *self {
1815 Interface::Success { target_info, args } => {
1816 a[1] = target_info.into();
1817 match args {
Imre Kis54773b62025-04-10 13:47:39 +02001818 SuccessArgs::Args64_2(regs) => a[2..18].copy_from_slice(&regs[..16]),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001819 _ => panic!("{:#x?} requires 8 registers", args),
1820 }
1821 }
1822 Interface::MsgSendDirectReq2 {
1823 src_id,
1824 dst_id,
1825 uuid,
1826 args,
1827 } => {
1828 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00001829 let (uuid_msb, uuid_lsb) = uuid.as_u64_pair();
1830 (a[2], a[3]) = (uuid_msb.swap_bytes(), uuid_lsb.swap_bytes());
Balint Dobszayde0dc802025-02-28 14:16:52 +01001831 a[4..18].copy_from_slice(&args.0[..14]);
1832 }
1833 Interface::MsgSendDirectResp2 {
1834 src_id,
1835 dst_id,
1836 args,
1837 } => {
1838 a[1] = ((src_id as u64) << 16) | dst_id as u64;
1839 a[2] = 0;
1840 a[3] = 0;
1841 a[4..18].copy_from_slice(&args.0[..14]);
1842 }
1843 Interface::ConsoleLog {
1844 char_cnt,
1845 char_lists,
1846 } => {
1847 a[1] = char_cnt.into();
1848 match char_lists {
1849 ConsoleLogChars::Reg64(regs) => a[2..18].copy_from_slice(&regs[..16]),
1850 _ => panic!("{:#x?} requires 8 registers", char_lists),
1851 }
1852 }
Tomás González0a058bc2025-03-11 11:20:55 +00001853 Interface::PartitionInfoGetRegs {
1854 uuid,
1855 start_index,
1856 info_tag,
1857 } => {
1858 if start_index == 0 && info_tag != 0 {
1859 panic!("Information Tag MBZ if start index is 0: {:#x?}", self);
1860 }
1861 let (uuid_msb, uuid_lsb) = uuid.as_u64_pair();
1862 (a[1], a[2]) = (uuid_msb.swap_bytes(), uuid_lsb.swap_bytes());
1863 a[3] = (u64::from(info_tag) << 16) | u64::from(start_index);
1864 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001865 _ => panic!("{:#x?} requires 8 registers", self),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001866 }
1867 }
1868
Balint Dobszaya5846852025-02-26 15:38:53 +01001869 /// Helper function to create an `FFA_SUCCESS` interface without any arguments.
Balint Dobszay3aad9572025-01-17 16:54:11 +01001870 pub fn success32_noargs() -> Self {
1871 Self::Success {
1872 target_info: 0,
Imre Kis54773b62025-04-10 13:47:39 +02001873 args: SuccessArgs::Args32([0; 6]),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001874 }
1875 }
1876
Tomás González4c8c7d22025-03-10 17:14:57 +00001877 /// Helper function to create an `FFA_SUCCESS` interface without any arguments.
1878 pub fn success64_noargs() -> Self {
1879 Self::Success {
1880 target_info: 0,
Imre Kis54773b62025-04-10 13:47:39 +02001881 args: SuccessArgs::Args64([0; 6]),
Tomás González4c8c7d22025-03-10 17:14:57 +00001882 }
1883 }
1884
Balint Dobszaya5846852025-02-26 15:38:53 +01001885 /// Helper function to create an `FFA_ERROR` interface with an error code.
Balint Dobszay3aad9572025-01-17 16:54:11 +01001886 pub fn error(error_code: FfaError) -> Self {
1887 Self::Error {
1888 target_info: TargetInfo {
1889 endpoint_id: 0,
1890 vcpu_id: 0,
1891 },
1892 error_code,
Balint Dobszayb727aab2025-04-07 10:24:59 +02001893 error_arg: 0,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001894 }
1895 }
1896}
1897
Balint Dobszaya5846852025-02-26 15:38:53 +01001898/// Maximum number of characters transmitted in a single `FFA_CONSOLE_LOG32` message.
1899pub const CONSOLE_LOG_32_MAX_CHAR_CNT: u8 = 24;
Balint Dobszayde0dc802025-02-28 14:16:52 +01001900/// Maximum number of characters transmitted in a single `FFA_CONSOLE_LOG64` message.
1901pub const CONSOLE_LOG_64_MAX_CHAR_CNT: u8 = 128;
Balint Dobszay3aad9572025-01-17 16:54:11 +01001902
Balint Dobszaya5846852025-02-26 15:38:53 +01001903/// Helper function to convert the "Tightly packed list of characters" format used by the
1904/// `FFA_CONSOLE_LOG` interface into a byte slice.
Balint Dobszay3aad9572025-01-17 16:54:11 +01001905pub fn parse_console_log(
1906 char_cnt: u8,
1907 char_lists: &ConsoleLogChars,
1908 log_bytes: &mut [u8],
1909) -> Result<(), FfaError> {
1910 match char_lists {
1911 ConsoleLogChars::Reg32(regs) => {
Balint Dobszaya5846852025-02-26 15:38:53 +01001912 if !(1..=CONSOLE_LOG_32_MAX_CHAR_CNT).contains(&char_cnt) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001913 return Err(FfaError::InvalidParameters);
1914 }
1915 for (i, reg) in regs.iter().enumerate() {
1916 log_bytes[4 * i..4 * (i + 1)].copy_from_slice(&reg.to_le_bytes());
1917 }
1918 }
1919 ConsoleLogChars::Reg64(regs) => {
Balint Dobszaya5846852025-02-26 15:38:53 +01001920 if !(1..=CONSOLE_LOG_64_MAX_CHAR_CNT).contains(&char_cnt) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001921 return Err(FfaError::InvalidParameters);
1922 }
1923 for (i, reg) in regs.iter().enumerate() {
1924 log_bytes[8 * i..8 * (i + 1)].copy_from_slice(&reg.to_le_bytes());
1925 }
1926 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001927 }
1928
Balint Dobszayc8802492025-01-15 18:11:39 +01001929 Ok(())
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001930}
Tomás González0a058bc2025-03-11 11:20:55 +00001931
1932#[cfg(test)]
1933mod tests {
1934 use super::*;
1935
1936 #[test]
1937 fn part_info_get_regs() {
1938 let uuid = Uuid::parse_str("a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8").unwrap();
1939 let uuid_bytes = uuid.as_bytes();
1940 let test_info_tag = 0b1101_1101;
1941 let test_start_index = 0b1101;
1942 let start_index_and_tag = (test_info_tag << 16) | test_start_index;
1943 let version = Version(1, 2);
1944
1945 // From spec:
1946 // Bytes[0...7] of UUID with byte 0 in the low-order bits.
1947 let reg_x1 = (uuid_bytes[7] as u64) << 56
1948 | (uuid_bytes[6] as u64) << 48
1949 | (uuid_bytes[5] as u64) << 40
1950 | (uuid_bytes[4] as u64) << 32
1951 | (uuid_bytes[3] as u64) << 24
1952 | (uuid_bytes[2] as u64) << 16
1953 | (uuid_bytes[1] as u64) << 8
1954 | (uuid_bytes[0] as u64);
1955
1956 // From spec:
1957 // Bytes[8...15] of UUID with byte 8 in the low-order bits.
1958 let reg_x2 = (uuid_bytes[15] as u64) << 56
1959 | (uuid_bytes[14] as u64) << 48
1960 | (uuid_bytes[13] as u64) << 40
1961 | (uuid_bytes[12] as u64) << 32
1962 | (uuid_bytes[11] as u64) << 24
1963 | (uuid_bytes[10] as u64) << 16
1964 | (uuid_bytes[9] as u64) << 8
1965 | (uuid_bytes[8] as u64);
1966
1967 // First, test for wrong tag:
1968 {
1969 let mut regs = [0u64; 18];
1970 regs[0] = FuncId::PartitionInfoGetRegs as u64;
1971 regs[1] = reg_x1;
1972 regs[2] = reg_x2;
1973 regs[3] = test_info_tag << 16;
1974
1975 assert!(Interface::from_regs(version, &regs).is_err_and(
1976 |e| e == Error::InvalidInformationTag(test_info_tag.try_into().unwrap())
1977 ));
1978 }
1979
1980 // Test for regs -> Interface -> regs
1981 {
1982 let mut orig_regs = [0u64; 18];
1983 orig_regs[0] = FuncId::PartitionInfoGetRegs as u64;
1984 orig_regs[1] = reg_x1;
1985 orig_regs[2] = reg_x2;
1986 orig_regs[3] = start_index_and_tag;
1987
1988 let mut test_regs = orig_regs.clone();
1989 let interface = Interface::from_regs(version, &mut test_regs).unwrap();
1990 match &interface {
1991 Interface::PartitionInfoGetRegs {
1992 info_tag,
1993 start_index,
1994 uuid: int_uuid,
1995 } => {
1996 assert_eq!(u64::from(*info_tag), test_info_tag);
1997 assert_eq!(u64::from(*start_index), test_start_index);
1998 assert_eq!(*int_uuid, uuid);
1999 }
2000 _ => panic!("Expecting Interface::PartitionInfoGetRegs!"),
2001 }
2002 test_regs.fill(0);
2003 interface.to_regs(version, &mut test_regs);
2004 assert_eq!(orig_regs, test_regs);
2005 }
2006
2007 // Test for Interface -> regs -> Interface
2008 {
2009 let interface = Interface::PartitionInfoGetRegs {
2010 info_tag: test_info_tag.try_into().unwrap(),
2011 start_index: test_start_index.try_into().unwrap(),
2012 uuid,
2013 };
2014
2015 let mut regs: [u64; 18] = [0; 18];
2016 interface.to_regs(version, &mut regs);
2017
2018 assert_eq!(Some(FuncId::PartitionInfoGetRegs), interface.function_id());
2019 assert_eq!(regs[0], interface.function_id().unwrap() as u64);
2020 assert_eq!(regs[1], reg_x1);
2021 assert_eq!(regs[2], reg_x2);
2022 assert_eq!(regs[3], (test_info_tag << 16) | test_start_index);
2023
2024 assert_eq!(Interface::from_regs(version, &regs).unwrap(), interface);
2025 }
2026 }
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00002027
2028 #[test]
2029 fn msg_send_direct_req2() {
2030 let uuid = Uuid::parse_str("a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8").unwrap();
2031 let uuid_bytes = uuid.as_bytes();
2032
2033 // From spec:
2034 // Bytes[0...7] of UUID with byte 0 in the low-order bits.
2035 let reg_x2 = (uuid_bytes[7] as u64) << 56
2036 | (uuid_bytes[6] as u64) << 48
2037 | (uuid_bytes[5] as u64) << 40
2038 | (uuid_bytes[4] as u64) << 32
2039 | (uuid_bytes[3] as u64) << 24
2040 | (uuid_bytes[2] as u64) << 16
2041 | (uuid_bytes[1] as u64) << 8
2042 | (uuid_bytes[0] as u64);
2043
2044 // From spec:
2045 // Bytes[8...15] of UUID with byte 8 in the low-order bits.
2046 let reg_x3 = (uuid_bytes[15] as u64) << 56
2047 | (uuid_bytes[14] as u64) << 48
2048 | (uuid_bytes[13] as u64) << 40
2049 | (uuid_bytes[12] as u64) << 32
2050 | (uuid_bytes[11] as u64) << 24
2051 | (uuid_bytes[10] as u64) << 16
2052 | (uuid_bytes[9] as u64) << 8
2053 | (uuid_bytes[8] as u64);
2054
2055 let test_sender = 0b1101_1101;
2056 let test_receiver = 0b1101;
2057 let test_sender_receiver = (test_sender << 16) | test_receiver;
2058 let version = Version(1, 2);
2059
2060 // Test for regs -> Interface -> regs
2061 {
2062 let mut orig_regs = [0u64; 18];
2063 orig_regs[0] = FuncId::MsgSendDirectReq64_2 as u64;
2064 orig_regs[1] = test_sender_receiver;
2065 orig_regs[2] = reg_x2;
2066 orig_regs[3] = reg_x3;
2067
2068 let mut test_regs = orig_regs.clone();
2069 let interface = Interface::from_regs(version, &mut test_regs).unwrap();
2070 match &interface {
2071 Interface::MsgSendDirectReq2 {
2072 dst_id,
2073 src_id,
2074 args: _,
2075 uuid: int_uuid,
2076 } => {
2077 assert_eq!(u64::from(*src_id), test_sender);
2078 assert_eq!(u64::from(*dst_id), test_receiver);
2079 assert_eq!(*int_uuid, uuid);
2080 }
2081 _ => panic!("Expecting Interface::MsgSendDirectReq2!"),
2082 }
2083 test_regs.fill(0);
2084 interface.to_regs(version, &mut test_regs);
2085 assert_eq!(orig_regs, test_regs);
2086 }
2087
2088 // Test for Interface -> regs -> Interface
2089 {
2090 let rest_of_regs: [u64; 14] = [0; 14];
2091
2092 let interface = Interface::MsgSendDirectReq2 {
2093 src_id: test_sender.try_into().unwrap(),
2094 dst_id: test_receiver.try_into().unwrap(),
2095 uuid,
2096 args: DirectMsg2Args(rest_of_regs),
2097 };
2098
2099 let mut regs: [u64; 18] = [0; 18];
2100 interface.to_regs(version, &mut regs);
2101
2102 assert_eq!(Some(FuncId::MsgSendDirectReq64_2), interface.function_id());
2103 assert_eq!(regs[0], interface.function_id().unwrap() as u64);
2104 assert_eq!(regs[1], test_sender_receiver);
2105 assert_eq!(regs[2], reg_x2);
2106 assert_eq!(regs[3], reg_x3);
2107 assert_eq!(regs[4], 0);
2108
2109 assert_eq!(Interface::from_regs(version, &regs).unwrap(), interface);
2110 }
2111 }
Tomás González6ccba0a2025-04-09 13:31:29 +01002112
2113 #[test]
2114 fn is_32bit() {
2115 let interface_64 = Interface::MsgSendDirectReq {
2116 src_id: 0,
2117 dst_id: 1,
2118 args: DirectMsgArgs::Args64([0, 0, 0, 0, 0]),
2119 };
2120 assert!(!interface_64.is_32bit());
2121
2122 let interface_32 = Interface::MsgSendDirectReq {
2123 src_id: 0,
2124 dst_id: 1,
2125 args: DirectMsgArgs::Args32([0, 0, 0, 0, 0]),
2126 };
2127 assert!(interface_32.is_32bit());
2128 }
Tomás González0a058bc2025-03-11 11:20:55 +00002129}