blob: 566cfbe07aebe65f20e9f7e20043964cca288ab5 [file] [log] [blame]
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001// SPDX-FileCopyrightText: Copyright 2023 Arm Limited and/or its affiliates <open-source-office@arm.com>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4#![cfg_attr(not(test), no_std)]
Balint Dobszaya5846852025-02-26 15:38:53 +01005#![deny(clippy::undocumented_unsafe_blocks)]
6#![deny(unsafe_op_in_unsafe_fn)]
7#![doc = include_str!("../README.md")]
Balint Dobszay5bf492f2024-07-29 17:21:32 +02008
Andrew Walbran19970ba2024-11-25 15:35:00 +00009use core::fmt::{self, Debug, Display, Formatter};
Andrew Walbran44029a02024-11-25 15:34:31 +000010use num_enum::{IntoPrimitive, TryFromPrimitive};
11use thiserror::Error;
Balint Dobszay5bf492f2024-07-29 17:21:32 +020012use uuid::Uuid;
Imre Kis787c5002025-04-10 14:25:51 +020013use zerocopy::transmute;
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 Dobszay3aad9572025-01-17 16:54:11 +010059}
60
61impl From<Error> for FfaError {
62 fn from(value: Error) -> Self {
63 match value {
64 Error::UnrecognisedFunctionId(_) | Error::UnrecognisedFeatureId(_) => {
65 Self::NotSupported
66 }
Tomás González0a058bc2025-03-11 11:20:55 +000067 Error::InvalidInformationTag(_) => Self::Retry,
Tomás González4d5b0ba2025-03-03 17:15:55 +000068 Error::UnrecognisedErrorCode(_)
69 | Error::UnrecognisedFwkMsg(_)
70 | Error::InvalidVersion(_)
Tomás González092202a2025-03-05 11:56:45 +000071 | Error::InvalidMsgWaitFlag(_)
Tomás González4d5b0ba2025-03-03 17:15:55 +000072 | Error::UnrecognisedVmAvailabilityStatus(_)
Tomás González7ffb6132025-04-03 12:28:58 +010073 | Error::InvalidNotificationSetFlag(_)
74 | Error::InvalidVmId(_)
Imre Kise295adb2025-04-10 13:26:28 +020075 | Error::UnrecognisedWarmBootType(_)
Imre Kis839eaef2025-04-11 17:38:36 +020076 | Error::InvalidPartitionInfoGetFlag(_)
Imre Kis787c5002025-04-10 14:25:51 +020077 | Error::InvalidSuccessArgsVariant
Imre Kis92b663e2025-04-10 14:15:05 +020078 | Error::InvalidNotificationCount
79 | Error::InvalidPartitionInfoGetRegsResponse => Self::InvalidParameters,
Balint Dobszay3aad9572025-01-17 16:54:11 +010080 }
81 }
82}
Balint Dobszay5bf492f2024-07-29 17:21:32 +020083
Balint Dobszaya5846852025-02-26 15:38:53 +010084/// 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 +020085#[derive(PartialEq, Clone, Copy)]
86pub enum Instance {
Balint Dobszaya5846852025-02-26 15:38:53 +010087 /// The instance between the SPMC and SPMD.
Balint Dobszay5bf492f2024-07-29 17:21:32 +020088 SecurePhysical,
Balint Dobszaya5846852025-02-26 15:38:53 +010089 /// The instance between the SPMC and a physical SP (contains the SP's endpoint ID).
Balint Dobszay5bf492f2024-07-29 17:21:32 +020090 SecureVirtual(u16),
91}
92
Balint Dobszaya5846852025-02-26 15:38:53 +010093/// Function IDs of the various FF-A interfaces.
Andrew Walbran969b9252024-11-25 15:35:42 +000094#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
Balint Dobszay3aad9572025-01-17 16:54:11 +010095#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedFunctionId))]
Balint Dobszay5bf492f2024-07-29 17:21:32 +020096#[repr(u32)]
97pub enum FuncId {
98 Error = 0x84000060,
99 Success32 = 0x84000061,
100 Success64 = 0xc4000061,
101 Interrupt = 0x84000062,
102 Version = 0x84000063,
103 Features = 0x84000064,
104 RxAcquire = 0x84000084,
105 RxRelease = 0x84000065,
106 RxTxMap32 = 0x84000066,
107 RxTxMap64 = 0xc4000066,
108 RxTxUnmap = 0x84000067,
109 PartitionInfoGet = 0x84000068,
Balint Dobszaye6aa4862025-02-28 16:37:56 +0100110 PartitionInfoGetRegs = 0xc400008b,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200111 IdGet = 0x84000069,
112 SpmIdGet = 0x84000085,
Balint Dobszaye6aa4862025-02-28 16:37:56 +0100113 ConsoleLog32 = 0x8400008a,
114 ConsoleLog64 = 0xc400008a,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200115 MsgWait = 0x8400006b,
116 Yield = 0x8400006c,
117 Run = 0x8400006d,
118 NormalWorldResume = 0x8400007c,
119 MsgSend2 = 0x84000086,
120 MsgSendDirectReq32 = 0x8400006f,
121 MsgSendDirectReq64 = 0xc400006f,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100122 MsgSendDirectReq64_2 = 0xc400008d,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200123 MsgSendDirectResp32 = 0x84000070,
124 MsgSendDirectResp64 = 0xc4000070,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100125 MsgSendDirectResp64_2 = 0xc400008e,
Balint Dobszaye6aa4862025-02-28 16:37:56 +0100126 NotificationBitmapCreate = 0x8400007d,
127 NotificationBitmapDestroy = 0x8400007e,
128 NotificationBind = 0x8400007f,
129 NotificationUnbind = 0x84000080,
130 NotificationSet = 0x84000081,
131 NotificationGet = 0x84000082,
132 NotificationInfoGet32 = 0x84000083,
133 NotificationInfoGet64 = 0xc4000083,
134 El3IntrHandle = 0x8400008c,
Tomás González17b92442025-03-10 16:45:04 +0000135 SecondaryEpRegister32 = 0x84000087,
136 SecondaryEpRegister64 = 0xc4000087,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200137 MemDonate32 = 0x84000071,
138 MemDonate64 = 0xc4000071,
139 MemLend32 = 0x84000072,
140 MemLend64 = 0xc4000072,
141 MemShare32 = 0x84000073,
142 MemShare64 = 0xc4000073,
143 MemRetrieveReq32 = 0x84000074,
144 MemRetrieveReq64 = 0xc4000074,
145 MemRetrieveResp = 0x84000075,
146 MemRelinquish = 0x84000076,
147 MemReclaim = 0x84000077,
148 MemPermGet32 = 0x84000088,
149 MemPermGet64 = 0xc4000088,
150 MemPermSet32 = 0x84000089,
151 MemPermSet64 = 0xc4000089,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200152}
153
Balint Dobszayde0dc802025-02-28 14:16:52 +0100154impl FuncId {
155 /// Returns true if this is a 32-bit call, or false if it is a 64-bit call.
156 pub fn is_32bit(&self) -> bool {
Tomás González6ccba0a2025-04-09 13:31:29 +0100157 u32::from(*self) & (1 << 30) == 0
Balint Dobszayde0dc802025-02-28 14:16:52 +0100158 }
159}
160
Balint Dobszaya5846852025-02-26 15:38:53 +0100161/// Error status codes used by the `FFA_ERROR` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100162#[derive(Clone, Copy, Debug, Eq, Error, IntoPrimitive, PartialEq, TryFromPrimitive)]
163#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedErrorCode))]
164#[repr(i32)]
165pub enum FfaError {
166 #[error("Not supported")]
167 NotSupported = -1,
168 #[error("Invalid parameters")]
169 InvalidParameters = -2,
170 #[error("No memory")]
171 NoMemory = -3,
172 #[error("Busy")]
173 Busy = -4,
174 #[error("Interrupted")]
175 Interrupted = -5,
176 #[error("Denied")]
177 Denied = -6,
178 #[error("Retry")]
179 Retry = -7,
180 #[error("Aborted")]
181 Aborted = -8,
182 #[error("No data")]
183 NoData = -9,
184}
185
Balint Dobszaya5846852025-02-26 15:38:53 +0100186/// Endpoint ID and vCPU ID pair, used by `FFA_ERROR`, `FFA_INTERRUPT` and `FFA_RUN` interfaces.
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200187#[derive(Debug, Eq, PartialEq, Clone, Copy)]
Balint Dobszay3aad9572025-01-17 16:54:11 +0100188pub struct TargetInfo {
189 pub endpoint_id: u16,
190 pub vcpu_id: u16,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200191}
192
Balint Dobszay3aad9572025-01-17 16:54:11 +0100193impl From<u32> for TargetInfo {
194 fn from(value: u32) -> Self {
195 Self {
196 endpoint_id: (value >> 16) as u16,
197 vcpu_id: value as u16,
198 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200199 }
200}
201
Balint Dobszay3aad9572025-01-17 16:54:11 +0100202impl From<TargetInfo> for u32 {
203 fn from(value: TargetInfo) -> Self {
Balint Dobszaye9a3e762025-02-26 17:29:57 +0100204 ((value.endpoint_id as u32) << 16) | value.vcpu_id as u32
Andrew Walbran0d315812024-11-25 15:36:36 +0000205 }
Balint Dobszay3aad9572025-01-17 16:54:11 +0100206}
Andrew Walbran0d315812024-11-25 15:36:36 +0000207
Imre Kis839eaef2025-04-11 17:38:36 +0200208/// Generic arguments of the `FFA_SUCCESS` interface. The interpretation of the arguments depends on
209/// the interface that initiated the request. The application code has knowledge of the request, so
210/// it has to convert `SuccessArgs` into/from a specific success args structure that matches the
211/// request.
Imre Kis4e9d8bc2025-04-10 13:48:26 +0200212///
213/// The current specialized success arguments types are:
214/// * `FFA_FEATURES` - [`SuccessArgsFeatures`]
Imre Kisbbef2872025-04-10 14:11:29 +0200215/// * `FFA_ID_GET` - [`SuccessArgsIdGet`]
216/// * `FFA_SPM_ID_GET` - [`SuccessArgsSpmIdGet`]
Imre Kis61c34092025-04-10 14:14:38 +0200217/// * `FFA_PARTITION_INFO_GET` - [`partition_info::SuccessArgsPartitionInfoGet`]
Imre Kis92b663e2025-04-10 14:15:05 +0200218/// * `FFA_PARTITION_INFO_GET_REGS` - [`partition_info::SuccessArgsPartitionInfoGetRegs`]
Imre Kis9959e062025-04-10 14:16:10 +0200219/// * `FFA_NOTIFICATION_GET` - [`SuccessArgsNotificationGet`]
Imre Kis787c5002025-04-10 14:25:51 +0200220/// * `FFA_NOTIFICATION_INFO_GET_32` - [`SuccessArgsNotificationInfoGet32`]
221/// * `FFA_NOTIFICATION_INFO_GET_64` - [`SuccessArgsNotificationInfoGet64`]
Balint Dobszay3aad9572025-01-17 16:54:11 +0100222#[derive(Debug, Eq, PartialEq, Clone, Copy)]
223pub enum SuccessArgs {
Imre Kis54773b62025-04-10 13:47:39 +0200224 Args32([u32; 6]),
225 Args64([u64; 6]),
226 Args64_2([u64; 16]),
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200227}
228
Imre Kis839eaef2025-04-11 17:38:36 +0200229impl SuccessArgs {
230 fn try_get_args32(self) -> Result<[u32; 6], Error> {
231 match self {
232 SuccessArgs::Args32(args) => Ok(args),
233 SuccessArgs::Args64(_) | SuccessArgs::Args64_2(_) => {
234 Err(Error::InvalidSuccessArgsVariant)
235 }
236 }
237 }
238
239 fn try_get_args64(self) -> Result<[u64; 6], Error> {
240 match self {
241 SuccessArgs::Args64(args) => Ok(args),
242 SuccessArgs::Args32(_) | SuccessArgs::Args64_2(_) => {
243 Err(Error::InvalidSuccessArgsVariant)
244 }
245 }
246 }
247
248 fn try_get_args64_2(self) -> Result<[u64; 16], Error> {
249 match self {
250 SuccessArgs::Args64_2(args) => Ok(args),
251 SuccessArgs::Args32(_) | SuccessArgs::Args64(_) => {
252 Err(Error::InvalidSuccessArgsVariant)
253 }
254 }
255 }
256}
257
Tomás González17b92442025-03-10 16:45:04 +0000258/// Entrypoint address argument for `FFA_SECONDARY_EP_REGISTER` interface.
259#[derive(Debug, Eq, PartialEq, Clone, Copy)]
260pub enum SecondaryEpRegisterAddr {
261 Addr32(u32),
262 Addr64(u64),
263}
264
Balint Dobszaya5846852025-02-26 15:38:53 +0100265/// Version number of the FF-A implementation, `.0` is the major, `.1` is minor the version.
Balint Dobszayde0dc802025-02-28 14:16:52 +0100266#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord)]
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200267pub struct Version(pub u16, pub u16);
268
Tomás González1f794352025-03-03 16:47:06 +0000269impl Version {
Tomás González83146af2025-03-04 11:32:41 +0000270 // The FF-A spec mandates that bit[31] of a version number must be 0
271 const MBZ_BITS: u32 = 1 << 31;
272
Tomás González1f794352025-03-03 16:47:06 +0000273 /// Returns whether the caller's version (self) is compatible with the callee's version (input
274 /// parameter)
275 pub fn is_compatible_to(&self, callee_version: &Version) -> bool {
276 self.0 == callee_version.0 && self.1 <= callee_version.1
277 }
278}
279
Tomás González83146af2025-03-04 11:32:41 +0000280impl TryFrom<u32> for Version {
281 type Error = Error;
282
283 fn try_from(val: u32) -> Result<Self, Self::Error> {
284 if (val & Self::MBZ_BITS) != 0 {
285 Err(Error::InvalidVersion(val))
286 } else {
287 Ok(Self((val >> 16) as u16, val as u16))
288 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200289 }
290}
291
292impl From<Version> for u32 {
293 fn from(v: Version) -> Self {
Tomás González83146af2025-03-04 11:32:41 +0000294 let v_u32 = ((v.0 as u32) << 16) | v.1 as u32;
295 assert!(v_u32 & Version::MBZ_BITS == 0);
296 v_u32
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200297 }
298}
299
Andrew Walbran19970ba2024-11-25 15:35:00 +0000300impl Display for Version {
301 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
302 write!(f, "{}.{}", self.0, self.1)
303 }
304}
305
306impl Debug for Version {
307 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
308 Display::fmt(self, f)
309 }
310}
311
Balint Dobszaya5846852025-02-26 15:38:53 +0100312/// Feature IDs used by the `FFA_FEATURES` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100313#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
314#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedFeatureId))]
315#[repr(u8)]
316pub enum FeatureId {
317 NotificationPendingInterrupt = 0x1,
318 ScheduleReceiverInterrupt = 0x2,
319 ManagedExitInterrupt = 0x3,
320}
Balint Dobszayc8802492025-01-15 18:11:39 +0100321
Balint Dobszaya5846852025-02-26 15:38:53 +0100322/// Arguments for the `FFA_FEATURES` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100323#[derive(Debug, Eq, PartialEq, Clone, Copy)]
324pub enum Feature {
325 FuncId(FuncId),
326 FeatureId(FeatureId),
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100327 Unknown(u32),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100328}
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200329
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100330impl From<u32> for Feature {
331 fn from(value: u32) -> Self {
332 // Bit[31] is set for all valid FF-A function IDs so we don't have to check it separately
333 if let Ok(func_id) = value.try_into() {
334 Self::FuncId(func_id)
335 } else if let Ok(feat_id) = (value as u8).try_into() {
336 Self::FeatureId(feat_id)
Balint Dobszay3aad9572025-01-17 16:54:11 +0100337 } else {
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100338 Self::Unknown(value)
339 }
Balint Dobszay3aad9572025-01-17 16:54:11 +0100340 }
341}
342
343impl From<Feature> for u32 {
344 fn from(value: Feature) -> Self {
345 match value {
346 Feature::FuncId(func_id) => (1 << 31) | func_id as u32,
347 Feature::FeatureId(feature_id) => feature_id as u32,
Imre Kis29c8ace2025-04-11 13:49:58 +0200348 Feature::Unknown(id) => id,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100349 }
350 }
351}
352
Imre Kis4e9d8bc2025-04-10 13:48:26 +0200353/// `FFA_FEATURES` specific success argument structure. This type needs further specialization based
354/// on 'FF-A function ID or Feature ID' field of the preceeding `FFA_FEATURES` request.
355#[derive(Debug, Eq, PartialEq, Clone, Copy)]
356pub struct SuccessArgsFeatures {
357 pub properties: [u32; 2],
358}
359
360impl From<SuccessArgsFeatures> for SuccessArgs {
361 fn from(value: SuccessArgsFeatures) -> Self {
362 Self::Args32([value.properties[0], value.properties[1], 0, 0, 0, 0])
363 }
364}
365
366impl TryFrom<SuccessArgs> for SuccessArgsFeatures {
367 type Error = Error;
368
369 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
370 let args = value.try_get_args32()?;
371
372 Ok(Self {
373 properties: [args[0], args[1]],
374 })
375 }
376}
377
Balint Dobszaya5846852025-02-26 15:38:53 +0100378/// RXTX buffer descriptor, used by `FFA_RXTX_MAP`.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100379#[derive(Debug, Eq, PartialEq, Clone, Copy)]
380pub enum RxTxAddr {
381 Addr32 { rx: u32, tx: u32 },
382 Addr64 { rx: u64, tx: u64 },
383}
384
Imre Kisbbef2872025-04-10 14:11:29 +0200385/// `FFA_ID_GET` specific success argument structure.
386#[derive(Debug, Eq, PartialEq, Clone, Copy)]
387pub struct SuccessArgsIdGet {
388 pub id: u16,
389}
390
391impl From<SuccessArgsIdGet> for SuccessArgs {
392 fn from(value: SuccessArgsIdGet) -> Self {
393 SuccessArgs::Args32([value.id as u32, 0, 0, 0, 0, 0])
394 }
395}
396
397impl TryFrom<SuccessArgs> for SuccessArgsIdGet {
398 type Error = Error;
399
400 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
401 let args = value.try_get_args32()?;
402 Ok(Self { id: args[0] as u16 })
403 }
404}
405
406/// `FFA_SPM_ID_GET` specific success argument structure.
407#[derive(Debug, Eq, PartialEq, Clone, Copy)]
408pub struct SuccessArgsSpmIdGet {
409 pub id: u16,
410}
411
412impl From<SuccessArgsSpmIdGet> for SuccessArgs {
413 fn from(value: SuccessArgsSpmIdGet) -> Self {
414 SuccessArgs::Args32([value.id as u32, 0, 0, 0, 0, 0])
415 }
416}
417
418impl TryFrom<SuccessArgs> for SuccessArgsSpmIdGet {
419 type Error = Error;
420
421 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
422 let args = value.try_get_args32()?;
423 Ok(Self { id: args[0] as u16 })
424 }
425}
426
Imre Kise295adb2025-04-10 13:26:28 +0200427/// Flags of the `FFA_PARTITION_INFO_GET` interface.
428#[derive(Debug, Eq, PartialEq, Clone, Copy)]
429pub struct PartitionInfoGetFlags {
430 pub count_only: bool,
431}
432
433impl PartitionInfoGetFlags {
434 const RETURN_INFORMATION_TYPE_FLAG: u32 = 1 << 0;
435 const MBZ_BITS: u32 = 0xffff_fffe;
436}
437
438impl TryFrom<u32> for PartitionInfoGetFlags {
439 type Error = Error;
440
441 fn try_from(val: u32) -> Result<Self, Self::Error> {
442 if (val & Self::MBZ_BITS) != 0 {
443 Err(Error::InvalidPartitionInfoGetFlag(val))
444 } else {
445 Ok(Self {
446 count_only: val & Self::RETURN_INFORMATION_TYPE_FLAG != 0,
447 })
448 }
449 }
450}
451
452impl From<PartitionInfoGetFlags> for u32 {
453 fn from(flags: PartitionInfoGetFlags) -> Self {
454 let mut bits: u32 = 0;
455 if flags.count_only {
456 bits |= PartitionInfoGetFlags::RETURN_INFORMATION_TYPE_FLAG;
457 }
458 bits
459 }
460}
461
Tomás González4d5b0ba2025-03-03 17:15:55 +0000462/// Composite type for capturing success and error return codes for the VM availability messages.
463///
464/// Error codes are handled by the `FfaError` type. Having a separate type for errors helps using
465/// `Result<(), FfaError>`. If a single type would include both success and error values,
466/// then `Err(FfaError::Success)` would be incomprehensible.
467#[derive(Debug, Eq, PartialEq, Clone, Copy)]
468pub enum VmAvailabilityStatus {
469 Success,
470 Error(FfaError),
471}
472
473impl TryFrom<i32> for VmAvailabilityStatus {
474 type Error = Error;
475 fn try_from(value: i32) -> Result<Self, <Self as TryFrom<i32>>::Error> {
476 Ok(match value {
477 0 => Self::Success,
478 error_code => Self::Error(FfaError::try_from(error_code)?),
479 })
480 }
481}
482
483impl From<VmAvailabilityStatus> for i32 {
484 fn from(value: VmAvailabilityStatus) -> Self {
485 match value {
486 VmAvailabilityStatus::Success => 0,
487 VmAvailabilityStatus::Error(error_code) => error_code.into(),
488 }
489 }
490}
491
492/// Arguments for the Power Warm Boot `FFA_MSG_SEND_DIRECT_REQ` interface.
493#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
494#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedWarmBootType))]
495#[repr(u32)]
496pub enum WarmBootType {
497 ExitFromSuspend = 0,
498 ExitFromLowPower = 1,
499}
500
Balint Dobszaya5846852025-02-26 15:38:53 +0100501/// Arguments for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}` interfaces.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100502#[derive(Debug, Eq, PartialEq, Clone, Copy)]
503pub enum DirectMsgArgs {
504 Args32([u32; 5]),
505 Args64([u64; 5]),
Tomás González4d5b0ba2025-03-03 17:15:55 +0000506 /// Message for forwarding FFA_VERSION call from Normal world to the SPMC
507 VersionReq {
508 version: Version,
509 },
510 /// Response message to forwarded FFA_VERSION call from the Normal world
511 /// Contains the version returned by the SPMC or None
512 VersionResp {
513 version: Option<Version>,
514 },
515 /// Message for a power management operation initiated by a PSCI function
516 PowerPsciReq32 {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000517 // params[i]: Input parameter in w[i] in PSCI function invocation at EL3.
Tomás González67f92c72025-03-20 16:50:42 +0000518 // params[0]: Function ID.
519 params: [u32; 4],
Tomás González4d5b0ba2025-03-03 17:15:55 +0000520 },
521 /// Message for a power management operation initiated by a PSCI function
522 PowerPsciReq64 {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000523 // params[i]: Input parameter in x[i] in PSCI function invocation at EL3.
Tomás González67f92c72025-03-20 16:50:42 +0000524 // params[0]: Function ID.
525 params: [u64; 4],
Tomás González4d5b0ba2025-03-03 17:15:55 +0000526 },
527 /// Message for a warm boot
528 PowerWarmBootReq {
529 boot_type: WarmBootType,
530 },
531 /// Response message to indicate return status of the last power management request message
532 /// Return error code SUCCESS or DENIED as defined in PSCI spec. Caller is left to do the
533 /// parsing of the return status.
534 PowerPsciResp {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000535 psci_status: i32,
536 },
537 /// Message to signal creation of a VM
538 VmCreated {
539 // Globally unique Handle to identify a memory region that contains IMPLEMENTATION DEFINED
540 // information associated with the created VM.
541 // The invalid memory region handle must be specified by the Hypervisor if this field is not
542 // used.
543 handle: memory_management::Handle,
544 vm_id: u16,
545 },
546 /// Message to acknowledge creation of a VM
547 VmCreatedAck {
548 sp_status: VmAvailabilityStatus,
549 },
550 /// Message to signal destruction of a VM
551 VmDestructed {
552 // Globally unique Handle to identify a memory region that contains IMPLEMENTATION DEFINED
553 // information associated with the created VM.
554 // The invalid memory region handle must be specified by the Hypervisor if this field is not
555 // used.
556 handle: memory_management::Handle,
557 vm_id: u16,
558 },
559 /// Message to acknowledge destruction of a VM
560 VmDestructedAck {
561 sp_status: VmAvailabilityStatus,
562 },
563}
564
565impl DirectMsgArgs {
566 // Flags for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}` interfaces.
567
568 const FWK_MSG_BITS: u32 = 1 << 31;
569 const VERSION_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b1000;
570 const VERSION_RESP: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b1001;
571 const POWER_PSCI_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS;
572 const POWER_WARM_BOOT_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0001;
573 const POWER_PSCI_RESP: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0010;
574 const VM_CREATED: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0100;
575 const VM_CREATED_ACK: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0101;
576 const VM_DESTRUCTED: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0110;
577 const VM_DESTRUCTED_ACK: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0111;
Balint Dobszay3aad9572025-01-17 16:54:11 +0100578}
579
Balint Dobszayde0dc802025-02-28 14:16:52 +0100580/// Arguments for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}2` interfaces.
581#[derive(Debug, Eq, PartialEq, Clone, Copy)]
582pub struct DirectMsg2Args([u64; 14]);
583
Tomás González092202a2025-03-05 11:56:45 +0000584#[derive(Debug, Eq, PartialEq, Clone, Copy)]
585pub struct MsgWaitFlags {
586 retain_rx_buffer: bool,
587}
588
589impl MsgWaitFlags {
590 const RETAIN_RX_BUFFER: u32 = 0x01;
591 const MBZ_BITS: u32 = 0xfffe;
592}
593
594impl TryFrom<u32> for MsgWaitFlags {
595 type Error = Error;
596
597 fn try_from(val: u32) -> Result<Self, Self::Error> {
598 if (val & Self::MBZ_BITS) != 0 {
599 Err(Error::InvalidMsgWaitFlag(val))
600 } else {
601 Ok(MsgWaitFlags {
602 retain_rx_buffer: val & Self::RETAIN_RX_BUFFER != 0,
603 })
604 }
605 }
606}
607
608impl From<MsgWaitFlags> for u32 {
609 fn from(flags: MsgWaitFlags) -> Self {
610 let mut bits: u32 = 0;
611 if flags.retain_rx_buffer {
612 bits |= MsgWaitFlags::RETAIN_RX_BUFFER;
613 }
614 bits
615 }
616}
617
Balint Dobszaya5846852025-02-26 15:38:53 +0100618/// Descriptor for a dynamically allocated memory buffer that contains the memory transaction
Tomás Gonzálezf268e322025-03-05 11:18:11 +0000619/// descriptor.
620///
621/// Used by `FFA_MEM_{DONATE,LEND,SHARE,RETRIEVE_REQ}` interfaces, only when the TX buffer is not
622/// used to transmit the transaction descriptor.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100623#[derive(Debug, Eq, PartialEq, Clone, Copy)]
624pub enum MemOpBuf {
625 Buf32 { addr: u32, page_cnt: u32 },
626 Buf64 { addr: u64, page_cnt: u32 },
627}
628
Balint Dobszaya5846852025-02-26 15:38:53 +0100629/// Memory address argument for `FFA_MEM_PERM_{GET,SET}` interfaces.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100630#[derive(Debug, Eq, PartialEq, Clone, Copy)]
631pub enum MemAddr {
632 Addr32(u32),
633 Addr64(u64),
634}
635
Balint Dobszayde0dc802025-02-28 14:16:52 +0100636/// Argument for the `FFA_CONSOLE_LOG` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100637#[derive(Debug, Eq, PartialEq, Clone, Copy)]
638pub enum ConsoleLogChars {
639 Reg32([u32; 6]),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100640 Reg64([u64; 16]),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100641}
642
Tomás González7ffb6132025-04-03 12:28:58 +0100643#[derive(Debug, Eq, PartialEq, Clone, Copy)]
644pub struct NotificationBindFlags {
645 per_vcpu_notification: bool,
646}
647
648impl NotificationBindFlags {
649 const PER_VCPU_NOTIFICATION: u32 = 1;
650}
651
652impl From<NotificationBindFlags> for u32 {
653 fn from(flags: NotificationBindFlags) -> Self {
654 let mut bits: u32 = 0;
655 if flags.per_vcpu_notification {
656 bits |= NotificationBindFlags::PER_VCPU_NOTIFICATION;
657 }
658 bits
659 }
660}
661
662impl From<u32> for NotificationBindFlags {
663 fn from(flags: u32) -> Self {
664 Self {
665 per_vcpu_notification: flags & Self::PER_VCPU_NOTIFICATION != 0,
666 }
667 }
668}
669
670#[derive(Debug, Eq, PartialEq, Clone, Copy)]
671pub struct NotificationSetFlags {
672 delay_schedule_receiver: bool,
673 vcpu_id: Option<u16>,
674}
675
676impl NotificationSetFlags {
677 const PER_VCP_NOTIFICATION: u32 = 1 << 0;
678 const DELAY_SCHEDULE_RECEIVER: u32 = 1 << 1;
679 const VCPU_ID_SHIFT: u32 = 16;
680
681 const MBZ_BITS: u32 = 0xfffc;
682}
683
684impl From<NotificationSetFlags> for u32 {
685 fn from(flags: NotificationSetFlags) -> Self {
686 let mut bits: u32 = 0;
687
688 if flags.delay_schedule_receiver {
689 bits |= NotificationSetFlags::DELAY_SCHEDULE_RECEIVER;
690 }
691 if let Some(vcpu_id) = flags.vcpu_id {
692 bits |= NotificationSetFlags::PER_VCP_NOTIFICATION;
693 bits |= u32::from(vcpu_id) << NotificationSetFlags::VCPU_ID_SHIFT;
694 }
695
696 bits
697 }
698}
699
700impl TryFrom<u32> for NotificationSetFlags {
701 type Error = Error;
702
703 fn try_from(flags: u32) -> Result<Self, Self::Error> {
704 if (flags & Self::MBZ_BITS) != 0 {
705 return Err(Error::InvalidNotificationSetFlag(flags));
706 }
707
708 let tentative_vcpu_id = (flags >> Self::VCPU_ID_SHIFT) as u16;
709
710 let vcpu_id = if (flags & Self::PER_VCP_NOTIFICATION) != 0 {
711 Some(tentative_vcpu_id)
712 } else {
713 if tentative_vcpu_id != 0 {
714 return Err(Error::InvalidNotificationSetFlag(flags));
715 }
716 None
717 };
718
719 Ok(Self {
720 delay_schedule_receiver: (flags & Self::DELAY_SCHEDULE_RECEIVER) != 0,
721 vcpu_id,
722 })
723 }
724}
725
726#[derive(Debug, Eq, PartialEq, Clone, Copy)]
727pub struct NotificationGetFlags {
728 sp_bitmap_id: bool,
729 vm_bitmap_id: bool,
730 spm_bitmap_id: bool,
731 hyp_bitmap_id: bool,
732}
733
734impl NotificationGetFlags {
735 const SP_BITMAP_ID: u32 = 1;
736 const VM_BITMAP_ID: u32 = 1 << 1;
737 const SPM_BITMAP_ID: u32 = 1 << 2;
738 const HYP_BITMAP_ID: u32 = 1 << 3;
739}
740
741impl From<NotificationGetFlags> for u32 {
742 fn from(flags: NotificationGetFlags) -> Self {
743 let mut bits: u32 = 0;
744 if flags.sp_bitmap_id {
745 bits |= NotificationGetFlags::SP_BITMAP_ID;
746 }
747 if flags.vm_bitmap_id {
748 bits |= NotificationGetFlags::VM_BITMAP_ID;
749 }
750 if flags.spm_bitmap_id {
751 bits |= NotificationGetFlags::SPM_BITMAP_ID;
752 }
753 if flags.hyp_bitmap_id {
754 bits |= NotificationGetFlags::HYP_BITMAP_ID;
755 }
756 bits
757 }
758}
759
760impl From<u32> for NotificationGetFlags {
761 // This is a "from" instead of a "try_from" because Reserved Bits are SBZ, *not* MBZ.
762 fn from(flags: u32) -> Self {
763 Self {
764 sp_bitmap_id: (flags & Self::SP_BITMAP_ID) != 0,
765 vm_bitmap_id: (flags & Self::VM_BITMAP_ID) != 0,
766 spm_bitmap_id: (flags & Self::SPM_BITMAP_ID) != 0,
767 hyp_bitmap_id: (flags & Self::HYP_BITMAP_ID) != 0,
768 }
769 }
770}
771
Imre Kis9959e062025-04-10 14:16:10 +0200772/// `FFA_NOTIFICATION_GET` specific success argument structure.
773#[derive(Debug, Eq, PartialEq, Clone, Copy)]
774pub struct SuccessArgsNotificationGet {
775 pub sp_notifications: Option<u64>,
776 pub vm_notifications: Option<u64>,
777 pub spm_notifications: Option<u32>,
778 pub hypervisor_notifications: Option<u32>,
779}
780
781impl From<SuccessArgsNotificationGet> for SuccessArgs {
782 fn from(value: SuccessArgsNotificationGet) -> Self {
783 let mut args = [0; 6];
784
785 if let Some(bitmap) = value.sp_notifications {
786 args[0] = bitmap as u32;
787 args[1] = (bitmap >> 32) as u32;
788 }
789
790 if let Some(bitmap) = value.vm_notifications {
791 args[2] = bitmap as u32;
792 args[3] = (bitmap >> 32) as u32;
793 }
794
795 if let Some(bitmap) = value.spm_notifications {
796 args[4] = bitmap;
797 }
798
799 if let Some(bitmap) = value.hypervisor_notifications {
800 args[5] = bitmap;
801 }
802
803 Self::Args32(args)
804 }
805}
806
807impl TryFrom<(NotificationGetFlags, SuccessArgs)> for SuccessArgsNotificationGet {
808 type Error = Error;
809
810 fn try_from(value: (NotificationGetFlags, SuccessArgs)) -> Result<Self, Self::Error> {
811 let (flags, value) = value;
812 let args = value.try_get_args32()?;
813
814 let sp_notifications = if flags.sp_bitmap_id {
815 Some(u64::from(args[0]) | (u64::from(args[1]) << 32))
816 } else {
817 None
818 };
819
820 let vm_notifications = if flags.vm_bitmap_id {
821 Some(u64::from(args[2]) | (u64::from(args[3]) << 32))
822 } else {
823 None
824 };
825
826 let spm_notifications = if flags.spm_bitmap_id {
827 Some(args[4])
828 } else {
829 None
830 };
831
832 let hypervisor_notifications = if flags.hyp_bitmap_id {
833 Some(args[5])
834 } else {
835 None
836 };
837
838 Ok(Self {
839 sp_notifications,
840 vm_notifications,
841 spm_notifications,
842 hypervisor_notifications,
843 })
844 }
845}
Imre Kis787c5002025-04-10 14:25:51 +0200846
847/// `FFA_NOTIFICATION_INFO_GET` specific success argument structure. The `MAX_COUNT` parameter
848/// depends on the 32-bit or 64-bit packing.
849#[derive(Debug, Eq, PartialEq, Clone, Copy)]
850pub struct SuccessArgsNotificationInfoGet<const MAX_COUNT: usize> {
851 pub more_pending_notifications: bool,
852 list_count: usize,
853 id_counts: [u8; MAX_COUNT],
854 ids: [u16; MAX_COUNT],
855}
856
857impl<const MAX_COUNT: usize> Default for SuccessArgsNotificationInfoGet<MAX_COUNT> {
858 fn default() -> Self {
859 Self {
860 more_pending_notifications: false,
861 list_count: 0,
862 id_counts: [0; MAX_COUNT],
863 ids: [0; MAX_COUNT],
864 }
865 }
866}
867
868impl<const MAX_COUNT: usize> SuccessArgsNotificationInfoGet<MAX_COUNT> {
869 const MORE_PENDING_NOTIFICATIONS_FLAG: u64 = 1 << 0;
870 const LIST_COUNT_SHIFT: usize = 7;
871 const LIST_COUNT_MASK: u64 = 0x1f;
872 const ID_COUNT_SHIFT: usize = 12;
873 const ID_COUNT_MASK: u64 = 0x03;
874 const ID_COUNT_BITS: usize = 2;
875
876 pub fn add_list(&mut self, endpoint: u16, vcpu_ids: &[u16]) -> Result<(), Error> {
877 if self.list_count >= MAX_COUNT || vcpu_ids.len() > Self::ID_COUNT_MASK as usize {
878 return Err(Error::InvalidNotificationCount);
879 }
880
881 // Each list contains at least one ID: the partition ID, followed by vCPU IDs. The number
882 // of vCPU IDs is recorded in `id_counts`.
883 let mut current_id_index = self.list_count + self.id_counts.iter().sum::<u8>() as usize;
884 if current_id_index + 1 + vcpu_ids.len() > MAX_COUNT {
885 // The new list does not fit into the available space for IDs.
886 return Err(Error::InvalidNotificationCount);
887 }
888
889 self.id_counts[self.list_count] = vcpu_ids.len() as u8;
890 self.list_count += 1;
891
892 // The first ID is the endpoint ID.
893 self.ids[current_id_index] = endpoint;
894 current_id_index += 1;
895
896 // Insert the vCPU IDs.
897 self.ids[current_id_index..current_id_index + vcpu_ids.len()].copy_from_slice(vcpu_ids);
898
899 Ok(())
900 }
901
902 pub fn iter(&self) -> NotificationInfoGetIterator<'_> {
903 NotificationInfoGetIterator {
904 list_index: 0,
905 id_index: 0,
906 id_count: &self.id_counts[0..self.list_count],
907 ids: &self.ids,
908 }
909 }
910
911 /// Pack flags field and IDs.
912 fn pack(self) -> (u64, [u16; MAX_COUNT]) {
913 let mut flags = if self.more_pending_notifications {
914 Self::MORE_PENDING_NOTIFICATIONS_FLAG
915 } else {
916 0
917 };
918
919 flags |= (self.list_count as u64) << Self::LIST_COUNT_SHIFT;
920 for (count, shift) in self.id_counts.iter().take(self.list_count).zip(
921 (Self::ID_COUNT_SHIFT..Self::ID_COUNT_SHIFT + Self::ID_COUNT_BITS * MAX_COUNT)
922 .step_by(Self::ID_COUNT_BITS),
923 ) {
924 flags |= u64::from(*count) << shift;
925 }
926
927 (flags, self.ids)
928 }
929
930 /// Unpack flags field and IDs.
931 fn unpack(flags: u64, ids: [u16; MAX_COUNT]) -> Result<Self, Error> {
932 let count_of_lists = ((flags >> Self::LIST_COUNT_SHIFT) & Self::LIST_COUNT_MASK) as usize;
933
934 if count_of_lists > MAX_COUNT {
935 return Err(Error::InvalidNotificationCount);
936 }
937
938 let mut count_of_ids = [0; MAX_COUNT];
939 let mut count_of_ids_bits = flags >> Self::ID_COUNT_SHIFT;
940
941 for id in count_of_ids.iter_mut().take(count_of_lists) {
942 *id = (count_of_ids_bits & Self::ID_COUNT_MASK) as u8;
943 count_of_ids_bits >>= Self::ID_COUNT_BITS;
944 }
945
Imre Kis7846c9f2025-04-15 09:45:00 +0200946 let id_field_count = count_of_lists + count_of_ids.iter().sum::<u8>() as usize;
947 if id_field_count > MAX_COUNT {
948 return Err(Error::InvalidNotificationCount);
949 }
950
Imre Kis787c5002025-04-10 14:25:51 +0200951 Ok(Self {
952 more_pending_notifications: (flags & Self::MORE_PENDING_NOTIFICATIONS_FLAG) != 0,
953 list_count: count_of_lists,
954 id_counts: count_of_ids,
955 ids,
956 })
957 }
958}
959
960/// `FFA_NOTIFICATION_INFO_GET_32` specific success argument structure.
961pub type SuccessArgsNotificationInfoGet32 = SuccessArgsNotificationInfoGet<10>;
962
963impl From<SuccessArgsNotificationInfoGet32> for SuccessArgs {
964 fn from(value: SuccessArgsNotificationInfoGet32) -> Self {
965 let (flags, ids) = value.pack();
966 let id_regs: [u32; 5] = transmute!(ids);
967
968 let mut args = [0; 6];
969 args[0] = flags as u32;
970 args[1..6].copy_from_slice(&id_regs);
971
972 SuccessArgs::Args32(args)
973 }
974}
975
976impl TryFrom<SuccessArgs> for SuccessArgsNotificationInfoGet32 {
977 type Error = Error;
978
979 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
980 let args = value.try_get_args32()?;
981 let flags = args[0].into();
982 let id_regs: [u32; 5] = args[1..6].try_into().unwrap();
983 Self::unpack(flags, transmute!(id_regs))
984 }
985}
986
987/// `FFA_NOTIFICATION_INFO_GET_64` specific success argument structure.
988pub type SuccessArgsNotificationInfoGet64 = SuccessArgsNotificationInfoGet<20>;
989
990impl From<SuccessArgsNotificationInfoGet64> for SuccessArgs {
991 fn from(value: SuccessArgsNotificationInfoGet64) -> Self {
992 let (flags, ids) = value.pack();
993 let id_regs: [u64; 5] = transmute!(ids);
994
995 let mut args = [0; 6];
996 args[0] = flags;
997 args[1..6].copy_from_slice(&id_regs);
998
999 SuccessArgs::Args64(args)
1000 }
1001}
1002
1003impl TryFrom<SuccessArgs> for SuccessArgsNotificationInfoGet64 {
1004 type Error = Error;
1005
1006 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
1007 let args = value.try_get_args64()?;
1008 let flags = args[0];
1009 let id_regs: [u64; 5] = args[1..6].try_into().unwrap();
1010 Self::unpack(flags, transmute!(id_regs))
1011 }
1012}
1013
1014pub struct NotificationInfoGetIterator<'a> {
1015 list_index: usize,
1016 id_index: usize,
1017 id_count: &'a [u8],
1018 ids: &'a [u16],
1019}
1020
1021impl<'a> Iterator for NotificationInfoGetIterator<'a> {
1022 type Item = (u16, &'a [u16]);
1023
1024 fn next(&mut self) -> Option<Self::Item> {
1025 if self.list_index < self.id_count.len() {
1026 let partition_id = self.ids[self.id_index];
1027 let id_range =
1028 (self.id_index + 1)..=(self.id_index + self.id_count[self.list_index] as usize);
1029
1030 self.id_index += 1 + self.id_count[self.list_index] as usize;
1031 self.list_index += 1;
1032
1033 Some((partition_id, &self.ids[id_range]))
1034 } else {
1035 None
1036 }
1037 }
1038}
1039
Tomás Gonzálezf268e322025-03-05 11:18:11 +00001040/// FF-A "message types", the terminology used by the spec is "interfaces".
1041///
1042/// The interfaces are used by FF-A components for communication at an FF-A instance. The spec also
1043/// describes the valid FF-A instances and conduits for each interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +01001044#[derive(Debug, Eq, PartialEq, Clone, Copy)]
1045pub enum Interface {
1046 Error {
1047 target_info: TargetInfo,
1048 error_code: FfaError,
Balint Dobszayb727aab2025-04-07 10:24:59 +02001049 error_arg: u32,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001050 },
1051 Success {
1052 target_info: u32,
1053 args: SuccessArgs,
1054 },
1055 Interrupt {
1056 target_info: TargetInfo,
1057 interrupt_id: u32,
1058 },
1059 Version {
1060 input_version: Version,
1061 },
1062 VersionOut {
1063 output_version: Version,
1064 },
1065 Features {
1066 feat_id: Feature,
1067 input_properties: u32,
1068 },
1069 RxAcquire {
1070 vm_id: u16,
1071 },
1072 RxRelease {
1073 vm_id: u16,
1074 },
1075 RxTxMap {
1076 addr: RxTxAddr,
1077 page_cnt: u32,
1078 },
1079 RxTxUnmap {
1080 id: u16,
1081 },
1082 PartitionInfoGet {
1083 uuid: Uuid,
Imre Kise295adb2025-04-10 13:26:28 +02001084 flags: PartitionInfoGetFlags,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001085 },
Tomás González0a058bc2025-03-11 11:20:55 +00001086 PartitionInfoGetRegs {
1087 uuid: Uuid,
1088 start_index: u16,
1089 info_tag: u16,
1090 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001091 IdGet,
1092 SpmIdGet,
Tomás González092202a2025-03-05 11:56:45 +00001093 MsgWait {
1094 flags: Option<MsgWaitFlags>,
1095 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001096 Yield,
1097 Run {
1098 target_info: TargetInfo,
1099 },
1100 NormalWorldResume,
Tomás González17b92442025-03-10 16:45:04 +00001101 SecondaryEpRegister {
1102 entrypoint: SecondaryEpRegisterAddr,
1103 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001104 MsgSend2 {
1105 sender_vm_id: u16,
1106 flags: u32,
1107 },
1108 MsgSendDirectReq {
1109 src_id: u16,
1110 dst_id: u16,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001111 args: DirectMsgArgs,
1112 },
1113 MsgSendDirectResp {
1114 src_id: u16,
1115 dst_id: u16,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001116 args: DirectMsgArgs,
1117 },
Balint Dobszayde0dc802025-02-28 14:16:52 +01001118 MsgSendDirectReq2 {
1119 src_id: u16,
1120 dst_id: u16,
1121 uuid: Uuid,
1122 args: DirectMsg2Args,
1123 },
1124 MsgSendDirectResp2 {
1125 src_id: u16,
1126 dst_id: u16,
1127 args: DirectMsg2Args,
1128 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001129 MemDonate {
1130 total_len: u32,
1131 frag_len: u32,
1132 buf: Option<MemOpBuf>,
1133 },
1134 MemLend {
1135 total_len: u32,
1136 frag_len: u32,
1137 buf: Option<MemOpBuf>,
1138 },
1139 MemShare {
1140 total_len: u32,
1141 frag_len: u32,
1142 buf: Option<MemOpBuf>,
1143 },
1144 MemRetrieveReq {
1145 total_len: u32,
1146 frag_len: u32,
1147 buf: Option<MemOpBuf>,
1148 },
1149 MemRetrieveResp {
1150 total_len: u32,
1151 frag_len: u32,
1152 },
1153 MemRelinquish,
1154 MemReclaim {
1155 handle: memory_management::Handle,
1156 flags: u32,
1157 },
1158 MemPermGet {
1159 addr: MemAddr,
Balint Dobszayde0dc802025-02-28 14:16:52 +01001160 page_cnt: Option<u32>,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001161 },
1162 MemPermSet {
1163 addr: MemAddr,
1164 page_cnt: u32,
1165 mem_perm: u32,
1166 },
1167 ConsoleLog {
1168 char_cnt: u8,
1169 char_lists: ConsoleLogChars,
1170 },
Tomás González7ffb6132025-04-03 12:28:58 +01001171 NotificationBitmapCreate {
1172 vm_id: u16,
1173 vcpu_cnt: u32,
1174 },
1175 NotificationBitmapDestroy {
1176 vm_id: u16,
1177 },
1178 NotificationBind {
1179 sender_id: u16,
1180 receiver_id: u16,
1181 flags: NotificationBindFlags,
1182 bitmap: u64,
1183 },
1184 NotificationUnBind {
1185 sender_id: u16,
1186 receiver_id: u16,
1187 bitmap: u64,
1188 },
1189 NotificationSet {
1190 sender_id: u16,
1191 receiver_id: u16,
1192 flags: NotificationSetFlags,
1193 bitmap: u64,
1194 },
1195 NotificationGet {
1196 vcpu_id: u16,
1197 endpoint_id: u16,
1198 flags: NotificationGetFlags,
1199 },
1200 NotificationInfoGet {
1201 is_32bit: bool,
1202 },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01001203 El3IntrHandle,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001204}
1205
Balint Dobszayde0dc802025-02-28 14:16:52 +01001206impl Interface {
1207 /// Returns the function ID for the call, if it has one.
1208 pub fn function_id(&self) -> Option<FuncId> {
1209 match self {
1210 Interface::Error { .. } => Some(FuncId::Error),
1211 Interface::Success { args, .. } => match args {
Imre Kis54773b62025-04-10 13:47:39 +02001212 SuccessArgs::Args32(..) => Some(FuncId::Success32),
1213 SuccessArgs::Args64(..) | SuccessArgs::Args64_2(..) => Some(FuncId::Success64),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001214 },
1215 Interface::Interrupt { .. } => Some(FuncId::Interrupt),
1216 Interface::Version { .. } => Some(FuncId::Version),
1217 Interface::VersionOut { .. } => None,
1218 Interface::Features { .. } => Some(FuncId::Features),
1219 Interface::RxAcquire { .. } => Some(FuncId::RxAcquire),
1220 Interface::RxRelease { .. } => Some(FuncId::RxRelease),
1221 Interface::RxTxMap { addr, .. } => match addr {
1222 RxTxAddr::Addr32 { .. } => Some(FuncId::RxTxMap32),
1223 RxTxAddr::Addr64 { .. } => Some(FuncId::RxTxMap64),
1224 },
1225 Interface::RxTxUnmap { .. } => Some(FuncId::RxTxUnmap),
1226 Interface::PartitionInfoGet { .. } => Some(FuncId::PartitionInfoGet),
Tomás González0a058bc2025-03-11 11:20:55 +00001227 Interface::PartitionInfoGetRegs { .. } => Some(FuncId::PartitionInfoGetRegs),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001228 Interface::IdGet => Some(FuncId::IdGet),
1229 Interface::SpmIdGet => Some(FuncId::SpmIdGet),
Tomás González092202a2025-03-05 11:56:45 +00001230 Interface::MsgWait { .. } => Some(FuncId::MsgWait),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001231 Interface::Yield => Some(FuncId::Yield),
1232 Interface::Run { .. } => Some(FuncId::Run),
1233 Interface::NormalWorldResume => Some(FuncId::NormalWorldResume),
Tomás González17b92442025-03-10 16:45:04 +00001234 Interface::SecondaryEpRegister { entrypoint } => match entrypoint {
1235 SecondaryEpRegisterAddr::Addr32 { .. } => Some(FuncId::SecondaryEpRegister32),
1236 SecondaryEpRegisterAddr::Addr64 { .. } => Some(FuncId::SecondaryEpRegister64),
1237 },
Balint Dobszayde0dc802025-02-28 14:16:52 +01001238 Interface::MsgSend2 { .. } => Some(FuncId::MsgSend2),
1239 Interface::MsgSendDirectReq { args, .. } => match args {
1240 DirectMsgArgs::Args32(_) => Some(FuncId::MsgSendDirectReq32),
1241 DirectMsgArgs::Args64(_) => Some(FuncId::MsgSendDirectReq64),
Tomás González4d5b0ba2025-03-03 17:15:55 +00001242 DirectMsgArgs::VersionReq { .. } => Some(FuncId::MsgSendDirectReq32),
1243 DirectMsgArgs::PowerPsciReq32 { .. } => Some(FuncId::MsgSendDirectReq32),
1244 DirectMsgArgs::PowerPsciReq64 { .. } => Some(FuncId::MsgSendDirectReq64),
1245 DirectMsgArgs::PowerWarmBootReq { .. } => Some(FuncId::MsgSendDirectReq32),
1246 DirectMsgArgs::VmCreated { .. } => Some(FuncId::MsgSendDirectReq32),
1247 DirectMsgArgs::VmDestructed { .. } => Some(FuncId::MsgSendDirectReq32),
1248 _ => None,
Balint Dobszayde0dc802025-02-28 14:16:52 +01001249 },
1250 Interface::MsgSendDirectResp { args, .. } => match args {
1251 DirectMsgArgs::Args32(_) => Some(FuncId::MsgSendDirectResp32),
1252 DirectMsgArgs::Args64(_) => Some(FuncId::MsgSendDirectResp64),
Tomás González4d5b0ba2025-03-03 17:15:55 +00001253 DirectMsgArgs::VersionResp { .. } => Some(FuncId::MsgSendDirectResp32),
1254 DirectMsgArgs::PowerPsciResp { .. } => Some(FuncId::MsgSendDirectResp32),
1255 DirectMsgArgs::VmCreatedAck { .. } => Some(FuncId::MsgSendDirectResp32),
1256 DirectMsgArgs::VmDestructedAck { .. } => Some(FuncId::MsgSendDirectResp32),
1257 _ => None,
Balint Dobszayde0dc802025-02-28 14:16:52 +01001258 },
1259 Interface::MsgSendDirectReq2 { .. } => Some(FuncId::MsgSendDirectReq64_2),
1260 Interface::MsgSendDirectResp2 { .. } => Some(FuncId::MsgSendDirectResp64_2),
1261 Interface::MemDonate { buf, .. } => match buf {
1262 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemDonate64),
1263 _ => Some(FuncId::MemDonate32),
1264 },
1265 Interface::MemLend { buf, .. } => match buf {
1266 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemLend64),
1267 _ => Some(FuncId::MemLend32),
1268 },
1269 Interface::MemShare { buf, .. } => match buf {
1270 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemShare64),
1271 _ => Some(FuncId::MemShare32),
1272 },
1273 Interface::MemRetrieveReq { buf, .. } => match buf {
1274 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemRetrieveReq64),
1275 _ => Some(FuncId::MemRetrieveReq32),
1276 },
1277 Interface::MemRetrieveResp { .. } => Some(FuncId::MemRetrieveResp),
1278 Interface::MemRelinquish => Some(FuncId::MemRelinquish),
1279 Interface::MemReclaim { .. } => Some(FuncId::MemReclaim),
1280 Interface::MemPermGet { addr, .. } => match addr {
1281 MemAddr::Addr32(_) => Some(FuncId::MemPermGet32),
1282 MemAddr::Addr64(_) => Some(FuncId::MemPermGet64),
1283 },
1284 Interface::MemPermSet { addr, .. } => match addr {
1285 MemAddr::Addr32(_) => Some(FuncId::MemPermSet32),
1286 MemAddr::Addr64(_) => Some(FuncId::MemPermSet64),
1287 },
1288 Interface::ConsoleLog { char_lists, .. } => match char_lists {
1289 ConsoleLogChars::Reg32(_) => Some(FuncId::ConsoleLog32),
1290 ConsoleLogChars::Reg64(_) => Some(FuncId::ConsoleLog64),
1291 },
Tomás González7ffb6132025-04-03 12:28:58 +01001292 Interface::NotificationBitmapCreate { .. } => Some(FuncId::NotificationBitmapCreate),
1293 Interface::NotificationBitmapDestroy { .. } => Some(FuncId::NotificationBitmapDestroy),
1294 Interface::NotificationBind { .. } => Some(FuncId::NotificationBind),
1295 Interface::NotificationUnBind { .. } => Some(FuncId::NotificationUnbind),
1296 Interface::NotificationSet { .. } => Some(FuncId::NotificationSet),
1297 Interface::NotificationGet { .. } => Some(FuncId::NotificationGet),
1298 Interface::NotificationInfoGet { is_32bit } => match is_32bit {
1299 true => Some(FuncId::NotificationInfoGet32),
1300 false => Some(FuncId::NotificationInfoGet64),
1301 },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01001302 Interface::El3IntrHandle => Some(FuncId::El3IntrHandle),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001303 }
1304 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01001305
Balint Dobszayde0dc802025-02-28 14:16:52 +01001306 /// Returns true if this is a 32-bit call, or false if it is a 64-bit call.
1307 pub fn is_32bit(&self) -> bool {
1308 // TODO: self should always have a function ID?
1309 self.function_id().unwrap().is_32bit()
1310 }
1311
1312 /// Parse interface from register contents. The caller must ensure that the `regs` argument has
1313 /// the correct length: 8 registers for FF-A v1.1 and lower, 18 registers for v1.2 and higher.
1314 pub fn from_regs(version: Version, regs: &[u64]) -> Result<Self, Error> {
1315 let reg_cnt = regs.len();
1316
1317 let msg = match reg_cnt {
1318 8 => {
1319 assert!(version <= Version(1, 1));
1320 Interface::unpack_regs8(version, regs.try_into().unwrap())?
1321 }
1322 18 => {
1323 assert!(version >= Version(1, 2));
1324 match FuncId::try_from(regs[0] as u32)? {
1325 FuncId::ConsoleLog64
1326 | FuncId::Success64
1327 | FuncId::MsgSendDirectReq64_2
Tomás González0a058bc2025-03-11 11:20:55 +00001328 | FuncId::MsgSendDirectResp64_2
1329 | FuncId::PartitionInfoGetRegs => {
Balint Dobszayde0dc802025-02-28 14:16:52 +01001330 Interface::unpack_regs18(version, regs.try_into().unwrap())?
1331 }
1332 _ => Interface::unpack_regs8(version, regs[..8].try_into().unwrap())?,
1333 }
1334 }
1335 _ => panic!(
1336 "Invalid number of registers ({}) for FF-A version {}",
1337 reg_cnt, version
1338 ),
1339 };
1340
1341 Ok(msg)
1342 }
1343
1344 fn unpack_regs8(version: Version, regs: &[u64; 8]) -> Result<Self, Error> {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001345 let fid = FuncId::try_from(regs[0] as u32)?;
1346
1347 let msg = match fid {
1348 FuncId::Error => Self::Error {
1349 target_info: (regs[1] as u32).into(),
1350 error_code: FfaError::try_from(regs[2] as i32)?,
Balint Dobszayb727aab2025-04-07 10:24:59 +02001351 error_arg: regs[3] as u32,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001352 },
1353 FuncId::Success32 => Self::Success {
1354 target_info: regs[1] as u32,
Imre Kis54773b62025-04-10 13:47:39 +02001355 args: SuccessArgs::Args32([
Balint Dobszay3aad9572025-01-17 16:54:11 +01001356 regs[2] as u32,
1357 regs[3] as u32,
1358 regs[4] as u32,
1359 regs[5] as u32,
1360 regs[6] as u32,
1361 regs[7] as u32,
1362 ]),
1363 },
1364 FuncId::Success64 => Self::Success {
1365 target_info: regs[1] as u32,
Imre Kis54773b62025-04-10 13:47:39 +02001366 args: SuccessArgs::Args64([regs[2], regs[3], regs[4], regs[5], regs[6], regs[7]]),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001367 },
1368 FuncId::Interrupt => Self::Interrupt {
1369 target_info: (regs[1] as u32).into(),
1370 interrupt_id: regs[2] as u32,
1371 },
1372 FuncId::Version => Self::Version {
Tomás González83146af2025-03-04 11:32:41 +00001373 input_version: (regs[1] as u32).try_into()?,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001374 },
1375 FuncId::Features => Self::Features {
Balint Dobszayc31e0b92025-03-03 20:16:56 +01001376 feat_id: (regs[1] as u32).into(),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001377 input_properties: regs[2] as u32,
1378 },
1379 FuncId::RxAcquire => Self::RxAcquire {
1380 vm_id: regs[1] as u16,
1381 },
1382 FuncId::RxRelease => Self::RxRelease {
1383 vm_id: regs[1] as u16,
1384 },
1385 FuncId::RxTxMap32 => {
1386 let addr = RxTxAddr::Addr32 {
1387 rx: regs[2] as u32,
1388 tx: regs[1] as u32,
1389 };
1390 let page_cnt = regs[3] as u32;
1391
1392 Self::RxTxMap { addr, page_cnt }
1393 }
1394 FuncId::RxTxMap64 => {
1395 let addr = RxTxAddr::Addr64 {
1396 rx: regs[2],
1397 tx: regs[1],
1398 };
1399 let page_cnt = regs[3] as u32;
1400
1401 Self::RxTxMap { addr, page_cnt }
1402 }
1403 FuncId::RxTxUnmap => Self::RxTxUnmap { id: regs[1] as u16 },
1404 FuncId::PartitionInfoGet => {
1405 let uuid_words = [
1406 regs[1] as u32,
1407 regs[2] as u32,
1408 regs[3] as u32,
1409 regs[4] as u32,
1410 ];
1411 let mut bytes: [u8; 16] = [0; 16];
1412 for (i, b) in uuid_words.iter().flat_map(|w| w.to_le_bytes()).enumerate() {
1413 bytes[i] = b;
1414 }
1415 Self::PartitionInfoGet {
1416 uuid: Uuid::from_bytes(bytes),
Imre Kise295adb2025-04-10 13:26:28 +02001417 flags: PartitionInfoGetFlags::try_from(regs[5] as u32)?,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001418 }
1419 }
1420 FuncId::IdGet => Self::IdGet,
1421 FuncId::SpmIdGet => Self::SpmIdGet,
Tomás González092202a2025-03-05 11:56:45 +00001422 FuncId::MsgWait => Self::MsgWait {
1423 flags: if version >= Version(1, 2) {
1424 Some(MsgWaitFlags::try_from(regs[2] as u32)?)
1425 } else {
1426 None
1427 },
1428 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001429 FuncId::Yield => Self::Yield,
1430 FuncId::Run => Self::Run {
1431 target_info: (regs[1] as u32).into(),
1432 },
1433 FuncId::NormalWorldResume => Self::NormalWorldResume,
Tomás González17b92442025-03-10 16:45:04 +00001434 FuncId::SecondaryEpRegister32 => Self::SecondaryEpRegister {
1435 entrypoint: SecondaryEpRegisterAddr::Addr32(regs[1] as u32),
1436 },
1437 FuncId::SecondaryEpRegister64 => Self::SecondaryEpRegister {
1438 entrypoint: SecondaryEpRegisterAddr::Addr64(regs[1]),
1439 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001440 FuncId::MsgSend2 => Self::MsgSend2 {
1441 sender_vm_id: regs[1] as u16,
1442 flags: regs[2] as u32,
1443 },
1444 FuncId::MsgSendDirectReq32 => Self::MsgSendDirectReq {
1445 src_id: (regs[1] >> 16) as u16,
1446 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001447 args: if (regs[2] as u32 & DirectMsgArgs::FWK_MSG_BITS) != 0 {
1448 match regs[2] as u32 {
1449 DirectMsgArgs::VERSION_REQ => DirectMsgArgs::VersionReq {
1450 version: Version::try_from(regs[3] as u32)?,
1451 },
1452 DirectMsgArgs::POWER_PSCI_REQ => DirectMsgArgs::PowerPsciReq32 {
Tomás González67f92c72025-03-20 16:50:42 +00001453 params: [
1454 regs[3] as u32,
1455 regs[4] as u32,
1456 regs[5] as u32,
1457 regs[6] as u32,
1458 ],
Tomás González4d5b0ba2025-03-03 17:15:55 +00001459 },
1460 DirectMsgArgs::POWER_WARM_BOOT_REQ => DirectMsgArgs::PowerWarmBootReq {
1461 boot_type: WarmBootType::try_from(regs[3] as u32)?,
1462 },
1463 DirectMsgArgs::VM_CREATED => DirectMsgArgs::VmCreated {
1464 handle: memory_management::Handle::from([
1465 regs[3] as u32,
1466 regs[4] as u32,
1467 ]),
1468 vm_id: regs[5] as u16,
1469 },
1470 DirectMsgArgs::VM_DESTRUCTED => DirectMsgArgs::VmDestructed {
1471 handle: memory_management::Handle::from([
1472 regs[3] as u32,
1473 regs[4] as u32,
1474 ]),
1475 vm_id: regs[5] as u16,
1476 },
1477 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1478 }
1479 } else {
1480 DirectMsgArgs::Args32([
1481 regs[3] as u32,
1482 regs[4] as u32,
1483 regs[5] as u32,
1484 regs[6] as u32,
1485 regs[7] as u32,
1486 ])
1487 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001488 },
1489 FuncId::MsgSendDirectReq64 => Self::MsgSendDirectReq {
1490 src_id: (regs[1] >> 16) as u16,
1491 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001492 args: if (regs[2] & DirectMsgArgs::FWK_MSG_BITS as u64) != 0 {
1493 match regs[2] as u32 {
1494 DirectMsgArgs::POWER_PSCI_REQ => DirectMsgArgs::PowerPsciReq64 {
Tomás González67f92c72025-03-20 16:50:42 +00001495 params: [regs[3], regs[4], regs[5], regs[6]],
Tomás González4d5b0ba2025-03-03 17:15:55 +00001496 },
1497 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1498 }
1499 } else {
1500 DirectMsgArgs::Args64([regs[3], regs[4], regs[5], regs[6], regs[7]])
1501 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001502 },
1503 FuncId::MsgSendDirectResp32 => Self::MsgSendDirectResp {
1504 src_id: (regs[1] >> 16) as u16,
1505 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001506 args: if (regs[2] as u32 & DirectMsgArgs::FWK_MSG_BITS) != 0 {
1507 match regs[2] as u32 {
1508 DirectMsgArgs::VERSION_RESP => {
1509 if regs[3] as i32 == FfaError::NotSupported.into() {
1510 DirectMsgArgs::VersionResp { version: None }
1511 } else {
1512 DirectMsgArgs::VersionResp {
1513 version: Some(Version::try_from(regs[3] as u32)?),
1514 }
1515 }
1516 }
1517 DirectMsgArgs::POWER_PSCI_RESP => DirectMsgArgs::PowerPsciResp {
1518 psci_status: regs[3] as i32,
1519 },
1520 DirectMsgArgs::VM_CREATED_ACK => DirectMsgArgs::VmCreatedAck {
1521 sp_status: (regs[3] as i32).try_into()?,
1522 },
1523 DirectMsgArgs::VM_DESTRUCTED_ACK => DirectMsgArgs::VmDestructedAck {
1524 sp_status: (regs[3] as i32).try_into()?,
1525 },
1526 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1527 }
1528 } else {
1529 DirectMsgArgs::Args32([
1530 regs[3] as u32,
1531 regs[4] as u32,
1532 regs[5] as u32,
1533 regs[6] as u32,
1534 regs[7] as u32,
1535 ])
1536 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001537 },
1538 FuncId::MsgSendDirectResp64 => Self::MsgSendDirectResp {
1539 src_id: (regs[1] >> 16) as u16,
1540 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001541 args: if (regs[2] & DirectMsgArgs::FWK_MSG_BITS as u64) != 0 {
1542 return Err(Error::UnrecognisedFwkMsg(regs[2] as u32));
1543 } else {
1544 DirectMsgArgs::Args64([regs[3], regs[4], regs[5], regs[6], regs[7]])
1545 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001546 },
1547 FuncId::MemDonate32 => Self::MemDonate {
1548 total_len: regs[1] as u32,
1549 frag_len: regs[2] as u32,
1550 buf: if regs[3] != 0 && regs[4] != 0 {
1551 Some(MemOpBuf::Buf32 {
1552 addr: regs[3] as u32,
1553 page_cnt: regs[4] as u32,
1554 })
1555 } else {
1556 None
1557 },
1558 },
1559 FuncId::MemDonate64 => Self::MemDonate {
1560 total_len: regs[1] as u32,
1561 frag_len: regs[2] as u32,
1562 buf: if regs[3] != 0 && regs[4] != 0 {
1563 Some(MemOpBuf::Buf64 {
1564 addr: regs[3],
1565 page_cnt: regs[4] as u32,
1566 })
1567 } else {
1568 None
1569 },
1570 },
1571 FuncId::MemLend32 => Self::MemLend {
1572 total_len: regs[1] as u32,
1573 frag_len: regs[2] as u32,
1574 buf: if regs[3] != 0 && regs[4] != 0 {
1575 Some(MemOpBuf::Buf32 {
1576 addr: regs[3] as u32,
1577 page_cnt: regs[4] as u32,
1578 })
1579 } else {
1580 None
1581 },
1582 },
1583 FuncId::MemLend64 => Self::MemLend {
1584 total_len: regs[1] as u32,
1585 frag_len: regs[2] as u32,
1586 buf: if regs[3] != 0 && regs[4] != 0 {
1587 Some(MemOpBuf::Buf64 {
1588 addr: regs[3],
1589 page_cnt: regs[4] as u32,
1590 })
1591 } else {
1592 None
1593 },
1594 },
1595 FuncId::MemShare32 => Self::MemShare {
1596 total_len: regs[1] as u32,
1597 frag_len: regs[2] as u32,
1598 buf: if regs[3] != 0 && regs[4] != 0 {
1599 Some(MemOpBuf::Buf32 {
1600 addr: regs[3] as u32,
1601 page_cnt: regs[4] as u32,
1602 })
1603 } else {
1604 None
1605 },
1606 },
1607 FuncId::MemShare64 => Self::MemShare {
1608 total_len: regs[1] as u32,
1609 frag_len: regs[2] as u32,
1610 buf: if regs[3] != 0 && regs[4] != 0 {
1611 Some(MemOpBuf::Buf64 {
1612 addr: regs[3],
1613 page_cnt: regs[4] as u32,
1614 })
1615 } else {
1616 None
1617 },
1618 },
1619 FuncId::MemRetrieveReq32 => Self::MemRetrieveReq {
1620 total_len: regs[1] as u32,
1621 frag_len: regs[2] as u32,
1622 buf: if regs[3] != 0 && regs[4] != 0 {
1623 Some(MemOpBuf::Buf32 {
1624 addr: regs[3] as u32,
1625 page_cnt: regs[4] as u32,
1626 })
1627 } else {
1628 None
1629 },
1630 },
1631 FuncId::MemRetrieveReq64 => Self::MemRetrieveReq {
1632 total_len: regs[1] as u32,
1633 frag_len: regs[2] as u32,
1634 buf: if regs[3] != 0 && regs[4] != 0 {
1635 Some(MemOpBuf::Buf64 {
1636 addr: regs[3],
1637 page_cnt: regs[4] as u32,
1638 })
1639 } else {
1640 None
1641 },
1642 },
1643 FuncId::MemRetrieveResp => Self::MemRetrieveResp {
1644 total_len: regs[1] as u32,
1645 frag_len: regs[2] as u32,
1646 },
1647 FuncId::MemRelinquish => Self::MemRelinquish,
1648 FuncId::MemReclaim => Self::MemReclaim {
1649 handle: memory_management::Handle::from([regs[1] as u32, regs[2] as u32]),
1650 flags: regs[3] as u32,
1651 },
1652 FuncId::MemPermGet32 => Self::MemPermGet {
1653 addr: MemAddr::Addr32(regs[1] as u32),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001654 page_cnt: if version >= Version(1, 3) {
1655 Some(regs[2] as u32)
1656 } else {
1657 None
1658 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001659 },
1660 FuncId::MemPermGet64 => Self::MemPermGet {
1661 addr: MemAddr::Addr64(regs[1]),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001662 page_cnt: if version >= Version(1, 3) {
1663 Some(regs[2] as u32)
1664 } else {
1665 None
1666 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001667 },
1668 FuncId::MemPermSet32 => Self::MemPermSet {
1669 addr: MemAddr::Addr32(regs[1] as u32),
1670 page_cnt: regs[2] as u32,
1671 mem_perm: regs[3] as u32,
1672 },
1673 FuncId::MemPermSet64 => Self::MemPermSet {
1674 addr: MemAddr::Addr64(regs[1]),
1675 page_cnt: regs[2] as u32,
1676 mem_perm: regs[3] as u32,
1677 },
1678 FuncId::ConsoleLog32 => Self::ConsoleLog {
1679 char_cnt: regs[1] as u8,
1680 char_lists: ConsoleLogChars::Reg32([
1681 regs[2] as u32,
1682 regs[3] as u32,
1683 regs[4] as u32,
1684 regs[5] as u32,
1685 regs[6] as u32,
1686 regs[7] as u32,
1687 ]),
1688 },
Tomás González7ffb6132025-04-03 12:28:58 +01001689 FuncId::NotificationBitmapCreate => {
1690 let tentative_vm_id = regs[1] as u32;
1691 if (tentative_vm_id >> 16) != 0 {
1692 return Err(Error::InvalidVmId(tentative_vm_id));
1693 }
1694 Self::NotificationBitmapCreate {
1695 vm_id: tentative_vm_id as u16,
1696 vcpu_cnt: regs[2] as u32,
1697 }
1698 }
1699 FuncId::NotificationBitmapDestroy => {
1700 let tentative_vm_id = regs[1] as u32;
1701 if (tentative_vm_id >> 16) != 0 {
1702 return Err(Error::InvalidVmId(tentative_vm_id));
1703 }
1704 Self::NotificationBitmapDestroy {
1705 vm_id: tentative_vm_id as u16,
1706 }
1707 }
1708 FuncId::NotificationBind => Self::NotificationBind {
1709 sender_id: (regs[1] >> 16) as u16,
1710 receiver_id: regs[1] as u16,
1711 flags: (regs[2] as u32).into(),
1712 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1713 },
1714 FuncId::NotificationUnbind => Self::NotificationUnBind {
1715 sender_id: (regs[1] >> 16) as u16,
1716 receiver_id: regs[1] as u16,
1717 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1718 },
1719 FuncId::NotificationSet => Self::NotificationSet {
1720 sender_id: (regs[1] >> 16) as u16,
1721 receiver_id: regs[1] as u16,
1722 flags: (regs[2] as u32).try_into()?,
1723 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1724 },
1725 FuncId::NotificationGet => Self::NotificationGet {
1726 vcpu_id: (regs[1] >> 16) as u16,
1727 endpoint_id: regs[1] as u16,
1728 flags: (regs[2] as u32).into(),
1729 },
1730 FuncId::NotificationInfoGet32 => Self::NotificationInfoGet { is_32bit: true },
1731 FuncId::NotificationInfoGet64 => Self::NotificationInfoGet { is_32bit: false },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01001732 FuncId::El3IntrHandle => Self::El3IntrHandle,
Balint Dobszayde0dc802025-02-28 14:16:52 +01001733 _ => panic!("Invalid number of registers (8) for function {:#x?}", fid),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001734 };
1735
1736 Ok(msg)
1737 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01001738
Balint Dobszayde0dc802025-02-28 14:16:52 +01001739 fn unpack_regs18(version: Version, regs: &[u64; 18]) -> Result<Self, Error> {
1740 assert!(version >= Version(1, 2));
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001741
Balint Dobszayde0dc802025-02-28 14:16:52 +01001742 let fid = FuncId::try_from(regs[0] as u32)?;
1743
1744 let msg = match fid {
1745 FuncId::Success64 => Self::Success {
1746 target_info: regs[1] as u32,
Imre Kis54773b62025-04-10 13:47:39 +02001747 args: SuccessArgs::Args64_2(regs[2..18].try_into().unwrap()),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001748 },
1749 FuncId::MsgSendDirectReq64_2 => Self::MsgSendDirectReq2 {
1750 src_id: (regs[1] >> 16) as u16,
1751 dst_id: regs[1] as u16,
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00001752 uuid: Uuid::from_u64_pair(regs[2].swap_bytes(), regs[3].swap_bytes()),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001753 args: DirectMsg2Args(regs[4..18].try_into().unwrap()),
1754 },
1755 FuncId::MsgSendDirectResp64_2 => Self::MsgSendDirectResp2 {
1756 src_id: (regs[1] >> 16) as u16,
1757 dst_id: regs[1] as u16,
1758 args: DirectMsg2Args(regs[4..18].try_into().unwrap()),
1759 },
1760 FuncId::ConsoleLog64 => Self::ConsoleLog {
1761 char_cnt: regs[1] as u8,
1762 char_lists: ConsoleLogChars::Reg64(regs[2..18].try_into().unwrap()),
1763 },
Tomás González0a058bc2025-03-11 11:20:55 +00001764 FuncId::PartitionInfoGetRegs => {
1765 // Bits[15:0]: Start index
1766 let start_index = (regs[3] & 0xffff) as u16;
1767 let info_tag = ((regs[3] >> 16) & 0xffff) as u16;
1768 Self::PartitionInfoGetRegs {
1769 uuid: Uuid::from_u64_pair(regs[1].swap_bytes(), regs[2].swap_bytes()),
1770 start_index,
1771 info_tag: if start_index == 0 && info_tag != 0 {
1772 return Err(Error::InvalidInformationTag(info_tag));
1773 } else {
1774 info_tag
1775 },
1776 }
1777 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001778 _ => panic!("Invalid number of registers (18) for function {:#x?}", fid),
1779 };
1780
1781 Ok(msg)
Balint Dobszay3aad9572025-01-17 16:54:11 +01001782 }
1783
Balint Dobszaya5846852025-02-26 15:38:53 +01001784 /// Create register contents for an interface.
Balint Dobszayde0dc802025-02-28 14:16:52 +01001785 pub fn to_regs(&self, version: Version, regs: &mut [u64]) {
1786 let reg_cnt = regs.len();
1787
1788 match reg_cnt {
1789 8 => {
1790 assert!(version <= Version(1, 1));
Balint Dobszay91bea9b2025-04-09 13:16:06 +02001791 regs.fill(0);
1792
Balint Dobszayde0dc802025-02-28 14:16:52 +01001793 self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
1794 }
1795 18 => {
1796 assert!(version >= Version(1, 2));
Balint Dobszay91bea9b2025-04-09 13:16:06 +02001797 regs.fill(0);
Balint Dobszayde0dc802025-02-28 14:16:52 +01001798
1799 match self {
1800 Interface::ConsoleLog {
1801 char_lists: ConsoleLogChars::Reg64(_),
1802 ..
1803 }
1804 | Interface::Success {
Imre Kis54773b62025-04-10 13:47:39 +02001805 args: SuccessArgs::Args64_2(_),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001806 ..
1807 }
1808 | Interface::MsgSendDirectReq2 { .. }
Tomás González0a058bc2025-03-11 11:20:55 +00001809 | Interface::MsgSendDirectResp2 { .. }
1810 | Interface::PartitionInfoGetRegs { .. } => {
Balint Dobszayde0dc802025-02-28 14:16:52 +01001811 self.pack_regs18(version, regs.try_into().unwrap());
1812 }
1813 _ => {
1814 self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
1815 }
1816 }
1817 }
1818 _ => panic!("Invalid number of registers {}", reg_cnt),
1819 }
1820 }
1821
1822 fn pack_regs8(&self, version: Version, a: &mut [u64; 8]) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001823 if let Some(function_id) = self.function_id() {
1824 a[0] = function_id as u64;
1825 }
1826
1827 match *self {
1828 Interface::Error {
1829 target_info,
1830 error_code,
Balint Dobszayb727aab2025-04-07 10:24:59 +02001831 error_arg,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001832 } => {
1833 a[1] = u32::from(target_info).into();
1834 a[2] = (error_code as u32).into();
Balint Dobszayb727aab2025-04-07 10:24:59 +02001835 a[3] = error_arg.into();
Balint Dobszay3aad9572025-01-17 16:54:11 +01001836 }
1837 Interface::Success { target_info, args } => {
1838 a[1] = target_info.into();
1839 match args {
Imre Kis54773b62025-04-10 13:47:39 +02001840 SuccessArgs::Args32(regs) => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001841 a[2] = regs[0].into();
1842 a[3] = regs[1].into();
1843 a[4] = regs[2].into();
1844 a[5] = regs[3].into();
1845 a[6] = regs[4].into();
1846 a[7] = regs[5].into();
1847 }
Imre Kis54773b62025-04-10 13:47:39 +02001848 SuccessArgs::Args64(regs) => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001849 a[2] = regs[0];
1850 a[3] = regs[1];
1851 a[4] = regs[2];
1852 a[5] = regs[3];
1853 a[6] = regs[4];
1854 a[7] = regs[5];
1855 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001856 _ => panic!("{:#x?} requires 18 registers", args),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001857 }
1858 }
1859 Interface::Interrupt {
1860 target_info,
1861 interrupt_id,
1862 } => {
1863 a[1] = u32::from(target_info).into();
1864 a[2] = interrupt_id.into();
1865 }
1866 Interface::Version { input_version } => {
1867 a[1] = u32::from(input_version).into();
1868 }
1869 Interface::VersionOut { output_version } => {
1870 a[0] = u32::from(output_version).into();
1871 }
1872 Interface::Features {
1873 feat_id,
1874 input_properties,
1875 } => {
1876 a[1] = u32::from(feat_id).into();
1877 a[2] = input_properties.into();
1878 }
1879 Interface::RxAcquire { vm_id } => {
1880 a[1] = vm_id.into();
1881 }
1882 Interface::RxRelease { vm_id } => {
1883 a[1] = vm_id.into();
1884 }
1885 Interface::RxTxMap { addr, page_cnt } => {
1886 match addr {
1887 RxTxAddr::Addr32 { rx, tx } => {
1888 a[1] = tx.into();
1889 a[2] = rx.into();
1890 }
1891 RxTxAddr::Addr64 { rx, tx } => {
1892 a[1] = tx;
1893 a[2] = rx;
1894 }
1895 }
1896 a[3] = page_cnt.into();
1897 }
1898 Interface::RxTxUnmap { id } => {
1899 a[1] = id.into();
1900 }
1901 Interface::PartitionInfoGet { uuid, flags } => {
1902 let bytes = uuid.into_bytes();
1903 a[1] = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]).into();
1904 a[2] = u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]).into();
1905 a[3] = u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]).into();
1906 a[4] = u32::from_le_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]).into();
Imre Kise295adb2025-04-10 13:26:28 +02001907 a[5] = u32::from(flags).into();
Balint Dobszay3aad9572025-01-17 16:54:11 +01001908 }
Tomás González092202a2025-03-05 11:56:45 +00001909 Interface::MsgWait { flags } => {
1910 if version >= Version(1, 2) {
1911 if let Some(flags) = flags {
1912 a[2] = u32::from(flags).into();
1913 }
1914 }
1915 }
1916 Interface::IdGet | Interface::SpmIdGet | Interface::Yield => {}
Balint Dobszay3aad9572025-01-17 16:54:11 +01001917 Interface::Run { target_info } => {
1918 a[1] = u32::from(target_info).into();
1919 }
1920 Interface::NormalWorldResume => {}
Tomás González17b92442025-03-10 16:45:04 +00001921 Interface::SecondaryEpRegister { entrypoint } => match entrypoint {
1922 SecondaryEpRegisterAddr::Addr32(addr) => a[1] = addr as u64,
1923 SecondaryEpRegisterAddr::Addr64(addr) => a[1] = addr,
1924 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001925 Interface::MsgSend2 {
1926 sender_vm_id,
1927 flags,
1928 } => {
1929 a[1] = sender_vm_id.into();
1930 a[2] = flags.into();
1931 }
1932 Interface::MsgSendDirectReq {
1933 src_id,
1934 dst_id,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001935 args,
1936 } => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +01001937 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Balint Dobszay3aad9572025-01-17 16:54:11 +01001938 match args {
1939 DirectMsgArgs::Args32(args) => {
1940 a[3] = args[0].into();
1941 a[4] = args[1].into();
1942 a[5] = args[2].into();
1943 a[6] = args[3].into();
1944 a[7] = args[4].into();
1945 }
1946 DirectMsgArgs::Args64(args) => {
1947 a[3] = args[0];
1948 a[4] = args[1];
1949 a[5] = args[2];
1950 a[6] = args[3];
1951 a[7] = args[4];
1952 }
Tomás González4d5b0ba2025-03-03 17:15:55 +00001953 DirectMsgArgs::VersionReq { version } => {
1954 a[2] = DirectMsgArgs::VERSION_REQ.into();
1955 a[3] = u32::from(version).into();
1956 }
Tomás González67f92c72025-03-20 16:50:42 +00001957 DirectMsgArgs::PowerPsciReq32 { params } => {
Tomás González4d5b0ba2025-03-03 17:15:55 +00001958 a[2] = DirectMsgArgs::POWER_PSCI_REQ.into();
Tomás González67f92c72025-03-20 16:50:42 +00001959 a[3] = params[0].into();
1960 a[4] = params[1].into();
1961 a[5] = params[2].into();
1962 a[6] = params[3].into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00001963 }
Tomás González67f92c72025-03-20 16:50:42 +00001964 DirectMsgArgs::PowerPsciReq64 { params } => {
Tomás González4d5b0ba2025-03-03 17:15:55 +00001965 a[2] = DirectMsgArgs::POWER_PSCI_REQ.into();
Tomás González67f92c72025-03-20 16:50:42 +00001966 a[3] = params[0];
1967 a[4] = params[1];
1968 a[5] = params[2];
1969 a[6] = params[3];
Tomás González4d5b0ba2025-03-03 17:15:55 +00001970 }
1971 DirectMsgArgs::PowerWarmBootReq { boot_type } => {
1972 a[2] = DirectMsgArgs::POWER_WARM_BOOT_REQ.into();
1973 a[3] = u32::from(boot_type).into();
1974 }
1975 DirectMsgArgs::VmCreated { handle, vm_id } => {
1976 a[2] = DirectMsgArgs::VM_CREATED.into();
1977 let handle_regs: [u32; 2] = handle.into();
1978 a[3] = handle_regs[0].into();
1979 a[4] = handle_regs[1].into();
1980 a[5] = vm_id.into();
1981 }
1982 DirectMsgArgs::VmDestructed { handle, vm_id } => {
1983 a[2] = DirectMsgArgs::VM_DESTRUCTED.into();
1984 let handle_regs: [u32; 2] = handle.into();
1985 a[3] = handle_regs[0].into();
1986 a[4] = handle_regs[1].into();
1987 a[5] = vm_id.into();
1988 }
1989 _ => panic!("Malformed MsgSendDirectReq interface"),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001990 }
1991 }
1992 Interface::MsgSendDirectResp {
1993 src_id,
1994 dst_id,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001995 args,
1996 } => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +01001997 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Balint Dobszay3aad9572025-01-17 16:54:11 +01001998 match args {
1999 DirectMsgArgs::Args32(args) => {
2000 a[3] = args[0].into();
2001 a[4] = args[1].into();
2002 a[5] = args[2].into();
2003 a[6] = args[3].into();
2004 a[7] = args[4].into();
2005 }
2006 DirectMsgArgs::Args64(args) => {
2007 a[3] = args[0];
2008 a[4] = args[1];
2009 a[5] = args[2];
2010 a[6] = args[3];
2011 a[7] = args[4];
2012 }
Tomás González4d5b0ba2025-03-03 17:15:55 +00002013 DirectMsgArgs::VersionResp { version } => {
2014 a[2] = DirectMsgArgs::VERSION_RESP.into();
2015 match version {
Tomás González67f92c72025-03-20 16:50:42 +00002016 None => a[3] = (i32::from(FfaError::NotSupported) as u32).into(),
Tomás González4d5b0ba2025-03-03 17:15:55 +00002017 Some(ver) => a[3] = u32::from(ver).into(),
2018 }
2019 }
2020 DirectMsgArgs::PowerPsciResp { psci_status } => {
2021 a[2] = DirectMsgArgs::POWER_PSCI_RESP.into();
Imre Kisb2d3c882025-04-11 14:19:35 +02002022 a[3] = (psci_status as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00002023 }
2024 DirectMsgArgs::VmCreatedAck { sp_status } => {
2025 a[2] = DirectMsgArgs::VM_CREATED_ACK.into();
Tomás González67f92c72025-03-20 16:50:42 +00002026 a[3] = (i32::from(sp_status) as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00002027 }
2028 DirectMsgArgs::VmDestructedAck { sp_status } => {
2029 a[2] = DirectMsgArgs::VM_DESTRUCTED_ACK.into();
Tomás González67f92c72025-03-20 16:50:42 +00002030 a[3] = (i32::from(sp_status) as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00002031 }
2032 _ => panic!("Malformed MsgSendDirectResp interface"),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002033 }
2034 }
2035 Interface::MemDonate {
2036 total_len,
2037 frag_len,
2038 buf,
2039 } => {
2040 a[1] = total_len.into();
2041 a[2] = frag_len.into();
2042 (a[3], a[4]) = match buf {
2043 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
2044 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
2045 None => (0, 0),
2046 };
2047 }
2048 Interface::MemLend {
2049 total_len,
2050 frag_len,
2051 buf,
2052 } => {
2053 a[1] = total_len.into();
2054 a[2] = frag_len.into();
2055 (a[3], a[4]) = match buf {
2056 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
2057 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
2058 None => (0, 0),
2059 };
2060 }
2061 Interface::MemShare {
2062 total_len,
2063 frag_len,
2064 buf,
2065 } => {
2066 a[1] = total_len.into();
2067 a[2] = frag_len.into();
2068 (a[3], a[4]) = match buf {
2069 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
2070 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
2071 None => (0, 0),
2072 };
2073 }
2074 Interface::MemRetrieveReq {
2075 total_len,
2076 frag_len,
2077 buf,
2078 } => {
2079 a[1] = total_len.into();
2080 a[2] = frag_len.into();
2081 (a[3], a[4]) = match buf {
2082 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
2083 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
2084 None => (0, 0),
2085 };
2086 }
2087 Interface::MemRetrieveResp {
2088 total_len,
2089 frag_len,
2090 } => {
2091 a[1] = total_len.into();
2092 a[2] = frag_len.into();
2093 }
2094 Interface::MemRelinquish => {}
2095 Interface::MemReclaim { handle, flags } => {
2096 let handle_regs: [u32; 2] = handle.into();
2097 a[1] = handle_regs[0].into();
2098 a[2] = handle_regs[1].into();
2099 a[3] = flags.into();
2100 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01002101 Interface::MemPermGet { addr, page_cnt } => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01002102 a[1] = match addr {
2103 MemAddr::Addr32(addr) => addr.into(),
2104 MemAddr::Addr64(addr) => addr,
2105 };
Balint Dobszayde0dc802025-02-28 14:16:52 +01002106 a[2] = if version >= Version(1, 3) {
2107 page_cnt.unwrap().into()
2108 } else {
2109 assert!(page_cnt.is_none());
2110 0
2111 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01002112 }
2113 Interface::MemPermSet {
2114 addr,
2115 page_cnt,
2116 mem_perm,
2117 } => {
2118 a[1] = match addr {
2119 MemAddr::Addr32(addr) => addr.into(),
2120 MemAddr::Addr64(addr) => addr,
2121 };
2122 a[2] = page_cnt.into();
2123 a[3] = mem_perm.into();
2124 }
2125 Interface::ConsoleLog {
2126 char_cnt,
2127 char_lists,
2128 } => {
2129 a[1] = char_cnt.into();
2130 match char_lists {
2131 ConsoleLogChars::Reg32(regs) => {
2132 a[2] = regs[0].into();
2133 a[3] = regs[1].into();
2134 a[4] = regs[2].into();
2135 a[5] = regs[3].into();
2136 a[6] = regs[4].into();
2137 a[7] = regs[5].into();
2138 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01002139 _ => panic!("{:#x?} requires 18 registers", char_lists),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002140 }
2141 }
Tomás González7ffb6132025-04-03 12:28:58 +01002142 Interface::NotificationBitmapCreate { vm_id, vcpu_cnt } => {
2143 a[1] = vm_id.into();
2144 a[2] = vcpu_cnt.into();
2145 }
2146 Interface::NotificationBitmapDestroy { vm_id } => {
2147 a[1] = vm_id.into();
2148 }
2149 Interface::NotificationBind {
2150 sender_id,
2151 receiver_id,
2152 flags,
2153 bitmap,
2154 } => {
2155 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
2156 a[2] = u32::from(flags).into();
2157 a[3] = bitmap & 0xffff_ffff;
2158 a[4] = bitmap >> 32;
2159 }
2160 Interface::NotificationUnBind {
2161 sender_id,
2162 receiver_id,
2163 bitmap,
2164 } => {
2165 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
2166 a[3] = bitmap & 0xffff_ffff;
2167 a[4] = bitmap >> 32;
2168 }
2169 Interface::NotificationSet {
2170 sender_id,
2171 receiver_id,
2172 flags,
2173 bitmap,
2174 } => {
2175 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
2176 a[2] = u32::from(flags).into();
2177 a[3] = bitmap & 0xffff_ffff;
2178 a[4] = bitmap >> 32;
2179 }
2180 Interface::NotificationGet {
2181 vcpu_id,
2182 endpoint_id,
2183 flags,
2184 } => {
2185 a[1] = (u64::from(vcpu_id) << 16) | u64::from(endpoint_id);
2186 a[2] = u32::from(flags).into();
2187 }
2188 Interface::NotificationInfoGet { .. } => {}
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01002189 Interface::El3IntrHandle => {}
Balint Dobszayde0dc802025-02-28 14:16:52 +01002190 _ => panic!("{:#x?} requires 18 registers", self),
2191 }
2192 }
2193
2194 fn pack_regs18(&self, version: Version, a: &mut [u64; 18]) {
2195 assert!(version >= Version(1, 2));
2196
Balint Dobszayde0dc802025-02-28 14:16:52 +01002197 if let Some(function_id) = self.function_id() {
2198 a[0] = function_id as u64;
2199 }
2200
2201 match *self {
2202 Interface::Success { target_info, args } => {
2203 a[1] = target_info.into();
2204 match args {
Imre Kis54773b62025-04-10 13:47:39 +02002205 SuccessArgs::Args64_2(regs) => a[2..18].copy_from_slice(&regs[..16]),
Balint Dobszayde0dc802025-02-28 14:16:52 +01002206 _ => panic!("{:#x?} requires 8 registers", args),
2207 }
2208 }
2209 Interface::MsgSendDirectReq2 {
2210 src_id,
2211 dst_id,
2212 uuid,
2213 args,
2214 } => {
2215 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00002216 let (uuid_msb, uuid_lsb) = uuid.as_u64_pair();
2217 (a[2], a[3]) = (uuid_msb.swap_bytes(), uuid_lsb.swap_bytes());
Balint Dobszayde0dc802025-02-28 14:16:52 +01002218 a[4..18].copy_from_slice(&args.0[..14]);
2219 }
2220 Interface::MsgSendDirectResp2 {
2221 src_id,
2222 dst_id,
2223 args,
2224 } => {
2225 a[1] = ((src_id as u64) << 16) | dst_id as u64;
2226 a[2] = 0;
2227 a[3] = 0;
2228 a[4..18].copy_from_slice(&args.0[..14]);
2229 }
2230 Interface::ConsoleLog {
2231 char_cnt,
2232 char_lists,
2233 } => {
2234 a[1] = char_cnt.into();
2235 match char_lists {
2236 ConsoleLogChars::Reg64(regs) => a[2..18].copy_from_slice(&regs[..16]),
2237 _ => panic!("{:#x?} requires 8 registers", char_lists),
2238 }
2239 }
Tomás González0a058bc2025-03-11 11:20:55 +00002240 Interface::PartitionInfoGetRegs {
2241 uuid,
2242 start_index,
2243 info_tag,
2244 } => {
2245 if start_index == 0 && info_tag != 0 {
2246 panic!("Information Tag MBZ if start index is 0: {:#x?}", self);
2247 }
2248 let (uuid_msb, uuid_lsb) = uuid.as_u64_pair();
2249 (a[1], a[2]) = (uuid_msb.swap_bytes(), uuid_lsb.swap_bytes());
2250 a[3] = (u64::from(info_tag) << 16) | u64::from(start_index);
2251 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01002252 _ => panic!("{:#x?} requires 8 registers", self),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002253 }
2254 }
2255
Balint Dobszaya5846852025-02-26 15:38:53 +01002256 /// Helper function to create an `FFA_SUCCESS` interface without any arguments.
Balint Dobszay3aad9572025-01-17 16:54:11 +01002257 pub fn success32_noargs() -> Self {
2258 Self::Success {
2259 target_info: 0,
Imre Kis54773b62025-04-10 13:47:39 +02002260 args: SuccessArgs::Args32([0; 6]),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002261 }
2262 }
2263
Tomás González4c8c7d22025-03-10 17:14:57 +00002264 /// Helper function to create an `FFA_SUCCESS` interface without any arguments.
2265 pub fn success64_noargs() -> Self {
2266 Self::Success {
2267 target_info: 0,
Imre Kis54773b62025-04-10 13:47:39 +02002268 args: SuccessArgs::Args64([0; 6]),
Tomás González4c8c7d22025-03-10 17:14:57 +00002269 }
2270 }
2271
Balint Dobszaya5846852025-02-26 15:38:53 +01002272 /// Helper function to create an `FFA_ERROR` interface with an error code.
Balint Dobszay3aad9572025-01-17 16:54:11 +01002273 pub fn error(error_code: FfaError) -> Self {
2274 Self::Error {
2275 target_info: TargetInfo {
2276 endpoint_id: 0,
2277 vcpu_id: 0,
2278 },
2279 error_code,
Balint Dobszayb727aab2025-04-07 10:24:59 +02002280 error_arg: 0,
Balint Dobszay3aad9572025-01-17 16:54:11 +01002281 }
2282 }
2283}
2284
Balint Dobszaya5846852025-02-26 15:38:53 +01002285/// Maximum number of characters transmitted in a single `FFA_CONSOLE_LOG32` message.
2286pub const CONSOLE_LOG_32_MAX_CHAR_CNT: u8 = 24;
Balint Dobszayde0dc802025-02-28 14:16:52 +01002287/// Maximum number of characters transmitted in a single `FFA_CONSOLE_LOG64` message.
2288pub const CONSOLE_LOG_64_MAX_CHAR_CNT: u8 = 128;
Balint Dobszay3aad9572025-01-17 16:54:11 +01002289
Balint Dobszaya5846852025-02-26 15:38:53 +01002290/// Helper function to convert the "Tightly packed list of characters" format used by the
2291/// `FFA_CONSOLE_LOG` interface into a byte slice.
Balint Dobszay3aad9572025-01-17 16:54:11 +01002292pub fn parse_console_log(
2293 char_cnt: u8,
2294 char_lists: &ConsoleLogChars,
2295 log_bytes: &mut [u8],
2296) -> Result<(), FfaError> {
2297 match char_lists {
2298 ConsoleLogChars::Reg32(regs) => {
Balint Dobszaya5846852025-02-26 15:38:53 +01002299 if !(1..=CONSOLE_LOG_32_MAX_CHAR_CNT).contains(&char_cnt) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01002300 return Err(FfaError::InvalidParameters);
2301 }
2302 for (i, reg) in regs.iter().enumerate() {
2303 log_bytes[4 * i..4 * (i + 1)].copy_from_slice(&reg.to_le_bytes());
2304 }
2305 }
2306 ConsoleLogChars::Reg64(regs) => {
Balint Dobszaya5846852025-02-26 15:38:53 +01002307 if !(1..=CONSOLE_LOG_64_MAX_CHAR_CNT).contains(&char_cnt) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01002308 return Err(FfaError::InvalidParameters);
2309 }
2310 for (i, reg) in regs.iter().enumerate() {
2311 log_bytes[8 * i..8 * (i + 1)].copy_from_slice(&reg.to_le_bytes());
2312 }
2313 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +02002314 }
2315
Balint Dobszayc8802492025-01-15 18:11:39 +01002316 Ok(())
Balint Dobszay5bf492f2024-07-29 17:21:32 +02002317}
Tomás González0a058bc2025-03-11 11:20:55 +00002318
2319#[cfg(test)]
2320mod tests {
2321 use super::*;
2322
2323 #[test]
2324 fn part_info_get_regs() {
2325 let uuid = Uuid::parse_str("a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8").unwrap();
2326 let uuid_bytes = uuid.as_bytes();
2327 let test_info_tag = 0b1101_1101;
2328 let test_start_index = 0b1101;
2329 let start_index_and_tag = (test_info_tag << 16) | test_start_index;
2330 let version = Version(1, 2);
2331
2332 // From spec:
2333 // Bytes[0...7] of UUID with byte 0 in the low-order bits.
2334 let reg_x1 = (uuid_bytes[7] as u64) << 56
2335 | (uuid_bytes[6] as u64) << 48
2336 | (uuid_bytes[5] as u64) << 40
2337 | (uuid_bytes[4] as u64) << 32
2338 | (uuid_bytes[3] as u64) << 24
2339 | (uuid_bytes[2] as u64) << 16
2340 | (uuid_bytes[1] as u64) << 8
2341 | (uuid_bytes[0] as u64);
2342
2343 // From spec:
2344 // Bytes[8...15] of UUID with byte 8 in the low-order bits.
2345 let reg_x2 = (uuid_bytes[15] as u64) << 56
2346 | (uuid_bytes[14] as u64) << 48
2347 | (uuid_bytes[13] as u64) << 40
2348 | (uuid_bytes[12] as u64) << 32
2349 | (uuid_bytes[11] as u64) << 24
2350 | (uuid_bytes[10] as u64) << 16
2351 | (uuid_bytes[9] as u64) << 8
2352 | (uuid_bytes[8] as u64);
2353
2354 // First, test for wrong tag:
2355 {
2356 let mut regs = [0u64; 18];
2357 regs[0] = FuncId::PartitionInfoGetRegs as u64;
2358 regs[1] = reg_x1;
2359 regs[2] = reg_x2;
2360 regs[3] = test_info_tag << 16;
2361
2362 assert!(Interface::from_regs(version, &regs).is_err_and(
2363 |e| e == Error::InvalidInformationTag(test_info_tag.try_into().unwrap())
2364 ));
2365 }
2366
2367 // Test for regs -> Interface -> regs
2368 {
2369 let mut orig_regs = [0u64; 18];
2370 orig_regs[0] = FuncId::PartitionInfoGetRegs as u64;
2371 orig_regs[1] = reg_x1;
2372 orig_regs[2] = reg_x2;
2373 orig_regs[3] = start_index_and_tag;
2374
2375 let mut test_regs = orig_regs.clone();
2376 let interface = Interface::from_regs(version, &mut test_regs).unwrap();
2377 match &interface {
2378 Interface::PartitionInfoGetRegs {
2379 info_tag,
2380 start_index,
2381 uuid: int_uuid,
2382 } => {
2383 assert_eq!(u64::from(*info_tag), test_info_tag);
2384 assert_eq!(u64::from(*start_index), test_start_index);
2385 assert_eq!(*int_uuid, uuid);
2386 }
2387 _ => panic!("Expecting Interface::PartitionInfoGetRegs!"),
2388 }
2389 test_regs.fill(0);
2390 interface.to_regs(version, &mut test_regs);
2391 assert_eq!(orig_regs, test_regs);
2392 }
2393
2394 // Test for Interface -> regs -> Interface
2395 {
2396 let interface = Interface::PartitionInfoGetRegs {
2397 info_tag: test_info_tag.try_into().unwrap(),
2398 start_index: test_start_index.try_into().unwrap(),
2399 uuid,
2400 };
2401
2402 let mut regs: [u64; 18] = [0; 18];
2403 interface.to_regs(version, &mut regs);
2404
2405 assert_eq!(Some(FuncId::PartitionInfoGetRegs), interface.function_id());
2406 assert_eq!(regs[0], interface.function_id().unwrap() as u64);
2407 assert_eq!(regs[1], reg_x1);
2408 assert_eq!(regs[2], reg_x2);
2409 assert_eq!(regs[3], (test_info_tag << 16) | test_start_index);
2410
2411 assert_eq!(Interface::from_regs(version, &regs).unwrap(), interface);
2412 }
2413 }
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00002414
2415 #[test]
2416 fn msg_send_direct_req2() {
2417 let uuid = Uuid::parse_str("a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8").unwrap();
2418 let uuid_bytes = uuid.as_bytes();
2419
2420 // From spec:
2421 // Bytes[0...7] of UUID with byte 0 in the low-order bits.
2422 let reg_x2 = (uuid_bytes[7] as u64) << 56
2423 | (uuid_bytes[6] as u64) << 48
2424 | (uuid_bytes[5] as u64) << 40
2425 | (uuid_bytes[4] as u64) << 32
2426 | (uuid_bytes[3] as u64) << 24
2427 | (uuid_bytes[2] as u64) << 16
2428 | (uuid_bytes[1] as u64) << 8
2429 | (uuid_bytes[0] as u64);
2430
2431 // From spec:
2432 // Bytes[8...15] of UUID with byte 8 in the low-order bits.
2433 let reg_x3 = (uuid_bytes[15] as u64) << 56
2434 | (uuid_bytes[14] as u64) << 48
2435 | (uuid_bytes[13] as u64) << 40
2436 | (uuid_bytes[12] as u64) << 32
2437 | (uuid_bytes[11] as u64) << 24
2438 | (uuid_bytes[10] as u64) << 16
2439 | (uuid_bytes[9] as u64) << 8
2440 | (uuid_bytes[8] as u64);
2441
2442 let test_sender = 0b1101_1101;
2443 let test_receiver = 0b1101;
2444 let test_sender_receiver = (test_sender << 16) | test_receiver;
2445 let version = Version(1, 2);
2446
2447 // Test for regs -> Interface -> regs
2448 {
2449 let mut orig_regs = [0u64; 18];
2450 orig_regs[0] = FuncId::MsgSendDirectReq64_2 as u64;
2451 orig_regs[1] = test_sender_receiver;
2452 orig_regs[2] = reg_x2;
2453 orig_regs[3] = reg_x3;
2454
2455 let mut test_regs = orig_regs.clone();
2456 let interface = Interface::from_regs(version, &mut test_regs).unwrap();
2457 match &interface {
2458 Interface::MsgSendDirectReq2 {
2459 dst_id,
2460 src_id,
2461 args: _,
2462 uuid: int_uuid,
2463 } => {
2464 assert_eq!(u64::from(*src_id), test_sender);
2465 assert_eq!(u64::from(*dst_id), test_receiver);
2466 assert_eq!(*int_uuid, uuid);
2467 }
2468 _ => panic!("Expecting Interface::MsgSendDirectReq2!"),
2469 }
2470 test_regs.fill(0);
2471 interface.to_regs(version, &mut test_regs);
2472 assert_eq!(orig_regs, test_regs);
2473 }
2474
2475 // Test for Interface -> regs -> Interface
2476 {
2477 let rest_of_regs: [u64; 14] = [0; 14];
2478
2479 let interface = Interface::MsgSendDirectReq2 {
2480 src_id: test_sender.try_into().unwrap(),
2481 dst_id: test_receiver.try_into().unwrap(),
2482 uuid,
2483 args: DirectMsg2Args(rest_of_regs),
2484 };
2485
2486 let mut regs: [u64; 18] = [0; 18];
2487 interface.to_regs(version, &mut regs);
2488
2489 assert_eq!(Some(FuncId::MsgSendDirectReq64_2), interface.function_id());
2490 assert_eq!(regs[0], interface.function_id().unwrap() as u64);
2491 assert_eq!(regs[1], test_sender_receiver);
2492 assert_eq!(regs[2], reg_x2);
2493 assert_eq!(regs[3], reg_x3);
2494 assert_eq!(regs[4], 0);
2495
2496 assert_eq!(Interface::from_regs(version, &regs).unwrap(), interface);
2497 }
2498 }
Tomás González6ccba0a2025-04-09 13:31:29 +01002499
2500 #[test]
2501 fn is_32bit() {
2502 let interface_64 = Interface::MsgSendDirectReq {
2503 src_id: 0,
2504 dst_id: 1,
2505 args: DirectMsgArgs::Args64([0, 0, 0, 0, 0]),
2506 };
2507 assert!(!interface_64.is_32bit());
2508
2509 let interface_32 = Interface::MsgSendDirectReq {
2510 src_id: 0,
2511 dst_id: 1,
2512 args: DirectMsgArgs::Args32([0, 0, 0, 0, 0]),
2513 };
2514 assert!(interface_32.is_32bit());
2515 }
Imre Kis787c5002025-04-10 14:25:51 +02002516
2517 #[test]
2518 fn success_args_notification_info_get32() {
2519 let mut notifications = SuccessArgsNotificationInfoGet32::default();
2520
2521 // 16.7.1.1 Example usage
2522 notifications.add_list(0x0000, &[0, 2, 3]).unwrap();
2523 notifications.add_list(0x0000, &[4, 6]).unwrap();
2524 notifications.add_list(0x0002, &[]).unwrap();
2525 notifications.add_list(0x0003, &[1]).unwrap();
2526
2527 let args: SuccessArgs = notifications.into();
2528 assert_eq!(
2529 SuccessArgs::Args32([
2530 0x0004_b200,
2531 0x0000_0000,
2532 0x0003_0002,
2533 0x0004_0000,
2534 0x0002_0006,
2535 0x0001_0003
2536 ]),
2537 args
2538 );
2539
2540 let notifications = SuccessArgsNotificationInfoGet32::try_from(args).unwrap();
2541 let mut iter = notifications.iter();
2542 assert_eq!(Some((0x0000, &[0, 2, 3][..])), iter.next());
2543 assert_eq!(Some((0x0000, &[4, 6][..])), iter.next());
2544 assert_eq!(Some((0x0002, &[][..])), iter.next());
2545 assert_eq!(Some((0x0003, &[1][..])), iter.next());
2546 }
2547
2548 #[test]
2549 fn success_args_notification_info_get64() {
2550 let mut notifications = SuccessArgsNotificationInfoGet64::default();
2551
2552 // 16.7.1.1 Example usage
2553 notifications.add_list(0x0000, &[0, 2, 3]).unwrap();
2554 notifications.add_list(0x0000, &[4, 6]).unwrap();
2555 notifications.add_list(0x0002, &[]).unwrap();
2556 notifications.add_list(0x0003, &[1]).unwrap();
2557
2558 let args: SuccessArgs = notifications.into();
2559 assert_eq!(
2560 SuccessArgs::Args64([
2561 0x0004_b200,
2562 0x0003_0002_0000_0000,
2563 0x0002_0006_0004_0000,
2564 0x0000_0000_0001_0003,
2565 0x0000_0000_0000_0000,
2566 0x0000_0000_0000_0000,
2567 ]),
2568 args
2569 );
2570
2571 let notifications = SuccessArgsNotificationInfoGet64::try_from(args).unwrap();
2572 let mut iter = notifications.iter();
2573 assert_eq!(Some((0x0000, &[0, 2, 3][..])), iter.next());
2574 assert_eq!(Some((0x0000, &[4, 6][..])), iter.next());
2575 assert_eq!(Some((0x0002, &[][..])), iter.next());
2576 assert_eq!(Some((0x0003, &[1][..])), iter.next());
2577 }
Tomás González0a058bc2025-03-11 11:20:55 +00002578}