blob: 349b4571ae97fa279824c6304f6261d85d8b30b5 [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),
Imre Kis839eaef2025-04-11 17:38:36 +020052 #[error("Invalid success argument variant")]
53 InvalidSuccessArgsVariant,
Balint Dobszay3aad9572025-01-17 16:54:11 +010054}
55
56impl From<Error> for FfaError {
57 fn from(value: Error) -> Self {
58 match value {
59 Error::UnrecognisedFunctionId(_) | Error::UnrecognisedFeatureId(_) => {
60 Self::NotSupported
61 }
Tomás González0a058bc2025-03-11 11:20:55 +000062 Error::InvalidInformationTag(_) => Self::Retry,
Tomás González4d5b0ba2025-03-03 17:15:55 +000063 Error::UnrecognisedErrorCode(_)
64 | Error::UnrecognisedFwkMsg(_)
65 | Error::InvalidVersion(_)
Tomás González092202a2025-03-05 11:56:45 +000066 | Error::InvalidMsgWaitFlag(_)
Tomás González4d5b0ba2025-03-03 17:15:55 +000067 | Error::UnrecognisedVmAvailabilityStatus(_)
Tomás González7ffb6132025-04-03 12:28:58 +010068 | Error::InvalidNotificationSetFlag(_)
69 | Error::InvalidVmId(_)
Imre Kise295adb2025-04-10 13:26:28 +020070 | Error::UnrecognisedWarmBootType(_)
Imre Kis839eaef2025-04-11 17:38:36 +020071 | Error::InvalidPartitionInfoGetFlag(_)
72 | Error::InvalidSuccessArgsVariant => Self::InvalidParameters,
Balint Dobszay3aad9572025-01-17 16:54:11 +010073 }
74 }
75}
Balint Dobszay5bf492f2024-07-29 17:21:32 +020076
Balint Dobszaya5846852025-02-26 15:38:53 +010077/// 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 +020078#[derive(PartialEq, Clone, Copy)]
79pub enum Instance {
Balint Dobszaya5846852025-02-26 15:38:53 +010080 /// The instance between the SPMC and SPMD.
Balint Dobszay5bf492f2024-07-29 17:21:32 +020081 SecurePhysical,
Balint Dobszaya5846852025-02-26 15:38:53 +010082 /// The instance between the SPMC and a physical SP (contains the SP's endpoint ID).
Balint Dobszay5bf492f2024-07-29 17:21:32 +020083 SecureVirtual(u16),
84}
85
Balint Dobszaya5846852025-02-26 15:38:53 +010086/// Function IDs of the various FF-A interfaces.
Andrew Walbran969b9252024-11-25 15:35:42 +000087#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
Balint Dobszay3aad9572025-01-17 16:54:11 +010088#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedFunctionId))]
Balint Dobszay5bf492f2024-07-29 17:21:32 +020089#[repr(u32)]
90pub enum FuncId {
91 Error = 0x84000060,
92 Success32 = 0x84000061,
93 Success64 = 0xc4000061,
94 Interrupt = 0x84000062,
95 Version = 0x84000063,
96 Features = 0x84000064,
97 RxAcquire = 0x84000084,
98 RxRelease = 0x84000065,
99 RxTxMap32 = 0x84000066,
100 RxTxMap64 = 0xc4000066,
101 RxTxUnmap = 0x84000067,
102 PartitionInfoGet = 0x84000068,
Balint Dobszaye6aa4862025-02-28 16:37:56 +0100103 PartitionInfoGetRegs = 0xc400008b,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200104 IdGet = 0x84000069,
105 SpmIdGet = 0x84000085,
Balint Dobszaye6aa4862025-02-28 16:37:56 +0100106 ConsoleLog32 = 0x8400008a,
107 ConsoleLog64 = 0xc400008a,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200108 MsgWait = 0x8400006b,
109 Yield = 0x8400006c,
110 Run = 0x8400006d,
111 NormalWorldResume = 0x8400007c,
112 MsgSend2 = 0x84000086,
113 MsgSendDirectReq32 = 0x8400006f,
114 MsgSendDirectReq64 = 0xc400006f,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100115 MsgSendDirectReq64_2 = 0xc400008d,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200116 MsgSendDirectResp32 = 0x84000070,
117 MsgSendDirectResp64 = 0xc4000070,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100118 MsgSendDirectResp64_2 = 0xc400008e,
Balint Dobszaye6aa4862025-02-28 16:37:56 +0100119 NotificationBitmapCreate = 0x8400007d,
120 NotificationBitmapDestroy = 0x8400007e,
121 NotificationBind = 0x8400007f,
122 NotificationUnbind = 0x84000080,
123 NotificationSet = 0x84000081,
124 NotificationGet = 0x84000082,
125 NotificationInfoGet32 = 0x84000083,
126 NotificationInfoGet64 = 0xc4000083,
127 El3IntrHandle = 0x8400008c,
Tomás González17b92442025-03-10 16:45:04 +0000128 SecondaryEpRegister32 = 0x84000087,
129 SecondaryEpRegister64 = 0xc4000087,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200130 MemDonate32 = 0x84000071,
131 MemDonate64 = 0xc4000071,
132 MemLend32 = 0x84000072,
133 MemLend64 = 0xc4000072,
134 MemShare32 = 0x84000073,
135 MemShare64 = 0xc4000073,
136 MemRetrieveReq32 = 0x84000074,
137 MemRetrieveReq64 = 0xc4000074,
138 MemRetrieveResp = 0x84000075,
139 MemRelinquish = 0x84000076,
140 MemReclaim = 0x84000077,
141 MemPermGet32 = 0x84000088,
142 MemPermGet64 = 0xc4000088,
143 MemPermSet32 = 0x84000089,
144 MemPermSet64 = 0xc4000089,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200145}
146
Balint Dobszayde0dc802025-02-28 14:16:52 +0100147impl FuncId {
148 /// Returns true if this is a 32-bit call, or false if it is a 64-bit call.
149 pub fn is_32bit(&self) -> bool {
Tomás González6ccba0a2025-04-09 13:31:29 +0100150 u32::from(*self) & (1 << 30) == 0
Balint Dobszayde0dc802025-02-28 14:16:52 +0100151 }
152}
153
Balint Dobszaya5846852025-02-26 15:38:53 +0100154/// Error status codes used by the `FFA_ERROR` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100155#[derive(Clone, Copy, Debug, Eq, Error, IntoPrimitive, PartialEq, TryFromPrimitive)]
156#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedErrorCode))]
157#[repr(i32)]
158pub enum FfaError {
159 #[error("Not supported")]
160 NotSupported = -1,
161 #[error("Invalid parameters")]
162 InvalidParameters = -2,
163 #[error("No memory")]
164 NoMemory = -3,
165 #[error("Busy")]
166 Busy = -4,
167 #[error("Interrupted")]
168 Interrupted = -5,
169 #[error("Denied")]
170 Denied = -6,
171 #[error("Retry")]
172 Retry = -7,
173 #[error("Aborted")]
174 Aborted = -8,
175 #[error("No data")]
176 NoData = -9,
177}
178
Balint Dobszaya5846852025-02-26 15:38:53 +0100179/// Endpoint ID and vCPU ID pair, used by `FFA_ERROR`, `FFA_INTERRUPT` and `FFA_RUN` interfaces.
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200180#[derive(Debug, Eq, PartialEq, Clone, Copy)]
Balint Dobszay3aad9572025-01-17 16:54:11 +0100181pub struct TargetInfo {
182 pub endpoint_id: u16,
183 pub vcpu_id: u16,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200184}
185
Balint Dobszay3aad9572025-01-17 16:54:11 +0100186impl From<u32> for TargetInfo {
187 fn from(value: u32) -> Self {
188 Self {
189 endpoint_id: (value >> 16) as u16,
190 vcpu_id: value as u16,
191 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200192 }
193}
194
Balint Dobszay3aad9572025-01-17 16:54:11 +0100195impl From<TargetInfo> for u32 {
196 fn from(value: TargetInfo) -> Self {
Balint Dobszaye9a3e762025-02-26 17:29:57 +0100197 ((value.endpoint_id as u32) << 16) | value.vcpu_id as u32
Andrew Walbran0d315812024-11-25 15:36:36 +0000198 }
Balint Dobszay3aad9572025-01-17 16:54:11 +0100199}
Andrew Walbran0d315812024-11-25 15:36:36 +0000200
Imre Kis839eaef2025-04-11 17:38:36 +0200201/// Generic arguments of the `FFA_SUCCESS` interface. The interpretation of the arguments depends on
202/// the interface that initiated the request. The application code has knowledge of the request, so
203/// it has to convert `SuccessArgs` into/from a specific success args structure that matches the
204/// request.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100205#[derive(Debug, Eq, PartialEq, Clone, Copy)]
206pub enum SuccessArgs {
Imre Kis54773b62025-04-10 13:47:39 +0200207 Args32([u32; 6]),
208 Args64([u64; 6]),
209 Args64_2([u64; 16]),
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200210}
211
Imre Kis839eaef2025-04-11 17:38:36 +0200212impl SuccessArgs {
213 fn try_get_args32(self) -> Result<[u32; 6], Error> {
214 match self {
215 SuccessArgs::Args32(args) => Ok(args),
216 SuccessArgs::Args64(_) | SuccessArgs::Args64_2(_) => {
217 Err(Error::InvalidSuccessArgsVariant)
218 }
219 }
220 }
221
222 fn try_get_args64(self) -> Result<[u64; 6], Error> {
223 match self {
224 SuccessArgs::Args64(args) => Ok(args),
225 SuccessArgs::Args32(_) | SuccessArgs::Args64_2(_) => {
226 Err(Error::InvalidSuccessArgsVariant)
227 }
228 }
229 }
230
231 fn try_get_args64_2(self) -> Result<[u64; 16], Error> {
232 match self {
233 SuccessArgs::Args64_2(args) => Ok(args),
234 SuccessArgs::Args32(_) | SuccessArgs::Args64(_) => {
235 Err(Error::InvalidSuccessArgsVariant)
236 }
237 }
238 }
239}
240
Tomás González17b92442025-03-10 16:45:04 +0000241/// Entrypoint address argument for `FFA_SECONDARY_EP_REGISTER` interface.
242#[derive(Debug, Eq, PartialEq, Clone, Copy)]
243pub enum SecondaryEpRegisterAddr {
244 Addr32(u32),
245 Addr64(u64),
246}
247
Balint Dobszaya5846852025-02-26 15:38:53 +0100248/// Version number of the FF-A implementation, `.0` is the major, `.1` is minor the version.
Balint Dobszayde0dc802025-02-28 14:16:52 +0100249#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord)]
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200250pub struct Version(pub u16, pub u16);
251
Tomás González1f794352025-03-03 16:47:06 +0000252impl Version {
Tomás González83146af2025-03-04 11:32:41 +0000253 // The FF-A spec mandates that bit[31] of a version number must be 0
254 const MBZ_BITS: u32 = 1 << 31;
255
Tomás González1f794352025-03-03 16:47:06 +0000256 /// Returns whether the caller's version (self) is compatible with the callee's version (input
257 /// parameter)
258 pub fn is_compatible_to(&self, callee_version: &Version) -> bool {
259 self.0 == callee_version.0 && self.1 <= callee_version.1
260 }
261}
262
Tomás González83146af2025-03-04 11:32:41 +0000263impl TryFrom<u32> for Version {
264 type Error = Error;
265
266 fn try_from(val: u32) -> Result<Self, Self::Error> {
267 if (val & Self::MBZ_BITS) != 0 {
268 Err(Error::InvalidVersion(val))
269 } else {
270 Ok(Self((val >> 16) as u16, val as u16))
271 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200272 }
273}
274
275impl From<Version> for u32 {
276 fn from(v: Version) -> Self {
Tomás González83146af2025-03-04 11:32:41 +0000277 let v_u32 = ((v.0 as u32) << 16) | v.1 as u32;
278 assert!(v_u32 & Version::MBZ_BITS == 0);
279 v_u32
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200280 }
281}
282
Andrew Walbran19970ba2024-11-25 15:35:00 +0000283impl Display for Version {
284 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
285 write!(f, "{}.{}", self.0, self.1)
286 }
287}
288
289impl Debug for Version {
290 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
291 Display::fmt(self, f)
292 }
293}
294
Balint Dobszaya5846852025-02-26 15:38:53 +0100295/// Feature IDs used by the `FFA_FEATURES` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100296#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
297#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedFeatureId))]
298#[repr(u8)]
299pub enum FeatureId {
300 NotificationPendingInterrupt = 0x1,
301 ScheduleReceiverInterrupt = 0x2,
302 ManagedExitInterrupt = 0x3,
303}
Balint Dobszayc8802492025-01-15 18:11:39 +0100304
Balint Dobszaya5846852025-02-26 15:38:53 +0100305/// Arguments for the `FFA_FEATURES` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100306#[derive(Debug, Eq, PartialEq, Clone, Copy)]
307pub enum Feature {
308 FuncId(FuncId),
309 FeatureId(FeatureId),
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100310 Unknown(u32),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100311}
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200312
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100313impl From<u32> for Feature {
314 fn from(value: u32) -> Self {
315 // Bit[31] is set for all valid FF-A function IDs so we don't have to check it separately
316 if let Ok(func_id) = value.try_into() {
317 Self::FuncId(func_id)
318 } else if let Ok(feat_id) = (value as u8).try_into() {
319 Self::FeatureId(feat_id)
Balint Dobszay3aad9572025-01-17 16:54:11 +0100320 } else {
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100321 Self::Unknown(value)
322 }
Balint Dobszay3aad9572025-01-17 16:54:11 +0100323 }
324}
325
326impl From<Feature> for u32 {
327 fn from(value: Feature) -> Self {
328 match value {
329 Feature::FuncId(func_id) => (1 << 31) | func_id as u32,
330 Feature::FeatureId(feature_id) => feature_id as u32,
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100331 Feature::Unknown(id) => panic!("Unknown feature or function ID {:#x?}", id),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100332 }
333 }
334}
335
Balint Dobszaya5846852025-02-26 15:38:53 +0100336/// RXTX buffer descriptor, used by `FFA_RXTX_MAP`.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100337#[derive(Debug, Eq, PartialEq, Clone, Copy)]
338pub enum RxTxAddr {
339 Addr32 { rx: u32, tx: u32 },
340 Addr64 { rx: u64, tx: u64 },
341}
342
Imre Kise295adb2025-04-10 13:26:28 +0200343/// Flags of the `FFA_PARTITION_INFO_GET` interface.
344#[derive(Debug, Eq, PartialEq, Clone, Copy)]
345pub struct PartitionInfoGetFlags {
346 pub count_only: bool,
347}
348
349impl PartitionInfoGetFlags {
350 const RETURN_INFORMATION_TYPE_FLAG: u32 = 1 << 0;
351 const MBZ_BITS: u32 = 0xffff_fffe;
352}
353
354impl TryFrom<u32> for PartitionInfoGetFlags {
355 type Error = Error;
356
357 fn try_from(val: u32) -> Result<Self, Self::Error> {
358 if (val & Self::MBZ_BITS) != 0 {
359 Err(Error::InvalidPartitionInfoGetFlag(val))
360 } else {
361 Ok(Self {
362 count_only: val & Self::RETURN_INFORMATION_TYPE_FLAG != 0,
363 })
364 }
365 }
366}
367
368impl From<PartitionInfoGetFlags> for u32 {
369 fn from(flags: PartitionInfoGetFlags) -> Self {
370 let mut bits: u32 = 0;
371 if flags.count_only {
372 bits |= PartitionInfoGetFlags::RETURN_INFORMATION_TYPE_FLAG;
373 }
374 bits
375 }
376}
377
Tomás González4d5b0ba2025-03-03 17:15:55 +0000378/// Composite type for capturing success and error return codes for the VM availability messages.
379///
380/// Error codes are handled by the `FfaError` type. Having a separate type for errors helps using
381/// `Result<(), FfaError>`. If a single type would include both success and error values,
382/// then `Err(FfaError::Success)` would be incomprehensible.
383#[derive(Debug, Eq, PartialEq, Clone, Copy)]
384pub enum VmAvailabilityStatus {
385 Success,
386 Error(FfaError),
387}
388
389impl TryFrom<i32> for VmAvailabilityStatus {
390 type Error = Error;
391 fn try_from(value: i32) -> Result<Self, <Self as TryFrom<i32>>::Error> {
392 Ok(match value {
393 0 => Self::Success,
394 error_code => Self::Error(FfaError::try_from(error_code)?),
395 })
396 }
397}
398
399impl From<VmAvailabilityStatus> for i32 {
400 fn from(value: VmAvailabilityStatus) -> Self {
401 match value {
402 VmAvailabilityStatus::Success => 0,
403 VmAvailabilityStatus::Error(error_code) => error_code.into(),
404 }
405 }
406}
407
408/// Arguments for the Power Warm Boot `FFA_MSG_SEND_DIRECT_REQ` interface.
409#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
410#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedWarmBootType))]
411#[repr(u32)]
412pub enum WarmBootType {
413 ExitFromSuspend = 0,
414 ExitFromLowPower = 1,
415}
416
Balint Dobszaya5846852025-02-26 15:38:53 +0100417/// Arguments for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}` interfaces.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100418#[derive(Debug, Eq, PartialEq, Clone, Copy)]
419pub enum DirectMsgArgs {
420 Args32([u32; 5]),
421 Args64([u64; 5]),
Tomás González4d5b0ba2025-03-03 17:15:55 +0000422 /// Message for forwarding FFA_VERSION call from Normal world to the SPMC
423 VersionReq {
424 version: Version,
425 },
426 /// Response message to forwarded FFA_VERSION call from the Normal world
427 /// Contains the version returned by the SPMC or None
428 VersionResp {
429 version: Option<Version>,
430 },
431 /// Message for a power management operation initiated by a PSCI function
432 PowerPsciReq32 {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000433 // params[i]: Input parameter in w[i] in PSCI function invocation at EL3.
Tomás González67f92c72025-03-20 16:50:42 +0000434 // params[0]: Function ID.
435 params: [u32; 4],
Tomás González4d5b0ba2025-03-03 17:15:55 +0000436 },
437 /// Message for a power management operation initiated by a PSCI function
438 PowerPsciReq64 {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000439 // params[i]: Input parameter in x[i] in PSCI function invocation at EL3.
Tomás González67f92c72025-03-20 16:50:42 +0000440 // params[0]: Function ID.
441 params: [u64; 4],
Tomás González4d5b0ba2025-03-03 17:15:55 +0000442 },
443 /// Message for a warm boot
444 PowerWarmBootReq {
445 boot_type: WarmBootType,
446 },
447 /// Response message to indicate return status of the last power management request message
448 /// Return error code SUCCESS or DENIED as defined in PSCI spec. Caller is left to do the
449 /// parsing of the return status.
450 PowerPsciResp {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000451 psci_status: i32,
452 },
453 /// Message to signal creation of a VM
454 VmCreated {
455 // Globally unique Handle to identify a memory region that contains IMPLEMENTATION DEFINED
456 // information associated with the created VM.
457 // The invalid memory region handle must be specified by the Hypervisor if this field is not
458 // used.
459 handle: memory_management::Handle,
460 vm_id: u16,
461 },
462 /// Message to acknowledge creation of a VM
463 VmCreatedAck {
464 sp_status: VmAvailabilityStatus,
465 },
466 /// Message to signal destruction of a VM
467 VmDestructed {
468 // Globally unique Handle to identify a memory region that contains IMPLEMENTATION DEFINED
469 // information associated with the created VM.
470 // The invalid memory region handle must be specified by the Hypervisor if this field is not
471 // used.
472 handle: memory_management::Handle,
473 vm_id: u16,
474 },
475 /// Message to acknowledge destruction of a VM
476 VmDestructedAck {
477 sp_status: VmAvailabilityStatus,
478 },
479}
480
481impl DirectMsgArgs {
482 // Flags for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}` interfaces.
483
484 const FWK_MSG_BITS: u32 = 1 << 31;
485 const VERSION_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b1000;
486 const VERSION_RESP: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b1001;
487 const POWER_PSCI_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS;
488 const POWER_WARM_BOOT_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0001;
489 const POWER_PSCI_RESP: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0010;
490 const VM_CREATED: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0100;
491 const VM_CREATED_ACK: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0101;
492 const VM_DESTRUCTED: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0110;
493 const VM_DESTRUCTED_ACK: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0111;
Balint Dobszay3aad9572025-01-17 16:54:11 +0100494}
495
Balint Dobszayde0dc802025-02-28 14:16:52 +0100496/// Arguments for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}2` interfaces.
497#[derive(Debug, Eq, PartialEq, Clone, Copy)]
498pub struct DirectMsg2Args([u64; 14]);
499
Tomás González092202a2025-03-05 11:56:45 +0000500#[derive(Debug, Eq, PartialEq, Clone, Copy)]
501pub struct MsgWaitFlags {
502 retain_rx_buffer: bool,
503}
504
505impl MsgWaitFlags {
506 const RETAIN_RX_BUFFER: u32 = 0x01;
507 const MBZ_BITS: u32 = 0xfffe;
508}
509
510impl TryFrom<u32> for MsgWaitFlags {
511 type Error = Error;
512
513 fn try_from(val: u32) -> Result<Self, Self::Error> {
514 if (val & Self::MBZ_BITS) != 0 {
515 Err(Error::InvalidMsgWaitFlag(val))
516 } else {
517 Ok(MsgWaitFlags {
518 retain_rx_buffer: val & Self::RETAIN_RX_BUFFER != 0,
519 })
520 }
521 }
522}
523
524impl From<MsgWaitFlags> for u32 {
525 fn from(flags: MsgWaitFlags) -> Self {
526 let mut bits: u32 = 0;
527 if flags.retain_rx_buffer {
528 bits |= MsgWaitFlags::RETAIN_RX_BUFFER;
529 }
530 bits
531 }
532}
533
Balint Dobszaya5846852025-02-26 15:38:53 +0100534/// Descriptor for a dynamically allocated memory buffer that contains the memory transaction
Tomás Gonzálezf268e322025-03-05 11:18:11 +0000535/// descriptor.
536///
537/// Used by `FFA_MEM_{DONATE,LEND,SHARE,RETRIEVE_REQ}` interfaces, only when the TX buffer is not
538/// used to transmit the transaction descriptor.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100539#[derive(Debug, Eq, PartialEq, Clone, Copy)]
540pub enum MemOpBuf {
541 Buf32 { addr: u32, page_cnt: u32 },
542 Buf64 { addr: u64, page_cnt: u32 },
543}
544
Balint Dobszaya5846852025-02-26 15:38:53 +0100545/// Memory address argument for `FFA_MEM_PERM_{GET,SET}` interfaces.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100546#[derive(Debug, Eq, PartialEq, Clone, Copy)]
547pub enum MemAddr {
548 Addr32(u32),
549 Addr64(u64),
550}
551
Balint Dobszayde0dc802025-02-28 14:16:52 +0100552/// Argument for the `FFA_CONSOLE_LOG` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100553#[derive(Debug, Eq, PartialEq, Clone, Copy)]
554pub enum ConsoleLogChars {
555 Reg32([u32; 6]),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100556 Reg64([u64; 16]),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100557}
558
Tomás González7ffb6132025-04-03 12:28:58 +0100559#[derive(Debug, Eq, PartialEq, Clone, Copy)]
560pub struct NotificationBindFlags {
561 per_vcpu_notification: bool,
562}
563
564impl NotificationBindFlags {
565 const PER_VCPU_NOTIFICATION: u32 = 1;
566}
567
568impl From<NotificationBindFlags> for u32 {
569 fn from(flags: NotificationBindFlags) -> Self {
570 let mut bits: u32 = 0;
571 if flags.per_vcpu_notification {
572 bits |= NotificationBindFlags::PER_VCPU_NOTIFICATION;
573 }
574 bits
575 }
576}
577
578impl From<u32> for NotificationBindFlags {
579 fn from(flags: u32) -> Self {
580 Self {
581 per_vcpu_notification: flags & Self::PER_VCPU_NOTIFICATION != 0,
582 }
583 }
584}
585
586#[derive(Debug, Eq, PartialEq, Clone, Copy)]
587pub struct NotificationSetFlags {
588 delay_schedule_receiver: bool,
589 vcpu_id: Option<u16>,
590}
591
592impl NotificationSetFlags {
593 const PER_VCP_NOTIFICATION: u32 = 1 << 0;
594 const DELAY_SCHEDULE_RECEIVER: u32 = 1 << 1;
595 const VCPU_ID_SHIFT: u32 = 16;
596
597 const MBZ_BITS: u32 = 0xfffc;
598}
599
600impl From<NotificationSetFlags> for u32 {
601 fn from(flags: NotificationSetFlags) -> Self {
602 let mut bits: u32 = 0;
603
604 if flags.delay_schedule_receiver {
605 bits |= NotificationSetFlags::DELAY_SCHEDULE_RECEIVER;
606 }
607 if let Some(vcpu_id) = flags.vcpu_id {
608 bits |= NotificationSetFlags::PER_VCP_NOTIFICATION;
609 bits |= u32::from(vcpu_id) << NotificationSetFlags::VCPU_ID_SHIFT;
610 }
611
612 bits
613 }
614}
615
616impl TryFrom<u32> for NotificationSetFlags {
617 type Error = Error;
618
619 fn try_from(flags: u32) -> Result<Self, Self::Error> {
620 if (flags & Self::MBZ_BITS) != 0 {
621 return Err(Error::InvalidNotificationSetFlag(flags));
622 }
623
624 let tentative_vcpu_id = (flags >> Self::VCPU_ID_SHIFT) as u16;
625
626 let vcpu_id = if (flags & Self::PER_VCP_NOTIFICATION) != 0 {
627 Some(tentative_vcpu_id)
628 } else {
629 if tentative_vcpu_id != 0 {
630 return Err(Error::InvalidNotificationSetFlag(flags));
631 }
632 None
633 };
634
635 Ok(Self {
636 delay_schedule_receiver: (flags & Self::DELAY_SCHEDULE_RECEIVER) != 0,
637 vcpu_id,
638 })
639 }
640}
641
642#[derive(Debug, Eq, PartialEq, Clone, Copy)]
643pub struct NotificationGetFlags {
644 sp_bitmap_id: bool,
645 vm_bitmap_id: bool,
646 spm_bitmap_id: bool,
647 hyp_bitmap_id: bool,
648}
649
650impl NotificationGetFlags {
651 const SP_BITMAP_ID: u32 = 1;
652 const VM_BITMAP_ID: u32 = 1 << 1;
653 const SPM_BITMAP_ID: u32 = 1 << 2;
654 const HYP_BITMAP_ID: u32 = 1 << 3;
655}
656
657impl From<NotificationGetFlags> for u32 {
658 fn from(flags: NotificationGetFlags) -> Self {
659 let mut bits: u32 = 0;
660 if flags.sp_bitmap_id {
661 bits |= NotificationGetFlags::SP_BITMAP_ID;
662 }
663 if flags.vm_bitmap_id {
664 bits |= NotificationGetFlags::VM_BITMAP_ID;
665 }
666 if flags.spm_bitmap_id {
667 bits |= NotificationGetFlags::SPM_BITMAP_ID;
668 }
669 if flags.hyp_bitmap_id {
670 bits |= NotificationGetFlags::HYP_BITMAP_ID;
671 }
672 bits
673 }
674}
675
676impl From<u32> for NotificationGetFlags {
677 // This is a "from" instead of a "try_from" because Reserved Bits are SBZ, *not* MBZ.
678 fn from(flags: u32) -> Self {
679 Self {
680 sp_bitmap_id: (flags & Self::SP_BITMAP_ID) != 0,
681 vm_bitmap_id: (flags & Self::VM_BITMAP_ID) != 0,
682 spm_bitmap_id: (flags & Self::SPM_BITMAP_ID) != 0,
683 hyp_bitmap_id: (flags & Self::HYP_BITMAP_ID) != 0,
684 }
685 }
686}
687
Tomás Gonzálezf268e322025-03-05 11:18:11 +0000688/// FF-A "message types", the terminology used by the spec is "interfaces".
689///
690/// The interfaces are used by FF-A components for communication at an FF-A instance. The spec also
691/// describes the valid FF-A instances and conduits for each interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100692#[derive(Debug, Eq, PartialEq, Clone, Copy)]
693pub enum Interface {
694 Error {
695 target_info: TargetInfo,
696 error_code: FfaError,
Balint Dobszayb727aab2025-04-07 10:24:59 +0200697 error_arg: u32,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100698 },
699 Success {
700 target_info: u32,
701 args: SuccessArgs,
702 },
703 Interrupt {
704 target_info: TargetInfo,
705 interrupt_id: u32,
706 },
707 Version {
708 input_version: Version,
709 },
710 VersionOut {
711 output_version: Version,
712 },
713 Features {
714 feat_id: Feature,
715 input_properties: u32,
716 },
717 RxAcquire {
718 vm_id: u16,
719 },
720 RxRelease {
721 vm_id: u16,
722 },
723 RxTxMap {
724 addr: RxTxAddr,
725 page_cnt: u32,
726 },
727 RxTxUnmap {
728 id: u16,
729 },
730 PartitionInfoGet {
731 uuid: Uuid,
Imre Kise295adb2025-04-10 13:26:28 +0200732 flags: PartitionInfoGetFlags,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100733 },
Tomás González0a058bc2025-03-11 11:20:55 +0000734 PartitionInfoGetRegs {
735 uuid: Uuid,
736 start_index: u16,
737 info_tag: u16,
738 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100739 IdGet,
740 SpmIdGet,
Tomás González092202a2025-03-05 11:56:45 +0000741 MsgWait {
742 flags: Option<MsgWaitFlags>,
743 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100744 Yield,
745 Run {
746 target_info: TargetInfo,
747 },
748 NormalWorldResume,
Tomás González17b92442025-03-10 16:45:04 +0000749 SecondaryEpRegister {
750 entrypoint: SecondaryEpRegisterAddr,
751 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100752 MsgSend2 {
753 sender_vm_id: u16,
754 flags: u32,
755 },
756 MsgSendDirectReq {
757 src_id: u16,
758 dst_id: u16,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100759 args: DirectMsgArgs,
760 },
761 MsgSendDirectResp {
762 src_id: u16,
763 dst_id: u16,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100764 args: DirectMsgArgs,
765 },
Balint Dobszayde0dc802025-02-28 14:16:52 +0100766 MsgSendDirectReq2 {
767 src_id: u16,
768 dst_id: u16,
769 uuid: Uuid,
770 args: DirectMsg2Args,
771 },
772 MsgSendDirectResp2 {
773 src_id: u16,
774 dst_id: u16,
775 args: DirectMsg2Args,
776 },
Balint Dobszay3aad9572025-01-17 16:54:11 +0100777 MemDonate {
778 total_len: u32,
779 frag_len: u32,
780 buf: Option<MemOpBuf>,
781 },
782 MemLend {
783 total_len: u32,
784 frag_len: u32,
785 buf: Option<MemOpBuf>,
786 },
787 MemShare {
788 total_len: u32,
789 frag_len: u32,
790 buf: Option<MemOpBuf>,
791 },
792 MemRetrieveReq {
793 total_len: u32,
794 frag_len: u32,
795 buf: Option<MemOpBuf>,
796 },
797 MemRetrieveResp {
798 total_len: u32,
799 frag_len: u32,
800 },
801 MemRelinquish,
802 MemReclaim {
803 handle: memory_management::Handle,
804 flags: u32,
805 },
806 MemPermGet {
807 addr: MemAddr,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100808 page_cnt: Option<u32>,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100809 },
810 MemPermSet {
811 addr: MemAddr,
812 page_cnt: u32,
813 mem_perm: u32,
814 },
815 ConsoleLog {
816 char_cnt: u8,
817 char_lists: ConsoleLogChars,
818 },
Tomás González7ffb6132025-04-03 12:28:58 +0100819 NotificationBitmapCreate {
820 vm_id: u16,
821 vcpu_cnt: u32,
822 },
823 NotificationBitmapDestroy {
824 vm_id: u16,
825 },
826 NotificationBind {
827 sender_id: u16,
828 receiver_id: u16,
829 flags: NotificationBindFlags,
830 bitmap: u64,
831 },
832 NotificationUnBind {
833 sender_id: u16,
834 receiver_id: u16,
835 bitmap: u64,
836 },
837 NotificationSet {
838 sender_id: u16,
839 receiver_id: u16,
840 flags: NotificationSetFlags,
841 bitmap: u64,
842 },
843 NotificationGet {
844 vcpu_id: u16,
845 endpoint_id: u16,
846 flags: NotificationGetFlags,
847 },
848 NotificationInfoGet {
849 is_32bit: bool,
850 },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +0100851 El3IntrHandle,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100852}
853
Balint Dobszayde0dc802025-02-28 14:16:52 +0100854impl Interface {
855 /// Returns the function ID for the call, if it has one.
856 pub fn function_id(&self) -> Option<FuncId> {
857 match self {
858 Interface::Error { .. } => Some(FuncId::Error),
859 Interface::Success { args, .. } => match args {
Imre Kis54773b62025-04-10 13:47:39 +0200860 SuccessArgs::Args32(..) => Some(FuncId::Success32),
861 SuccessArgs::Args64(..) | SuccessArgs::Args64_2(..) => Some(FuncId::Success64),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100862 },
863 Interface::Interrupt { .. } => Some(FuncId::Interrupt),
864 Interface::Version { .. } => Some(FuncId::Version),
865 Interface::VersionOut { .. } => None,
866 Interface::Features { .. } => Some(FuncId::Features),
867 Interface::RxAcquire { .. } => Some(FuncId::RxAcquire),
868 Interface::RxRelease { .. } => Some(FuncId::RxRelease),
869 Interface::RxTxMap { addr, .. } => match addr {
870 RxTxAddr::Addr32 { .. } => Some(FuncId::RxTxMap32),
871 RxTxAddr::Addr64 { .. } => Some(FuncId::RxTxMap64),
872 },
873 Interface::RxTxUnmap { .. } => Some(FuncId::RxTxUnmap),
874 Interface::PartitionInfoGet { .. } => Some(FuncId::PartitionInfoGet),
Tomás González0a058bc2025-03-11 11:20:55 +0000875 Interface::PartitionInfoGetRegs { .. } => Some(FuncId::PartitionInfoGetRegs),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100876 Interface::IdGet => Some(FuncId::IdGet),
877 Interface::SpmIdGet => Some(FuncId::SpmIdGet),
Tomás González092202a2025-03-05 11:56:45 +0000878 Interface::MsgWait { .. } => Some(FuncId::MsgWait),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100879 Interface::Yield => Some(FuncId::Yield),
880 Interface::Run { .. } => Some(FuncId::Run),
881 Interface::NormalWorldResume => Some(FuncId::NormalWorldResume),
Tomás González17b92442025-03-10 16:45:04 +0000882 Interface::SecondaryEpRegister { entrypoint } => match entrypoint {
883 SecondaryEpRegisterAddr::Addr32 { .. } => Some(FuncId::SecondaryEpRegister32),
884 SecondaryEpRegisterAddr::Addr64 { .. } => Some(FuncId::SecondaryEpRegister64),
885 },
Balint Dobszayde0dc802025-02-28 14:16:52 +0100886 Interface::MsgSend2 { .. } => Some(FuncId::MsgSend2),
887 Interface::MsgSendDirectReq { args, .. } => match args {
888 DirectMsgArgs::Args32(_) => Some(FuncId::MsgSendDirectReq32),
889 DirectMsgArgs::Args64(_) => Some(FuncId::MsgSendDirectReq64),
Tomás González4d5b0ba2025-03-03 17:15:55 +0000890 DirectMsgArgs::VersionReq { .. } => Some(FuncId::MsgSendDirectReq32),
891 DirectMsgArgs::PowerPsciReq32 { .. } => Some(FuncId::MsgSendDirectReq32),
892 DirectMsgArgs::PowerPsciReq64 { .. } => Some(FuncId::MsgSendDirectReq64),
893 DirectMsgArgs::PowerWarmBootReq { .. } => Some(FuncId::MsgSendDirectReq32),
894 DirectMsgArgs::VmCreated { .. } => Some(FuncId::MsgSendDirectReq32),
895 DirectMsgArgs::VmDestructed { .. } => Some(FuncId::MsgSendDirectReq32),
896 _ => None,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100897 },
898 Interface::MsgSendDirectResp { args, .. } => match args {
899 DirectMsgArgs::Args32(_) => Some(FuncId::MsgSendDirectResp32),
900 DirectMsgArgs::Args64(_) => Some(FuncId::MsgSendDirectResp64),
Tomás González4d5b0ba2025-03-03 17:15:55 +0000901 DirectMsgArgs::VersionResp { .. } => Some(FuncId::MsgSendDirectResp32),
902 DirectMsgArgs::PowerPsciResp { .. } => Some(FuncId::MsgSendDirectResp32),
903 DirectMsgArgs::VmCreatedAck { .. } => Some(FuncId::MsgSendDirectResp32),
904 DirectMsgArgs::VmDestructedAck { .. } => Some(FuncId::MsgSendDirectResp32),
905 _ => None,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100906 },
907 Interface::MsgSendDirectReq2 { .. } => Some(FuncId::MsgSendDirectReq64_2),
908 Interface::MsgSendDirectResp2 { .. } => Some(FuncId::MsgSendDirectResp64_2),
909 Interface::MemDonate { buf, .. } => match buf {
910 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemDonate64),
911 _ => Some(FuncId::MemDonate32),
912 },
913 Interface::MemLend { buf, .. } => match buf {
914 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemLend64),
915 _ => Some(FuncId::MemLend32),
916 },
917 Interface::MemShare { buf, .. } => match buf {
918 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemShare64),
919 _ => Some(FuncId::MemShare32),
920 },
921 Interface::MemRetrieveReq { buf, .. } => match buf {
922 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemRetrieveReq64),
923 _ => Some(FuncId::MemRetrieveReq32),
924 },
925 Interface::MemRetrieveResp { .. } => Some(FuncId::MemRetrieveResp),
926 Interface::MemRelinquish => Some(FuncId::MemRelinquish),
927 Interface::MemReclaim { .. } => Some(FuncId::MemReclaim),
928 Interface::MemPermGet { addr, .. } => match addr {
929 MemAddr::Addr32(_) => Some(FuncId::MemPermGet32),
930 MemAddr::Addr64(_) => Some(FuncId::MemPermGet64),
931 },
932 Interface::MemPermSet { addr, .. } => match addr {
933 MemAddr::Addr32(_) => Some(FuncId::MemPermSet32),
934 MemAddr::Addr64(_) => Some(FuncId::MemPermSet64),
935 },
936 Interface::ConsoleLog { char_lists, .. } => match char_lists {
937 ConsoleLogChars::Reg32(_) => Some(FuncId::ConsoleLog32),
938 ConsoleLogChars::Reg64(_) => Some(FuncId::ConsoleLog64),
939 },
Tomás González7ffb6132025-04-03 12:28:58 +0100940 Interface::NotificationBitmapCreate { .. } => Some(FuncId::NotificationBitmapCreate),
941 Interface::NotificationBitmapDestroy { .. } => Some(FuncId::NotificationBitmapDestroy),
942 Interface::NotificationBind { .. } => Some(FuncId::NotificationBind),
943 Interface::NotificationUnBind { .. } => Some(FuncId::NotificationUnbind),
944 Interface::NotificationSet { .. } => Some(FuncId::NotificationSet),
945 Interface::NotificationGet { .. } => Some(FuncId::NotificationGet),
946 Interface::NotificationInfoGet { is_32bit } => match is_32bit {
947 true => Some(FuncId::NotificationInfoGet32),
948 false => Some(FuncId::NotificationInfoGet64),
949 },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +0100950 Interface::El3IntrHandle => Some(FuncId::El3IntrHandle),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100951 }
952 }
Balint Dobszay3aad9572025-01-17 16:54:11 +0100953
Balint Dobszayde0dc802025-02-28 14:16:52 +0100954 /// Returns true if this is a 32-bit call, or false if it is a 64-bit call.
955 pub fn is_32bit(&self) -> bool {
956 // TODO: self should always have a function ID?
957 self.function_id().unwrap().is_32bit()
958 }
959
960 /// Parse interface from register contents. The caller must ensure that the `regs` argument has
961 /// the correct length: 8 registers for FF-A v1.1 and lower, 18 registers for v1.2 and higher.
962 pub fn from_regs(version: Version, regs: &[u64]) -> Result<Self, Error> {
963 let reg_cnt = regs.len();
964
965 let msg = match reg_cnt {
966 8 => {
967 assert!(version <= Version(1, 1));
968 Interface::unpack_regs8(version, regs.try_into().unwrap())?
969 }
970 18 => {
971 assert!(version >= Version(1, 2));
972 match FuncId::try_from(regs[0] as u32)? {
973 FuncId::ConsoleLog64
974 | FuncId::Success64
975 | FuncId::MsgSendDirectReq64_2
Tomás González0a058bc2025-03-11 11:20:55 +0000976 | FuncId::MsgSendDirectResp64_2
977 | FuncId::PartitionInfoGetRegs => {
Balint Dobszayde0dc802025-02-28 14:16:52 +0100978 Interface::unpack_regs18(version, regs.try_into().unwrap())?
979 }
980 _ => Interface::unpack_regs8(version, regs[..8].try_into().unwrap())?,
981 }
982 }
983 _ => panic!(
984 "Invalid number of registers ({}) for FF-A version {}",
985 reg_cnt, version
986 ),
987 };
988
989 Ok(msg)
990 }
991
992 fn unpack_regs8(version: Version, regs: &[u64; 8]) -> Result<Self, Error> {
Balint Dobszay3aad9572025-01-17 16:54:11 +0100993 let fid = FuncId::try_from(regs[0] as u32)?;
994
995 let msg = match fid {
996 FuncId::Error => Self::Error {
997 target_info: (regs[1] as u32).into(),
998 error_code: FfaError::try_from(regs[2] as i32)?,
Balint Dobszayb727aab2025-04-07 10:24:59 +0200999 error_arg: regs[3] as u32,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001000 },
1001 FuncId::Success32 => Self::Success {
1002 target_info: regs[1] as u32,
Imre Kis54773b62025-04-10 13:47:39 +02001003 args: SuccessArgs::Args32([
Balint Dobszay3aad9572025-01-17 16:54:11 +01001004 regs[2] as u32,
1005 regs[3] as u32,
1006 regs[4] as u32,
1007 regs[5] as u32,
1008 regs[6] as u32,
1009 regs[7] as u32,
1010 ]),
1011 },
1012 FuncId::Success64 => Self::Success {
1013 target_info: regs[1] as u32,
Imre Kis54773b62025-04-10 13:47:39 +02001014 args: SuccessArgs::Args64([regs[2], regs[3], regs[4], regs[5], regs[6], regs[7]]),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001015 },
1016 FuncId::Interrupt => Self::Interrupt {
1017 target_info: (regs[1] as u32).into(),
1018 interrupt_id: regs[2] as u32,
1019 },
1020 FuncId::Version => Self::Version {
Tomás González83146af2025-03-04 11:32:41 +00001021 input_version: (regs[1] as u32).try_into()?,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001022 },
1023 FuncId::Features => Self::Features {
Balint Dobszayc31e0b92025-03-03 20:16:56 +01001024 feat_id: (regs[1] as u32).into(),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001025 input_properties: regs[2] as u32,
1026 },
1027 FuncId::RxAcquire => Self::RxAcquire {
1028 vm_id: regs[1] as u16,
1029 },
1030 FuncId::RxRelease => Self::RxRelease {
1031 vm_id: regs[1] as u16,
1032 },
1033 FuncId::RxTxMap32 => {
1034 let addr = RxTxAddr::Addr32 {
1035 rx: regs[2] as u32,
1036 tx: regs[1] as u32,
1037 };
1038 let page_cnt = regs[3] as u32;
1039
1040 Self::RxTxMap { addr, page_cnt }
1041 }
1042 FuncId::RxTxMap64 => {
1043 let addr = RxTxAddr::Addr64 {
1044 rx: regs[2],
1045 tx: regs[1],
1046 };
1047 let page_cnt = regs[3] as u32;
1048
1049 Self::RxTxMap { addr, page_cnt }
1050 }
1051 FuncId::RxTxUnmap => Self::RxTxUnmap { id: regs[1] as u16 },
1052 FuncId::PartitionInfoGet => {
1053 let uuid_words = [
1054 regs[1] as u32,
1055 regs[2] as u32,
1056 regs[3] as u32,
1057 regs[4] as u32,
1058 ];
1059 let mut bytes: [u8; 16] = [0; 16];
1060 for (i, b) in uuid_words.iter().flat_map(|w| w.to_le_bytes()).enumerate() {
1061 bytes[i] = b;
1062 }
1063 Self::PartitionInfoGet {
1064 uuid: Uuid::from_bytes(bytes),
Imre Kise295adb2025-04-10 13:26:28 +02001065 flags: PartitionInfoGetFlags::try_from(regs[5] as u32)?,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001066 }
1067 }
1068 FuncId::IdGet => Self::IdGet,
1069 FuncId::SpmIdGet => Self::SpmIdGet,
Tomás González092202a2025-03-05 11:56:45 +00001070 FuncId::MsgWait => Self::MsgWait {
1071 flags: if version >= Version(1, 2) {
1072 Some(MsgWaitFlags::try_from(regs[2] as u32)?)
1073 } else {
1074 None
1075 },
1076 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001077 FuncId::Yield => Self::Yield,
1078 FuncId::Run => Self::Run {
1079 target_info: (regs[1] as u32).into(),
1080 },
1081 FuncId::NormalWorldResume => Self::NormalWorldResume,
Tomás González17b92442025-03-10 16:45:04 +00001082 FuncId::SecondaryEpRegister32 => Self::SecondaryEpRegister {
1083 entrypoint: SecondaryEpRegisterAddr::Addr32(regs[1] as u32),
1084 },
1085 FuncId::SecondaryEpRegister64 => Self::SecondaryEpRegister {
1086 entrypoint: SecondaryEpRegisterAddr::Addr64(regs[1]),
1087 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001088 FuncId::MsgSend2 => Self::MsgSend2 {
1089 sender_vm_id: regs[1] as u16,
1090 flags: regs[2] as u32,
1091 },
1092 FuncId::MsgSendDirectReq32 => Self::MsgSendDirectReq {
1093 src_id: (regs[1] >> 16) as u16,
1094 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001095 args: if (regs[2] as u32 & DirectMsgArgs::FWK_MSG_BITS) != 0 {
1096 match regs[2] as u32 {
1097 DirectMsgArgs::VERSION_REQ => DirectMsgArgs::VersionReq {
1098 version: Version::try_from(regs[3] as u32)?,
1099 },
1100 DirectMsgArgs::POWER_PSCI_REQ => DirectMsgArgs::PowerPsciReq32 {
Tomás González67f92c72025-03-20 16:50:42 +00001101 params: [
1102 regs[3] as u32,
1103 regs[4] as u32,
1104 regs[5] as u32,
1105 regs[6] as u32,
1106 ],
Tomás González4d5b0ba2025-03-03 17:15:55 +00001107 },
1108 DirectMsgArgs::POWER_WARM_BOOT_REQ => DirectMsgArgs::PowerWarmBootReq {
1109 boot_type: WarmBootType::try_from(regs[3] as u32)?,
1110 },
1111 DirectMsgArgs::VM_CREATED => DirectMsgArgs::VmCreated {
1112 handle: memory_management::Handle::from([
1113 regs[3] as u32,
1114 regs[4] as u32,
1115 ]),
1116 vm_id: regs[5] as u16,
1117 },
1118 DirectMsgArgs::VM_DESTRUCTED => DirectMsgArgs::VmDestructed {
1119 handle: memory_management::Handle::from([
1120 regs[3] as u32,
1121 regs[4] as u32,
1122 ]),
1123 vm_id: regs[5] as u16,
1124 },
1125 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1126 }
1127 } else {
1128 DirectMsgArgs::Args32([
1129 regs[3] as u32,
1130 regs[4] as u32,
1131 regs[5] as u32,
1132 regs[6] as u32,
1133 regs[7] as u32,
1134 ])
1135 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001136 },
1137 FuncId::MsgSendDirectReq64 => Self::MsgSendDirectReq {
1138 src_id: (regs[1] >> 16) as u16,
1139 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001140 args: if (regs[2] & DirectMsgArgs::FWK_MSG_BITS as u64) != 0 {
1141 match regs[2] as u32 {
1142 DirectMsgArgs::POWER_PSCI_REQ => DirectMsgArgs::PowerPsciReq64 {
Tomás González67f92c72025-03-20 16:50:42 +00001143 params: [regs[3], regs[4], regs[5], regs[6]],
Tomás González4d5b0ba2025-03-03 17:15:55 +00001144 },
1145 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1146 }
1147 } else {
1148 DirectMsgArgs::Args64([regs[3], regs[4], regs[5], regs[6], regs[7]])
1149 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001150 },
1151 FuncId::MsgSendDirectResp32 => 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] as u32 & DirectMsgArgs::FWK_MSG_BITS) != 0 {
1155 match regs[2] as u32 {
1156 DirectMsgArgs::VERSION_RESP => {
1157 if regs[3] as i32 == FfaError::NotSupported.into() {
1158 DirectMsgArgs::VersionResp { version: None }
1159 } else {
1160 DirectMsgArgs::VersionResp {
1161 version: Some(Version::try_from(regs[3] as u32)?),
1162 }
1163 }
1164 }
1165 DirectMsgArgs::POWER_PSCI_RESP => DirectMsgArgs::PowerPsciResp {
1166 psci_status: regs[3] as i32,
1167 },
1168 DirectMsgArgs::VM_CREATED_ACK => DirectMsgArgs::VmCreatedAck {
1169 sp_status: (regs[3] as i32).try_into()?,
1170 },
1171 DirectMsgArgs::VM_DESTRUCTED_ACK => DirectMsgArgs::VmDestructedAck {
1172 sp_status: (regs[3] as i32).try_into()?,
1173 },
1174 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1175 }
1176 } else {
1177 DirectMsgArgs::Args32([
1178 regs[3] as u32,
1179 regs[4] as u32,
1180 regs[5] as u32,
1181 regs[6] as u32,
1182 regs[7] as u32,
1183 ])
1184 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001185 },
1186 FuncId::MsgSendDirectResp64 => Self::MsgSendDirectResp {
1187 src_id: (regs[1] >> 16) as u16,
1188 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001189 args: if (regs[2] & DirectMsgArgs::FWK_MSG_BITS as u64) != 0 {
1190 return Err(Error::UnrecognisedFwkMsg(regs[2] as u32));
1191 } else {
1192 DirectMsgArgs::Args64([regs[3], regs[4], regs[5], regs[6], regs[7]])
1193 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001194 },
1195 FuncId::MemDonate32 => Self::MemDonate {
1196 total_len: regs[1] as u32,
1197 frag_len: regs[2] as u32,
1198 buf: if regs[3] != 0 && regs[4] != 0 {
1199 Some(MemOpBuf::Buf32 {
1200 addr: regs[3] as u32,
1201 page_cnt: regs[4] as u32,
1202 })
1203 } else {
1204 None
1205 },
1206 },
1207 FuncId::MemDonate64 => Self::MemDonate {
1208 total_len: regs[1] as u32,
1209 frag_len: regs[2] as u32,
1210 buf: if regs[3] != 0 && regs[4] != 0 {
1211 Some(MemOpBuf::Buf64 {
1212 addr: regs[3],
1213 page_cnt: regs[4] as u32,
1214 })
1215 } else {
1216 None
1217 },
1218 },
1219 FuncId::MemLend32 => Self::MemLend {
1220 total_len: regs[1] as u32,
1221 frag_len: regs[2] as u32,
1222 buf: if regs[3] != 0 && regs[4] != 0 {
1223 Some(MemOpBuf::Buf32 {
1224 addr: regs[3] as u32,
1225 page_cnt: regs[4] as u32,
1226 })
1227 } else {
1228 None
1229 },
1230 },
1231 FuncId::MemLend64 => Self::MemLend {
1232 total_len: regs[1] as u32,
1233 frag_len: regs[2] as u32,
1234 buf: if regs[3] != 0 && regs[4] != 0 {
1235 Some(MemOpBuf::Buf64 {
1236 addr: regs[3],
1237 page_cnt: regs[4] as u32,
1238 })
1239 } else {
1240 None
1241 },
1242 },
1243 FuncId::MemShare32 => Self::MemShare {
1244 total_len: regs[1] as u32,
1245 frag_len: regs[2] as u32,
1246 buf: if regs[3] != 0 && regs[4] != 0 {
1247 Some(MemOpBuf::Buf32 {
1248 addr: regs[3] as u32,
1249 page_cnt: regs[4] as u32,
1250 })
1251 } else {
1252 None
1253 },
1254 },
1255 FuncId::MemShare64 => Self::MemShare {
1256 total_len: regs[1] as u32,
1257 frag_len: regs[2] as u32,
1258 buf: if regs[3] != 0 && regs[4] != 0 {
1259 Some(MemOpBuf::Buf64 {
1260 addr: regs[3],
1261 page_cnt: regs[4] as u32,
1262 })
1263 } else {
1264 None
1265 },
1266 },
1267 FuncId::MemRetrieveReq32 => Self::MemRetrieveReq {
1268 total_len: regs[1] as u32,
1269 frag_len: regs[2] as u32,
1270 buf: if regs[3] != 0 && regs[4] != 0 {
1271 Some(MemOpBuf::Buf32 {
1272 addr: regs[3] as u32,
1273 page_cnt: regs[4] as u32,
1274 })
1275 } else {
1276 None
1277 },
1278 },
1279 FuncId::MemRetrieveReq64 => Self::MemRetrieveReq {
1280 total_len: regs[1] as u32,
1281 frag_len: regs[2] as u32,
1282 buf: if regs[3] != 0 && regs[4] != 0 {
1283 Some(MemOpBuf::Buf64 {
1284 addr: regs[3],
1285 page_cnt: regs[4] as u32,
1286 })
1287 } else {
1288 None
1289 },
1290 },
1291 FuncId::MemRetrieveResp => Self::MemRetrieveResp {
1292 total_len: regs[1] as u32,
1293 frag_len: regs[2] as u32,
1294 },
1295 FuncId::MemRelinquish => Self::MemRelinquish,
1296 FuncId::MemReclaim => Self::MemReclaim {
1297 handle: memory_management::Handle::from([regs[1] as u32, regs[2] as u32]),
1298 flags: regs[3] as u32,
1299 },
1300 FuncId::MemPermGet32 => Self::MemPermGet {
1301 addr: MemAddr::Addr32(regs[1] as u32),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001302 page_cnt: if version >= Version(1, 3) {
1303 Some(regs[2] as u32)
1304 } else {
1305 None
1306 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001307 },
1308 FuncId::MemPermGet64 => Self::MemPermGet {
1309 addr: MemAddr::Addr64(regs[1]),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001310 page_cnt: if version >= Version(1, 3) {
1311 Some(regs[2] as u32)
1312 } else {
1313 None
1314 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001315 },
1316 FuncId::MemPermSet32 => Self::MemPermSet {
1317 addr: MemAddr::Addr32(regs[1] as u32),
1318 page_cnt: regs[2] as u32,
1319 mem_perm: regs[3] as u32,
1320 },
1321 FuncId::MemPermSet64 => Self::MemPermSet {
1322 addr: MemAddr::Addr64(regs[1]),
1323 page_cnt: regs[2] as u32,
1324 mem_perm: regs[3] as u32,
1325 },
1326 FuncId::ConsoleLog32 => Self::ConsoleLog {
1327 char_cnt: regs[1] as u8,
1328 char_lists: ConsoleLogChars::Reg32([
1329 regs[2] as u32,
1330 regs[3] as u32,
1331 regs[4] as u32,
1332 regs[5] as u32,
1333 regs[6] as u32,
1334 regs[7] as u32,
1335 ]),
1336 },
Tomás González7ffb6132025-04-03 12:28:58 +01001337 FuncId::NotificationBitmapCreate => {
1338 let tentative_vm_id = regs[1] as u32;
1339 if (tentative_vm_id >> 16) != 0 {
1340 return Err(Error::InvalidVmId(tentative_vm_id));
1341 }
1342 Self::NotificationBitmapCreate {
1343 vm_id: tentative_vm_id as u16,
1344 vcpu_cnt: regs[2] as u32,
1345 }
1346 }
1347 FuncId::NotificationBitmapDestroy => {
1348 let tentative_vm_id = regs[1] as u32;
1349 if (tentative_vm_id >> 16) != 0 {
1350 return Err(Error::InvalidVmId(tentative_vm_id));
1351 }
1352 Self::NotificationBitmapDestroy {
1353 vm_id: tentative_vm_id as u16,
1354 }
1355 }
1356 FuncId::NotificationBind => Self::NotificationBind {
1357 sender_id: (regs[1] >> 16) as u16,
1358 receiver_id: regs[1] as u16,
1359 flags: (regs[2] as u32).into(),
1360 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1361 },
1362 FuncId::NotificationUnbind => Self::NotificationUnBind {
1363 sender_id: (regs[1] >> 16) as u16,
1364 receiver_id: regs[1] as u16,
1365 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1366 },
1367 FuncId::NotificationSet => Self::NotificationSet {
1368 sender_id: (regs[1] >> 16) as u16,
1369 receiver_id: regs[1] as u16,
1370 flags: (regs[2] as u32).try_into()?,
1371 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1372 },
1373 FuncId::NotificationGet => Self::NotificationGet {
1374 vcpu_id: (regs[1] >> 16) as u16,
1375 endpoint_id: regs[1] as u16,
1376 flags: (regs[2] as u32).into(),
1377 },
1378 FuncId::NotificationInfoGet32 => Self::NotificationInfoGet { is_32bit: true },
1379 FuncId::NotificationInfoGet64 => Self::NotificationInfoGet { is_32bit: false },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01001380 FuncId::El3IntrHandle => Self::El3IntrHandle,
Balint Dobszayde0dc802025-02-28 14:16:52 +01001381 _ => panic!("Invalid number of registers (8) for function {:#x?}", fid),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001382 };
1383
1384 Ok(msg)
1385 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01001386
Balint Dobszayde0dc802025-02-28 14:16:52 +01001387 fn unpack_regs18(version: Version, regs: &[u64; 18]) -> Result<Self, Error> {
1388 assert!(version >= Version(1, 2));
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001389
Balint Dobszayde0dc802025-02-28 14:16:52 +01001390 let fid = FuncId::try_from(regs[0] as u32)?;
1391
1392 let msg = match fid {
1393 FuncId::Success64 => Self::Success {
1394 target_info: regs[1] as u32,
Imre Kis54773b62025-04-10 13:47:39 +02001395 args: SuccessArgs::Args64_2(regs[2..18].try_into().unwrap()),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001396 },
1397 FuncId::MsgSendDirectReq64_2 => Self::MsgSendDirectReq2 {
1398 src_id: (regs[1] >> 16) as u16,
1399 dst_id: regs[1] as u16,
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00001400 uuid: Uuid::from_u64_pair(regs[2].swap_bytes(), regs[3].swap_bytes()),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001401 args: DirectMsg2Args(regs[4..18].try_into().unwrap()),
1402 },
1403 FuncId::MsgSendDirectResp64_2 => Self::MsgSendDirectResp2 {
1404 src_id: (regs[1] >> 16) as u16,
1405 dst_id: regs[1] as u16,
1406 args: DirectMsg2Args(regs[4..18].try_into().unwrap()),
1407 },
1408 FuncId::ConsoleLog64 => Self::ConsoleLog {
1409 char_cnt: regs[1] as u8,
1410 char_lists: ConsoleLogChars::Reg64(regs[2..18].try_into().unwrap()),
1411 },
Tomás González0a058bc2025-03-11 11:20:55 +00001412 FuncId::PartitionInfoGetRegs => {
1413 // Bits[15:0]: Start index
1414 let start_index = (regs[3] & 0xffff) as u16;
1415 let info_tag = ((regs[3] >> 16) & 0xffff) as u16;
1416 Self::PartitionInfoGetRegs {
1417 uuid: Uuid::from_u64_pair(regs[1].swap_bytes(), regs[2].swap_bytes()),
1418 start_index,
1419 info_tag: if start_index == 0 && info_tag != 0 {
1420 return Err(Error::InvalidInformationTag(info_tag));
1421 } else {
1422 info_tag
1423 },
1424 }
1425 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001426 _ => panic!("Invalid number of registers (18) for function {:#x?}", fid),
1427 };
1428
1429 Ok(msg)
Balint Dobszay3aad9572025-01-17 16:54:11 +01001430 }
1431
Balint Dobszaya5846852025-02-26 15:38:53 +01001432 /// Create register contents for an interface.
Balint Dobszayde0dc802025-02-28 14:16:52 +01001433 pub fn to_regs(&self, version: Version, regs: &mut [u64]) {
1434 let reg_cnt = regs.len();
1435
1436 match reg_cnt {
1437 8 => {
1438 assert!(version <= Version(1, 1));
Balint Dobszay91bea9b2025-04-09 13:16:06 +02001439 regs.fill(0);
1440
Balint Dobszayde0dc802025-02-28 14:16:52 +01001441 self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
1442 }
1443 18 => {
1444 assert!(version >= Version(1, 2));
Balint Dobszay91bea9b2025-04-09 13:16:06 +02001445 regs.fill(0);
Balint Dobszayde0dc802025-02-28 14:16:52 +01001446
1447 match self {
1448 Interface::ConsoleLog {
1449 char_lists: ConsoleLogChars::Reg64(_),
1450 ..
1451 }
1452 | Interface::Success {
Imre Kis54773b62025-04-10 13:47:39 +02001453 args: SuccessArgs::Args64_2(_),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001454 ..
1455 }
1456 | Interface::MsgSendDirectReq2 { .. }
Tomás González0a058bc2025-03-11 11:20:55 +00001457 | Interface::MsgSendDirectResp2 { .. }
1458 | Interface::PartitionInfoGetRegs { .. } => {
Balint Dobszayde0dc802025-02-28 14:16:52 +01001459 self.pack_regs18(version, regs.try_into().unwrap());
1460 }
1461 _ => {
1462 self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
1463 }
1464 }
1465 }
1466 _ => panic!("Invalid number of registers {}", reg_cnt),
1467 }
1468 }
1469
1470 fn pack_regs8(&self, version: Version, a: &mut [u64; 8]) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001471 if let Some(function_id) = self.function_id() {
1472 a[0] = function_id as u64;
1473 }
1474
1475 match *self {
1476 Interface::Error {
1477 target_info,
1478 error_code,
Balint Dobszayb727aab2025-04-07 10:24:59 +02001479 error_arg,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001480 } => {
1481 a[1] = u32::from(target_info).into();
1482 a[2] = (error_code as u32).into();
Balint Dobszayb727aab2025-04-07 10:24:59 +02001483 a[3] = error_arg.into();
Balint Dobszay3aad9572025-01-17 16:54:11 +01001484 }
1485 Interface::Success { target_info, args } => {
1486 a[1] = target_info.into();
1487 match args {
Imre Kis54773b62025-04-10 13:47:39 +02001488 SuccessArgs::Args32(regs) => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001489 a[2] = regs[0].into();
1490 a[3] = regs[1].into();
1491 a[4] = regs[2].into();
1492 a[5] = regs[3].into();
1493 a[6] = regs[4].into();
1494 a[7] = regs[5].into();
1495 }
Imre Kis54773b62025-04-10 13:47:39 +02001496 SuccessArgs::Args64(regs) => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001497 a[2] = regs[0];
1498 a[3] = regs[1];
1499 a[4] = regs[2];
1500 a[5] = regs[3];
1501 a[6] = regs[4];
1502 a[7] = regs[5];
1503 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001504 _ => panic!("{:#x?} requires 18 registers", args),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001505 }
1506 }
1507 Interface::Interrupt {
1508 target_info,
1509 interrupt_id,
1510 } => {
1511 a[1] = u32::from(target_info).into();
1512 a[2] = interrupt_id.into();
1513 }
1514 Interface::Version { input_version } => {
1515 a[1] = u32::from(input_version).into();
1516 }
1517 Interface::VersionOut { output_version } => {
1518 a[0] = u32::from(output_version).into();
1519 }
1520 Interface::Features {
1521 feat_id,
1522 input_properties,
1523 } => {
1524 a[1] = u32::from(feat_id).into();
1525 a[2] = input_properties.into();
1526 }
1527 Interface::RxAcquire { vm_id } => {
1528 a[1] = vm_id.into();
1529 }
1530 Interface::RxRelease { vm_id } => {
1531 a[1] = vm_id.into();
1532 }
1533 Interface::RxTxMap { addr, page_cnt } => {
1534 match addr {
1535 RxTxAddr::Addr32 { rx, tx } => {
1536 a[1] = tx.into();
1537 a[2] = rx.into();
1538 }
1539 RxTxAddr::Addr64 { rx, tx } => {
1540 a[1] = tx;
1541 a[2] = rx;
1542 }
1543 }
1544 a[3] = page_cnt.into();
1545 }
1546 Interface::RxTxUnmap { id } => {
1547 a[1] = id.into();
1548 }
1549 Interface::PartitionInfoGet { uuid, flags } => {
1550 let bytes = uuid.into_bytes();
1551 a[1] = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]).into();
1552 a[2] = u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]).into();
1553 a[3] = u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]).into();
1554 a[4] = u32::from_le_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]).into();
Imre Kise295adb2025-04-10 13:26:28 +02001555 a[5] = u32::from(flags).into();
Balint Dobszay3aad9572025-01-17 16:54:11 +01001556 }
Tomás González092202a2025-03-05 11:56:45 +00001557 Interface::MsgWait { flags } => {
1558 if version >= Version(1, 2) {
1559 if let Some(flags) = flags {
1560 a[2] = u32::from(flags).into();
1561 }
1562 }
1563 }
1564 Interface::IdGet | Interface::SpmIdGet | Interface::Yield => {}
Balint Dobszay3aad9572025-01-17 16:54:11 +01001565 Interface::Run { target_info } => {
1566 a[1] = u32::from(target_info).into();
1567 }
1568 Interface::NormalWorldResume => {}
Tomás González17b92442025-03-10 16:45:04 +00001569 Interface::SecondaryEpRegister { entrypoint } => match entrypoint {
1570 SecondaryEpRegisterAddr::Addr32(addr) => a[1] = addr as u64,
1571 SecondaryEpRegisterAddr::Addr64(addr) => a[1] = addr,
1572 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001573 Interface::MsgSend2 {
1574 sender_vm_id,
1575 flags,
1576 } => {
1577 a[1] = sender_vm_id.into();
1578 a[2] = flags.into();
1579 }
1580 Interface::MsgSendDirectReq {
1581 src_id,
1582 dst_id,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001583 args,
1584 } => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +01001585 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Balint Dobszay3aad9572025-01-17 16:54:11 +01001586 match args {
1587 DirectMsgArgs::Args32(args) => {
1588 a[3] = args[0].into();
1589 a[4] = args[1].into();
1590 a[5] = args[2].into();
1591 a[6] = args[3].into();
1592 a[7] = args[4].into();
1593 }
1594 DirectMsgArgs::Args64(args) => {
1595 a[3] = args[0];
1596 a[4] = args[1];
1597 a[5] = args[2];
1598 a[6] = args[3];
1599 a[7] = args[4];
1600 }
Tomás González4d5b0ba2025-03-03 17:15:55 +00001601 DirectMsgArgs::VersionReq { version } => {
1602 a[2] = DirectMsgArgs::VERSION_REQ.into();
1603 a[3] = u32::from(version).into();
1604 }
Tomás González67f92c72025-03-20 16:50:42 +00001605 DirectMsgArgs::PowerPsciReq32 { params } => {
Tomás González4d5b0ba2025-03-03 17:15:55 +00001606 a[2] = DirectMsgArgs::POWER_PSCI_REQ.into();
Tomás González67f92c72025-03-20 16:50:42 +00001607 a[3] = params[0].into();
1608 a[4] = params[1].into();
1609 a[5] = params[2].into();
1610 a[6] = params[3].into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00001611 }
Tomás González67f92c72025-03-20 16:50:42 +00001612 DirectMsgArgs::PowerPsciReq64 { params } => {
Tomás González4d5b0ba2025-03-03 17:15:55 +00001613 a[2] = DirectMsgArgs::POWER_PSCI_REQ.into();
Tomás González67f92c72025-03-20 16:50:42 +00001614 a[3] = params[0];
1615 a[4] = params[1];
1616 a[5] = params[2];
1617 a[6] = params[3];
Tomás González4d5b0ba2025-03-03 17:15:55 +00001618 }
1619 DirectMsgArgs::PowerWarmBootReq { boot_type } => {
1620 a[2] = DirectMsgArgs::POWER_WARM_BOOT_REQ.into();
1621 a[3] = u32::from(boot_type).into();
1622 }
1623 DirectMsgArgs::VmCreated { handle, vm_id } => {
1624 a[2] = DirectMsgArgs::VM_CREATED.into();
1625 let handle_regs: [u32; 2] = handle.into();
1626 a[3] = handle_regs[0].into();
1627 a[4] = handle_regs[1].into();
1628 a[5] = vm_id.into();
1629 }
1630 DirectMsgArgs::VmDestructed { handle, vm_id } => {
1631 a[2] = DirectMsgArgs::VM_DESTRUCTED.into();
1632 let handle_regs: [u32; 2] = handle.into();
1633 a[3] = handle_regs[0].into();
1634 a[4] = handle_regs[1].into();
1635 a[5] = vm_id.into();
1636 }
1637 _ => panic!("Malformed MsgSendDirectReq interface"),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001638 }
1639 }
1640 Interface::MsgSendDirectResp {
1641 src_id,
1642 dst_id,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001643 args,
1644 } => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +01001645 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Balint Dobszay3aad9572025-01-17 16:54:11 +01001646 match args {
1647 DirectMsgArgs::Args32(args) => {
1648 a[3] = args[0].into();
1649 a[4] = args[1].into();
1650 a[5] = args[2].into();
1651 a[6] = args[3].into();
1652 a[7] = args[4].into();
1653 }
1654 DirectMsgArgs::Args64(args) => {
1655 a[3] = args[0];
1656 a[4] = args[1];
1657 a[5] = args[2];
1658 a[6] = args[3];
1659 a[7] = args[4];
1660 }
Tomás González4d5b0ba2025-03-03 17:15:55 +00001661 DirectMsgArgs::VersionResp { version } => {
1662 a[2] = DirectMsgArgs::VERSION_RESP.into();
1663 match version {
Tomás González67f92c72025-03-20 16:50:42 +00001664 None => a[3] = (i32::from(FfaError::NotSupported) as u32).into(),
Tomás González4d5b0ba2025-03-03 17:15:55 +00001665 Some(ver) => a[3] = u32::from(ver).into(),
1666 }
1667 }
1668 DirectMsgArgs::PowerPsciResp { psci_status } => {
1669 a[2] = DirectMsgArgs::POWER_PSCI_RESP.into();
1670 a[3] = psci_status as u64;
1671 }
1672 DirectMsgArgs::VmCreatedAck { sp_status } => {
1673 a[2] = DirectMsgArgs::VM_CREATED_ACK.into();
Tomás González67f92c72025-03-20 16:50:42 +00001674 a[3] = (i32::from(sp_status) as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00001675 }
1676 DirectMsgArgs::VmDestructedAck { sp_status } => {
1677 a[2] = DirectMsgArgs::VM_DESTRUCTED_ACK.into();
Tomás González67f92c72025-03-20 16:50:42 +00001678 a[3] = (i32::from(sp_status) as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00001679 }
1680 _ => panic!("Malformed MsgSendDirectResp interface"),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001681 }
1682 }
1683 Interface::MemDonate {
1684 total_len,
1685 frag_len,
1686 buf,
1687 } => {
1688 a[1] = total_len.into();
1689 a[2] = frag_len.into();
1690 (a[3], a[4]) = match buf {
1691 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1692 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1693 None => (0, 0),
1694 };
1695 }
1696 Interface::MemLend {
1697 total_len,
1698 frag_len,
1699 buf,
1700 } => {
1701 a[1] = total_len.into();
1702 a[2] = frag_len.into();
1703 (a[3], a[4]) = match buf {
1704 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1705 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1706 None => (0, 0),
1707 };
1708 }
1709 Interface::MemShare {
1710 total_len,
1711 frag_len,
1712 buf,
1713 } => {
1714 a[1] = total_len.into();
1715 a[2] = frag_len.into();
1716 (a[3], a[4]) = match buf {
1717 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1718 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1719 None => (0, 0),
1720 };
1721 }
1722 Interface::MemRetrieveReq {
1723 total_len,
1724 frag_len,
1725 buf,
1726 } => {
1727 a[1] = total_len.into();
1728 a[2] = frag_len.into();
1729 (a[3], a[4]) = match buf {
1730 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
1731 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
1732 None => (0, 0),
1733 };
1734 }
1735 Interface::MemRetrieveResp {
1736 total_len,
1737 frag_len,
1738 } => {
1739 a[1] = total_len.into();
1740 a[2] = frag_len.into();
1741 }
1742 Interface::MemRelinquish => {}
1743 Interface::MemReclaim { handle, flags } => {
1744 let handle_regs: [u32; 2] = handle.into();
1745 a[1] = handle_regs[0].into();
1746 a[2] = handle_regs[1].into();
1747 a[3] = flags.into();
1748 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001749 Interface::MemPermGet { addr, page_cnt } => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001750 a[1] = match addr {
1751 MemAddr::Addr32(addr) => addr.into(),
1752 MemAddr::Addr64(addr) => addr,
1753 };
Balint Dobszayde0dc802025-02-28 14:16:52 +01001754 a[2] = if version >= Version(1, 3) {
1755 page_cnt.unwrap().into()
1756 } else {
1757 assert!(page_cnt.is_none());
1758 0
1759 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01001760 }
1761 Interface::MemPermSet {
1762 addr,
1763 page_cnt,
1764 mem_perm,
1765 } => {
1766 a[1] = match addr {
1767 MemAddr::Addr32(addr) => addr.into(),
1768 MemAddr::Addr64(addr) => addr,
1769 };
1770 a[2] = page_cnt.into();
1771 a[3] = mem_perm.into();
1772 }
1773 Interface::ConsoleLog {
1774 char_cnt,
1775 char_lists,
1776 } => {
1777 a[1] = char_cnt.into();
1778 match char_lists {
1779 ConsoleLogChars::Reg32(regs) => {
1780 a[2] = regs[0].into();
1781 a[3] = regs[1].into();
1782 a[4] = regs[2].into();
1783 a[5] = regs[3].into();
1784 a[6] = regs[4].into();
1785 a[7] = regs[5].into();
1786 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001787 _ => panic!("{:#x?} requires 18 registers", char_lists),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001788 }
1789 }
Tomás González7ffb6132025-04-03 12:28:58 +01001790 Interface::NotificationBitmapCreate { vm_id, vcpu_cnt } => {
1791 a[1] = vm_id.into();
1792 a[2] = vcpu_cnt.into();
1793 }
1794 Interface::NotificationBitmapDestroy { vm_id } => {
1795 a[1] = vm_id.into();
1796 }
1797 Interface::NotificationBind {
1798 sender_id,
1799 receiver_id,
1800 flags,
1801 bitmap,
1802 } => {
1803 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
1804 a[2] = u32::from(flags).into();
1805 a[3] = bitmap & 0xffff_ffff;
1806 a[4] = bitmap >> 32;
1807 }
1808 Interface::NotificationUnBind {
1809 sender_id,
1810 receiver_id,
1811 bitmap,
1812 } => {
1813 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
1814 a[3] = bitmap & 0xffff_ffff;
1815 a[4] = bitmap >> 32;
1816 }
1817 Interface::NotificationSet {
1818 sender_id,
1819 receiver_id,
1820 flags,
1821 bitmap,
1822 } => {
1823 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
1824 a[2] = u32::from(flags).into();
1825 a[3] = bitmap & 0xffff_ffff;
1826 a[4] = bitmap >> 32;
1827 }
1828 Interface::NotificationGet {
1829 vcpu_id,
1830 endpoint_id,
1831 flags,
1832 } => {
1833 a[1] = (u64::from(vcpu_id) << 16) | u64::from(endpoint_id);
1834 a[2] = u32::from(flags).into();
1835 }
1836 Interface::NotificationInfoGet { .. } => {}
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01001837 Interface::El3IntrHandle => {}
Balint Dobszayde0dc802025-02-28 14:16:52 +01001838 _ => panic!("{:#x?} requires 18 registers", self),
1839 }
1840 }
1841
1842 fn pack_regs18(&self, version: Version, a: &mut [u64; 18]) {
1843 assert!(version >= Version(1, 2));
1844
Balint Dobszayde0dc802025-02-28 14:16:52 +01001845 if let Some(function_id) = self.function_id() {
1846 a[0] = function_id as u64;
1847 }
1848
1849 match *self {
1850 Interface::Success { target_info, args } => {
1851 a[1] = target_info.into();
1852 match args {
Imre Kis54773b62025-04-10 13:47:39 +02001853 SuccessArgs::Args64_2(regs) => a[2..18].copy_from_slice(&regs[..16]),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001854 _ => panic!("{:#x?} requires 8 registers", args),
1855 }
1856 }
1857 Interface::MsgSendDirectReq2 {
1858 src_id,
1859 dst_id,
1860 uuid,
1861 args,
1862 } => {
1863 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00001864 let (uuid_msb, uuid_lsb) = uuid.as_u64_pair();
1865 (a[2], a[3]) = (uuid_msb.swap_bytes(), uuid_lsb.swap_bytes());
Balint Dobszayde0dc802025-02-28 14:16:52 +01001866 a[4..18].copy_from_slice(&args.0[..14]);
1867 }
1868 Interface::MsgSendDirectResp2 {
1869 src_id,
1870 dst_id,
1871 args,
1872 } => {
1873 a[1] = ((src_id as u64) << 16) | dst_id as u64;
1874 a[2] = 0;
1875 a[3] = 0;
1876 a[4..18].copy_from_slice(&args.0[..14]);
1877 }
1878 Interface::ConsoleLog {
1879 char_cnt,
1880 char_lists,
1881 } => {
1882 a[1] = char_cnt.into();
1883 match char_lists {
1884 ConsoleLogChars::Reg64(regs) => a[2..18].copy_from_slice(&regs[..16]),
1885 _ => panic!("{:#x?} requires 8 registers", char_lists),
1886 }
1887 }
Tomás González0a058bc2025-03-11 11:20:55 +00001888 Interface::PartitionInfoGetRegs {
1889 uuid,
1890 start_index,
1891 info_tag,
1892 } => {
1893 if start_index == 0 && info_tag != 0 {
1894 panic!("Information Tag MBZ if start index is 0: {:#x?}", self);
1895 }
1896 let (uuid_msb, uuid_lsb) = uuid.as_u64_pair();
1897 (a[1], a[2]) = (uuid_msb.swap_bytes(), uuid_lsb.swap_bytes());
1898 a[3] = (u64::from(info_tag) << 16) | u64::from(start_index);
1899 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001900 _ => panic!("{:#x?} requires 8 registers", self),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001901 }
1902 }
1903
Balint Dobszaya5846852025-02-26 15:38:53 +01001904 /// Helper function to create an `FFA_SUCCESS` interface without any arguments.
Balint Dobszay3aad9572025-01-17 16:54:11 +01001905 pub fn success32_noargs() -> Self {
1906 Self::Success {
1907 target_info: 0,
Imre Kis54773b62025-04-10 13:47:39 +02001908 args: SuccessArgs::Args32([0; 6]),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001909 }
1910 }
1911
Tomás González4c8c7d22025-03-10 17:14:57 +00001912 /// Helper function to create an `FFA_SUCCESS` interface without any arguments.
1913 pub fn success64_noargs() -> Self {
1914 Self::Success {
1915 target_info: 0,
Imre Kis54773b62025-04-10 13:47:39 +02001916 args: SuccessArgs::Args64([0; 6]),
Tomás González4c8c7d22025-03-10 17:14:57 +00001917 }
1918 }
1919
Balint Dobszaya5846852025-02-26 15:38:53 +01001920 /// Helper function to create an `FFA_ERROR` interface with an error code.
Balint Dobszay3aad9572025-01-17 16:54:11 +01001921 pub fn error(error_code: FfaError) -> Self {
1922 Self::Error {
1923 target_info: TargetInfo {
1924 endpoint_id: 0,
1925 vcpu_id: 0,
1926 },
1927 error_code,
Balint Dobszayb727aab2025-04-07 10:24:59 +02001928 error_arg: 0,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001929 }
1930 }
1931}
1932
Balint Dobszaya5846852025-02-26 15:38:53 +01001933/// Maximum number of characters transmitted in a single `FFA_CONSOLE_LOG32` message.
1934pub const CONSOLE_LOG_32_MAX_CHAR_CNT: u8 = 24;
Balint Dobszayde0dc802025-02-28 14:16:52 +01001935/// Maximum number of characters transmitted in a single `FFA_CONSOLE_LOG64` message.
1936pub const CONSOLE_LOG_64_MAX_CHAR_CNT: u8 = 128;
Balint Dobszay3aad9572025-01-17 16:54:11 +01001937
Balint Dobszaya5846852025-02-26 15:38:53 +01001938/// Helper function to convert the "Tightly packed list of characters" format used by the
1939/// `FFA_CONSOLE_LOG` interface into a byte slice.
Balint Dobszay3aad9572025-01-17 16:54:11 +01001940pub fn parse_console_log(
1941 char_cnt: u8,
1942 char_lists: &ConsoleLogChars,
1943 log_bytes: &mut [u8],
1944) -> Result<(), FfaError> {
1945 match char_lists {
1946 ConsoleLogChars::Reg32(regs) => {
Balint Dobszaya5846852025-02-26 15:38:53 +01001947 if !(1..=CONSOLE_LOG_32_MAX_CHAR_CNT).contains(&char_cnt) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001948 return Err(FfaError::InvalidParameters);
1949 }
1950 for (i, reg) in regs.iter().enumerate() {
1951 log_bytes[4 * i..4 * (i + 1)].copy_from_slice(&reg.to_le_bytes());
1952 }
1953 }
1954 ConsoleLogChars::Reg64(regs) => {
Balint Dobszaya5846852025-02-26 15:38:53 +01001955 if !(1..=CONSOLE_LOG_64_MAX_CHAR_CNT).contains(&char_cnt) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001956 return Err(FfaError::InvalidParameters);
1957 }
1958 for (i, reg) in regs.iter().enumerate() {
1959 log_bytes[8 * i..8 * (i + 1)].copy_from_slice(&reg.to_le_bytes());
1960 }
1961 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001962 }
1963
Balint Dobszayc8802492025-01-15 18:11:39 +01001964 Ok(())
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001965}
Tomás González0a058bc2025-03-11 11:20:55 +00001966
1967#[cfg(test)]
1968mod tests {
1969 use super::*;
1970
1971 #[test]
1972 fn part_info_get_regs() {
1973 let uuid = Uuid::parse_str("a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8").unwrap();
1974 let uuid_bytes = uuid.as_bytes();
1975 let test_info_tag = 0b1101_1101;
1976 let test_start_index = 0b1101;
1977 let start_index_and_tag = (test_info_tag << 16) | test_start_index;
1978 let version = Version(1, 2);
1979
1980 // From spec:
1981 // Bytes[0...7] of UUID with byte 0 in the low-order bits.
1982 let reg_x1 = (uuid_bytes[7] as u64) << 56
1983 | (uuid_bytes[6] as u64) << 48
1984 | (uuid_bytes[5] as u64) << 40
1985 | (uuid_bytes[4] as u64) << 32
1986 | (uuid_bytes[3] as u64) << 24
1987 | (uuid_bytes[2] as u64) << 16
1988 | (uuid_bytes[1] as u64) << 8
1989 | (uuid_bytes[0] as u64);
1990
1991 // From spec:
1992 // Bytes[8...15] of UUID with byte 8 in the low-order bits.
1993 let reg_x2 = (uuid_bytes[15] as u64) << 56
1994 | (uuid_bytes[14] as u64) << 48
1995 | (uuid_bytes[13] as u64) << 40
1996 | (uuid_bytes[12] as u64) << 32
1997 | (uuid_bytes[11] as u64) << 24
1998 | (uuid_bytes[10] as u64) << 16
1999 | (uuid_bytes[9] as u64) << 8
2000 | (uuid_bytes[8] as u64);
2001
2002 // First, test for wrong tag:
2003 {
2004 let mut regs = [0u64; 18];
2005 regs[0] = FuncId::PartitionInfoGetRegs as u64;
2006 regs[1] = reg_x1;
2007 regs[2] = reg_x2;
2008 regs[3] = test_info_tag << 16;
2009
2010 assert!(Interface::from_regs(version, &regs).is_err_and(
2011 |e| e == Error::InvalidInformationTag(test_info_tag.try_into().unwrap())
2012 ));
2013 }
2014
2015 // Test for regs -> Interface -> regs
2016 {
2017 let mut orig_regs = [0u64; 18];
2018 orig_regs[0] = FuncId::PartitionInfoGetRegs as u64;
2019 orig_regs[1] = reg_x1;
2020 orig_regs[2] = reg_x2;
2021 orig_regs[3] = start_index_and_tag;
2022
2023 let mut test_regs = orig_regs.clone();
2024 let interface = Interface::from_regs(version, &mut test_regs).unwrap();
2025 match &interface {
2026 Interface::PartitionInfoGetRegs {
2027 info_tag,
2028 start_index,
2029 uuid: int_uuid,
2030 } => {
2031 assert_eq!(u64::from(*info_tag), test_info_tag);
2032 assert_eq!(u64::from(*start_index), test_start_index);
2033 assert_eq!(*int_uuid, uuid);
2034 }
2035 _ => panic!("Expecting Interface::PartitionInfoGetRegs!"),
2036 }
2037 test_regs.fill(0);
2038 interface.to_regs(version, &mut test_regs);
2039 assert_eq!(orig_regs, test_regs);
2040 }
2041
2042 // Test for Interface -> regs -> Interface
2043 {
2044 let interface = Interface::PartitionInfoGetRegs {
2045 info_tag: test_info_tag.try_into().unwrap(),
2046 start_index: test_start_index.try_into().unwrap(),
2047 uuid,
2048 };
2049
2050 let mut regs: [u64; 18] = [0; 18];
2051 interface.to_regs(version, &mut regs);
2052
2053 assert_eq!(Some(FuncId::PartitionInfoGetRegs), interface.function_id());
2054 assert_eq!(regs[0], interface.function_id().unwrap() as u64);
2055 assert_eq!(regs[1], reg_x1);
2056 assert_eq!(regs[2], reg_x2);
2057 assert_eq!(regs[3], (test_info_tag << 16) | test_start_index);
2058
2059 assert_eq!(Interface::from_regs(version, &regs).unwrap(), interface);
2060 }
2061 }
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00002062
2063 #[test]
2064 fn msg_send_direct_req2() {
2065 let uuid = Uuid::parse_str("a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8").unwrap();
2066 let uuid_bytes = uuid.as_bytes();
2067
2068 // From spec:
2069 // Bytes[0...7] of UUID with byte 0 in the low-order bits.
2070 let reg_x2 = (uuid_bytes[7] as u64) << 56
2071 | (uuid_bytes[6] as u64) << 48
2072 | (uuid_bytes[5] as u64) << 40
2073 | (uuid_bytes[4] as u64) << 32
2074 | (uuid_bytes[3] as u64) << 24
2075 | (uuid_bytes[2] as u64) << 16
2076 | (uuid_bytes[1] as u64) << 8
2077 | (uuid_bytes[0] as u64);
2078
2079 // From spec:
2080 // Bytes[8...15] of UUID with byte 8 in the low-order bits.
2081 let reg_x3 = (uuid_bytes[15] as u64) << 56
2082 | (uuid_bytes[14] as u64) << 48
2083 | (uuid_bytes[13] as u64) << 40
2084 | (uuid_bytes[12] as u64) << 32
2085 | (uuid_bytes[11] as u64) << 24
2086 | (uuid_bytes[10] as u64) << 16
2087 | (uuid_bytes[9] as u64) << 8
2088 | (uuid_bytes[8] as u64);
2089
2090 let test_sender = 0b1101_1101;
2091 let test_receiver = 0b1101;
2092 let test_sender_receiver = (test_sender << 16) | test_receiver;
2093 let version = Version(1, 2);
2094
2095 // Test for regs -> Interface -> regs
2096 {
2097 let mut orig_regs = [0u64; 18];
2098 orig_regs[0] = FuncId::MsgSendDirectReq64_2 as u64;
2099 orig_regs[1] = test_sender_receiver;
2100 orig_regs[2] = reg_x2;
2101 orig_regs[3] = reg_x3;
2102
2103 let mut test_regs = orig_regs.clone();
2104 let interface = Interface::from_regs(version, &mut test_regs).unwrap();
2105 match &interface {
2106 Interface::MsgSendDirectReq2 {
2107 dst_id,
2108 src_id,
2109 args: _,
2110 uuid: int_uuid,
2111 } => {
2112 assert_eq!(u64::from(*src_id), test_sender);
2113 assert_eq!(u64::from(*dst_id), test_receiver);
2114 assert_eq!(*int_uuid, uuid);
2115 }
2116 _ => panic!("Expecting Interface::MsgSendDirectReq2!"),
2117 }
2118 test_regs.fill(0);
2119 interface.to_regs(version, &mut test_regs);
2120 assert_eq!(orig_regs, test_regs);
2121 }
2122
2123 // Test for Interface -> regs -> Interface
2124 {
2125 let rest_of_regs: [u64; 14] = [0; 14];
2126
2127 let interface = Interface::MsgSendDirectReq2 {
2128 src_id: test_sender.try_into().unwrap(),
2129 dst_id: test_receiver.try_into().unwrap(),
2130 uuid,
2131 args: DirectMsg2Args(rest_of_regs),
2132 };
2133
2134 let mut regs: [u64; 18] = [0; 18];
2135 interface.to_regs(version, &mut regs);
2136
2137 assert_eq!(Some(FuncId::MsgSendDirectReq64_2), interface.function_id());
2138 assert_eq!(regs[0], interface.function_id().unwrap() as u64);
2139 assert_eq!(regs[1], test_sender_receiver);
2140 assert_eq!(regs[2], reg_x2);
2141 assert_eq!(regs[3], reg_x3);
2142 assert_eq!(regs[4], 0);
2143
2144 assert_eq!(Interface::from_regs(version, &regs).unwrap(), interface);
2145 }
2146 }
Tomás González6ccba0a2025-04-09 13:31:29 +01002147
2148 #[test]
2149 fn is_32bit() {
2150 let interface_64 = Interface::MsgSendDirectReq {
2151 src_id: 0,
2152 dst_id: 1,
2153 args: DirectMsgArgs::Args64([0, 0, 0, 0, 0]),
2154 };
2155 assert!(!interface_64.is_32bit());
2156
2157 let interface_32 = Interface::MsgSendDirectReq {
2158 src_id: 0,
2159 dst_id: 1,
2160 args: DirectMsgArgs::Args32([0, 0, 0, 0, 0]),
2161 };
2162 assert!(interface_32.is_32bit());
2163 }
Tomás González0a058bc2025-03-11 11:20:55 +00002164}