blob: 2f4fc7108ca03bcafee4b78dc80015b3a2c17961 [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;
Imre Kisc0b062c2025-05-26 19:31:00 +020012pub use uuid::Uuid;
Imre Kis189f18c2025-05-26 19:33:05 +020013use zerocopy::{transmute, FromBytes, Immutable, IntoBytes};
Balint Dobszay5bf492f2024-07-29 17:21:32 +020014
15pub mod boot_info;
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +010016mod ffa_v1_1;
Balint Dobszayde0dc802025-02-28 14:16:52 +010017mod ffa_v1_2;
Balint Dobszay5bf492f2024-07-29 17:21:32 +020018pub mod memory_management;
19pub mod partition_info;
20
Balint Dobszaya5846852025-02-26 15:38:53 +010021/// Constant for 4K page size. On many occasions the FF-A spec defines memory size as count of 4K
22/// pages, regardless of the current translation granule.
Balint Dobszay3aad9572025-01-17 16:54:11 +010023pub const FFA_PAGE_SIZE_4K: usize = 4096;
24
Balint Dobszaya5846852025-02-26 15:38:53 +010025/// Rich error types returned by this module. Should be converted to [`crate::FfaError`] when used
26/// with the `FFA_ERROR` interface.
Tomás González0a058bc2025-03-11 11:20:55 +000027#[derive(Debug, Error, PartialEq)]
Balint Dobszay3aad9572025-01-17 16:54:11 +010028pub enum Error {
29 #[error("Unrecognised FF-A function ID {0}")]
30 UnrecognisedFunctionId(u32),
31 #[error("Unrecognised FF-A feature ID {0}")]
32 UnrecognisedFeatureId(u8),
33 #[error("Unrecognised FF-A error code {0}")]
34 UnrecognisedErrorCode(i32),
Tomás González4d5b0ba2025-03-03 17:15:55 +000035 #[error("Unrecognised FF-A Framework Message {0}")]
36 UnrecognisedFwkMsg(u32),
Tomás González092202a2025-03-05 11:56:45 +000037 #[error("Invalid FF-A Msg Wait Flag {0}")]
38 InvalidMsgWaitFlag(u32),
Tomás González4d5b0ba2025-03-03 17:15:55 +000039 #[error("Unrecognised VM availability status {0}")]
40 UnrecognisedVmAvailabilityStatus(i32),
41 #[error("Unrecognised FF-A Warm Boot Type {0}")]
42 UnrecognisedWarmBootType(u32),
43 #[error("Invalid version {0}")]
44 InvalidVersion(u32),
Tomás González0a058bc2025-03-11 11:20:55 +000045 #[error("Invalid Information Tag {0}")]
46 InvalidInformationTag(u16),
Tomás González7ffb6132025-04-03 12:28:58 +010047 #[error("Invalid Flag for Notification Set")]
48 InvalidNotificationSetFlag(u32),
49 #[error("Invalid Vm ID")]
50 InvalidVmId(u32),
Imre Kise295adb2025-04-10 13:26:28 +020051 #[error("Invalid FF-A Partition Info Get Flag {0}")]
52 InvalidPartitionInfoGetFlag(u32),
Imre Kis839eaef2025-04-11 17:38:36 +020053 #[error("Invalid success argument variant")]
54 InvalidSuccessArgsVariant,
Imre Kis787c5002025-04-10 14:25:51 +020055 #[error("Invalid notification count")]
56 InvalidNotificationCount,
Imre Kis92b663e2025-04-10 14:15:05 +020057 #[error("Invalid Partition Info Get Regs response")]
58 InvalidPartitionInfoGetRegsResponse,
Balint Dobszay82c71dd2025-04-15 10:16:44 +020059 #[error("Invalid FF-A version {0} for function ID {1:?}")]
60 InvalidVersionForFunctionId(Version, FuncId),
Imre Kis189f18c2025-05-26 19:33:05 +020061 #[error("Invalid character count {0}")]
62 InvalidCharacterCount(u8),
Balint Dobszay3aad9572025-01-17 16:54:11 +010063}
64
65impl From<Error> for FfaError {
66 fn from(value: Error) -> Self {
67 match value {
Balint Dobszay82c71dd2025-04-15 10:16:44 +020068 Error::UnrecognisedFunctionId(_)
69 | Error::UnrecognisedFeatureId(_)
70 | Error::InvalidVersionForFunctionId(..) => Self::NotSupported,
Tomás González0a058bc2025-03-11 11:20:55 +000071 Error::InvalidInformationTag(_) => Self::Retry,
Tomás González4d5b0ba2025-03-03 17:15:55 +000072 Error::UnrecognisedErrorCode(_)
73 | Error::UnrecognisedFwkMsg(_)
74 | Error::InvalidVersion(_)
Tomás González092202a2025-03-05 11:56:45 +000075 | Error::InvalidMsgWaitFlag(_)
Tomás González4d5b0ba2025-03-03 17:15:55 +000076 | Error::UnrecognisedVmAvailabilityStatus(_)
Tomás González7ffb6132025-04-03 12:28:58 +010077 | Error::InvalidNotificationSetFlag(_)
78 | Error::InvalidVmId(_)
Imre Kise295adb2025-04-10 13:26:28 +020079 | Error::UnrecognisedWarmBootType(_)
Imre Kis839eaef2025-04-11 17:38:36 +020080 | Error::InvalidPartitionInfoGetFlag(_)
Imre Kis787c5002025-04-10 14:25:51 +020081 | Error::InvalidSuccessArgsVariant
Imre Kis92b663e2025-04-10 14:15:05 +020082 | Error::InvalidNotificationCount
Imre Kis189f18c2025-05-26 19:33:05 +020083 | Error::InvalidPartitionInfoGetRegsResponse
84 | Error::InvalidCharacterCount(_) => Self::InvalidParameters,
Balint Dobszay3aad9572025-01-17 16:54:11 +010085 }
86 }
87}
Balint Dobszay5bf492f2024-07-29 17:21:32 +020088
Balint Dobszaya5846852025-02-26 15:38:53 +010089/// 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 +020090#[derive(PartialEq, Clone, Copy)]
91pub enum Instance {
Balint Dobszaya5846852025-02-26 15:38:53 +010092 /// The instance between the SPMC and SPMD.
Balint Dobszay5bf492f2024-07-29 17:21:32 +020093 SecurePhysical,
Balint Dobszaya5846852025-02-26 15:38:53 +010094 /// The instance between the SPMC and a physical SP (contains the SP's endpoint ID).
Balint Dobszay5bf492f2024-07-29 17:21:32 +020095 SecureVirtual(u16),
96}
97
Balint Dobszaya5846852025-02-26 15:38:53 +010098/// Function IDs of the various FF-A interfaces.
Andrew Walbran969b9252024-11-25 15:35:42 +000099#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
Balint Dobszay3aad9572025-01-17 16:54:11 +0100100#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedFunctionId))]
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200101#[repr(u32)]
102pub enum FuncId {
103 Error = 0x84000060,
104 Success32 = 0x84000061,
105 Success64 = 0xc4000061,
106 Interrupt = 0x84000062,
107 Version = 0x84000063,
108 Features = 0x84000064,
109 RxAcquire = 0x84000084,
110 RxRelease = 0x84000065,
111 RxTxMap32 = 0x84000066,
112 RxTxMap64 = 0xc4000066,
113 RxTxUnmap = 0x84000067,
114 PartitionInfoGet = 0x84000068,
Balint Dobszaye6aa4862025-02-28 16:37:56 +0100115 PartitionInfoGetRegs = 0xc400008b,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200116 IdGet = 0x84000069,
117 SpmIdGet = 0x84000085,
Balint Dobszaye6aa4862025-02-28 16:37:56 +0100118 ConsoleLog32 = 0x8400008a,
119 ConsoleLog64 = 0xc400008a,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200120 MsgWait = 0x8400006b,
121 Yield = 0x8400006c,
122 Run = 0x8400006d,
123 NormalWorldResume = 0x8400007c,
124 MsgSend2 = 0x84000086,
125 MsgSendDirectReq32 = 0x8400006f,
126 MsgSendDirectReq64 = 0xc400006f,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100127 MsgSendDirectReq64_2 = 0xc400008d,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200128 MsgSendDirectResp32 = 0x84000070,
129 MsgSendDirectResp64 = 0xc4000070,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100130 MsgSendDirectResp64_2 = 0xc400008e,
Balint Dobszaye6aa4862025-02-28 16:37:56 +0100131 NotificationBitmapCreate = 0x8400007d,
132 NotificationBitmapDestroy = 0x8400007e,
133 NotificationBind = 0x8400007f,
134 NotificationUnbind = 0x84000080,
135 NotificationSet = 0x84000081,
136 NotificationGet = 0x84000082,
137 NotificationInfoGet32 = 0x84000083,
138 NotificationInfoGet64 = 0xc4000083,
139 El3IntrHandle = 0x8400008c,
Tomás González17b92442025-03-10 16:45:04 +0000140 SecondaryEpRegister32 = 0x84000087,
141 SecondaryEpRegister64 = 0xc4000087,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200142 MemDonate32 = 0x84000071,
143 MemDonate64 = 0xc4000071,
144 MemLend32 = 0x84000072,
145 MemLend64 = 0xc4000072,
146 MemShare32 = 0x84000073,
147 MemShare64 = 0xc4000073,
148 MemRetrieveReq32 = 0x84000074,
149 MemRetrieveReq64 = 0xc4000074,
150 MemRetrieveResp = 0x84000075,
151 MemRelinquish = 0x84000076,
152 MemReclaim = 0x84000077,
153 MemPermGet32 = 0x84000088,
154 MemPermGet64 = 0xc4000088,
155 MemPermSet32 = 0x84000089,
156 MemPermSet64 = 0xc4000089,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200157}
158
Balint Dobszayde0dc802025-02-28 14:16:52 +0100159impl FuncId {
160 /// Returns true if this is a 32-bit call, or false if it is a 64-bit call.
161 pub fn is_32bit(&self) -> bool {
Tomás González6ccba0a2025-04-09 13:31:29 +0100162 u32::from(*self) & (1 << 30) == 0
Balint Dobszayde0dc802025-02-28 14:16:52 +0100163 }
Balint Dobszay82c71dd2025-04-15 10:16:44 +0200164
165 /// Returns the FF-A version that has introduced the function ID.
166 pub fn minimum_ffa_version(&self) -> Version {
167 match self {
168 FuncId::Error
169 | FuncId::Success32
170 | FuncId::Success64
171 | FuncId::Interrupt
172 | FuncId::Version
173 | FuncId::Features
174 | FuncId::RxRelease
175 | FuncId::RxTxMap32
176 | FuncId::RxTxMap64
177 | FuncId::RxTxUnmap
178 | FuncId::PartitionInfoGet
179 | FuncId::IdGet
180 | FuncId::MsgWait
181 | FuncId::Yield
182 | FuncId::Run
183 | FuncId::NormalWorldResume
184 | FuncId::MsgSendDirectReq32
185 | FuncId::MsgSendDirectReq64
186 | FuncId::MsgSendDirectResp32
187 | FuncId::MsgSendDirectResp64
188 | FuncId::MemDonate32
189 | FuncId::MemDonate64
190 | FuncId::MemLend32
191 | FuncId::MemLend64
192 | FuncId::MemShare32
193 | FuncId::MemShare64
194 | FuncId::MemRetrieveReq32
195 | FuncId::MemRetrieveReq64
196 | FuncId::MemRetrieveResp
197 | FuncId::MemRelinquish
198 | FuncId::MemReclaim => Version(1, 0),
199
200 FuncId::RxAcquire
201 | FuncId::SpmIdGet
202 | FuncId::MsgSend2
203 | FuncId::MemPermGet32
204 | FuncId::MemPermGet64
205 | FuncId::MemPermSet32
206 | FuncId::MemPermSet64
207 | FuncId::NotificationBitmapCreate
208 | FuncId::NotificationBitmapDestroy
209 | FuncId::NotificationBind
210 | FuncId::NotificationUnbind
211 | FuncId::NotificationSet
212 | FuncId::NotificationGet
213 | FuncId::NotificationInfoGet32
214 | FuncId::NotificationInfoGet64
215 | FuncId::SecondaryEpRegister32
216 | FuncId::SecondaryEpRegister64 => Version(1, 1),
217
218 FuncId::PartitionInfoGetRegs
219 | FuncId::ConsoleLog32
220 | FuncId::ConsoleLog64
221 | FuncId::MsgSendDirectReq64_2
222 | FuncId::MsgSendDirectResp64_2
223 | FuncId::El3IntrHandle => Version(1, 2),
224 }
225 }
Balint Dobszayde0dc802025-02-28 14:16:52 +0100226}
227
Balint Dobszaya5846852025-02-26 15:38:53 +0100228/// Error status codes used by the `FFA_ERROR` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100229#[derive(Clone, Copy, Debug, Eq, Error, IntoPrimitive, PartialEq, TryFromPrimitive)]
230#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedErrorCode))]
231#[repr(i32)]
232pub enum FfaError {
233 #[error("Not supported")]
234 NotSupported = -1,
235 #[error("Invalid parameters")]
236 InvalidParameters = -2,
237 #[error("No memory")]
238 NoMemory = -3,
239 #[error("Busy")]
240 Busy = -4,
241 #[error("Interrupted")]
242 Interrupted = -5,
243 #[error("Denied")]
244 Denied = -6,
245 #[error("Retry")]
246 Retry = -7,
247 #[error("Aborted")]
248 Aborted = -8,
249 #[error("No data")]
250 NoData = -9,
251}
252
Balint Dobszaya5846852025-02-26 15:38:53 +0100253/// Endpoint ID and vCPU ID pair, used by `FFA_ERROR`, `FFA_INTERRUPT` and `FFA_RUN` interfaces.
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200254#[derive(Debug, Eq, PartialEq, Clone, Copy)]
Balint Dobszay3aad9572025-01-17 16:54:11 +0100255pub struct TargetInfo {
256 pub endpoint_id: u16,
257 pub vcpu_id: u16,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200258}
259
Balint Dobszay3aad9572025-01-17 16:54:11 +0100260impl From<u32> for TargetInfo {
261 fn from(value: u32) -> Self {
262 Self {
263 endpoint_id: (value >> 16) as u16,
264 vcpu_id: value as u16,
265 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200266 }
267}
268
Balint Dobszay3aad9572025-01-17 16:54:11 +0100269impl From<TargetInfo> for u32 {
270 fn from(value: TargetInfo) -> Self {
Balint Dobszaye9a3e762025-02-26 17:29:57 +0100271 ((value.endpoint_id as u32) << 16) | value.vcpu_id as u32
Andrew Walbran0d315812024-11-25 15:36:36 +0000272 }
Balint Dobszay3aad9572025-01-17 16:54:11 +0100273}
Andrew Walbran0d315812024-11-25 15:36:36 +0000274
Imre Kis839eaef2025-04-11 17:38:36 +0200275/// Generic arguments of the `FFA_SUCCESS` interface. The interpretation of the arguments depends on
276/// the interface that initiated the request. The application code has knowledge of the request, so
277/// it has to convert `SuccessArgs` into/from a specific success args structure that matches the
278/// request.
Imre Kis4e9d8bc2025-04-10 13:48:26 +0200279///
280/// The current specialized success arguments types are:
281/// * `FFA_FEATURES` - [`SuccessArgsFeatures`]
Imre Kisbbef2872025-04-10 14:11:29 +0200282/// * `FFA_ID_GET` - [`SuccessArgsIdGet`]
283/// * `FFA_SPM_ID_GET` - [`SuccessArgsSpmIdGet`]
Imre Kis61c34092025-04-10 14:14:38 +0200284/// * `FFA_PARTITION_INFO_GET` - [`partition_info::SuccessArgsPartitionInfoGet`]
Imre Kis92b663e2025-04-10 14:15:05 +0200285/// * `FFA_PARTITION_INFO_GET_REGS` - [`partition_info::SuccessArgsPartitionInfoGetRegs`]
Imre Kis9959e062025-04-10 14:16:10 +0200286/// * `FFA_NOTIFICATION_GET` - [`SuccessArgsNotificationGet`]
Imre Kis787c5002025-04-10 14:25:51 +0200287/// * `FFA_NOTIFICATION_INFO_GET_32` - [`SuccessArgsNotificationInfoGet32`]
288/// * `FFA_NOTIFICATION_INFO_GET_64` - [`SuccessArgsNotificationInfoGet64`]
Balint Dobszay3aad9572025-01-17 16:54:11 +0100289#[derive(Debug, Eq, PartialEq, Clone, Copy)]
290pub enum SuccessArgs {
Imre Kis54773b62025-04-10 13:47:39 +0200291 Args32([u32; 6]),
292 Args64([u64; 6]),
293 Args64_2([u64; 16]),
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200294}
295
Imre Kis839eaef2025-04-11 17:38:36 +0200296impl SuccessArgs {
297 fn try_get_args32(self) -> Result<[u32; 6], Error> {
298 match self {
299 SuccessArgs::Args32(args) => Ok(args),
300 SuccessArgs::Args64(_) | SuccessArgs::Args64_2(_) => {
301 Err(Error::InvalidSuccessArgsVariant)
302 }
303 }
304 }
305
306 fn try_get_args64(self) -> Result<[u64; 6], Error> {
307 match self {
308 SuccessArgs::Args64(args) => Ok(args),
309 SuccessArgs::Args32(_) | SuccessArgs::Args64_2(_) => {
310 Err(Error::InvalidSuccessArgsVariant)
311 }
312 }
313 }
314
315 fn try_get_args64_2(self) -> Result<[u64; 16], Error> {
316 match self {
317 SuccessArgs::Args64_2(args) => Ok(args),
318 SuccessArgs::Args32(_) | SuccessArgs::Args64(_) => {
319 Err(Error::InvalidSuccessArgsVariant)
320 }
321 }
322 }
323}
324
Tomás González17b92442025-03-10 16:45:04 +0000325/// Entrypoint address argument for `FFA_SECONDARY_EP_REGISTER` interface.
326#[derive(Debug, Eq, PartialEq, Clone, Copy)]
327pub enum SecondaryEpRegisterAddr {
328 Addr32(u32),
329 Addr64(u64),
330}
331
Balint Dobszaya5846852025-02-26 15:38:53 +0100332/// Version number of the FF-A implementation, `.0` is the major, `.1` is minor the version.
Balint Dobszayde0dc802025-02-28 14:16:52 +0100333#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord)]
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200334pub struct Version(pub u16, pub u16);
335
Tomás González1f794352025-03-03 16:47:06 +0000336impl Version {
Tomás González83146af2025-03-04 11:32:41 +0000337 // The FF-A spec mandates that bit[31] of a version number must be 0
338 const MBZ_BITS: u32 = 1 << 31;
339
Tomás González1f794352025-03-03 16:47:06 +0000340 /// Returns whether the caller's version (self) is compatible with the callee's version (input
341 /// parameter)
342 pub fn is_compatible_to(&self, callee_version: &Version) -> bool {
343 self.0 == callee_version.0 && self.1 <= callee_version.1
344 }
345}
346
Tomás González83146af2025-03-04 11:32:41 +0000347impl TryFrom<u32> for Version {
348 type Error = Error;
349
350 fn try_from(val: u32) -> Result<Self, Self::Error> {
351 if (val & Self::MBZ_BITS) != 0 {
352 Err(Error::InvalidVersion(val))
353 } else {
354 Ok(Self((val >> 16) as u16, val as u16))
355 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200356 }
357}
358
359impl From<Version> for u32 {
360 fn from(v: Version) -> Self {
Tomás González83146af2025-03-04 11:32:41 +0000361 let v_u32 = ((v.0 as u32) << 16) | v.1 as u32;
362 assert!(v_u32 & Version::MBZ_BITS == 0);
363 v_u32
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200364 }
365}
366
Andrew Walbran19970ba2024-11-25 15:35:00 +0000367impl Display for Version {
368 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
369 write!(f, "{}.{}", self.0, self.1)
370 }
371}
372
373impl Debug for Version {
374 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
375 Display::fmt(self, f)
376 }
377}
378
Balint Dobszaya5846852025-02-26 15:38:53 +0100379/// Feature IDs used by the `FFA_FEATURES` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100380#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
381#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedFeatureId))]
382#[repr(u8)]
383pub enum FeatureId {
384 NotificationPendingInterrupt = 0x1,
385 ScheduleReceiverInterrupt = 0x2,
386 ManagedExitInterrupt = 0x3,
387}
Balint Dobszayc8802492025-01-15 18:11:39 +0100388
Balint Dobszaya5846852025-02-26 15:38:53 +0100389/// Arguments for the `FFA_FEATURES` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100390#[derive(Debug, Eq, PartialEq, Clone, Copy)]
391pub enum Feature {
392 FuncId(FuncId),
393 FeatureId(FeatureId),
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100394 Unknown(u32),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100395}
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200396
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100397impl From<u32> for Feature {
398 fn from(value: u32) -> Self {
399 // Bit[31] is set for all valid FF-A function IDs so we don't have to check it separately
400 if let Ok(func_id) = value.try_into() {
401 Self::FuncId(func_id)
402 } else if let Ok(feat_id) = (value as u8).try_into() {
403 Self::FeatureId(feat_id)
Balint Dobszay3aad9572025-01-17 16:54:11 +0100404 } else {
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100405 Self::Unknown(value)
406 }
Balint Dobszay3aad9572025-01-17 16:54:11 +0100407 }
408}
409
410impl From<Feature> for u32 {
411 fn from(value: Feature) -> Self {
412 match value {
413 Feature::FuncId(func_id) => (1 << 31) | func_id as u32,
414 Feature::FeatureId(feature_id) => feature_id as u32,
Imre Kis29c8ace2025-04-11 13:49:58 +0200415 Feature::Unknown(id) => id,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100416 }
417 }
418}
419
Imre Kis4e9d8bc2025-04-10 13:48:26 +0200420/// `FFA_FEATURES` specific success argument structure. This type needs further specialization based
421/// on 'FF-A function ID or Feature ID' field of the preceeding `FFA_FEATURES` request.
422#[derive(Debug, Eq, PartialEq, Clone, Copy)]
423pub struct SuccessArgsFeatures {
424 pub properties: [u32; 2],
425}
426
427impl From<SuccessArgsFeatures> for SuccessArgs {
428 fn from(value: SuccessArgsFeatures) -> Self {
429 Self::Args32([value.properties[0], value.properties[1], 0, 0, 0, 0])
430 }
431}
432
433impl TryFrom<SuccessArgs> for SuccessArgsFeatures {
434 type Error = Error;
435
436 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
437 let args = value.try_get_args32()?;
438
439 Ok(Self {
440 properties: [args[0], args[1]],
441 })
442 }
443}
444
Balint Dobszaya5846852025-02-26 15:38:53 +0100445/// RXTX buffer descriptor, used by `FFA_RXTX_MAP`.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100446#[derive(Debug, Eq, PartialEq, Clone, Copy)]
447pub enum RxTxAddr {
448 Addr32 { rx: u32, tx: u32 },
449 Addr64 { rx: u64, tx: u64 },
450}
451
Imre Kisbbef2872025-04-10 14:11:29 +0200452/// `FFA_ID_GET` specific success argument structure.
453#[derive(Debug, Eq, PartialEq, Clone, Copy)]
454pub struct SuccessArgsIdGet {
455 pub id: u16,
456}
457
458impl From<SuccessArgsIdGet> for SuccessArgs {
459 fn from(value: SuccessArgsIdGet) -> Self {
460 SuccessArgs::Args32([value.id as u32, 0, 0, 0, 0, 0])
461 }
462}
463
464impl TryFrom<SuccessArgs> for SuccessArgsIdGet {
465 type Error = Error;
466
467 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
468 let args = value.try_get_args32()?;
469 Ok(Self { id: args[0] as u16 })
470 }
471}
472
473/// `FFA_SPM_ID_GET` specific success argument structure.
474#[derive(Debug, Eq, PartialEq, Clone, Copy)]
475pub struct SuccessArgsSpmIdGet {
476 pub id: u16,
477}
478
479impl From<SuccessArgsSpmIdGet> for SuccessArgs {
480 fn from(value: SuccessArgsSpmIdGet) -> Self {
481 SuccessArgs::Args32([value.id as u32, 0, 0, 0, 0, 0])
482 }
483}
484
485impl TryFrom<SuccessArgs> for SuccessArgsSpmIdGet {
486 type Error = Error;
487
488 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
489 let args = value.try_get_args32()?;
490 Ok(Self { id: args[0] as u16 })
491 }
492}
493
Imre Kise295adb2025-04-10 13:26:28 +0200494/// Flags of the `FFA_PARTITION_INFO_GET` interface.
495#[derive(Debug, Eq, PartialEq, Clone, Copy)]
496pub struct PartitionInfoGetFlags {
497 pub count_only: bool,
498}
499
500impl PartitionInfoGetFlags {
501 const RETURN_INFORMATION_TYPE_FLAG: u32 = 1 << 0;
502 const MBZ_BITS: u32 = 0xffff_fffe;
503}
504
505impl TryFrom<u32> for PartitionInfoGetFlags {
506 type Error = Error;
507
508 fn try_from(val: u32) -> Result<Self, Self::Error> {
509 if (val & Self::MBZ_BITS) != 0 {
510 Err(Error::InvalidPartitionInfoGetFlag(val))
511 } else {
512 Ok(Self {
513 count_only: val & Self::RETURN_INFORMATION_TYPE_FLAG != 0,
514 })
515 }
516 }
517}
518
519impl From<PartitionInfoGetFlags> for u32 {
520 fn from(flags: PartitionInfoGetFlags) -> Self {
521 let mut bits: u32 = 0;
522 if flags.count_only {
523 bits |= PartitionInfoGetFlags::RETURN_INFORMATION_TYPE_FLAG;
524 }
525 bits
526 }
527}
528
Tomás González4d5b0ba2025-03-03 17:15:55 +0000529/// Composite type for capturing success and error return codes for the VM availability messages.
530///
531/// Error codes are handled by the `FfaError` type. Having a separate type for errors helps using
532/// `Result<(), FfaError>`. If a single type would include both success and error values,
533/// then `Err(FfaError::Success)` would be incomprehensible.
534#[derive(Debug, Eq, PartialEq, Clone, Copy)]
535pub enum VmAvailabilityStatus {
536 Success,
537 Error(FfaError),
538}
539
540impl TryFrom<i32> for VmAvailabilityStatus {
541 type Error = Error;
542 fn try_from(value: i32) -> Result<Self, <Self as TryFrom<i32>>::Error> {
543 Ok(match value {
544 0 => Self::Success,
545 error_code => Self::Error(FfaError::try_from(error_code)?),
546 })
547 }
548}
549
550impl From<VmAvailabilityStatus> for i32 {
551 fn from(value: VmAvailabilityStatus) -> Self {
552 match value {
553 VmAvailabilityStatus::Success => 0,
554 VmAvailabilityStatus::Error(error_code) => error_code.into(),
555 }
556 }
557}
558
559/// Arguments for the Power Warm Boot `FFA_MSG_SEND_DIRECT_REQ` interface.
560#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
561#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedWarmBootType))]
562#[repr(u32)]
563pub enum WarmBootType {
564 ExitFromSuspend = 0,
565 ExitFromLowPower = 1,
566}
567
Balint Dobszaya5846852025-02-26 15:38:53 +0100568/// Arguments for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}` interfaces.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100569#[derive(Debug, Eq, PartialEq, Clone, Copy)]
570pub enum DirectMsgArgs {
571 Args32([u32; 5]),
572 Args64([u64; 5]),
Tomás González4d5b0ba2025-03-03 17:15:55 +0000573 /// Message for forwarding FFA_VERSION call from Normal world to the SPMC
574 VersionReq {
575 version: Version,
576 },
577 /// Response message to forwarded FFA_VERSION call from the Normal world
578 /// Contains the version returned by the SPMC or None
579 VersionResp {
580 version: Option<Version>,
581 },
582 /// Message for a power management operation initiated by a PSCI function
583 PowerPsciReq32 {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000584 // params[i]: Input parameter in w[i] in PSCI function invocation at EL3.
Tomás González67f92c72025-03-20 16:50:42 +0000585 // params[0]: Function ID.
586 params: [u32; 4],
Tomás González4d5b0ba2025-03-03 17:15:55 +0000587 },
588 /// Message for a power management operation initiated by a PSCI function
589 PowerPsciReq64 {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000590 // params[i]: Input parameter in x[i] in PSCI function invocation at EL3.
Tomás González67f92c72025-03-20 16:50:42 +0000591 // params[0]: Function ID.
592 params: [u64; 4],
Tomás González4d5b0ba2025-03-03 17:15:55 +0000593 },
594 /// Message for a warm boot
595 PowerWarmBootReq {
596 boot_type: WarmBootType,
597 },
598 /// Response message to indicate return status of the last power management request message
599 /// Return error code SUCCESS or DENIED as defined in PSCI spec. Caller is left to do the
600 /// parsing of the return status.
601 PowerPsciResp {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000602 psci_status: i32,
603 },
604 /// Message to signal creation of a VM
605 VmCreated {
606 // Globally unique Handle to identify a memory region that contains IMPLEMENTATION DEFINED
607 // information associated with the created VM.
608 // The invalid memory region handle must be specified by the Hypervisor if this field is not
609 // used.
610 handle: memory_management::Handle,
611 vm_id: u16,
612 },
613 /// Message to acknowledge creation of a VM
614 VmCreatedAck {
615 sp_status: VmAvailabilityStatus,
616 },
617 /// Message to signal destruction of a VM
618 VmDestructed {
619 // Globally unique Handle to identify a memory region that contains IMPLEMENTATION DEFINED
620 // information associated with the created VM.
621 // The invalid memory region handle must be specified by the Hypervisor if this field is not
622 // used.
623 handle: memory_management::Handle,
624 vm_id: u16,
625 },
626 /// Message to acknowledge destruction of a VM
627 VmDestructedAck {
628 sp_status: VmAvailabilityStatus,
629 },
630}
631
632impl DirectMsgArgs {
633 // Flags for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}` interfaces.
634
635 const FWK_MSG_BITS: u32 = 1 << 31;
636 const VERSION_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b1000;
637 const VERSION_RESP: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b1001;
638 const POWER_PSCI_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS;
639 const POWER_WARM_BOOT_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0001;
640 const POWER_PSCI_RESP: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0010;
641 const VM_CREATED: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0100;
642 const VM_CREATED_ACK: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0101;
643 const VM_DESTRUCTED: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0110;
644 const VM_DESTRUCTED_ACK: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0111;
Balint Dobszay3aad9572025-01-17 16:54:11 +0100645}
646
Balint Dobszayde0dc802025-02-28 14:16:52 +0100647/// Arguments for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}2` interfaces.
648#[derive(Debug, Eq, PartialEq, Clone, Copy)]
649pub struct DirectMsg2Args([u64; 14]);
650
Tomás González092202a2025-03-05 11:56:45 +0000651#[derive(Debug, Eq, PartialEq, Clone, Copy)]
652pub struct MsgWaitFlags {
653 retain_rx_buffer: bool,
654}
655
656impl MsgWaitFlags {
657 const RETAIN_RX_BUFFER: u32 = 0x01;
658 const MBZ_BITS: u32 = 0xfffe;
659}
660
661impl TryFrom<u32> for MsgWaitFlags {
662 type Error = Error;
663
664 fn try_from(val: u32) -> Result<Self, Self::Error> {
665 if (val & Self::MBZ_BITS) != 0 {
666 Err(Error::InvalidMsgWaitFlag(val))
667 } else {
668 Ok(MsgWaitFlags {
669 retain_rx_buffer: val & Self::RETAIN_RX_BUFFER != 0,
670 })
671 }
672 }
673}
674
675impl From<MsgWaitFlags> for u32 {
676 fn from(flags: MsgWaitFlags) -> Self {
677 let mut bits: u32 = 0;
678 if flags.retain_rx_buffer {
679 bits |= MsgWaitFlags::RETAIN_RX_BUFFER;
680 }
681 bits
682 }
683}
684
Balint Dobszaya5846852025-02-26 15:38:53 +0100685/// Descriptor for a dynamically allocated memory buffer that contains the memory transaction
Tomás Gonzálezf268e322025-03-05 11:18:11 +0000686/// descriptor.
687///
688/// Used by `FFA_MEM_{DONATE,LEND,SHARE,RETRIEVE_REQ}` interfaces, only when the TX buffer is not
689/// used to transmit the transaction descriptor.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100690#[derive(Debug, Eq, PartialEq, Clone, Copy)]
691pub enum MemOpBuf {
692 Buf32 { addr: u32, page_cnt: u32 },
693 Buf64 { addr: u64, page_cnt: u32 },
694}
695
Balint Dobszaya5846852025-02-26 15:38:53 +0100696/// Memory address argument for `FFA_MEM_PERM_{GET,SET}` interfaces.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100697#[derive(Debug, Eq, PartialEq, Clone, Copy)]
698pub enum MemAddr {
699 Addr32(u32),
700 Addr64(u64),
701}
702
Balint Dobszayde0dc802025-02-28 14:16:52 +0100703/// Argument for the `FFA_CONSOLE_LOG` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100704#[derive(Debug, Eq, PartialEq, Clone, Copy)]
705pub enum ConsoleLogChars {
Imre Kis189f18c2025-05-26 19:33:05 +0200706 Chars32(ConsoleLogChars32),
707 Chars64(ConsoleLogChars64),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100708}
709
Imre Kis189f18c2025-05-26 19:33:05 +0200710/// Generic type for storing `FFA_CONSOLE_LOG` character payload and its length in bytes.
711#[derive(Debug, Default, Eq, PartialEq, Clone, Copy)]
712pub struct LogChars<T>
713where
714 T: IntoBytes + FromBytes + Immutable,
715{
716 char_cnt: u8,
717 char_lists: T,
718}
719
720impl<T> LogChars<T>
721where
722 T: IntoBytes + FromBytes + Immutable,
723{
724 const MAX_LENGTH: u8 = core::mem::size_of::<T>() as u8;
725
726 /// Returns true if there are no characters in the structure.
727 pub fn empty(&self) -> bool {
728 self.char_cnt == 0
729 }
730
731 /// Returns true if the structure is full.
732 pub fn full(&self) -> bool {
733 self.char_cnt as usize >= core::mem::size_of::<T>()
734 }
735
736 /// Returns the payload bytes.
737 pub fn bytes(&self) -> &[u8] {
738 &self.char_lists.as_bytes()[..self.char_cnt as usize]
739 }
740
741 /// Append byte slice to the end of the characters.
742 pub fn push(&mut self, source: &[u8]) -> usize {
743 let empty_area = &mut self.char_lists.as_mut_bytes()[self.char_cnt.into()..];
744 let len = empty_area.len().min(source.len());
745
746 empty_area[..len].copy_from_slice(&source[..len]);
747 self.char_cnt += len as u8;
748
749 len
750 }
751}
752
753/// Specialized type for 32-bit `FFA_CONSOLE_LOG` payload.
754pub type ConsoleLogChars32 = LogChars<[u32; 6]>;
755
756/// Specialized type for 64-bit `FFA_CONSOLE_LOG` payload.
757pub type ConsoleLogChars64 = LogChars<[u64; 16]>;
758
Tomás González7ffb6132025-04-03 12:28:58 +0100759#[derive(Debug, Eq, PartialEq, Clone, Copy)]
760pub struct NotificationBindFlags {
761 per_vcpu_notification: bool,
762}
763
764impl NotificationBindFlags {
765 const PER_VCPU_NOTIFICATION: u32 = 1;
766}
767
768impl From<NotificationBindFlags> for u32 {
769 fn from(flags: NotificationBindFlags) -> Self {
770 let mut bits: u32 = 0;
771 if flags.per_vcpu_notification {
772 bits |= NotificationBindFlags::PER_VCPU_NOTIFICATION;
773 }
774 bits
775 }
776}
777
778impl From<u32> for NotificationBindFlags {
779 fn from(flags: u32) -> Self {
780 Self {
781 per_vcpu_notification: flags & Self::PER_VCPU_NOTIFICATION != 0,
782 }
783 }
784}
785
786#[derive(Debug, Eq, PartialEq, Clone, Copy)]
787pub struct NotificationSetFlags {
788 delay_schedule_receiver: bool,
789 vcpu_id: Option<u16>,
790}
791
792impl NotificationSetFlags {
793 const PER_VCP_NOTIFICATION: u32 = 1 << 0;
794 const DELAY_SCHEDULE_RECEIVER: u32 = 1 << 1;
795 const VCPU_ID_SHIFT: u32 = 16;
796
797 const MBZ_BITS: u32 = 0xfffc;
798}
799
800impl From<NotificationSetFlags> for u32 {
801 fn from(flags: NotificationSetFlags) -> Self {
802 let mut bits: u32 = 0;
803
804 if flags.delay_schedule_receiver {
805 bits |= NotificationSetFlags::DELAY_SCHEDULE_RECEIVER;
806 }
807 if let Some(vcpu_id) = flags.vcpu_id {
808 bits |= NotificationSetFlags::PER_VCP_NOTIFICATION;
809 bits |= u32::from(vcpu_id) << NotificationSetFlags::VCPU_ID_SHIFT;
810 }
811
812 bits
813 }
814}
815
816impl TryFrom<u32> for NotificationSetFlags {
817 type Error = Error;
818
819 fn try_from(flags: u32) -> Result<Self, Self::Error> {
820 if (flags & Self::MBZ_BITS) != 0 {
821 return Err(Error::InvalidNotificationSetFlag(flags));
822 }
823
824 let tentative_vcpu_id = (flags >> Self::VCPU_ID_SHIFT) as u16;
825
826 let vcpu_id = if (flags & Self::PER_VCP_NOTIFICATION) != 0 {
827 Some(tentative_vcpu_id)
828 } else {
829 if tentative_vcpu_id != 0 {
830 return Err(Error::InvalidNotificationSetFlag(flags));
831 }
832 None
833 };
834
835 Ok(Self {
836 delay_schedule_receiver: (flags & Self::DELAY_SCHEDULE_RECEIVER) != 0,
837 vcpu_id,
838 })
839 }
840}
841
842#[derive(Debug, Eq, PartialEq, Clone, Copy)]
843pub struct NotificationGetFlags {
844 sp_bitmap_id: bool,
845 vm_bitmap_id: bool,
846 spm_bitmap_id: bool,
847 hyp_bitmap_id: bool,
848}
849
850impl NotificationGetFlags {
851 const SP_BITMAP_ID: u32 = 1;
852 const VM_BITMAP_ID: u32 = 1 << 1;
853 const SPM_BITMAP_ID: u32 = 1 << 2;
854 const HYP_BITMAP_ID: u32 = 1 << 3;
855}
856
857impl From<NotificationGetFlags> for u32 {
858 fn from(flags: NotificationGetFlags) -> Self {
859 let mut bits: u32 = 0;
860 if flags.sp_bitmap_id {
861 bits |= NotificationGetFlags::SP_BITMAP_ID;
862 }
863 if flags.vm_bitmap_id {
864 bits |= NotificationGetFlags::VM_BITMAP_ID;
865 }
866 if flags.spm_bitmap_id {
867 bits |= NotificationGetFlags::SPM_BITMAP_ID;
868 }
869 if flags.hyp_bitmap_id {
870 bits |= NotificationGetFlags::HYP_BITMAP_ID;
871 }
872 bits
873 }
874}
875
876impl From<u32> for NotificationGetFlags {
877 // This is a "from" instead of a "try_from" because Reserved Bits are SBZ, *not* MBZ.
878 fn from(flags: u32) -> Self {
879 Self {
880 sp_bitmap_id: (flags & Self::SP_BITMAP_ID) != 0,
881 vm_bitmap_id: (flags & Self::VM_BITMAP_ID) != 0,
882 spm_bitmap_id: (flags & Self::SPM_BITMAP_ID) != 0,
883 hyp_bitmap_id: (flags & Self::HYP_BITMAP_ID) != 0,
884 }
885 }
886}
887
Imre Kis9959e062025-04-10 14:16:10 +0200888/// `FFA_NOTIFICATION_GET` specific success argument structure.
889#[derive(Debug, Eq, PartialEq, Clone, Copy)]
890pub struct SuccessArgsNotificationGet {
891 pub sp_notifications: Option<u64>,
892 pub vm_notifications: Option<u64>,
893 pub spm_notifications: Option<u32>,
894 pub hypervisor_notifications: Option<u32>,
895}
896
897impl From<SuccessArgsNotificationGet> for SuccessArgs {
898 fn from(value: SuccessArgsNotificationGet) -> Self {
899 let mut args = [0; 6];
900
901 if let Some(bitmap) = value.sp_notifications {
902 args[0] = bitmap as u32;
903 args[1] = (bitmap >> 32) as u32;
904 }
905
906 if let Some(bitmap) = value.vm_notifications {
907 args[2] = bitmap as u32;
908 args[3] = (bitmap >> 32) as u32;
909 }
910
911 if let Some(bitmap) = value.spm_notifications {
912 args[4] = bitmap;
913 }
914
915 if let Some(bitmap) = value.hypervisor_notifications {
916 args[5] = bitmap;
917 }
918
919 Self::Args32(args)
920 }
921}
922
923impl TryFrom<(NotificationGetFlags, SuccessArgs)> for SuccessArgsNotificationGet {
924 type Error = Error;
925
926 fn try_from(value: (NotificationGetFlags, SuccessArgs)) -> Result<Self, Self::Error> {
927 let (flags, value) = value;
928 let args = value.try_get_args32()?;
929
930 let sp_notifications = if flags.sp_bitmap_id {
931 Some(u64::from(args[0]) | (u64::from(args[1]) << 32))
932 } else {
933 None
934 };
935
936 let vm_notifications = if flags.vm_bitmap_id {
937 Some(u64::from(args[2]) | (u64::from(args[3]) << 32))
938 } else {
939 None
940 };
941
942 let spm_notifications = if flags.spm_bitmap_id {
943 Some(args[4])
944 } else {
945 None
946 };
947
948 let hypervisor_notifications = if flags.hyp_bitmap_id {
949 Some(args[5])
950 } else {
951 None
952 };
953
954 Ok(Self {
955 sp_notifications,
956 vm_notifications,
957 spm_notifications,
958 hypervisor_notifications,
959 })
960 }
961}
Imre Kis787c5002025-04-10 14:25:51 +0200962
963/// `FFA_NOTIFICATION_INFO_GET` specific success argument structure. The `MAX_COUNT` parameter
964/// depends on the 32-bit or 64-bit packing.
965#[derive(Debug, Eq, PartialEq, Clone, Copy)]
966pub struct SuccessArgsNotificationInfoGet<const MAX_COUNT: usize> {
967 pub more_pending_notifications: bool,
968 list_count: usize,
969 id_counts: [u8; MAX_COUNT],
970 ids: [u16; MAX_COUNT],
971}
972
973impl<const MAX_COUNT: usize> Default for SuccessArgsNotificationInfoGet<MAX_COUNT> {
974 fn default() -> Self {
975 Self {
976 more_pending_notifications: false,
977 list_count: 0,
978 id_counts: [0; MAX_COUNT],
979 ids: [0; MAX_COUNT],
980 }
981 }
982}
983
984impl<const MAX_COUNT: usize> SuccessArgsNotificationInfoGet<MAX_COUNT> {
985 const MORE_PENDING_NOTIFICATIONS_FLAG: u64 = 1 << 0;
986 const LIST_COUNT_SHIFT: usize = 7;
987 const LIST_COUNT_MASK: u64 = 0x1f;
988 const ID_COUNT_SHIFT: usize = 12;
989 const ID_COUNT_MASK: u64 = 0x03;
990 const ID_COUNT_BITS: usize = 2;
991
992 pub fn add_list(&mut self, endpoint: u16, vcpu_ids: &[u16]) -> Result<(), Error> {
993 if self.list_count >= MAX_COUNT || vcpu_ids.len() > Self::ID_COUNT_MASK as usize {
994 return Err(Error::InvalidNotificationCount);
995 }
996
997 // Each list contains at least one ID: the partition ID, followed by vCPU IDs. The number
998 // of vCPU IDs is recorded in `id_counts`.
999 let mut current_id_index = self.list_count + self.id_counts.iter().sum::<u8>() as usize;
1000 if current_id_index + 1 + vcpu_ids.len() > MAX_COUNT {
1001 // The new list does not fit into the available space for IDs.
1002 return Err(Error::InvalidNotificationCount);
1003 }
1004
1005 self.id_counts[self.list_count] = vcpu_ids.len() as u8;
1006 self.list_count += 1;
1007
1008 // The first ID is the endpoint ID.
1009 self.ids[current_id_index] = endpoint;
1010 current_id_index += 1;
1011
1012 // Insert the vCPU IDs.
1013 self.ids[current_id_index..current_id_index + vcpu_ids.len()].copy_from_slice(vcpu_ids);
1014
1015 Ok(())
1016 }
1017
1018 pub fn iter(&self) -> NotificationInfoGetIterator<'_> {
1019 NotificationInfoGetIterator {
1020 list_index: 0,
1021 id_index: 0,
1022 id_count: &self.id_counts[0..self.list_count],
1023 ids: &self.ids,
1024 }
1025 }
1026
1027 /// Pack flags field and IDs.
1028 fn pack(self) -> (u64, [u16; MAX_COUNT]) {
1029 let mut flags = if self.more_pending_notifications {
1030 Self::MORE_PENDING_NOTIFICATIONS_FLAG
1031 } else {
1032 0
1033 };
1034
1035 flags |= (self.list_count as u64) << Self::LIST_COUNT_SHIFT;
1036 for (count, shift) in self.id_counts.iter().take(self.list_count).zip(
1037 (Self::ID_COUNT_SHIFT..Self::ID_COUNT_SHIFT + Self::ID_COUNT_BITS * MAX_COUNT)
1038 .step_by(Self::ID_COUNT_BITS),
1039 ) {
1040 flags |= u64::from(*count) << shift;
1041 }
1042
1043 (flags, self.ids)
1044 }
1045
1046 /// Unpack flags field and IDs.
1047 fn unpack(flags: u64, ids: [u16; MAX_COUNT]) -> Result<Self, Error> {
1048 let count_of_lists = ((flags >> Self::LIST_COUNT_SHIFT) & Self::LIST_COUNT_MASK) as usize;
1049
1050 if count_of_lists > MAX_COUNT {
1051 return Err(Error::InvalidNotificationCount);
1052 }
1053
1054 let mut count_of_ids = [0; MAX_COUNT];
1055 let mut count_of_ids_bits = flags >> Self::ID_COUNT_SHIFT;
1056
1057 for id in count_of_ids.iter_mut().take(count_of_lists) {
1058 *id = (count_of_ids_bits & Self::ID_COUNT_MASK) as u8;
1059 count_of_ids_bits >>= Self::ID_COUNT_BITS;
1060 }
1061
Imre Kis7846c9f2025-04-15 09:45:00 +02001062 let id_field_count = count_of_lists + count_of_ids.iter().sum::<u8>() as usize;
1063 if id_field_count > MAX_COUNT {
1064 return Err(Error::InvalidNotificationCount);
1065 }
1066
Imre Kis787c5002025-04-10 14:25:51 +02001067 Ok(Self {
1068 more_pending_notifications: (flags & Self::MORE_PENDING_NOTIFICATIONS_FLAG) != 0,
1069 list_count: count_of_lists,
1070 id_counts: count_of_ids,
1071 ids,
1072 })
1073 }
1074}
1075
1076/// `FFA_NOTIFICATION_INFO_GET_32` specific success argument structure.
1077pub type SuccessArgsNotificationInfoGet32 = SuccessArgsNotificationInfoGet<10>;
1078
1079impl From<SuccessArgsNotificationInfoGet32> for SuccessArgs {
1080 fn from(value: SuccessArgsNotificationInfoGet32) -> Self {
1081 let (flags, ids) = value.pack();
1082 let id_regs: [u32; 5] = transmute!(ids);
1083
1084 let mut args = [0; 6];
1085 args[0] = flags as u32;
1086 args[1..6].copy_from_slice(&id_regs);
1087
1088 SuccessArgs::Args32(args)
1089 }
1090}
1091
1092impl TryFrom<SuccessArgs> for SuccessArgsNotificationInfoGet32 {
1093 type Error = Error;
1094
1095 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
1096 let args = value.try_get_args32()?;
1097 let flags = args[0].into();
1098 let id_regs: [u32; 5] = args[1..6].try_into().unwrap();
1099 Self::unpack(flags, transmute!(id_regs))
1100 }
1101}
1102
1103/// `FFA_NOTIFICATION_INFO_GET_64` specific success argument structure.
1104pub type SuccessArgsNotificationInfoGet64 = SuccessArgsNotificationInfoGet<20>;
1105
1106impl From<SuccessArgsNotificationInfoGet64> for SuccessArgs {
1107 fn from(value: SuccessArgsNotificationInfoGet64) -> Self {
1108 let (flags, ids) = value.pack();
1109 let id_regs: [u64; 5] = transmute!(ids);
1110
1111 let mut args = [0; 6];
1112 args[0] = flags;
1113 args[1..6].copy_from_slice(&id_regs);
1114
1115 SuccessArgs::Args64(args)
1116 }
1117}
1118
1119impl TryFrom<SuccessArgs> for SuccessArgsNotificationInfoGet64 {
1120 type Error = Error;
1121
1122 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
1123 let args = value.try_get_args64()?;
1124 let flags = args[0];
1125 let id_regs: [u64; 5] = args[1..6].try_into().unwrap();
1126 Self::unpack(flags, transmute!(id_regs))
1127 }
1128}
1129
1130pub struct NotificationInfoGetIterator<'a> {
1131 list_index: usize,
1132 id_index: usize,
1133 id_count: &'a [u8],
1134 ids: &'a [u16],
1135}
1136
1137impl<'a> Iterator for NotificationInfoGetIterator<'a> {
1138 type Item = (u16, &'a [u16]);
1139
1140 fn next(&mut self) -> Option<Self::Item> {
1141 if self.list_index < self.id_count.len() {
1142 let partition_id = self.ids[self.id_index];
1143 let id_range =
1144 (self.id_index + 1)..=(self.id_index + self.id_count[self.list_index] as usize);
1145
1146 self.id_index += 1 + self.id_count[self.list_index] as usize;
1147 self.list_index += 1;
1148
1149 Some((partition_id, &self.ids[id_range]))
1150 } else {
1151 None
1152 }
1153 }
1154}
1155
Tomás Gonzálezf268e322025-03-05 11:18:11 +00001156/// FF-A "message types", the terminology used by the spec is "interfaces".
1157///
1158/// The interfaces are used by FF-A components for communication at an FF-A instance. The spec also
1159/// describes the valid FF-A instances and conduits for each interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +01001160#[derive(Debug, Eq, PartialEq, Clone, Copy)]
1161pub enum Interface {
1162 Error {
1163 target_info: TargetInfo,
1164 error_code: FfaError,
Balint Dobszayb727aab2025-04-07 10:24:59 +02001165 error_arg: u32,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001166 },
1167 Success {
1168 target_info: u32,
1169 args: SuccessArgs,
1170 },
1171 Interrupt {
1172 target_info: TargetInfo,
1173 interrupt_id: u32,
1174 },
1175 Version {
1176 input_version: Version,
1177 },
1178 VersionOut {
1179 output_version: Version,
1180 },
1181 Features {
1182 feat_id: Feature,
1183 input_properties: u32,
1184 },
1185 RxAcquire {
1186 vm_id: u16,
1187 },
1188 RxRelease {
1189 vm_id: u16,
1190 },
1191 RxTxMap {
1192 addr: RxTxAddr,
1193 page_cnt: u32,
1194 },
1195 RxTxUnmap {
1196 id: u16,
1197 },
1198 PartitionInfoGet {
1199 uuid: Uuid,
Imre Kise295adb2025-04-10 13:26:28 +02001200 flags: PartitionInfoGetFlags,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001201 },
Tomás González0a058bc2025-03-11 11:20:55 +00001202 PartitionInfoGetRegs {
1203 uuid: Uuid,
1204 start_index: u16,
1205 info_tag: u16,
1206 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001207 IdGet,
1208 SpmIdGet,
Tomás González092202a2025-03-05 11:56:45 +00001209 MsgWait {
1210 flags: Option<MsgWaitFlags>,
1211 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001212 Yield,
1213 Run {
1214 target_info: TargetInfo,
1215 },
1216 NormalWorldResume,
Tomás González17b92442025-03-10 16:45:04 +00001217 SecondaryEpRegister {
1218 entrypoint: SecondaryEpRegisterAddr,
1219 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001220 MsgSend2 {
1221 sender_vm_id: u16,
1222 flags: u32,
1223 },
1224 MsgSendDirectReq {
1225 src_id: u16,
1226 dst_id: u16,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001227 args: DirectMsgArgs,
1228 },
1229 MsgSendDirectResp {
1230 src_id: u16,
1231 dst_id: u16,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001232 args: DirectMsgArgs,
1233 },
Balint Dobszayde0dc802025-02-28 14:16:52 +01001234 MsgSendDirectReq2 {
1235 src_id: u16,
1236 dst_id: u16,
1237 uuid: Uuid,
1238 args: DirectMsg2Args,
1239 },
1240 MsgSendDirectResp2 {
1241 src_id: u16,
1242 dst_id: u16,
1243 args: DirectMsg2Args,
1244 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001245 MemDonate {
1246 total_len: u32,
1247 frag_len: u32,
1248 buf: Option<MemOpBuf>,
1249 },
1250 MemLend {
1251 total_len: u32,
1252 frag_len: u32,
1253 buf: Option<MemOpBuf>,
1254 },
1255 MemShare {
1256 total_len: u32,
1257 frag_len: u32,
1258 buf: Option<MemOpBuf>,
1259 },
1260 MemRetrieveReq {
1261 total_len: u32,
1262 frag_len: u32,
1263 buf: Option<MemOpBuf>,
1264 },
1265 MemRetrieveResp {
1266 total_len: u32,
1267 frag_len: u32,
1268 },
1269 MemRelinquish,
1270 MemReclaim {
1271 handle: memory_management::Handle,
1272 flags: u32,
1273 },
1274 MemPermGet {
1275 addr: MemAddr,
Balint Dobszayde0dc802025-02-28 14:16:52 +01001276 page_cnt: Option<u32>,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001277 },
1278 MemPermSet {
1279 addr: MemAddr,
1280 page_cnt: u32,
1281 mem_perm: u32,
1282 },
1283 ConsoleLog {
Imre Kis189f18c2025-05-26 19:33:05 +02001284 chars: ConsoleLogChars,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001285 },
Tomás González7ffb6132025-04-03 12:28:58 +01001286 NotificationBitmapCreate {
1287 vm_id: u16,
1288 vcpu_cnt: u32,
1289 },
1290 NotificationBitmapDestroy {
1291 vm_id: u16,
1292 },
1293 NotificationBind {
1294 sender_id: u16,
1295 receiver_id: u16,
1296 flags: NotificationBindFlags,
1297 bitmap: u64,
1298 },
Imre Kis3571f2c2025-05-26 19:29:23 +02001299 NotificationUnbind {
Tomás González7ffb6132025-04-03 12:28:58 +01001300 sender_id: u16,
1301 receiver_id: u16,
1302 bitmap: u64,
1303 },
1304 NotificationSet {
1305 sender_id: u16,
1306 receiver_id: u16,
1307 flags: NotificationSetFlags,
1308 bitmap: u64,
1309 },
1310 NotificationGet {
1311 vcpu_id: u16,
1312 endpoint_id: u16,
1313 flags: NotificationGetFlags,
1314 },
1315 NotificationInfoGet {
1316 is_32bit: bool,
1317 },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01001318 El3IntrHandle,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001319}
1320
Balint Dobszayde0dc802025-02-28 14:16:52 +01001321impl Interface {
1322 /// Returns the function ID for the call, if it has one.
1323 pub fn function_id(&self) -> Option<FuncId> {
1324 match self {
1325 Interface::Error { .. } => Some(FuncId::Error),
1326 Interface::Success { args, .. } => match args {
Imre Kis54773b62025-04-10 13:47:39 +02001327 SuccessArgs::Args32(..) => Some(FuncId::Success32),
1328 SuccessArgs::Args64(..) | SuccessArgs::Args64_2(..) => Some(FuncId::Success64),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001329 },
1330 Interface::Interrupt { .. } => Some(FuncId::Interrupt),
1331 Interface::Version { .. } => Some(FuncId::Version),
1332 Interface::VersionOut { .. } => None,
1333 Interface::Features { .. } => Some(FuncId::Features),
1334 Interface::RxAcquire { .. } => Some(FuncId::RxAcquire),
1335 Interface::RxRelease { .. } => Some(FuncId::RxRelease),
1336 Interface::RxTxMap { addr, .. } => match addr {
1337 RxTxAddr::Addr32 { .. } => Some(FuncId::RxTxMap32),
1338 RxTxAddr::Addr64 { .. } => Some(FuncId::RxTxMap64),
1339 },
1340 Interface::RxTxUnmap { .. } => Some(FuncId::RxTxUnmap),
1341 Interface::PartitionInfoGet { .. } => Some(FuncId::PartitionInfoGet),
Tomás González0a058bc2025-03-11 11:20:55 +00001342 Interface::PartitionInfoGetRegs { .. } => Some(FuncId::PartitionInfoGetRegs),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001343 Interface::IdGet => Some(FuncId::IdGet),
1344 Interface::SpmIdGet => Some(FuncId::SpmIdGet),
Tomás González092202a2025-03-05 11:56:45 +00001345 Interface::MsgWait { .. } => Some(FuncId::MsgWait),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001346 Interface::Yield => Some(FuncId::Yield),
1347 Interface::Run { .. } => Some(FuncId::Run),
1348 Interface::NormalWorldResume => Some(FuncId::NormalWorldResume),
Tomás González17b92442025-03-10 16:45:04 +00001349 Interface::SecondaryEpRegister { entrypoint } => match entrypoint {
1350 SecondaryEpRegisterAddr::Addr32 { .. } => Some(FuncId::SecondaryEpRegister32),
1351 SecondaryEpRegisterAddr::Addr64 { .. } => Some(FuncId::SecondaryEpRegister64),
1352 },
Balint Dobszayde0dc802025-02-28 14:16:52 +01001353 Interface::MsgSend2 { .. } => Some(FuncId::MsgSend2),
1354 Interface::MsgSendDirectReq { args, .. } => match args {
1355 DirectMsgArgs::Args32(_) => Some(FuncId::MsgSendDirectReq32),
1356 DirectMsgArgs::Args64(_) => Some(FuncId::MsgSendDirectReq64),
Tomás González4d5b0ba2025-03-03 17:15:55 +00001357 DirectMsgArgs::VersionReq { .. } => Some(FuncId::MsgSendDirectReq32),
1358 DirectMsgArgs::PowerPsciReq32 { .. } => Some(FuncId::MsgSendDirectReq32),
1359 DirectMsgArgs::PowerPsciReq64 { .. } => Some(FuncId::MsgSendDirectReq64),
1360 DirectMsgArgs::PowerWarmBootReq { .. } => Some(FuncId::MsgSendDirectReq32),
1361 DirectMsgArgs::VmCreated { .. } => Some(FuncId::MsgSendDirectReq32),
1362 DirectMsgArgs::VmDestructed { .. } => Some(FuncId::MsgSendDirectReq32),
Balint Dobszay3c1c89a2025-04-25 17:36:46 +02001363 _ => panic!("Invalid direct request arguments: {:#?}", args),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001364 },
1365 Interface::MsgSendDirectResp { args, .. } => match args {
1366 DirectMsgArgs::Args32(_) => Some(FuncId::MsgSendDirectResp32),
1367 DirectMsgArgs::Args64(_) => Some(FuncId::MsgSendDirectResp64),
Tomás González4d5b0ba2025-03-03 17:15:55 +00001368 DirectMsgArgs::VersionResp { .. } => Some(FuncId::MsgSendDirectResp32),
1369 DirectMsgArgs::PowerPsciResp { .. } => Some(FuncId::MsgSendDirectResp32),
1370 DirectMsgArgs::VmCreatedAck { .. } => Some(FuncId::MsgSendDirectResp32),
1371 DirectMsgArgs::VmDestructedAck { .. } => Some(FuncId::MsgSendDirectResp32),
Balint Dobszay3c1c89a2025-04-25 17:36:46 +02001372 _ => panic!("Invalid direct response arguments: {:#?}", args),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001373 },
1374 Interface::MsgSendDirectReq2 { .. } => Some(FuncId::MsgSendDirectReq64_2),
1375 Interface::MsgSendDirectResp2 { .. } => Some(FuncId::MsgSendDirectResp64_2),
1376 Interface::MemDonate { buf, .. } => match buf {
1377 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemDonate64),
1378 _ => Some(FuncId::MemDonate32),
1379 },
1380 Interface::MemLend { buf, .. } => match buf {
1381 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemLend64),
1382 _ => Some(FuncId::MemLend32),
1383 },
1384 Interface::MemShare { buf, .. } => match buf {
1385 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemShare64),
1386 _ => Some(FuncId::MemShare32),
1387 },
1388 Interface::MemRetrieveReq { buf, .. } => match buf {
1389 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemRetrieveReq64),
1390 _ => Some(FuncId::MemRetrieveReq32),
1391 },
1392 Interface::MemRetrieveResp { .. } => Some(FuncId::MemRetrieveResp),
1393 Interface::MemRelinquish => Some(FuncId::MemRelinquish),
1394 Interface::MemReclaim { .. } => Some(FuncId::MemReclaim),
1395 Interface::MemPermGet { addr, .. } => match addr {
1396 MemAddr::Addr32(_) => Some(FuncId::MemPermGet32),
1397 MemAddr::Addr64(_) => Some(FuncId::MemPermGet64),
1398 },
1399 Interface::MemPermSet { addr, .. } => match addr {
1400 MemAddr::Addr32(_) => Some(FuncId::MemPermSet32),
1401 MemAddr::Addr64(_) => Some(FuncId::MemPermSet64),
1402 },
Imre Kis189f18c2025-05-26 19:33:05 +02001403 Interface::ConsoleLog { chars, .. } => match chars {
1404 ConsoleLogChars::Chars32(_) => Some(FuncId::ConsoleLog32),
1405 ConsoleLogChars::Chars64(_) => Some(FuncId::ConsoleLog64),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001406 },
Tomás González7ffb6132025-04-03 12:28:58 +01001407 Interface::NotificationBitmapCreate { .. } => Some(FuncId::NotificationBitmapCreate),
1408 Interface::NotificationBitmapDestroy { .. } => Some(FuncId::NotificationBitmapDestroy),
1409 Interface::NotificationBind { .. } => Some(FuncId::NotificationBind),
Imre Kis3571f2c2025-05-26 19:29:23 +02001410 Interface::NotificationUnbind { .. } => Some(FuncId::NotificationUnbind),
Tomás González7ffb6132025-04-03 12:28:58 +01001411 Interface::NotificationSet { .. } => Some(FuncId::NotificationSet),
1412 Interface::NotificationGet { .. } => Some(FuncId::NotificationGet),
1413 Interface::NotificationInfoGet { is_32bit } => match is_32bit {
1414 true => Some(FuncId::NotificationInfoGet32),
1415 false => Some(FuncId::NotificationInfoGet64),
1416 },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01001417 Interface::El3IntrHandle => Some(FuncId::El3IntrHandle),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001418 }
1419 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01001420
Balint Dobszayde0dc802025-02-28 14:16:52 +01001421 /// Returns true if this is a 32-bit call, or false if it is a 64-bit call.
1422 pub fn is_32bit(&self) -> bool {
Balint Dobszay3c1c89a2025-04-25 17:36:46 +02001423 if matches!(self, Self::VersionOut { .. }) {
1424 return true;
1425 }
1426
Balint Dobszayde0dc802025-02-28 14:16:52 +01001427 self.function_id().unwrap().is_32bit()
1428 }
1429
Balint Dobszay82c71dd2025-04-15 10:16:44 +02001430 /// Returns the FF-A version that has introduced the function ID.
1431 pub fn minimum_ffa_version(&self) -> Version {
Balint Dobszay3c1c89a2025-04-25 17:36:46 +02001432 if matches!(self, Self::VersionOut { .. }) {
1433 return Version(1, 0);
1434 }
1435
Balint Dobszay82c71dd2025-04-15 10:16:44 +02001436 self.function_id().unwrap().minimum_ffa_version()
1437 }
1438
Balint Dobszayde0dc802025-02-28 14:16:52 +01001439 /// Parse interface from register contents. The caller must ensure that the `regs` argument has
1440 /// the correct length: 8 registers for FF-A v1.1 and lower, 18 registers for v1.2 and higher.
1441 pub fn from_regs(version: Version, regs: &[u64]) -> Result<Self, Error> {
Balint Dobszay82c71dd2025-04-15 10:16:44 +02001442 let func_id = FuncId::try_from(regs[0] as u32)?;
1443 if version < func_id.minimum_ffa_version() {
1444 return Err(Error::InvalidVersionForFunctionId(version, func_id));
1445 }
1446
Balint Dobszayde0dc802025-02-28 14:16:52 +01001447 let reg_cnt = regs.len();
1448
1449 let msg = match reg_cnt {
1450 8 => {
1451 assert!(version <= Version(1, 1));
1452 Interface::unpack_regs8(version, regs.try_into().unwrap())?
1453 }
1454 18 => {
1455 assert!(version >= Version(1, 2));
Balint Dobszay82c71dd2025-04-15 10:16:44 +02001456 match func_id {
Balint Dobszayde0dc802025-02-28 14:16:52 +01001457 FuncId::ConsoleLog64
1458 | FuncId::Success64
1459 | FuncId::MsgSendDirectReq64_2
Tomás González0a058bc2025-03-11 11:20:55 +00001460 | FuncId::MsgSendDirectResp64_2
1461 | FuncId::PartitionInfoGetRegs => {
Balint Dobszayde0dc802025-02-28 14:16:52 +01001462 Interface::unpack_regs18(version, regs.try_into().unwrap())?
1463 }
1464 _ => Interface::unpack_regs8(version, regs[..8].try_into().unwrap())?,
1465 }
1466 }
1467 _ => panic!(
1468 "Invalid number of registers ({}) for FF-A version {}",
1469 reg_cnt, version
1470 ),
1471 };
1472
1473 Ok(msg)
1474 }
1475
1476 fn unpack_regs8(version: Version, regs: &[u64; 8]) -> Result<Self, Error> {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001477 let fid = FuncId::try_from(regs[0] as u32)?;
1478
1479 let msg = match fid {
1480 FuncId::Error => Self::Error {
1481 target_info: (regs[1] as u32).into(),
1482 error_code: FfaError::try_from(regs[2] as i32)?,
Balint Dobszayb727aab2025-04-07 10:24:59 +02001483 error_arg: regs[3] as u32,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001484 },
1485 FuncId::Success32 => Self::Success {
1486 target_info: regs[1] as u32,
Imre Kis54773b62025-04-10 13:47:39 +02001487 args: SuccessArgs::Args32([
Balint Dobszay3aad9572025-01-17 16:54:11 +01001488 regs[2] as u32,
1489 regs[3] as u32,
1490 regs[4] as u32,
1491 regs[5] as u32,
1492 regs[6] as u32,
1493 regs[7] as u32,
1494 ]),
1495 },
1496 FuncId::Success64 => Self::Success {
1497 target_info: regs[1] as u32,
Imre Kis54773b62025-04-10 13:47:39 +02001498 args: SuccessArgs::Args64([regs[2], regs[3], regs[4], regs[5], regs[6], regs[7]]),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001499 },
1500 FuncId::Interrupt => Self::Interrupt {
1501 target_info: (regs[1] as u32).into(),
1502 interrupt_id: regs[2] as u32,
1503 },
1504 FuncId::Version => Self::Version {
Tomás González83146af2025-03-04 11:32:41 +00001505 input_version: (regs[1] as u32).try_into()?,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001506 },
1507 FuncId::Features => Self::Features {
Balint Dobszayc31e0b92025-03-03 20:16:56 +01001508 feat_id: (regs[1] as u32).into(),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001509 input_properties: regs[2] as u32,
1510 },
1511 FuncId::RxAcquire => Self::RxAcquire {
1512 vm_id: regs[1] as u16,
1513 },
1514 FuncId::RxRelease => Self::RxRelease {
1515 vm_id: regs[1] as u16,
1516 },
1517 FuncId::RxTxMap32 => {
1518 let addr = RxTxAddr::Addr32 {
1519 rx: regs[2] as u32,
1520 tx: regs[1] as u32,
1521 };
1522 let page_cnt = regs[3] as u32;
1523
1524 Self::RxTxMap { addr, page_cnt }
1525 }
1526 FuncId::RxTxMap64 => {
1527 let addr = RxTxAddr::Addr64 {
1528 rx: regs[2],
1529 tx: regs[1],
1530 };
1531 let page_cnt = regs[3] as u32;
1532
1533 Self::RxTxMap { addr, page_cnt }
1534 }
1535 FuncId::RxTxUnmap => Self::RxTxUnmap { id: regs[1] as u16 },
1536 FuncId::PartitionInfoGet => {
1537 let uuid_words = [
1538 regs[1] as u32,
1539 regs[2] as u32,
1540 regs[3] as u32,
1541 regs[4] as u32,
1542 ];
1543 let mut bytes: [u8; 16] = [0; 16];
1544 for (i, b) in uuid_words.iter().flat_map(|w| w.to_le_bytes()).enumerate() {
1545 bytes[i] = b;
1546 }
1547 Self::PartitionInfoGet {
1548 uuid: Uuid::from_bytes(bytes),
Imre Kise295adb2025-04-10 13:26:28 +02001549 flags: PartitionInfoGetFlags::try_from(regs[5] as u32)?,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001550 }
1551 }
1552 FuncId::IdGet => Self::IdGet,
1553 FuncId::SpmIdGet => Self::SpmIdGet,
Tomás González092202a2025-03-05 11:56:45 +00001554 FuncId::MsgWait => Self::MsgWait {
1555 flags: if version >= Version(1, 2) {
1556 Some(MsgWaitFlags::try_from(regs[2] as u32)?)
1557 } else {
1558 None
1559 },
1560 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001561 FuncId::Yield => Self::Yield,
1562 FuncId::Run => Self::Run {
1563 target_info: (regs[1] as u32).into(),
1564 },
1565 FuncId::NormalWorldResume => Self::NormalWorldResume,
Tomás González17b92442025-03-10 16:45:04 +00001566 FuncId::SecondaryEpRegister32 => Self::SecondaryEpRegister {
1567 entrypoint: SecondaryEpRegisterAddr::Addr32(regs[1] as u32),
1568 },
1569 FuncId::SecondaryEpRegister64 => Self::SecondaryEpRegister {
1570 entrypoint: SecondaryEpRegisterAddr::Addr64(regs[1]),
1571 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001572 FuncId::MsgSend2 => Self::MsgSend2 {
1573 sender_vm_id: regs[1] as u16,
1574 flags: regs[2] as u32,
1575 },
1576 FuncId::MsgSendDirectReq32 => Self::MsgSendDirectReq {
1577 src_id: (regs[1] >> 16) as u16,
1578 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001579 args: if (regs[2] as u32 & DirectMsgArgs::FWK_MSG_BITS) != 0 {
1580 match regs[2] as u32 {
1581 DirectMsgArgs::VERSION_REQ => DirectMsgArgs::VersionReq {
1582 version: Version::try_from(regs[3] as u32)?,
1583 },
1584 DirectMsgArgs::POWER_PSCI_REQ => DirectMsgArgs::PowerPsciReq32 {
Tomás González67f92c72025-03-20 16:50:42 +00001585 params: [
1586 regs[3] as u32,
1587 regs[4] as u32,
1588 regs[5] as u32,
1589 regs[6] as u32,
1590 ],
Tomás González4d5b0ba2025-03-03 17:15:55 +00001591 },
1592 DirectMsgArgs::POWER_WARM_BOOT_REQ => DirectMsgArgs::PowerWarmBootReq {
1593 boot_type: WarmBootType::try_from(regs[3] as u32)?,
1594 },
1595 DirectMsgArgs::VM_CREATED => DirectMsgArgs::VmCreated {
1596 handle: memory_management::Handle::from([
1597 regs[3] as u32,
1598 regs[4] as u32,
1599 ]),
1600 vm_id: regs[5] as u16,
1601 },
1602 DirectMsgArgs::VM_DESTRUCTED => DirectMsgArgs::VmDestructed {
1603 handle: memory_management::Handle::from([
1604 regs[3] as u32,
1605 regs[4] as u32,
1606 ]),
1607 vm_id: regs[5] as u16,
1608 },
1609 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1610 }
1611 } else {
1612 DirectMsgArgs::Args32([
1613 regs[3] as u32,
1614 regs[4] as u32,
1615 regs[5] as u32,
1616 regs[6] as u32,
1617 regs[7] as u32,
1618 ])
1619 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001620 },
1621 FuncId::MsgSendDirectReq64 => Self::MsgSendDirectReq {
1622 src_id: (regs[1] >> 16) as u16,
1623 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001624 args: if (regs[2] & DirectMsgArgs::FWK_MSG_BITS as u64) != 0 {
1625 match regs[2] as u32 {
1626 DirectMsgArgs::POWER_PSCI_REQ => DirectMsgArgs::PowerPsciReq64 {
Tomás González67f92c72025-03-20 16:50:42 +00001627 params: [regs[3], regs[4], regs[5], regs[6]],
Tomás González4d5b0ba2025-03-03 17:15:55 +00001628 },
1629 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1630 }
1631 } else {
1632 DirectMsgArgs::Args64([regs[3], regs[4], regs[5], regs[6], regs[7]])
1633 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001634 },
1635 FuncId::MsgSendDirectResp32 => Self::MsgSendDirectResp {
1636 src_id: (regs[1] >> 16) as u16,
1637 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001638 args: if (regs[2] as u32 & DirectMsgArgs::FWK_MSG_BITS) != 0 {
1639 match regs[2] as u32 {
1640 DirectMsgArgs::VERSION_RESP => {
1641 if regs[3] as i32 == FfaError::NotSupported.into() {
1642 DirectMsgArgs::VersionResp { version: None }
1643 } else {
1644 DirectMsgArgs::VersionResp {
1645 version: Some(Version::try_from(regs[3] as u32)?),
1646 }
1647 }
1648 }
1649 DirectMsgArgs::POWER_PSCI_RESP => DirectMsgArgs::PowerPsciResp {
1650 psci_status: regs[3] as i32,
1651 },
1652 DirectMsgArgs::VM_CREATED_ACK => DirectMsgArgs::VmCreatedAck {
1653 sp_status: (regs[3] as i32).try_into()?,
1654 },
1655 DirectMsgArgs::VM_DESTRUCTED_ACK => DirectMsgArgs::VmDestructedAck {
1656 sp_status: (regs[3] as i32).try_into()?,
1657 },
1658 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1659 }
1660 } else {
1661 DirectMsgArgs::Args32([
1662 regs[3] as u32,
1663 regs[4] as u32,
1664 regs[5] as u32,
1665 regs[6] as u32,
1666 regs[7] as u32,
1667 ])
1668 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001669 },
1670 FuncId::MsgSendDirectResp64 => Self::MsgSendDirectResp {
1671 src_id: (regs[1] >> 16) as u16,
1672 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001673 args: if (regs[2] & DirectMsgArgs::FWK_MSG_BITS as u64) != 0 {
1674 return Err(Error::UnrecognisedFwkMsg(regs[2] as u32));
1675 } else {
1676 DirectMsgArgs::Args64([regs[3], regs[4], regs[5], regs[6], regs[7]])
1677 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001678 },
1679 FuncId::MemDonate32 => Self::MemDonate {
1680 total_len: regs[1] as u32,
1681 frag_len: regs[2] as u32,
1682 buf: if regs[3] != 0 && regs[4] != 0 {
1683 Some(MemOpBuf::Buf32 {
1684 addr: regs[3] as u32,
1685 page_cnt: regs[4] as u32,
1686 })
1687 } else {
1688 None
1689 },
1690 },
1691 FuncId::MemDonate64 => Self::MemDonate {
1692 total_len: regs[1] as u32,
1693 frag_len: regs[2] as u32,
1694 buf: if regs[3] != 0 && regs[4] != 0 {
1695 Some(MemOpBuf::Buf64 {
1696 addr: regs[3],
1697 page_cnt: regs[4] as u32,
1698 })
1699 } else {
1700 None
1701 },
1702 },
1703 FuncId::MemLend32 => Self::MemLend {
1704 total_len: regs[1] as u32,
1705 frag_len: regs[2] as u32,
1706 buf: if regs[3] != 0 && regs[4] != 0 {
1707 Some(MemOpBuf::Buf32 {
1708 addr: regs[3] as u32,
1709 page_cnt: regs[4] as u32,
1710 })
1711 } else {
1712 None
1713 },
1714 },
1715 FuncId::MemLend64 => Self::MemLend {
1716 total_len: regs[1] as u32,
1717 frag_len: regs[2] as u32,
1718 buf: if regs[3] != 0 && regs[4] != 0 {
1719 Some(MemOpBuf::Buf64 {
1720 addr: regs[3],
1721 page_cnt: regs[4] as u32,
1722 })
1723 } else {
1724 None
1725 },
1726 },
1727 FuncId::MemShare32 => Self::MemShare {
1728 total_len: regs[1] as u32,
1729 frag_len: regs[2] as u32,
1730 buf: if regs[3] != 0 && regs[4] != 0 {
1731 Some(MemOpBuf::Buf32 {
1732 addr: regs[3] as u32,
1733 page_cnt: regs[4] as u32,
1734 })
1735 } else {
1736 None
1737 },
1738 },
1739 FuncId::MemShare64 => Self::MemShare {
1740 total_len: regs[1] as u32,
1741 frag_len: regs[2] as u32,
1742 buf: if regs[3] != 0 && regs[4] != 0 {
1743 Some(MemOpBuf::Buf64 {
1744 addr: regs[3],
1745 page_cnt: regs[4] as u32,
1746 })
1747 } else {
1748 None
1749 },
1750 },
1751 FuncId::MemRetrieveReq32 => Self::MemRetrieveReq {
1752 total_len: regs[1] as u32,
1753 frag_len: regs[2] as u32,
1754 buf: if regs[3] != 0 && regs[4] != 0 {
1755 Some(MemOpBuf::Buf32 {
1756 addr: regs[3] as u32,
1757 page_cnt: regs[4] as u32,
1758 })
1759 } else {
1760 None
1761 },
1762 },
1763 FuncId::MemRetrieveReq64 => Self::MemRetrieveReq {
1764 total_len: regs[1] as u32,
1765 frag_len: regs[2] as u32,
1766 buf: if regs[3] != 0 && regs[4] != 0 {
1767 Some(MemOpBuf::Buf64 {
1768 addr: regs[3],
1769 page_cnt: regs[4] as u32,
1770 })
1771 } else {
1772 None
1773 },
1774 },
1775 FuncId::MemRetrieveResp => Self::MemRetrieveResp {
1776 total_len: regs[1] as u32,
1777 frag_len: regs[2] as u32,
1778 },
1779 FuncId::MemRelinquish => Self::MemRelinquish,
1780 FuncId::MemReclaim => Self::MemReclaim {
1781 handle: memory_management::Handle::from([regs[1] as u32, regs[2] as u32]),
1782 flags: regs[3] as u32,
1783 },
1784 FuncId::MemPermGet32 => Self::MemPermGet {
1785 addr: MemAddr::Addr32(regs[1] as u32),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001786 page_cnt: if version >= Version(1, 3) {
1787 Some(regs[2] as u32)
1788 } else {
1789 None
1790 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001791 },
1792 FuncId::MemPermGet64 => Self::MemPermGet {
1793 addr: MemAddr::Addr64(regs[1]),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001794 page_cnt: if version >= Version(1, 3) {
1795 Some(regs[2] as u32)
1796 } else {
1797 None
1798 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001799 },
1800 FuncId::MemPermSet32 => Self::MemPermSet {
1801 addr: MemAddr::Addr32(regs[1] as u32),
1802 page_cnt: regs[2] as u32,
1803 mem_perm: regs[3] as u32,
1804 },
1805 FuncId::MemPermSet64 => Self::MemPermSet {
1806 addr: MemAddr::Addr64(regs[1]),
1807 page_cnt: regs[2] as u32,
1808 mem_perm: regs[3] as u32,
1809 },
Imre Kis189f18c2025-05-26 19:33:05 +02001810 FuncId::ConsoleLog32 => {
1811 let char_cnt = regs[1] as u8;
1812 if char_cnt > ConsoleLogChars32::MAX_LENGTH {
1813 return Err(Error::InvalidCharacterCount(char_cnt));
1814 }
1815
1816 Self::ConsoleLog {
1817 chars: ConsoleLogChars::Chars32(ConsoleLogChars32 {
1818 char_cnt,
1819 char_lists: [
1820 regs[2] as u32,
1821 regs[3] as u32,
1822 regs[4] as u32,
1823 regs[5] as u32,
1824 regs[6] as u32,
1825 regs[7] as u32,
1826 ],
1827 }),
1828 }
1829 }
Tomás González7ffb6132025-04-03 12:28:58 +01001830 FuncId::NotificationBitmapCreate => {
1831 let tentative_vm_id = regs[1] as u32;
1832 if (tentative_vm_id >> 16) != 0 {
1833 return Err(Error::InvalidVmId(tentative_vm_id));
1834 }
1835 Self::NotificationBitmapCreate {
1836 vm_id: tentative_vm_id as u16,
1837 vcpu_cnt: regs[2] as u32,
1838 }
1839 }
1840 FuncId::NotificationBitmapDestroy => {
1841 let tentative_vm_id = regs[1] as u32;
1842 if (tentative_vm_id >> 16) != 0 {
1843 return Err(Error::InvalidVmId(tentative_vm_id));
1844 }
1845 Self::NotificationBitmapDestroy {
1846 vm_id: tentative_vm_id as u16,
1847 }
1848 }
1849 FuncId::NotificationBind => Self::NotificationBind {
1850 sender_id: (regs[1] >> 16) as u16,
1851 receiver_id: regs[1] as u16,
1852 flags: (regs[2] as u32).into(),
1853 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1854 },
Imre Kis3571f2c2025-05-26 19:29:23 +02001855 FuncId::NotificationUnbind => Self::NotificationUnbind {
Tomás González7ffb6132025-04-03 12:28:58 +01001856 sender_id: (regs[1] >> 16) as u16,
1857 receiver_id: regs[1] as u16,
1858 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1859 },
1860 FuncId::NotificationSet => Self::NotificationSet {
1861 sender_id: (regs[1] >> 16) as u16,
1862 receiver_id: regs[1] as u16,
1863 flags: (regs[2] as u32).try_into()?,
1864 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1865 },
1866 FuncId::NotificationGet => Self::NotificationGet {
1867 vcpu_id: (regs[1] >> 16) as u16,
1868 endpoint_id: regs[1] as u16,
1869 flags: (regs[2] as u32).into(),
1870 },
1871 FuncId::NotificationInfoGet32 => Self::NotificationInfoGet { is_32bit: true },
1872 FuncId::NotificationInfoGet64 => Self::NotificationInfoGet { is_32bit: false },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01001873 FuncId::El3IntrHandle => Self::El3IntrHandle,
Balint Dobszayde0dc802025-02-28 14:16:52 +01001874 _ => panic!("Invalid number of registers (8) for function {:#x?}", fid),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001875 };
1876
1877 Ok(msg)
1878 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01001879
Balint Dobszayde0dc802025-02-28 14:16:52 +01001880 fn unpack_regs18(version: Version, regs: &[u64; 18]) -> Result<Self, Error> {
1881 assert!(version >= Version(1, 2));
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001882
Balint Dobszayde0dc802025-02-28 14:16:52 +01001883 let fid = FuncId::try_from(regs[0] as u32)?;
1884
1885 let msg = match fid {
1886 FuncId::Success64 => Self::Success {
1887 target_info: regs[1] as u32,
Imre Kis54773b62025-04-10 13:47:39 +02001888 args: SuccessArgs::Args64_2(regs[2..18].try_into().unwrap()),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001889 },
1890 FuncId::MsgSendDirectReq64_2 => Self::MsgSendDirectReq2 {
1891 src_id: (regs[1] >> 16) as u16,
1892 dst_id: regs[1] as u16,
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00001893 uuid: Uuid::from_u64_pair(regs[2].swap_bytes(), regs[3].swap_bytes()),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001894 args: DirectMsg2Args(regs[4..18].try_into().unwrap()),
1895 },
1896 FuncId::MsgSendDirectResp64_2 => Self::MsgSendDirectResp2 {
1897 src_id: (regs[1] >> 16) as u16,
1898 dst_id: regs[1] as u16,
1899 args: DirectMsg2Args(regs[4..18].try_into().unwrap()),
1900 },
Imre Kis189f18c2025-05-26 19:33:05 +02001901 FuncId::ConsoleLog64 => {
1902 let char_cnt = regs[1] as u8;
1903 if char_cnt > ConsoleLogChars64::MAX_LENGTH {
1904 return Err(Error::InvalidCharacterCount(char_cnt));
1905 }
1906
1907 Self::ConsoleLog {
1908 chars: ConsoleLogChars::Chars64(ConsoleLogChars64 {
1909 char_cnt,
1910 char_lists: regs[2..18].try_into().unwrap(),
1911 }),
1912 }
1913 }
Tomás González0a058bc2025-03-11 11:20:55 +00001914 FuncId::PartitionInfoGetRegs => {
1915 // Bits[15:0]: Start index
1916 let start_index = (regs[3] & 0xffff) as u16;
1917 let info_tag = ((regs[3] >> 16) & 0xffff) as u16;
1918 Self::PartitionInfoGetRegs {
1919 uuid: Uuid::from_u64_pair(regs[1].swap_bytes(), regs[2].swap_bytes()),
1920 start_index,
1921 info_tag: if start_index == 0 && info_tag != 0 {
1922 return Err(Error::InvalidInformationTag(info_tag));
1923 } else {
1924 info_tag
1925 },
1926 }
1927 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001928 _ => panic!("Invalid number of registers (18) for function {:#x?}", fid),
1929 };
1930
1931 Ok(msg)
Balint Dobszay3aad9572025-01-17 16:54:11 +01001932 }
1933
Balint Dobszaya5846852025-02-26 15:38:53 +01001934 /// Create register contents for an interface.
Balint Dobszayde0dc802025-02-28 14:16:52 +01001935 pub fn to_regs(&self, version: Version, regs: &mut [u64]) {
Balint Dobszay82c71dd2025-04-15 10:16:44 +02001936 assert!(self.minimum_ffa_version() <= version);
1937
Balint Dobszayde0dc802025-02-28 14:16:52 +01001938 let reg_cnt = regs.len();
1939
1940 match reg_cnt {
1941 8 => {
1942 assert!(version <= Version(1, 1));
Balint Dobszay91bea9b2025-04-09 13:16:06 +02001943 regs.fill(0);
1944
Balint Dobszayde0dc802025-02-28 14:16:52 +01001945 self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
1946 }
1947 18 => {
1948 assert!(version >= Version(1, 2));
Balint Dobszay91bea9b2025-04-09 13:16:06 +02001949 regs.fill(0);
Balint Dobszayde0dc802025-02-28 14:16:52 +01001950
1951 match self {
1952 Interface::ConsoleLog {
Imre Kis189f18c2025-05-26 19:33:05 +02001953 chars: ConsoleLogChars::Chars64(_),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001954 ..
1955 }
1956 | Interface::Success {
Imre Kis54773b62025-04-10 13:47:39 +02001957 args: SuccessArgs::Args64_2(_),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001958 ..
1959 }
1960 | Interface::MsgSendDirectReq2 { .. }
Tomás González0a058bc2025-03-11 11:20:55 +00001961 | Interface::MsgSendDirectResp2 { .. }
1962 | Interface::PartitionInfoGetRegs { .. } => {
Balint Dobszayde0dc802025-02-28 14:16:52 +01001963 self.pack_regs18(version, regs.try_into().unwrap());
1964 }
1965 _ => {
1966 self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
1967 }
1968 }
1969 }
1970 _ => panic!("Invalid number of registers {}", reg_cnt),
1971 }
1972 }
1973
1974 fn pack_regs8(&self, version: Version, a: &mut [u64; 8]) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001975 if let Some(function_id) = self.function_id() {
1976 a[0] = function_id as u64;
1977 }
1978
1979 match *self {
1980 Interface::Error {
1981 target_info,
1982 error_code,
Balint Dobszayb727aab2025-04-07 10:24:59 +02001983 error_arg,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001984 } => {
1985 a[1] = u32::from(target_info).into();
1986 a[2] = (error_code as u32).into();
Balint Dobszayb727aab2025-04-07 10:24:59 +02001987 a[3] = error_arg.into();
Balint Dobszay3aad9572025-01-17 16:54:11 +01001988 }
1989 Interface::Success { target_info, args } => {
1990 a[1] = target_info.into();
1991 match args {
Imre Kis54773b62025-04-10 13:47:39 +02001992 SuccessArgs::Args32(regs) => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001993 a[2] = regs[0].into();
1994 a[3] = regs[1].into();
1995 a[4] = regs[2].into();
1996 a[5] = regs[3].into();
1997 a[6] = regs[4].into();
1998 a[7] = regs[5].into();
1999 }
Imre Kis54773b62025-04-10 13:47:39 +02002000 SuccessArgs::Args64(regs) => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01002001 a[2] = regs[0];
2002 a[3] = regs[1];
2003 a[4] = regs[2];
2004 a[5] = regs[3];
2005 a[6] = regs[4];
2006 a[7] = regs[5];
2007 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01002008 _ => panic!("{:#x?} requires 18 registers", args),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002009 }
2010 }
2011 Interface::Interrupt {
2012 target_info,
2013 interrupt_id,
2014 } => {
2015 a[1] = u32::from(target_info).into();
2016 a[2] = interrupt_id.into();
2017 }
2018 Interface::Version { input_version } => {
2019 a[1] = u32::from(input_version).into();
2020 }
2021 Interface::VersionOut { output_version } => {
2022 a[0] = u32::from(output_version).into();
2023 }
2024 Interface::Features {
2025 feat_id,
2026 input_properties,
2027 } => {
2028 a[1] = u32::from(feat_id).into();
2029 a[2] = input_properties.into();
2030 }
2031 Interface::RxAcquire { vm_id } => {
2032 a[1] = vm_id.into();
2033 }
2034 Interface::RxRelease { vm_id } => {
2035 a[1] = vm_id.into();
2036 }
2037 Interface::RxTxMap { addr, page_cnt } => {
2038 match addr {
2039 RxTxAddr::Addr32 { rx, tx } => {
2040 a[1] = tx.into();
2041 a[2] = rx.into();
2042 }
2043 RxTxAddr::Addr64 { rx, tx } => {
2044 a[1] = tx;
2045 a[2] = rx;
2046 }
2047 }
2048 a[3] = page_cnt.into();
2049 }
2050 Interface::RxTxUnmap { id } => {
2051 a[1] = id.into();
2052 }
2053 Interface::PartitionInfoGet { uuid, flags } => {
2054 let bytes = uuid.into_bytes();
2055 a[1] = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]).into();
2056 a[2] = u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]).into();
2057 a[3] = u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]).into();
2058 a[4] = u32::from_le_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]).into();
Imre Kise295adb2025-04-10 13:26:28 +02002059 a[5] = u32::from(flags).into();
Balint Dobszay3aad9572025-01-17 16:54:11 +01002060 }
Tomás González092202a2025-03-05 11:56:45 +00002061 Interface::MsgWait { flags } => {
2062 if version >= Version(1, 2) {
2063 if let Some(flags) = flags {
2064 a[2] = u32::from(flags).into();
2065 }
2066 }
2067 }
2068 Interface::IdGet | Interface::SpmIdGet | Interface::Yield => {}
Balint Dobszay3aad9572025-01-17 16:54:11 +01002069 Interface::Run { target_info } => {
2070 a[1] = u32::from(target_info).into();
2071 }
2072 Interface::NormalWorldResume => {}
Tomás González17b92442025-03-10 16:45:04 +00002073 Interface::SecondaryEpRegister { entrypoint } => match entrypoint {
2074 SecondaryEpRegisterAddr::Addr32(addr) => a[1] = addr as u64,
2075 SecondaryEpRegisterAddr::Addr64(addr) => a[1] = addr,
2076 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01002077 Interface::MsgSend2 {
2078 sender_vm_id,
2079 flags,
2080 } => {
2081 a[1] = sender_vm_id.into();
2082 a[2] = flags.into();
2083 }
2084 Interface::MsgSendDirectReq {
2085 src_id,
2086 dst_id,
Balint Dobszay3aad9572025-01-17 16:54:11 +01002087 args,
2088 } => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +01002089 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Balint Dobszay3aad9572025-01-17 16:54:11 +01002090 match args {
2091 DirectMsgArgs::Args32(args) => {
2092 a[3] = args[0].into();
2093 a[4] = args[1].into();
2094 a[5] = args[2].into();
2095 a[6] = args[3].into();
2096 a[7] = args[4].into();
2097 }
2098 DirectMsgArgs::Args64(args) => {
2099 a[3] = args[0];
2100 a[4] = args[1];
2101 a[5] = args[2];
2102 a[6] = args[3];
2103 a[7] = args[4];
2104 }
Tomás González4d5b0ba2025-03-03 17:15:55 +00002105 DirectMsgArgs::VersionReq { version } => {
2106 a[2] = DirectMsgArgs::VERSION_REQ.into();
2107 a[3] = u32::from(version).into();
2108 }
Tomás González67f92c72025-03-20 16:50:42 +00002109 DirectMsgArgs::PowerPsciReq32 { params } => {
Tomás González4d5b0ba2025-03-03 17:15:55 +00002110 a[2] = DirectMsgArgs::POWER_PSCI_REQ.into();
Tomás González67f92c72025-03-20 16:50:42 +00002111 a[3] = params[0].into();
2112 a[4] = params[1].into();
2113 a[5] = params[2].into();
2114 a[6] = params[3].into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00002115 }
Tomás González67f92c72025-03-20 16:50:42 +00002116 DirectMsgArgs::PowerPsciReq64 { params } => {
Tomás González4d5b0ba2025-03-03 17:15:55 +00002117 a[2] = DirectMsgArgs::POWER_PSCI_REQ.into();
Tomás González67f92c72025-03-20 16:50:42 +00002118 a[3] = params[0];
2119 a[4] = params[1];
2120 a[5] = params[2];
2121 a[6] = params[3];
Tomás González4d5b0ba2025-03-03 17:15:55 +00002122 }
2123 DirectMsgArgs::PowerWarmBootReq { boot_type } => {
2124 a[2] = DirectMsgArgs::POWER_WARM_BOOT_REQ.into();
2125 a[3] = u32::from(boot_type).into();
2126 }
2127 DirectMsgArgs::VmCreated { handle, vm_id } => {
2128 a[2] = DirectMsgArgs::VM_CREATED.into();
2129 let handle_regs: [u32; 2] = handle.into();
2130 a[3] = handle_regs[0].into();
2131 a[4] = handle_regs[1].into();
2132 a[5] = vm_id.into();
2133 }
2134 DirectMsgArgs::VmDestructed { handle, vm_id } => {
2135 a[2] = DirectMsgArgs::VM_DESTRUCTED.into();
2136 let handle_regs: [u32; 2] = handle.into();
2137 a[3] = handle_regs[0].into();
2138 a[4] = handle_regs[1].into();
2139 a[5] = vm_id.into();
2140 }
2141 _ => panic!("Malformed MsgSendDirectReq interface"),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002142 }
2143 }
2144 Interface::MsgSendDirectResp {
2145 src_id,
2146 dst_id,
Balint Dobszay3aad9572025-01-17 16:54:11 +01002147 args,
2148 } => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +01002149 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Balint Dobszay3aad9572025-01-17 16:54:11 +01002150 match args {
2151 DirectMsgArgs::Args32(args) => {
2152 a[3] = args[0].into();
2153 a[4] = args[1].into();
2154 a[5] = args[2].into();
2155 a[6] = args[3].into();
2156 a[7] = args[4].into();
2157 }
2158 DirectMsgArgs::Args64(args) => {
2159 a[3] = args[0];
2160 a[4] = args[1];
2161 a[5] = args[2];
2162 a[6] = args[3];
2163 a[7] = args[4];
2164 }
Tomás González4d5b0ba2025-03-03 17:15:55 +00002165 DirectMsgArgs::VersionResp { version } => {
2166 a[2] = DirectMsgArgs::VERSION_RESP.into();
2167 match version {
Tomás González67f92c72025-03-20 16:50:42 +00002168 None => a[3] = (i32::from(FfaError::NotSupported) as u32).into(),
Tomás González4d5b0ba2025-03-03 17:15:55 +00002169 Some(ver) => a[3] = u32::from(ver).into(),
2170 }
2171 }
2172 DirectMsgArgs::PowerPsciResp { psci_status } => {
2173 a[2] = DirectMsgArgs::POWER_PSCI_RESP.into();
Imre Kisb2d3c882025-04-11 14:19:35 +02002174 a[3] = (psci_status as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00002175 }
2176 DirectMsgArgs::VmCreatedAck { sp_status } => {
2177 a[2] = DirectMsgArgs::VM_CREATED_ACK.into();
Tomás González67f92c72025-03-20 16:50:42 +00002178 a[3] = (i32::from(sp_status) as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00002179 }
2180 DirectMsgArgs::VmDestructedAck { sp_status } => {
2181 a[2] = DirectMsgArgs::VM_DESTRUCTED_ACK.into();
Tomás González67f92c72025-03-20 16:50:42 +00002182 a[3] = (i32::from(sp_status) as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00002183 }
2184 _ => panic!("Malformed MsgSendDirectResp interface"),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002185 }
2186 }
2187 Interface::MemDonate {
2188 total_len,
2189 frag_len,
2190 buf,
2191 } => {
2192 a[1] = total_len.into();
2193 a[2] = frag_len.into();
2194 (a[3], a[4]) = match buf {
2195 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
2196 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
2197 None => (0, 0),
2198 };
2199 }
2200 Interface::MemLend {
2201 total_len,
2202 frag_len,
2203 buf,
2204 } => {
2205 a[1] = total_len.into();
2206 a[2] = frag_len.into();
2207 (a[3], a[4]) = match buf {
2208 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
2209 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
2210 None => (0, 0),
2211 };
2212 }
2213 Interface::MemShare {
2214 total_len,
2215 frag_len,
2216 buf,
2217 } => {
2218 a[1] = total_len.into();
2219 a[2] = frag_len.into();
2220 (a[3], a[4]) = match buf {
2221 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
2222 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
2223 None => (0, 0),
2224 };
2225 }
2226 Interface::MemRetrieveReq {
2227 total_len,
2228 frag_len,
2229 buf,
2230 } => {
2231 a[1] = total_len.into();
2232 a[2] = frag_len.into();
2233 (a[3], a[4]) = match buf {
2234 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
2235 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
2236 None => (0, 0),
2237 };
2238 }
2239 Interface::MemRetrieveResp {
2240 total_len,
2241 frag_len,
2242 } => {
2243 a[1] = total_len.into();
2244 a[2] = frag_len.into();
2245 }
2246 Interface::MemRelinquish => {}
2247 Interface::MemReclaim { handle, flags } => {
2248 let handle_regs: [u32; 2] = handle.into();
2249 a[1] = handle_regs[0].into();
2250 a[2] = handle_regs[1].into();
2251 a[3] = flags.into();
2252 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01002253 Interface::MemPermGet { addr, page_cnt } => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01002254 a[1] = match addr {
2255 MemAddr::Addr32(addr) => addr.into(),
2256 MemAddr::Addr64(addr) => addr,
2257 };
Balint Dobszayde0dc802025-02-28 14:16:52 +01002258 a[2] = if version >= Version(1, 3) {
2259 page_cnt.unwrap().into()
2260 } else {
2261 assert!(page_cnt.is_none());
2262 0
2263 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01002264 }
2265 Interface::MemPermSet {
2266 addr,
2267 page_cnt,
2268 mem_perm,
2269 } => {
2270 a[1] = match addr {
2271 MemAddr::Addr32(addr) => addr.into(),
2272 MemAddr::Addr64(addr) => addr,
2273 };
2274 a[2] = page_cnt.into();
2275 a[3] = mem_perm.into();
2276 }
Imre Kis189f18c2025-05-26 19:33:05 +02002277 Interface::ConsoleLog { chars } => match chars {
2278 ConsoleLogChars::Chars32(ConsoleLogChars32 {
2279 char_cnt,
2280 char_lists,
2281 }) => {
2282 a[1] = char_cnt.into();
2283 a[2] = char_lists[0].into();
2284 a[3] = char_lists[1].into();
2285 a[4] = char_lists[2].into();
2286 a[5] = char_lists[3].into();
2287 a[6] = char_lists[4].into();
2288 a[7] = char_lists[5].into();
Balint Dobszay3aad9572025-01-17 16:54:11 +01002289 }
Imre Kis189f18c2025-05-26 19:33:05 +02002290 _ => panic!("{:#x?} requires 18 registers", chars),
2291 },
Tomás González7ffb6132025-04-03 12:28:58 +01002292 Interface::NotificationBitmapCreate { vm_id, vcpu_cnt } => {
2293 a[1] = vm_id.into();
2294 a[2] = vcpu_cnt.into();
2295 }
2296 Interface::NotificationBitmapDestroy { vm_id } => {
2297 a[1] = vm_id.into();
2298 }
2299 Interface::NotificationBind {
2300 sender_id,
2301 receiver_id,
2302 flags,
2303 bitmap,
2304 } => {
2305 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
2306 a[2] = u32::from(flags).into();
2307 a[3] = bitmap & 0xffff_ffff;
2308 a[4] = bitmap >> 32;
2309 }
Imre Kis3571f2c2025-05-26 19:29:23 +02002310 Interface::NotificationUnbind {
Tomás González7ffb6132025-04-03 12:28:58 +01002311 sender_id,
2312 receiver_id,
2313 bitmap,
2314 } => {
2315 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
2316 a[3] = bitmap & 0xffff_ffff;
2317 a[4] = bitmap >> 32;
2318 }
2319 Interface::NotificationSet {
2320 sender_id,
2321 receiver_id,
2322 flags,
2323 bitmap,
2324 } => {
2325 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
2326 a[2] = u32::from(flags).into();
2327 a[3] = bitmap & 0xffff_ffff;
2328 a[4] = bitmap >> 32;
2329 }
2330 Interface::NotificationGet {
2331 vcpu_id,
2332 endpoint_id,
2333 flags,
2334 } => {
2335 a[1] = (u64::from(vcpu_id) << 16) | u64::from(endpoint_id);
2336 a[2] = u32::from(flags).into();
2337 }
2338 Interface::NotificationInfoGet { .. } => {}
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01002339 Interface::El3IntrHandle => {}
Balint Dobszayde0dc802025-02-28 14:16:52 +01002340 _ => panic!("{:#x?} requires 18 registers", self),
2341 }
2342 }
2343
2344 fn pack_regs18(&self, version: Version, a: &mut [u64; 18]) {
2345 assert!(version >= Version(1, 2));
2346
Balint Dobszayde0dc802025-02-28 14:16:52 +01002347 if let Some(function_id) = self.function_id() {
2348 a[0] = function_id as u64;
2349 }
2350
2351 match *self {
2352 Interface::Success { target_info, args } => {
2353 a[1] = target_info.into();
2354 match args {
Imre Kis54773b62025-04-10 13:47:39 +02002355 SuccessArgs::Args64_2(regs) => a[2..18].copy_from_slice(&regs[..16]),
Balint Dobszayde0dc802025-02-28 14:16:52 +01002356 _ => panic!("{:#x?} requires 8 registers", args),
2357 }
2358 }
2359 Interface::MsgSendDirectReq2 {
2360 src_id,
2361 dst_id,
2362 uuid,
2363 args,
2364 } => {
2365 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00002366 let (uuid_msb, uuid_lsb) = uuid.as_u64_pair();
2367 (a[2], a[3]) = (uuid_msb.swap_bytes(), uuid_lsb.swap_bytes());
Balint Dobszayde0dc802025-02-28 14:16:52 +01002368 a[4..18].copy_from_slice(&args.0[..14]);
2369 }
2370 Interface::MsgSendDirectResp2 {
2371 src_id,
2372 dst_id,
2373 args,
2374 } => {
2375 a[1] = ((src_id as u64) << 16) | dst_id as u64;
2376 a[2] = 0;
2377 a[3] = 0;
2378 a[4..18].copy_from_slice(&args.0[..14]);
2379 }
Imre Kis189f18c2025-05-26 19:33:05 +02002380 Interface::ConsoleLog { chars: char_lists } => match char_lists {
2381 ConsoleLogChars::Chars64(ConsoleLogChars64 {
2382 char_cnt,
2383 char_lists,
2384 }) => {
2385 a[1] = char_cnt.into();
2386 a[2..18].copy_from_slice(&char_lists[..16])
Balint Dobszayde0dc802025-02-28 14:16:52 +01002387 }
Imre Kis189f18c2025-05-26 19:33:05 +02002388 _ => panic!("{:#x?} requires 8 registers", char_lists),
2389 },
Tomás González0a058bc2025-03-11 11:20:55 +00002390 Interface::PartitionInfoGetRegs {
2391 uuid,
2392 start_index,
2393 info_tag,
2394 } => {
2395 if start_index == 0 && info_tag != 0 {
2396 panic!("Information Tag MBZ if start index is 0: {:#x?}", self);
2397 }
2398 let (uuid_msb, uuid_lsb) = uuid.as_u64_pair();
2399 (a[1], a[2]) = (uuid_msb.swap_bytes(), uuid_lsb.swap_bytes());
2400 a[3] = (u64::from(info_tag) << 16) | u64::from(start_index);
2401 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01002402 _ => panic!("{:#x?} requires 8 registers", self),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002403 }
2404 }
2405
Balint Dobszaya5846852025-02-26 15:38:53 +01002406 /// Helper function to create an `FFA_SUCCESS` interface without any arguments.
Balint Dobszay3aad9572025-01-17 16:54:11 +01002407 pub fn success32_noargs() -> Self {
2408 Self::Success {
2409 target_info: 0,
Imre Kis54773b62025-04-10 13:47:39 +02002410 args: SuccessArgs::Args32([0; 6]),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002411 }
2412 }
2413
Balint Dobszaya5846852025-02-26 15:38:53 +01002414 /// Helper function to create an `FFA_ERROR` interface with an error code.
Balint Dobszay3aad9572025-01-17 16:54:11 +01002415 pub fn error(error_code: FfaError) -> Self {
2416 Self::Error {
2417 target_info: TargetInfo {
2418 endpoint_id: 0,
2419 vcpu_id: 0,
2420 },
2421 error_code,
Balint Dobszayb727aab2025-04-07 10:24:59 +02002422 error_arg: 0,
Balint Dobszay3aad9572025-01-17 16:54:11 +01002423 }
2424 }
2425}
2426
Tomás González0a058bc2025-03-11 11:20:55 +00002427#[cfg(test)]
2428mod tests {
2429 use super::*;
2430
2431 #[test]
2432 fn part_info_get_regs() {
2433 let uuid = Uuid::parse_str("a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8").unwrap();
2434 let uuid_bytes = uuid.as_bytes();
2435 let test_info_tag = 0b1101_1101;
2436 let test_start_index = 0b1101;
2437 let start_index_and_tag = (test_info_tag << 16) | test_start_index;
2438 let version = Version(1, 2);
2439
2440 // From spec:
2441 // Bytes[0...7] of UUID with byte 0 in the low-order bits.
Balint Dobszayb2e9bed2025-04-15 12:57:36 +02002442 let reg_x1 = ((uuid_bytes[7] as u64) << 56)
2443 | ((uuid_bytes[6] as u64) << 48)
2444 | ((uuid_bytes[5] as u64) << 40)
2445 | ((uuid_bytes[4] as u64) << 32)
2446 | ((uuid_bytes[3] as u64) << 24)
2447 | ((uuid_bytes[2] as u64) << 16)
2448 | ((uuid_bytes[1] as u64) << 8)
Tomás González0a058bc2025-03-11 11:20:55 +00002449 | (uuid_bytes[0] as u64);
2450
2451 // From spec:
2452 // Bytes[8...15] of UUID with byte 8 in the low-order bits.
Balint Dobszayb2e9bed2025-04-15 12:57:36 +02002453 let reg_x2 = ((uuid_bytes[15] as u64) << 56)
2454 | ((uuid_bytes[14] as u64) << 48)
2455 | ((uuid_bytes[13] as u64) << 40)
2456 | ((uuid_bytes[12] as u64) << 32)
2457 | ((uuid_bytes[11] as u64) << 24)
2458 | ((uuid_bytes[10] as u64) << 16)
2459 | ((uuid_bytes[9] as u64) << 8)
Tomás González0a058bc2025-03-11 11:20:55 +00002460 | (uuid_bytes[8] as u64);
2461
2462 // First, test for wrong tag:
2463 {
2464 let mut regs = [0u64; 18];
2465 regs[0] = FuncId::PartitionInfoGetRegs as u64;
2466 regs[1] = reg_x1;
2467 regs[2] = reg_x2;
2468 regs[3] = test_info_tag << 16;
2469
2470 assert!(Interface::from_regs(version, &regs).is_err_and(
2471 |e| e == Error::InvalidInformationTag(test_info_tag.try_into().unwrap())
2472 ));
2473 }
2474
2475 // Test for regs -> Interface -> regs
2476 {
2477 let mut orig_regs = [0u64; 18];
2478 orig_regs[0] = FuncId::PartitionInfoGetRegs as u64;
2479 orig_regs[1] = reg_x1;
2480 orig_regs[2] = reg_x2;
2481 orig_regs[3] = start_index_and_tag;
2482
Balint Dobszayb2e9bed2025-04-15 12:57:36 +02002483 let mut test_regs = orig_regs;
2484 let interface = Interface::from_regs(version, &test_regs).unwrap();
Tomás González0a058bc2025-03-11 11:20:55 +00002485 match &interface {
2486 Interface::PartitionInfoGetRegs {
2487 info_tag,
2488 start_index,
2489 uuid: int_uuid,
2490 } => {
2491 assert_eq!(u64::from(*info_tag), test_info_tag);
2492 assert_eq!(u64::from(*start_index), test_start_index);
2493 assert_eq!(*int_uuid, uuid);
2494 }
2495 _ => panic!("Expecting Interface::PartitionInfoGetRegs!"),
2496 }
2497 test_regs.fill(0);
2498 interface.to_regs(version, &mut test_regs);
2499 assert_eq!(orig_regs, test_regs);
2500 }
2501
2502 // Test for Interface -> regs -> Interface
2503 {
2504 let interface = Interface::PartitionInfoGetRegs {
2505 info_tag: test_info_tag.try_into().unwrap(),
2506 start_index: test_start_index.try_into().unwrap(),
2507 uuid,
2508 };
2509
2510 let mut regs: [u64; 18] = [0; 18];
2511 interface.to_regs(version, &mut regs);
2512
2513 assert_eq!(Some(FuncId::PartitionInfoGetRegs), interface.function_id());
2514 assert_eq!(regs[0], interface.function_id().unwrap() as u64);
2515 assert_eq!(regs[1], reg_x1);
2516 assert_eq!(regs[2], reg_x2);
2517 assert_eq!(regs[3], (test_info_tag << 16) | test_start_index);
2518
2519 assert_eq!(Interface::from_regs(version, &regs).unwrap(), interface);
2520 }
2521 }
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00002522
2523 #[test]
2524 fn msg_send_direct_req2() {
2525 let uuid = Uuid::parse_str("a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8").unwrap();
2526 let uuid_bytes = uuid.as_bytes();
2527
2528 // From spec:
2529 // Bytes[0...7] of UUID with byte 0 in the low-order bits.
Balint Dobszayb2e9bed2025-04-15 12:57:36 +02002530 let reg_x2 = ((uuid_bytes[7] as u64) << 56)
2531 | ((uuid_bytes[6] as u64) << 48)
2532 | ((uuid_bytes[5] as u64) << 40)
2533 | ((uuid_bytes[4] as u64) << 32)
2534 | ((uuid_bytes[3] as u64) << 24)
2535 | ((uuid_bytes[2] as u64) << 16)
2536 | ((uuid_bytes[1] as u64) << 8)
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00002537 | (uuid_bytes[0] as u64);
2538
2539 // From spec:
2540 // Bytes[8...15] of UUID with byte 8 in the low-order bits.
Balint Dobszayb2e9bed2025-04-15 12:57:36 +02002541 let reg_x3 = ((uuid_bytes[15] as u64) << 56)
2542 | ((uuid_bytes[14] as u64) << 48)
2543 | ((uuid_bytes[13] as u64) << 40)
2544 | ((uuid_bytes[12] as u64) << 32)
2545 | ((uuid_bytes[11] as u64) << 24)
2546 | ((uuid_bytes[10] as u64) << 16)
2547 | ((uuid_bytes[9] as u64) << 8)
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00002548 | (uuid_bytes[8] as u64);
2549
2550 let test_sender = 0b1101_1101;
2551 let test_receiver = 0b1101;
2552 let test_sender_receiver = (test_sender << 16) | test_receiver;
2553 let version = Version(1, 2);
2554
2555 // Test for regs -> Interface -> regs
2556 {
2557 let mut orig_regs = [0u64; 18];
2558 orig_regs[0] = FuncId::MsgSendDirectReq64_2 as u64;
2559 orig_regs[1] = test_sender_receiver;
2560 orig_regs[2] = reg_x2;
2561 orig_regs[3] = reg_x3;
2562
Balint Dobszayb2e9bed2025-04-15 12:57:36 +02002563 let mut test_regs = orig_regs;
2564 let interface = Interface::from_regs(version, &test_regs).unwrap();
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00002565 match &interface {
2566 Interface::MsgSendDirectReq2 {
2567 dst_id,
2568 src_id,
2569 args: _,
2570 uuid: int_uuid,
2571 } => {
2572 assert_eq!(u64::from(*src_id), test_sender);
2573 assert_eq!(u64::from(*dst_id), test_receiver);
2574 assert_eq!(*int_uuid, uuid);
2575 }
2576 _ => panic!("Expecting Interface::MsgSendDirectReq2!"),
2577 }
2578 test_regs.fill(0);
2579 interface.to_regs(version, &mut test_regs);
2580 assert_eq!(orig_regs, test_regs);
2581 }
2582
2583 // Test for Interface -> regs -> Interface
2584 {
2585 let rest_of_regs: [u64; 14] = [0; 14];
2586
2587 let interface = Interface::MsgSendDirectReq2 {
2588 src_id: test_sender.try_into().unwrap(),
2589 dst_id: test_receiver.try_into().unwrap(),
2590 uuid,
2591 args: DirectMsg2Args(rest_of_regs),
2592 };
2593
2594 let mut regs: [u64; 18] = [0; 18];
2595 interface.to_regs(version, &mut regs);
2596
2597 assert_eq!(Some(FuncId::MsgSendDirectReq64_2), interface.function_id());
2598 assert_eq!(regs[0], interface.function_id().unwrap() as u64);
2599 assert_eq!(regs[1], test_sender_receiver);
2600 assert_eq!(regs[2], reg_x2);
2601 assert_eq!(regs[3], reg_x3);
2602 assert_eq!(regs[4], 0);
2603
2604 assert_eq!(Interface::from_regs(version, &regs).unwrap(), interface);
2605 }
2606 }
Tomás González6ccba0a2025-04-09 13:31:29 +01002607
2608 #[test]
2609 fn is_32bit() {
2610 let interface_64 = Interface::MsgSendDirectReq {
2611 src_id: 0,
2612 dst_id: 1,
2613 args: DirectMsgArgs::Args64([0, 0, 0, 0, 0]),
2614 };
2615 assert!(!interface_64.is_32bit());
2616
2617 let interface_32 = Interface::MsgSendDirectReq {
2618 src_id: 0,
2619 dst_id: 1,
2620 args: DirectMsgArgs::Args32([0, 0, 0, 0, 0]),
2621 };
2622 assert!(interface_32.is_32bit());
2623 }
Imre Kis787c5002025-04-10 14:25:51 +02002624
2625 #[test]
2626 fn success_args_notification_info_get32() {
2627 let mut notifications = SuccessArgsNotificationInfoGet32::default();
2628
2629 // 16.7.1.1 Example usage
2630 notifications.add_list(0x0000, &[0, 2, 3]).unwrap();
2631 notifications.add_list(0x0000, &[4, 6]).unwrap();
2632 notifications.add_list(0x0002, &[]).unwrap();
2633 notifications.add_list(0x0003, &[1]).unwrap();
2634
2635 let args: SuccessArgs = notifications.into();
2636 assert_eq!(
2637 SuccessArgs::Args32([
2638 0x0004_b200,
2639 0x0000_0000,
2640 0x0003_0002,
2641 0x0004_0000,
2642 0x0002_0006,
2643 0x0001_0003
2644 ]),
2645 args
2646 );
2647
2648 let notifications = SuccessArgsNotificationInfoGet32::try_from(args).unwrap();
2649 let mut iter = notifications.iter();
2650 assert_eq!(Some((0x0000, &[0, 2, 3][..])), iter.next());
2651 assert_eq!(Some((0x0000, &[4, 6][..])), iter.next());
2652 assert_eq!(Some((0x0002, &[][..])), iter.next());
2653 assert_eq!(Some((0x0003, &[1][..])), iter.next());
2654 }
2655
2656 #[test]
2657 fn success_args_notification_info_get64() {
2658 let mut notifications = SuccessArgsNotificationInfoGet64::default();
2659
2660 // 16.7.1.1 Example usage
2661 notifications.add_list(0x0000, &[0, 2, 3]).unwrap();
2662 notifications.add_list(0x0000, &[4, 6]).unwrap();
2663 notifications.add_list(0x0002, &[]).unwrap();
2664 notifications.add_list(0x0003, &[1]).unwrap();
2665
2666 let args: SuccessArgs = notifications.into();
2667 assert_eq!(
2668 SuccessArgs::Args64([
2669 0x0004_b200,
2670 0x0003_0002_0000_0000,
2671 0x0002_0006_0004_0000,
2672 0x0000_0000_0001_0003,
2673 0x0000_0000_0000_0000,
2674 0x0000_0000_0000_0000,
2675 ]),
2676 args
2677 );
2678
2679 let notifications = SuccessArgsNotificationInfoGet64::try_from(args).unwrap();
2680 let mut iter = notifications.iter();
2681 assert_eq!(Some((0x0000, &[0, 2, 3][..])), iter.next());
2682 assert_eq!(Some((0x0000, &[4, 6][..])), iter.next());
2683 assert_eq!(Some((0x0002, &[][..])), iter.next());
2684 assert_eq!(Some((0x0003, &[1][..])), iter.next());
2685 }
Tomás González0a058bc2025-03-11 11:20:55 +00002686}