blob: 417bad8ab5baedc44d189ef924d8367baf5a98fa [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,
Balint Dobszay3aad9572025-01-17 16:54:11 +010057}
58
59impl From<Error> for FfaError {
60 fn from(value: Error) -> Self {
61 match value {
62 Error::UnrecognisedFunctionId(_) | Error::UnrecognisedFeatureId(_) => {
63 Self::NotSupported
64 }
Tomás González0a058bc2025-03-11 11:20:55 +000065 Error::InvalidInformationTag(_) => Self::Retry,
Tomás González4d5b0ba2025-03-03 17:15:55 +000066 Error::UnrecognisedErrorCode(_)
67 | Error::UnrecognisedFwkMsg(_)
68 | Error::InvalidVersion(_)
Tomás González092202a2025-03-05 11:56:45 +000069 | Error::InvalidMsgWaitFlag(_)
Tomás González4d5b0ba2025-03-03 17:15:55 +000070 | Error::UnrecognisedVmAvailabilityStatus(_)
Tomás González7ffb6132025-04-03 12:28:58 +010071 | Error::InvalidNotificationSetFlag(_)
72 | Error::InvalidVmId(_)
Imre Kise295adb2025-04-10 13:26:28 +020073 | Error::UnrecognisedWarmBootType(_)
Imre Kis839eaef2025-04-11 17:38:36 +020074 | Error::InvalidPartitionInfoGetFlag(_)
Imre Kis787c5002025-04-10 14:25:51 +020075 | Error::InvalidSuccessArgsVariant
76 | Error::InvalidNotificationCount => Self::InvalidParameters,
Balint Dobszay3aad9572025-01-17 16:54:11 +010077 }
78 }
79}
Balint Dobszay5bf492f2024-07-29 17:21:32 +020080
Balint Dobszaya5846852025-02-26 15:38:53 +010081/// 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 +020082#[derive(PartialEq, Clone, Copy)]
83pub enum Instance {
Balint Dobszaya5846852025-02-26 15:38:53 +010084 /// The instance between the SPMC and SPMD.
Balint Dobszay5bf492f2024-07-29 17:21:32 +020085 SecurePhysical,
Balint Dobszaya5846852025-02-26 15:38:53 +010086 /// The instance between the SPMC and a physical SP (contains the SP's endpoint ID).
Balint Dobszay5bf492f2024-07-29 17:21:32 +020087 SecureVirtual(u16),
88}
89
Balint Dobszaya5846852025-02-26 15:38:53 +010090/// Function IDs of the various FF-A interfaces.
Andrew Walbran969b9252024-11-25 15:35:42 +000091#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
Balint Dobszay3aad9572025-01-17 16:54:11 +010092#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedFunctionId))]
Balint Dobszay5bf492f2024-07-29 17:21:32 +020093#[repr(u32)]
94pub enum FuncId {
95 Error = 0x84000060,
96 Success32 = 0x84000061,
97 Success64 = 0xc4000061,
98 Interrupt = 0x84000062,
99 Version = 0x84000063,
100 Features = 0x84000064,
101 RxAcquire = 0x84000084,
102 RxRelease = 0x84000065,
103 RxTxMap32 = 0x84000066,
104 RxTxMap64 = 0xc4000066,
105 RxTxUnmap = 0x84000067,
106 PartitionInfoGet = 0x84000068,
Balint Dobszaye6aa4862025-02-28 16:37:56 +0100107 PartitionInfoGetRegs = 0xc400008b,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200108 IdGet = 0x84000069,
109 SpmIdGet = 0x84000085,
Balint Dobszaye6aa4862025-02-28 16:37:56 +0100110 ConsoleLog32 = 0x8400008a,
111 ConsoleLog64 = 0xc400008a,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200112 MsgWait = 0x8400006b,
113 Yield = 0x8400006c,
114 Run = 0x8400006d,
115 NormalWorldResume = 0x8400007c,
116 MsgSend2 = 0x84000086,
117 MsgSendDirectReq32 = 0x8400006f,
118 MsgSendDirectReq64 = 0xc400006f,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100119 MsgSendDirectReq64_2 = 0xc400008d,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200120 MsgSendDirectResp32 = 0x84000070,
121 MsgSendDirectResp64 = 0xc4000070,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100122 MsgSendDirectResp64_2 = 0xc400008e,
Balint Dobszaye6aa4862025-02-28 16:37:56 +0100123 NotificationBitmapCreate = 0x8400007d,
124 NotificationBitmapDestroy = 0x8400007e,
125 NotificationBind = 0x8400007f,
126 NotificationUnbind = 0x84000080,
127 NotificationSet = 0x84000081,
128 NotificationGet = 0x84000082,
129 NotificationInfoGet32 = 0x84000083,
130 NotificationInfoGet64 = 0xc4000083,
131 El3IntrHandle = 0x8400008c,
Tomás González17b92442025-03-10 16:45:04 +0000132 SecondaryEpRegister32 = 0x84000087,
133 SecondaryEpRegister64 = 0xc4000087,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200134 MemDonate32 = 0x84000071,
135 MemDonate64 = 0xc4000071,
136 MemLend32 = 0x84000072,
137 MemLend64 = 0xc4000072,
138 MemShare32 = 0x84000073,
139 MemShare64 = 0xc4000073,
140 MemRetrieveReq32 = 0x84000074,
141 MemRetrieveReq64 = 0xc4000074,
142 MemRetrieveResp = 0x84000075,
143 MemRelinquish = 0x84000076,
144 MemReclaim = 0x84000077,
145 MemPermGet32 = 0x84000088,
146 MemPermGet64 = 0xc4000088,
147 MemPermSet32 = 0x84000089,
148 MemPermSet64 = 0xc4000089,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200149}
150
Balint Dobszayde0dc802025-02-28 14:16:52 +0100151impl FuncId {
152 /// Returns true if this is a 32-bit call, or false if it is a 64-bit call.
153 pub fn is_32bit(&self) -> bool {
Tomás González6ccba0a2025-04-09 13:31:29 +0100154 u32::from(*self) & (1 << 30) == 0
Balint Dobszayde0dc802025-02-28 14:16:52 +0100155 }
156}
157
Balint Dobszaya5846852025-02-26 15:38:53 +0100158/// Error status codes used by the `FFA_ERROR` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100159#[derive(Clone, Copy, Debug, Eq, Error, IntoPrimitive, PartialEq, TryFromPrimitive)]
160#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedErrorCode))]
161#[repr(i32)]
162pub enum FfaError {
163 #[error("Not supported")]
164 NotSupported = -1,
165 #[error("Invalid parameters")]
166 InvalidParameters = -2,
167 #[error("No memory")]
168 NoMemory = -3,
169 #[error("Busy")]
170 Busy = -4,
171 #[error("Interrupted")]
172 Interrupted = -5,
173 #[error("Denied")]
174 Denied = -6,
175 #[error("Retry")]
176 Retry = -7,
177 #[error("Aborted")]
178 Aborted = -8,
179 #[error("No data")]
180 NoData = -9,
181}
182
Balint Dobszaya5846852025-02-26 15:38:53 +0100183/// Endpoint ID and vCPU ID pair, used by `FFA_ERROR`, `FFA_INTERRUPT` and `FFA_RUN` interfaces.
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200184#[derive(Debug, Eq, PartialEq, Clone, Copy)]
Balint Dobszay3aad9572025-01-17 16:54:11 +0100185pub struct TargetInfo {
186 pub endpoint_id: u16,
187 pub vcpu_id: u16,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200188}
189
Balint Dobszay3aad9572025-01-17 16:54:11 +0100190impl From<u32> for TargetInfo {
191 fn from(value: u32) -> Self {
192 Self {
193 endpoint_id: (value >> 16) as u16,
194 vcpu_id: value as u16,
195 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200196 }
197}
198
Balint Dobszay3aad9572025-01-17 16:54:11 +0100199impl From<TargetInfo> for u32 {
200 fn from(value: TargetInfo) -> Self {
Balint Dobszaye9a3e762025-02-26 17:29:57 +0100201 ((value.endpoint_id as u32) << 16) | value.vcpu_id as u32
Andrew Walbran0d315812024-11-25 15:36:36 +0000202 }
Balint Dobszay3aad9572025-01-17 16:54:11 +0100203}
Andrew Walbran0d315812024-11-25 15:36:36 +0000204
Imre Kis839eaef2025-04-11 17:38:36 +0200205/// Generic arguments of the `FFA_SUCCESS` interface. The interpretation of the arguments depends on
206/// the interface that initiated the request. The application code has knowledge of the request, so
207/// it has to convert `SuccessArgs` into/from a specific success args structure that matches the
208/// request.
Imre Kis4e9d8bc2025-04-10 13:48:26 +0200209///
210/// The current specialized success arguments types are:
211/// * `FFA_FEATURES` - [`SuccessArgsFeatures`]
Imre Kisbbef2872025-04-10 14:11:29 +0200212/// * `FFA_ID_GET` - [`SuccessArgsIdGet`]
213/// * `FFA_SPM_ID_GET` - [`SuccessArgsSpmIdGet`]
Imre Kis61c34092025-04-10 14:14:38 +0200214/// * `FFA_PARTITION_INFO_GET` - [`partition_info::SuccessArgsPartitionInfoGet`]
Imre Kis9959e062025-04-10 14:16:10 +0200215/// * `FFA_NOTIFICATION_GET` - [`SuccessArgsNotificationGet`]
Imre Kis787c5002025-04-10 14:25:51 +0200216/// * `FFA_NOTIFICATION_INFO_GET_32` - [`SuccessArgsNotificationInfoGet32`]
217/// * `FFA_NOTIFICATION_INFO_GET_64` - [`SuccessArgsNotificationInfoGet64`]
Balint Dobszay3aad9572025-01-17 16:54:11 +0100218#[derive(Debug, Eq, PartialEq, Clone, Copy)]
219pub enum SuccessArgs {
Imre Kis54773b62025-04-10 13:47:39 +0200220 Args32([u32; 6]),
221 Args64([u64; 6]),
222 Args64_2([u64; 16]),
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200223}
224
Imre Kis839eaef2025-04-11 17:38:36 +0200225impl SuccessArgs {
226 fn try_get_args32(self) -> Result<[u32; 6], Error> {
227 match self {
228 SuccessArgs::Args32(args) => Ok(args),
229 SuccessArgs::Args64(_) | SuccessArgs::Args64_2(_) => {
230 Err(Error::InvalidSuccessArgsVariant)
231 }
232 }
233 }
234
235 fn try_get_args64(self) -> Result<[u64; 6], Error> {
236 match self {
237 SuccessArgs::Args64(args) => Ok(args),
238 SuccessArgs::Args32(_) | SuccessArgs::Args64_2(_) => {
239 Err(Error::InvalidSuccessArgsVariant)
240 }
241 }
242 }
243
244 fn try_get_args64_2(self) -> Result<[u64; 16], Error> {
245 match self {
246 SuccessArgs::Args64_2(args) => Ok(args),
247 SuccessArgs::Args32(_) | SuccessArgs::Args64(_) => {
248 Err(Error::InvalidSuccessArgsVariant)
249 }
250 }
251 }
252}
253
Tomás González17b92442025-03-10 16:45:04 +0000254/// Entrypoint address argument for `FFA_SECONDARY_EP_REGISTER` interface.
255#[derive(Debug, Eq, PartialEq, Clone, Copy)]
256pub enum SecondaryEpRegisterAddr {
257 Addr32(u32),
258 Addr64(u64),
259}
260
Balint Dobszaya5846852025-02-26 15:38:53 +0100261/// Version number of the FF-A implementation, `.0` is the major, `.1` is minor the version.
Balint Dobszayde0dc802025-02-28 14:16:52 +0100262#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord)]
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200263pub struct Version(pub u16, pub u16);
264
Tomás González1f794352025-03-03 16:47:06 +0000265impl Version {
Tomás González83146af2025-03-04 11:32:41 +0000266 // The FF-A spec mandates that bit[31] of a version number must be 0
267 const MBZ_BITS: u32 = 1 << 31;
268
Tomás González1f794352025-03-03 16:47:06 +0000269 /// Returns whether the caller's version (self) is compatible with the callee's version (input
270 /// parameter)
271 pub fn is_compatible_to(&self, callee_version: &Version) -> bool {
272 self.0 == callee_version.0 && self.1 <= callee_version.1
273 }
274}
275
Tomás González83146af2025-03-04 11:32:41 +0000276impl TryFrom<u32> for Version {
277 type Error = Error;
278
279 fn try_from(val: u32) -> Result<Self, Self::Error> {
280 if (val & Self::MBZ_BITS) != 0 {
281 Err(Error::InvalidVersion(val))
282 } else {
283 Ok(Self((val >> 16) as u16, val as u16))
284 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200285 }
286}
287
288impl From<Version> for u32 {
289 fn from(v: Version) -> Self {
Tomás González83146af2025-03-04 11:32:41 +0000290 let v_u32 = ((v.0 as u32) << 16) | v.1 as u32;
291 assert!(v_u32 & Version::MBZ_BITS == 0);
292 v_u32
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200293 }
294}
295
Andrew Walbran19970ba2024-11-25 15:35:00 +0000296impl Display for Version {
297 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
298 write!(f, "{}.{}", self.0, self.1)
299 }
300}
301
302impl Debug for Version {
303 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
304 Display::fmt(self, f)
305 }
306}
307
Balint Dobszaya5846852025-02-26 15:38:53 +0100308/// Feature IDs used by the `FFA_FEATURES` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100309#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
310#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedFeatureId))]
311#[repr(u8)]
312pub enum FeatureId {
313 NotificationPendingInterrupt = 0x1,
314 ScheduleReceiverInterrupt = 0x2,
315 ManagedExitInterrupt = 0x3,
316}
Balint Dobszayc8802492025-01-15 18:11:39 +0100317
Balint Dobszaya5846852025-02-26 15:38:53 +0100318/// Arguments for the `FFA_FEATURES` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100319#[derive(Debug, Eq, PartialEq, Clone, Copy)]
320pub enum Feature {
321 FuncId(FuncId),
322 FeatureId(FeatureId),
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100323 Unknown(u32),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100324}
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200325
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100326impl From<u32> for Feature {
327 fn from(value: u32) -> Self {
328 // Bit[31] is set for all valid FF-A function IDs so we don't have to check it separately
329 if let Ok(func_id) = value.try_into() {
330 Self::FuncId(func_id)
331 } else if let Ok(feat_id) = (value as u8).try_into() {
332 Self::FeatureId(feat_id)
Balint Dobszay3aad9572025-01-17 16:54:11 +0100333 } else {
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100334 Self::Unknown(value)
335 }
Balint Dobszay3aad9572025-01-17 16:54:11 +0100336 }
337}
338
339impl From<Feature> for u32 {
340 fn from(value: Feature) -> Self {
341 match value {
342 Feature::FuncId(func_id) => (1 << 31) | func_id as u32,
343 Feature::FeatureId(feature_id) => feature_id as u32,
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100344 Feature::Unknown(id) => panic!("Unknown feature or function ID {:#x?}", id),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100345 }
346 }
347}
348
Imre Kis4e9d8bc2025-04-10 13:48:26 +0200349/// `FFA_FEATURES` specific success argument structure. This type needs further specialization based
350/// on 'FF-A function ID or Feature ID' field of the preceeding `FFA_FEATURES` request.
351#[derive(Debug, Eq, PartialEq, Clone, Copy)]
352pub struct SuccessArgsFeatures {
353 pub properties: [u32; 2],
354}
355
356impl From<SuccessArgsFeatures> for SuccessArgs {
357 fn from(value: SuccessArgsFeatures) -> Self {
358 Self::Args32([value.properties[0], value.properties[1], 0, 0, 0, 0])
359 }
360}
361
362impl TryFrom<SuccessArgs> for SuccessArgsFeatures {
363 type Error = Error;
364
365 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
366 let args = value.try_get_args32()?;
367
368 Ok(Self {
369 properties: [args[0], args[1]],
370 })
371 }
372}
373
Balint Dobszaya5846852025-02-26 15:38:53 +0100374/// RXTX buffer descriptor, used by `FFA_RXTX_MAP`.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100375#[derive(Debug, Eq, PartialEq, Clone, Copy)]
376pub enum RxTxAddr {
377 Addr32 { rx: u32, tx: u32 },
378 Addr64 { rx: u64, tx: u64 },
379}
380
Imre Kisbbef2872025-04-10 14:11:29 +0200381/// `FFA_ID_GET` specific success argument structure.
382#[derive(Debug, Eq, PartialEq, Clone, Copy)]
383pub struct SuccessArgsIdGet {
384 pub id: u16,
385}
386
387impl From<SuccessArgsIdGet> for SuccessArgs {
388 fn from(value: SuccessArgsIdGet) -> Self {
389 SuccessArgs::Args32([value.id as u32, 0, 0, 0, 0, 0])
390 }
391}
392
393impl TryFrom<SuccessArgs> for SuccessArgsIdGet {
394 type Error = Error;
395
396 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
397 let args = value.try_get_args32()?;
398 Ok(Self { id: args[0] as u16 })
399 }
400}
401
402/// `FFA_SPM_ID_GET` specific success argument structure.
403#[derive(Debug, Eq, PartialEq, Clone, Copy)]
404pub struct SuccessArgsSpmIdGet {
405 pub id: u16,
406}
407
408impl From<SuccessArgsSpmIdGet> for SuccessArgs {
409 fn from(value: SuccessArgsSpmIdGet) -> Self {
410 SuccessArgs::Args32([value.id as u32, 0, 0, 0, 0, 0])
411 }
412}
413
414impl TryFrom<SuccessArgs> for SuccessArgsSpmIdGet {
415 type Error = Error;
416
417 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
418 let args = value.try_get_args32()?;
419 Ok(Self { id: args[0] as u16 })
420 }
421}
422
Imre Kise295adb2025-04-10 13:26:28 +0200423/// Flags of the `FFA_PARTITION_INFO_GET` interface.
424#[derive(Debug, Eq, PartialEq, Clone, Copy)]
425pub struct PartitionInfoGetFlags {
426 pub count_only: bool,
427}
428
429impl PartitionInfoGetFlags {
430 const RETURN_INFORMATION_TYPE_FLAG: u32 = 1 << 0;
431 const MBZ_BITS: u32 = 0xffff_fffe;
432}
433
434impl TryFrom<u32> for PartitionInfoGetFlags {
435 type Error = Error;
436
437 fn try_from(val: u32) -> Result<Self, Self::Error> {
438 if (val & Self::MBZ_BITS) != 0 {
439 Err(Error::InvalidPartitionInfoGetFlag(val))
440 } else {
441 Ok(Self {
442 count_only: val & Self::RETURN_INFORMATION_TYPE_FLAG != 0,
443 })
444 }
445 }
446}
447
448impl From<PartitionInfoGetFlags> for u32 {
449 fn from(flags: PartitionInfoGetFlags) -> Self {
450 let mut bits: u32 = 0;
451 if flags.count_only {
452 bits |= PartitionInfoGetFlags::RETURN_INFORMATION_TYPE_FLAG;
453 }
454 bits
455 }
456}
457
Tomás González4d5b0ba2025-03-03 17:15:55 +0000458/// Composite type for capturing success and error return codes for the VM availability messages.
459///
460/// Error codes are handled by the `FfaError` type. Having a separate type for errors helps using
461/// `Result<(), FfaError>`. If a single type would include both success and error values,
462/// then `Err(FfaError::Success)` would be incomprehensible.
463#[derive(Debug, Eq, PartialEq, Clone, Copy)]
464pub enum VmAvailabilityStatus {
465 Success,
466 Error(FfaError),
467}
468
469impl TryFrom<i32> for VmAvailabilityStatus {
470 type Error = Error;
471 fn try_from(value: i32) -> Result<Self, <Self as TryFrom<i32>>::Error> {
472 Ok(match value {
473 0 => Self::Success,
474 error_code => Self::Error(FfaError::try_from(error_code)?),
475 })
476 }
477}
478
479impl From<VmAvailabilityStatus> for i32 {
480 fn from(value: VmAvailabilityStatus) -> Self {
481 match value {
482 VmAvailabilityStatus::Success => 0,
483 VmAvailabilityStatus::Error(error_code) => error_code.into(),
484 }
485 }
486}
487
488/// Arguments for the Power Warm Boot `FFA_MSG_SEND_DIRECT_REQ` interface.
489#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
490#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedWarmBootType))]
491#[repr(u32)]
492pub enum WarmBootType {
493 ExitFromSuspend = 0,
494 ExitFromLowPower = 1,
495}
496
Balint Dobszaya5846852025-02-26 15:38:53 +0100497/// Arguments for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}` interfaces.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100498#[derive(Debug, Eq, PartialEq, Clone, Copy)]
499pub enum DirectMsgArgs {
500 Args32([u32; 5]),
501 Args64([u64; 5]),
Tomás González4d5b0ba2025-03-03 17:15:55 +0000502 /// Message for forwarding FFA_VERSION call from Normal world to the SPMC
503 VersionReq {
504 version: Version,
505 },
506 /// Response message to forwarded FFA_VERSION call from the Normal world
507 /// Contains the version returned by the SPMC or None
508 VersionResp {
509 version: Option<Version>,
510 },
511 /// Message for a power management operation initiated by a PSCI function
512 PowerPsciReq32 {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000513 // params[i]: Input parameter in w[i] in PSCI function invocation at EL3.
Tomás González67f92c72025-03-20 16:50:42 +0000514 // params[0]: Function ID.
515 params: [u32; 4],
Tomás González4d5b0ba2025-03-03 17:15:55 +0000516 },
517 /// Message for a power management operation initiated by a PSCI function
518 PowerPsciReq64 {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000519 // params[i]: Input parameter in x[i] in PSCI function invocation at EL3.
Tomás González67f92c72025-03-20 16:50:42 +0000520 // params[0]: Function ID.
521 params: [u64; 4],
Tomás González4d5b0ba2025-03-03 17:15:55 +0000522 },
523 /// Message for a warm boot
524 PowerWarmBootReq {
525 boot_type: WarmBootType,
526 },
527 /// Response message to indicate return status of the last power management request message
528 /// Return error code SUCCESS or DENIED as defined in PSCI spec. Caller is left to do the
529 /// parsing of the return status.
530 PowerPsciResp {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000531 psci_status: i32,
532 },
533 /// Message to signal creation of a VM
534 VmCreated {
535 // Globally unique Handle to identify a memory region that contains IMPLEMENTATION DEFINED
536 // information associated with the created VM.
537 // The invalid memory region handle must be specified by the Hypervisor if this field is not
538 // used.
539 handle: memory_management::Handle,
540 vm_id: u16,
541 },
542 /// Message to acknowledge creation of a VM
543 VmCreatedAck {
544 sp_status: VmAvailabilityStatus,
545 },
546 /// Message to signal destruction of a VM
547 VmDestructed {
548 // Globally unique Handle to identify a memory region that contains IMPLEMENTATION DEFINED
549 // information associated with the created VM.
550 // The invalid memory region handle must be specified by the Hypervisor if this field is not
551 // used.
552 handle: memory_management::Handle,
553 vm_id: u16,
554 },
555 /// Message to acknowledge destruction of a VM
556 VmDestructedAck {
557 sp_status: VmAvailabilityStatus,
558 },
559}
560
561impl DirectMsgArgs {
562 // Flags for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}` interfaces.
563
564 const FWK_MSG_BITS: u32 = 1 << 31;
565 const VERSION_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b1000;
566 const VERSION_RESP: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b1001;
567 const POWER_PSCI_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS;
568 const POWER_WARM_BOOT_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0001;
569 const POWER_PSCI_RESP: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0010;
570 const VM_CREATED: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0100;
571 const VM_CREATED_ACK: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0101;
572 const VM_DESTRUCTED: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0110;
573 const VM_DESTRUCTED_ACK: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0111;
Balint Dobszay3aad9572025-01-17 16:54:11 +0100574}
575
Balint Dobszayde0dc802025-02-28 14:16:52 +0100576/// Arguments for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}2` interfaces.
577#[derive(Debug, Eq, PartialEq, Clone, Copy)]
578pub struct DirectMsg2Args([u64; 14]);
579
Tomás González092202a2025-03-05 11:56:45 +0000580#[derive(Debug, Eq, PartialEq, Clone, Copy)]
581pub struct MsgWaitFlags {
582 retain_rx_buffer: bool,
583}
584
585impl MsgWaitFlags {
586 const RETAIN_RX_BUFFER: u32 = 0x01;
587 const MBZ_BITS: u32 = 0xfffe;
588}
589
590impl TryFrom<u32> for MsgWaitFlags {
591 type Error = Error;
592
593 fn try_from(val: u32) -> Result<Self, Self::Error> {
594 if (val & Self::MBZ_BITS) != 0 {
595 Err(Error::InvalidMsgWaitFlag(val))
596 } else {
597 Ok(MsgWaitFlags {
598 retain_rx_buffer: val & Self::RETAIN_RX_BUFFER != 0,
599 })
600 }
601 }
602}
603
604impl From<MsgWaitFlags> for u32 {
605 fn from(flags: MsgWaitFlags) -> Self {
606 let mut bits: u32 = 0;
607 if flags.retain_rx_buffer {
608 bits |= MsgWaitFlags::RETAIN_RX_BUFFER;
609 }
610 bits
611 }
612}
613
Balint Dobszaya5846852025-02-26 15:38:53 +0100614/// Descriptor for a dynamically allocated memory buffer that contains the memory transaction
Tomás Gonzálezf268e322025-03-05 11:18:11 +0000615/// descriptor.
616///
617/// Used by `FFA_MEM_{DONATE,LEND,SHARE,RETRIEVE_REQ}` interfaces, only when the TX buffer is not
618/// used to transmit the transaction descriptor.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100619#[derive(Debug, Eq, PartialEq, Clone, Copy)]
620pub enum MemOpBuf {
621 Buf32 { addr: u32, page_cnt: u32 },
622 Buf64 { addr: u64, page_cnt: u32 },
623}
624
Balint Dobszaya5846852025-02-26 15:38:53 +0100625/// Memory address argument for `FFA_MEM_PERM_{GET,SET}` interfaces.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100626#[derive(Debug, Eq, PartialEq, Clone, Copy)]
627pub enum MemAddr {
628 Addr32(u32),
629 Addr64(u64),
630}
631
Balint Dobszayde0dc802025-02-28 14:16:52 +0100632/// Argument for the `FFA_CONSOLE_LOG` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100633#[derive(Debug, Eq, PartialEq, Clone, Copy)]
634pub enum ConsoleLogChars {
635 Reg32([u32; 6]),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100636 Reg64([u64; 16]),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100637}
638
Tomás González7ffb6132025-04-03 12:28:58 +0100639#[derive(Debug, Eq, PartialEq, Clone, Copy)]
640pub struct NotificationBindFlags {
641 per_vcpu_notification: bool,
642}
643
644impl NotificationBindFlags {
645 const PER_VCPU_NOTIFICATION: u32 = 1;
646}
647
648impl From<NotificationBindFlags> for u32 {
649 fn from(flags: NotificationBindFlags) -> Self {
650 let mut bits: u32 = 0;
651 if flags.per_vcpu_notification {
652 bits |= NotificationBindFlags::PER_VCPU_NOTIFICATION;
653 }
654 bits
655 }
656}
657
658impl From<u32> for NotificationBindFlags {
659 fn from(flags: u32) -> Self {
660 Self {
661 per_vcpu_notification: flags & Self::PER_VCPU_NOTIFICATION != 0,
662 }
663 }
664}
665
666#[derive(Debug, Eq, PartialEq, Clone, Copy)]
667pub struct NotificationSetFlags {
668 delay_schedule_receiver: bool,
669 vcpu_id: Option<u16>,
670}
671
672impl NotificationSetFlags {
673 const PER_VCP_NOTIFICATION: u32 = 1 << 0;
674 const DELAY_SCHEDULE_RECEIVER: u32 = 1 << 1;
675 const VCPU_ID_SHIFT: u32 = 16;
676
677 const MBZ_BITS: u32 = 0xfffc;
678}
679
680impl From<NotificationSetFlags> for u32 {
681 fn from(flags: NotificationSetFlags) -> Self {
682 let mut bits: u32 = 0;
683
684 if flags.delay_schedule_receiver {
685 bits |= NotificationSetFlags::DELAY_SCHEDULE_RECEIVER;
686 }
687 if let Some(vcpu_id) = flags.vcpu_id {
688 bits |= NotificationSetFlags::PER_VCP_NOTIFICATION;
689 bits |= u32::from(vcpu_id) << NotificationSetFlags::VCPU_ID_SHIFT;
690 }
691
692 bits
693 }
694}
695
696impl TryFrom<u32> for NotificationSetFlags {
697 type Error = Error;
698
699 fn try_from(flags: u32) -> Result<Self, Self::Error> {
700 if (flags & Self::MBZ_BITS) != 0 {
701 return Err(Error::InvalidNotificationSetFlag(flags));
702 }
703
704 let tentative_vcpu_id = (flags >> Self::VCPU_ID_SHIFT) as u16;
705
706 let vcpu_id = if (flags & Self::PER_VCP_NOTIFICATION) != 0 {
707 Some(tentative_vcpu_id)
708 } else {
709 if tentative_vcpu_id != 0 {
710 return Err(Error::InvalidNotificationSetFlag(flags));
711 }
712 None
713 };
714
715 Ok(Self {
716 delay_schedule_receiver: (flags & Self::DELAY_SCHEDULE_RECEIVER) != 0,
717 vcpu_id,
718 })
719 }
720}
721
722#[derive(Debug, Eq, PartialEq, Clone, Copy)]
723pub struct NotificationGetFlags {
724 sp_bitmap_id: bool,
725 vm_bitmap_id: bool,
726 spm_bitmap_id: bool,
727 hyp_bitmap_id: bool,
728}
729
730impl NotificationGetFlags {
731 const SP_BITMAP_ID: u32 = 1;
732 const VM_BITMAP_ID: u32 = 1 << 1;
733 const SPM_BITMAP_ID: u32 = 1 << 2;
734 const HYP_BITMAP_ID: u32 = 1 << 3;
735}
736
737impl From<NotificationGetFlags> for u32 {
738 fn from(flags: NotificationGetFlags) -> Self {
739 let mut bits: u32 = 0;
740 if flags.sp_bitmap_id {
741 bits |= NotificationGetFlags::SP_BITMAP_ID;
742 }
743 if flags.vm_bitmap_id {
744 bits |= NotificationGetFlags::VM_BITMAP_ID;
745 }
746 if flags.spm_bitmap_id {
747 bits |= NotificationGetFlags::SPM_BITMAP_ID;
748 }
749 if flags.hyp_bitmap_id {
750 bits |= NotificationGetFlags::HYP_BITMAP_ID;
751 }
752 bits
753 }
754}
755
756impl From<u32> for NotificationGetFlags {
757 // This is a "from" instead of a "try_from" because Reserved Bits are SBZ, *not* MBZ.
758 fn from(flags: u32) -> Self {
759 Self {
760 sp_bitmap_id: (flags & Self::SP_BITMAP_ID) != 0,
761 vm_bitmap_id: (flags & Self::VM_BITMAP_ID) != 0,
762 spm_bitmap_id: (flags & Self::SPM_BITMAP_ID) != 0,
763 hyp_bitmap_id: (flags & Self::HYP_BITMAP_ID) != 0,
764 }
765 }
766}
767
Imre Kis9959e062025-04-10 14:16:10 +0200768/// `FFA_NOTIFICATION_GET` specific success argument structure.
769#[derive(Debug, Eq, PartialEq, Clone, Copy)]
770pub struct SuccessArgsNotificationGet {
771 pub sp_notifications: Option<u64>,
772 pub vm_notifications: Option<u64>,
773 pub spm_notifications: Option<u32>,
774 pub hypervisor_notifications: Option<u32>,
775}
776
777impl From<SuccessArgsNotificationGet> for SuccessArgs {
778 fn from(value: SuccessArgsNotificationGet) -> Self {
779 let mut args = [0; 6];
780
781 if let Some(bitmap) = value.sp_notifications {
782 args[0] = bitmap as u32;
783 args[1] = (bitmap >> 32) as u32;
784 }
785
786 if let Some(bitmap) = value.vm_notifications {
787 args[2] = bitmap as u32;
788 args[3] = (bitmap >> 32) as u32;
789 }
790
791 if let Some(bitmap) = value.spm_notifications {
792 args[4] = bitmap;
793 }
794
795 if let Some(bitmap) = value.hypervisor_notifications {
796 args[5] = bitmap;
797 }
798
799 Self::Args32(args)
800 }
801}
802
803impl TryFrom<(NotificationGetFlags, SuccessArgs)> for SuccessArgsNotificationGet {
804 type Error = Error;
805
806 fn try_from(value: (NotificationGetFlags, SuccessArgs)) -> Result<Self, Self::Error> {
807 let (flags, value) = value;
808 let args = value.try_get_args32()?;
809
810 let sp_notifications = if flags.sp_bitmap_id {
811 Some(u64::from(args[0]) | (u64::from(args[1]) << 32))
812 } else {
813 None
814 };
815
816 let vm_notifications = if flags.vm_bitmap_id {
817 Some(u64::from(args[2]) | (u64::from(args[3]) << 32))
818 } else {
819 None
820 };
821
822 let spm_notifications = if flags.spm_bitmap_id {
823 Some(args[4])
824 } else {
825 None
826 };
827
828 let hypervisor_notifications = if flags.hyp_bitmap_id {
829 Some(args[5])
830 } else {
831 None
832 };
833
834 Ok(Self {
835 sp_notifications,
836 vm_notifications,
837 spm_notifications,
838 hypervisor_notifications,
839 })
840 }
841}
Imre Kis787c5002025-04-10 14:25:51 +0200842
843/// `FFA_NOTIFICATION_INFO_GET` specific success argument structure. The `MAX_COUNT` parameter
844/// depends on the 32-bit or 64-bit packing.
845#[derive(Debug, Eq, PartialEq, Clone, Copy)]
846pub struct SuccessArgsNotificationInfoGet<const MAX_COUNT: usize> {
847 pub more_pending_notifications: bool,
848 list_count: usize,
849 id_counts: [u8; MAX_COUNT],
850 ids: [u16; MAX_COUNT],
851}
852
853impl<const MAX_COUNT: usize> Default for SuccessArgsNotificationInfoGet<MAX_COUNT> {
854 fn default() -> Self {
855 Self {
856 more_pending_notifications: false,
857 list_count: 0,
858 id_counts: [0; MAX_COUNT],
859 ids: [0; MAX_COUNT],
860 }
861 }
862}
863
864impl<const MAX_COUNT: usize> SuccessArgsNotificationInfoGet<MAX_COUNT> {
865 const MORE_PENDING_NOTIFICATIONS_FLAG: u64 = 1 << 0;
866 const LIST_COUNT_SHIFT: usize = 7;
867 const LIST_COUNT_MASK: u64 = 0x1f;
868 const ID_COUNT_SHIFT: usize = 12;
869 const ID_COUNT_MASK: u64 = 0x03;
870 const ID_COUNT_BITS: usize = 2;
871
872 pub fn add_list(&mut self, endpoint: u16, vcpu_ids: &[u16]) -> Result<(), Error> {
873 if self.list_count >= MAX_COUNT || vcpu_ids.len() > Self::ID_COUNT_MASK as usize {
874 return Err(Error::InvalidNotificationCount);
875 }
876
877 // Each list contains at least one ID: the partition ID, followed by vCPU IDs. The number
878 // of vCPU IDs is recorded in `id_counts`.
879 let mut current_id_index = self.list_count + self.id_counts.iter().sum::<u8>() as usize;
880 if current_id_index + 1 + vcpu_ids.len() > MAX_COUNT {
881 // The new list does not fit into the available space for IDs.
882 return Err(Error::InvalidNotificationCount);
883 }
884
885 self.id_counts[self.list_count] = vcpu_ids.len() as u8;
886 self.list_count += 1;
887
888 // The first ID is the endpoint ID.
889 self.ids[current_id_index] = endpoint;
890 current_id_index += 1;
891
892 // Insert the vCPU IDs.
893 self.ids[current_id_index..current_id_index + vcpu_ids.len()].copy_from_slice(vcpu_ids);
894
895 Ok(())
896 }
897
898 pub fn iter(&self) -> NotificationInfoGetIterator<'_> {
899 NotificationInfoGetIterator {
900 list_index: 0,
901 id_index: 0,
902 id_count: &self.id_counts[0..self.list_count],
903 ids: &self.ids,
904 }
905 }
906
907 /// Pack flags field and IDs.
908 fn pack(self) -> (u64, [u16; MAX_COUNT]) {
909 let mut flags = if self.more_pending_notifications {
910 Self::MORE_PENDING_NOTIFICATIONS_FLAG
911 } else {
912 0
913 };
914
915 flags |= (self.list_count as u64) << Self::LIST_COUNT_SHIFT;
916 for (count, shift) in self.id_counts.iter().take(self.list_count).zip(
917 (Self::ID_COUNT_SHIFT..Self::ID_COUNT_SHIFT + Self::ID_COUNT_BITS * MAX_COUNT)
918 .step_by(Self::ID_COUNT_BITS),
919 ) {
920 flags |= u64::from(*count) << shift;
921 }
922
923 (flags, self.ids)
924 }
925
926 /// Unpack flags field and IDs.
927 fn unpack(flags: u64, ids: [u16; MAX_COUNT]) -> Result<Self, Error> {
928 let count_of_lists = ((flags >> Self::LIST_COUNT_SHIFT) & Self::LIST_COUNT_MASK) as usize;
929
930 if count_of_lists > MAX_COUNT {
931 return Err(Error::InvalidNotificationCount);
932 }
933
934 let mut count_of_ids = [0; MAX_COUNT];
935 let mut count_of_ids_bits = flags >> Self::ID_COUNT_SHIFT;
936
937 for id in count_of_ids.iter_mut().take(count_of_lists) {
938 *id = (count_of_ids_bits & Self::ID_COUNT_MASK) as u8;
939 count_of_ids_bits >>= Self::ID_COUNT_BITS;
940 }
941
942 Ok(Self {
943 more_pending_notifications: (flags & Self::MORE_PENDING_NOTIFICATIONS_FLAG) != 0,
944 list_count: count_of_lists,
945 id_counts: count_of_ids,
946 ids,
947 })
948 }
949}
950
951/// `FFA_NOTIFICATION_INFO_GET_32` specific success argument structure.
952pub type SuccessArgsNotificationInfoGet32 = SuccessArgsNotificationInfoGet<10>;
953
954impl From<SuccessArgsNotificationInfoGet32> for SuccessArgs {
955 fn from(value: SuccessArgsNotificationInfoGet32) -> Self {
956 let (flags, ids) = value.pack();
957 let id_regs: [u32; 5] = transmute!(ids);
958
959 let mut args = [0; 6];
960 args[0] = flags as u32;
961 args[1..6].copy_from_slice(&id_regs);
962
963 SuccessArgs::Args32(args)
964 }
965}
966
967impl TryFrom<SuccessArgs> for SuccessArgsNotificationInfoGet32 {
968 type Error = Error;
969
970 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
971 let args = value.try_get_args32()?;
972 let flags = args[0].into();
973 let id_regs: [u32; 5] = args[1..6].try_into().unwrap();
974 Self::unpack(flags, transmute!(id_regs))
975 }
976}
977
978/// `FFA_NOTIFICATION_INFO_GET_64` specific success argument structure.
979pub type SuccessArgsNotificationInfoGet64 = SuccessArgsNotificationInfoGet<20>;
980
981impl From<SuccessArgsNotificationInfoGet64> for SuccessArgs {
982 fn from(value: SuccessArgsNotificationInfoGet64) -> Self {
983 let (flags, ids) = value.pack();
984 let id_regs: [u64; 5] = transmute!(ids);
985
986 let mut args = [0; 6];
987 args[0] = flags;
988 args[1..6].copy_from_slice(&id_regs);
989
990 SuccessArgs::Args64(args)
991 }
992}
993
994impl TryFrom<SuccessArgs> for SuccessArgsNotificationInfoGet64 {
995 type Error = Error;
996
997 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
998 let args = value.try_get_args64()?;
999 let flags = args[0];
1000 let id_regs: [u64; 5] = args[1..6].try_into().unwrap();
1001 Self::unpack(flags, transmute!(id_regs))
1002 }
1003}
1004
1005pub struct NotificationInfoGetIterator<'a> {
1006 list_index: usize,
1007 id_index: usize,
1008 id_count: &'a [u8],
1009 ids: &'a [u16],
1010}
1011
1012impl<'a> Iterator for NotificationInfoGetIterator<'a> {
1013 type Item = (u16, &'a [u16]);
1014
1015 fn next(&mut self) -> Option<Self::Item> {
1016 if self.list_index < self.id_count.len() {
1017 let partition_id = self.ids[self.id_index];
1018 let id_range =
1019 (self.id_index + 1)..=(self.id_index + self.id_count[self.list_index] as usize);
1020
1021 self.id_index += 1 + self.id_count[self.list_index] as usize;
1022 self.list_index += 1;
1023
1024 Some((partition_id, &self.ids[id_range]))
1025 } else {
1026 None
1027 }
1028 }
1029}
1030
Tomás Gonzálezf268e322025-03-05 11:18:11 +00001031/// FF-A "message types", the terminology used by the spec is "interfaces".
1032///
1033/// The interfaces are used by FF-A components for communication at an FF-A instance. The spec also
1034/// describes the valid FF-A instances and conduits for each interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +01001035#[derive(Debug, Eq, PartialEq, Clone, Copy)]
1036pub enum Interface {
1037 Error {
1038 target_info: TargetInfo,
1039 error_code: FfaError,
Balint Dobszayb727aab2025-04-07 10:24:59 +02001040 error_arg: u32,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001041 },
1042 Success {
1043 target_info: u32,
1044 args: SuccessArgs,
1045 },
1046 Interrupt {
1047 target_info: TargetInfo,
1048 interrupt_id: u32,
1049 },
1050 Version {
1051 input_version: Version,
1052 },
1053 VersionOut {
1054 output_version: Version,
1055 },
1056 Features {
1057 feat_id: Feature,
1058 input_properties: u32,
1059 },
1060 RxAcquire {
1061 vm_id: u16,
1062 },
1063 RxRelease {
1064 vm_id: u16,
1065 },
1066 RxTxMap {
1067 addr: RxTxAddr,
1068 page_cnt: u32,
1069 },
1070 RxTxUnmap {
1071 id: u16,
1072 },
1073 PartitionInfoGet {
1074 uuid: Uuid,
Imre Kise295adb2025-04-10 13:26:28 +02001075 flags: PartitionInfoGetFlags,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001076 },
Tomás González0a058bc2025-03-11 11:20:55 +00001077 PartitionInfoGetRegs {
1078 uuid: Uuid,
1079 start_index: u16,
1080 info_tag: u16,
1081 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001082 IdGet,
1083 SpmIdGet,
Tomás González092202a2025-03-05 11:56:45 +00001084 MsgWait {
1085 flags: Option<MsgWaitFlags>,
1086 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001087 Yield,
1088 Run {
1089 target_info: TargetInfo,
1090 },
1091 NormalWorldResume,
Tomás González17b92442025-03-10 16:45:04 +00001092 SecondaryEpRegister {
1093 entrypoint: SecondaryEpRegisterAddr,
1094 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001095 MsgSend2 {
1096 sender_vm_id: u16,
1097 flags: u32,
1098 },
1099 MsgSendDirectReq {
1100 src_id: u16,
1101 dst_id: u16,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001102 args: DirectMsgArgs,
1103 },
1104 MsgSendDirectResp {
1105 src_id: u16,
1106 dst_id: u16,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001107 args: DirectMsgArgs,
1108 },
Balint Dobszayde0dc802025-02-28 14:16:52 +01001109 MsgSendDirectReq2 {
1110 src_id: u16,
1111 dst_id: u16,
1112 uuid: Uuid,
1113 args: DirectMsg2Args,
1114 },
1115 MsgSendDirectResp2 {
1116 src_id: u16,
1117 dst_id: u16,
1118 args: DirectMsg2Args,
1119 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001120 MemDonate {
1121 total_len: u32,
1122 frag_len: u32,
1123 buf: Option<MemOpBuf>,
1124 },
1125 MemLend {
1126 total_len: u32,
1127 frag_len: u32,
1128 buf: Option<MemOpBuf>,
1129 },
1130 MemShare {
1131 total_len: u32,
1132 frag_len: u32,
1133 buf: Option<MemOpBuf>,
1134 },
1135 MemRetrieveReq {
1136 total_len: u32,
1137 frag_len: u32,
1138 buf: Option<MemOpBuf>,
1139 },
1140 MemRetrieveResp {
1141 total_len: u32,
1142 frag_len: u32,
1143 },
1144 MemRelinquish,
1145 MemReclaim {
1146 handle: memory_management::Handle,
1147 flags: u32,
1148 },
1149 MemPermGet {
1150 addr: MemAddr,
Balint Dobszayde0dc802025-02-28 14:16:52 +01001151 page_cnt: Option<u32>,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001152 },
1153 MemPermSet {
1154 addr: MemAddr,
1155 page_cnt: u32,
1156 mem_perm: u32,
1157 },
1158 ConsoleLog {
1159 char_cnt: u8,
1160 char_lists: ConsoleLogChars,
1161 },
Tomás González7ffb6132025-04-03 12:28:58 +01001162 NotificationBitmapCreate {
1163 vm_id: u16,
1164 vcpu_cnt: u32,
1165 },
1166 NotificationBitmapDestroy {
1167 vm_id: u16,
1168 },
1169 NotificationBind {
1170 sender_id: u16,
1171 receiver_id: u16,
1172 flags: NotificationBindFlags,
1173 bitmap: u64,
1174 },
1175 NotificationUnBind {
1176 sender_id: u16,
1177 receiver_id: u16,
1178 bitmap: u64,
1179 },
1180 NotificationSet {
1181 sender_id: u16,
1182 receiver_id: u16,
1183 flags: NotificationSetFlags,
1184 bitmap: u64,
1185 },
1186 NotificationGet {
1187 vcpu_id: u16,
1188 endpoint_id: u16,
1189 flags: NotificationGetFlags,
1190 },
1191 NotificationInfoGet {
1192 is_32bit: bool,
1193 },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01001194 El3IntrHandle,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001195}
1196
Balint Dobszayde0dc802025-02-28 14:16:52 +01001197impl Interface {
1198 /// Returns the function ID for the call, if it has one.
1199 pub fn function_id(&self) -> Option<FuncId> {
1200 match self {
1201 Interface::Error { .. } => Some(FuncId::Error),
1202 Interface::Success { args, .. } => match args {
Imre Kis54773b62025-04-10 13:47:39 +02001203 SuccessArgs::Args32(..) => Some(FuncId::Success32),
1204 SuccessArgs::Args64(..) | SuccessArgs::Args64_2(..) => Some(FuncId::Success64),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001205 },
1206 Interface::Interrupt { .. } => Some(FuncId::Interrupt),
1207 Interface::Version { .. } => Some(FuncId::Version),
1208 Interface::VersionOut { .. } => None,
1209 Interface::Features { .. } => Some(FuncId::Features),
1210 Interface::RxAcquire { .. } => Some(FuncId::RxAcquire),
1211 Interface::RxRelease { .. } => Some(FuncId::RxRelease),
1212 Interface::RxTxMap { addr, .. } => match addr {
1213 RxTxAddr::Addr32 { .. } => Some(FuncId::RxTxMap32),
1214 RxTxAddr::Addr64 { .. } => Some(FuncId::RxTxMap64),
1215 },
1216 Interface::RxTxUnmap { .. } => Some(FuncId::RxTxUnmap),
1217 Interface::PartitionInfoGet { .. } => Some(FuncId::PartitionInfoGet),
Tomás González0a058bc2025-03-11 11:20:55 +00001218 Interface::PartitionInfoGetRegs { .. } => Some(FuncId::PartitionInfoGetRegs),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001219 Interface::IdGet => Some(FuncId::IdGet),
1220 Interface::SpmIdGet => Some(FuncId::SpmIdGet),
Tomás González092202a2025-03-05 11:56:45 +00001221 Interface::MsgWait { .. } => Some(FuncId::MsgWait),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001222 Interface::Yield => Some(FuncId::Yield),
1223 Interface::Run { .. } => Some(FuncId::Run),
1224 Interface::NormalWorldResume => Some(FuncId::NormalWorldResume),
Tomás González17b92442025-03-10 16:45:04 +00001225 Interface::SecondaryEpRegister { entrypoint } => match entrypoint {
1226 SecondaryEpRegisterAddr::Addr32 { .. } => Some(FuncId::SecondaryEpRegister32),
1227 SecondaryEpRegisterAddr::Addr64 { .. } => Some(FuncId::SecondaryEpRegister64),
1228 },
Balint Dobszayde0dc802025-02-28 14:16:52 +01001229 Interface::MsgSend2 { .. } => Some(FuncId::MsgSend2),
1230 Interface::MsgSendDirectReq { args, .. } => match args {
1231 DirectMsgArgs::Args32(_) => Some(FuncId::MsgSendDirectReq32),
1232 DirectMsgArgs::Args64(_) => Some(FuncId::MsgSendDirectReq64),
Tomás González4d5b0ba2025-03-03 17:15:55 +00001233 DirectMsgArgs::VersionReq { .. } => Some(FuncId::MsgSendDirectReq32),
1234 DirectMsgArgs::PowerPsciReq32 { .. } => Some(FuncId::MsgSendDirectReq32),
1235 DirectMsgArgs::PowerPsciReq64 { .. } => Some(FuncId::MsgSendDirectReq64),
1236 DirectMsgArgs::PowerWarmBootReq { .. } => Some(FuncId::MsgSendDirectReq32),
1237 DirectMsgArgs::VmCreated { .. } => Some(FuncId::MsgSendDirectReq32),
1238 DirectMsgArgs::VmDestructed { .. } => Some(FuncId::MsgSendDirectReq32),
1239 _ => None,
Balint Dobszayde0dc802025-02-28 14:16:52 +01001240 },
1241 Interface::MsgSendDirectResp { args, .. } => match args {
1242 DirectMsgArgs::Args32(_) => Some(FuncId::MsgSendDirectResp32),
1243 DirectMsgArgs::Args64(_) => Some(FuncId::MsgSendDirectResp64),
Tomás González4d5b0ba2025-03-03 17:15:55 +00001244 DirectMsgArgs::VersionResp { .. } => Some(FuncId::MsgSendDirectResp32),
1245 DirectMsgArgs::PowerPsciResp { .. } => Some(FuncId::MsgSendDirectResp32),
1246 DirectMsgArgs::VmCreatedAck { .. } => Some(FuncId::MsgSendDirectResp32),
1247 DirectMsgArgs::VmDestructedAck { .. } => Some(FuncId::MsgSendDirectResp32),
1248 _ => None,
Balint Dobszayde0dc802025-02-28 14:16:52 +01001249 },
1250 Interface::MsgSendDirectReq2 { .. } => Some(FuncId::MsgSendDirectReq64_2),
1251 Interface::MsgSendDirectResp2 { .. } => Some(FuncId::MsgSendDirectResp64_2),
1252 Interface::MemDonate { buf, .. } => match buf {
1253 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemDonate64),
1254 _ => Some(FuncId::MemDonate32),
1255 },
1256 Interface::MemLend { buf, .. } => match buf {
1257 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemLend64),
1258 _ => Some(FuncId::MemLend32),
1259 },
1260 Interface::MemShare { buf, .. } => match buf {
1261 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemShare64),
1262 _ => Some(FuncId::MemShare32),
1263 },
1264 Interface::MemRetrieveReq { buf, .. } => match buf {
1265 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemRetrieveReq64),
1266 _ => Some(FuncId::MemRetrieveReq32),
1267 },
1268 Interface::MemRetrieveResp { .. } => Some(FuncId::MemRetrieveResp),
1269 Interface::MemRelinquish => Some(FuncId::MemRelinquish),
1270 Interface::MemReclaim { .. } => Some(FuncId::MemReclaim),
1271 Interface::MemPermGet { addr, .. } => match addr {
1272 MemAddr::Addr32(_) => Some(FuncId::MemPermGet32),
1273 MemAddr::Addr64(_) => Some(FuncId::MemPermGet64),
1274 },
1275 Interface::MemPermSet { addr, .. } => match addr {
1276 MemAddr::Addr32(_) => Some(FuncId::MemPermSet32),
1277 MemAddr::Addr64(_) => Some(FuncId::MemPermSet64),
1278 },
1279 Interface::ConsoleLog { char_lists, .. } => match char_lists {
1280 ConsoleLogChars::Reg32(_) => Some(FuncId::ConsoleLog32),
1281 ConsoleLogChars::Reg64(_) => Some(FuncId::ConsoleLog64),
1282 },
Tomás González7ffb6132025-04-03 12:28:58 +01001283 Interface::NotificationBitmapCreate { .. } => Some(FuncId::NotificationBitmapCreate),
1284 Interface::NotificationBitmapDestroy { .. } => Some(FuncId::NotificationBitmapDestroy),
1285 Interface::NotificationBind { .. } => Some(FuncId::NotificationBind),
1286 Interface::NotificationUnBind { .. } => Some(FuncId::NotificationUnbind),
1287 Interface::NotificationSet { .. } => Some(FuncId::NotificationSet),
1288 Interface::NotificationGet { .. } => Some(FuncId::NotificationGet),
1289 Interface::NotificationInfoGet { is_32bit } => match is_32bit {
1290 true => Some(FuncId::NotificationInfoGet32),
1291 false => Some(FuncId::NotificationInfoGet64),
1292 },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01001293 Interface::El3IntrHandle => Some(FuncId::El3IntrHandle),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001294 }
1295 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01001296
Balint Dobszayde0dc802025-02-28 14:16:52 +01001297 /// Returns true if this is a 32-bit call, or false if it is a 64-bit call.
1298 pub fn is_32bit(&self) -> bool {
1299 // TODO: self should always have a function ID?
1300 self.function_id().unwrap().is_32bit()
1301 }
1302
1303 /// Parse interface from register contents. The caller must ensure that the `regs` argument has
1304 /// the correct length: 8 registers for FF-A v1.1 and lower, 18 registers for v1.2 and higher.
1305 pub fn from_regs(version: Version, regs: &[u64]) -> Result<Self, Error> {
1306 let reg_cnt = regs.len();
1307
1308 let msg = match reg_cnt {
1309 8 => {
1310 assert!(version <= Version(1, 1));
1311 Interface::unpack_regs8(version, regs.try_into().unwrap())?
1312 }
1313 18 => {
1314 assert!(version >= Version(1, 2));
1315 match FuncId::try_from(regs[0] as u32)? {
1316 FuncId::ConsoleLog64
1317 | FuncId::Success64
1318 | FuncId::MsgSendDirectReq64_2
Tomás González0a058bc2025-03-11 11:20:55 +00001319 | FuncId::MsgSendDirectResp64_2
1320 | FuncId::PartitionInfoGetRegs => {
Balint Dobszayde0dc802025-02-28 14:16:52 +01001321 Interface::unpack_regs18(version, regs.try_into().unwrap())?
1322 }
1323 _ => Interface::unpack_regs8(version, regs[..8].try_into().unwrap())?,
1324 }
1325 }
1326 _ => panic!(
1327 "Invalid number of registers ({}) for FF-A version {}",
1328 reg_cnt, version
1329 ),
1330 };
1331
1332 Ok(msg)
1333 }
1334
1335 fn unpack_regs8(version: Version, regs: &[u64; 8]) -> Result<Self, Error> {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001336 let fid = FuncId::try_from(regs[0] as u32)?;
1337
1338 let msg = match fid {
1339 FuncId::Error => Self::Error {
1340 target_info: (regs[1] as u32).into(),
1341 error_code: FfaError::try_from(regs[2] as i32)?,
Balint Dobszayb727aab2025-04-07 10:24:59 +02001342 error_arg: regs[3] as u32,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001343 },
1344 FuncId::Success32 => Self::Success {
1345 target_info: regs[1] as u32,
Imre Kis54773b62025-04-10 13:47:39 +02001346 args: SuccessArgs::Args32([
Balint Dobszay3aad9572025-01-17 16:54:11 +01001347 regs[2] as u32,
1348 regs[3] as u32,
1349 regs[4] as u32,
1350 regs[5] as u32,
1351 regs[6] as u32,
1352 regs[7] as u32,
1353 ]),
1354 },
1355 FuncId::Success64 => Self::Success {
1356 target_info: regs[1] as u32,
Imre Kis54773b62025-04-10 13:47:39 +02001357 args: SuccessArgs::Args64([regs[2], regs[3], regs[4], regs[5], regs[6], regs[7]]),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001358 },
1359 FuncId::Interrupt => Self::Interrupt {
1360 target_info: (regs[1] as u32).into(),
1361 interrupt_id: regs[2] as u32,
1362 },
1363 FuncId::Version => Self::Version {
Tomás González83146af2025-03-04 11:32:41 +00001364 input_version: (regs[1] as u32).try_into()?,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001365 },
1366 FuncId::Features => Self::Features {
Balint Dobszayc31e0b92025-03-03 20:16:56 +01001367 feat_id: (regs[1] as u32).into(),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001368 input_properties: regs[2] as u32,
1369 },
1370 FuncId::RxAcquire => Self::RxAcquire {
1371 vm_id: regs[1] as u16,
1372 },
1373 FuncId::RxRelease => Self::RxRelease {
1374 vm_id: regs[1] as u16,
1375 },
1376 FuncId::RxTxMap32 => {
1377 let addr = RxTxAddr::Addr32 {
1378 rx: regs[2] as u32,
1379 tx: regs[1] as u32,
1380 };
1381 let page_cnt = regs[3] as u32;
1382
1383 Self::RxTxMap { addr, page_cnt }
1384 }
1385 FuncId::RxTxMap64 => {
1386 let addr = RxTxAddr::Addr64 {
1387 rx: regs[2],
1388 tx: regs[1],
1389 };
1390 let page_cnt = regs[3] as u32;
1391
1392 Self::RxTxMap { addr, page_cnt }
1393 }
1394 FuncId::RxTxUnmap => Self::RxTxUnmap { id: regs[1] as u16 },
1395 FuncId::PartitionInfoGet => {
1396 let uuid_words = [
1397 regs[1] as u32,
1398 regs[2] as u32,
1399 regs[3] as u32,
1400 regs[4] as u32,
1401 ];
1402 let mut bytes: [u8; 16] = [0; 16];
1403 for (i, b) in uuid_words.iter().flat_map(|w| w.to_le_bytes()).enumerate() {
1404 bytes[i] = b;
1405 }
1406 Self::PartitionInfoGet {
1407 uuid: Uuid::from_bytes(bytes),
Imre Kise295adb2025-04-10 13:26:28 +02001408 flags: PartitionInfoGetFlags::try_from(regs[5] as u32)?,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001409 }
1410 }
1411 FuncId::IdGet => Self::IdGet,
1412 FuncId::SpmIdGet => Self::SpmIdGet,
Tomás González092202a2025-03-05 11:56:45 +00001413 FuncId::MsgWait => Self::MsgWait {
1414 flags: if version >= Version(1, 2) {
1415 Some(MsgWaitFlags::try_from(regs[2] as u32)?)
1416 } else {
1417 None
1418 },
1419 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001420 FuncId::Yield => Self::Yield,
1421 FuncId::Run => Self::Run {
1422 target_info: (regs[1] as u32).into(),
1423 },
1424 FuncId::NormalWorldResume => Self::NormalWorldResume,
Tomás González17b92442025-03-10 16:45:04 +00001425 FuncId::SecondaryEpRegister32 => Self::SecondaryEpRegister {
1426 entrypoint: SecondaryEpRegisterAddr::Addr32(regs[1] as u32),
1427 },
1428 FuncId::SecondaryEpRegister64 => Self::SecondaryEpRegister {
1429 entrypoint: SecondaryEpRegisterAddr::Addr64(regs[1]),
1430 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001431 FuncId::MsgSend2 => Self::MsgSend2 {
1432 sender_vm_id: regs[1] as u16,
1433 flags: regs[2] as u32,
1434 },
1435 FuncId::MsgSendDirectReq32 => Self::MsgSendDirectReq {
1436 src_id: (regs[1] >> 16) as u16,
1437 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001438 args: if (regs[2] as u32 & DirectMsgArgs::FWK_MSG_BITS) != 0 {
1439 match regs[2] as u32 {
1440 DirectMsgArgs::VERSION_REQ => DirectMsgArgs::VersionReq {
1441 version: Version::try_from(regs[3] as u32)?,
1442 },
1443 DirectMsgArgs::POWER_PSCI_REQ => DirectMsgArgs::PowerPsciReq32 {
Tomás González67f92c72025-03-20 16:50:42 +00001444 params: [
1445 regs[3] as u32,
1446 regs[4] as u32,
1447 regs[5] as u32,
1448 regs[6] as u32,
1449 ],
Tomás González4d5b0ba2025-03-03 17:15:55 +00001450 },
1451 DirectMsgArgs::POWER_WARM_BOOT_REQ => DirectMsgArgs::PowerWarmBootReq {
1452 boot_type: WarmBootType::try_from(regs[3] as u32)?,
1453 },
1454 DirectMsgArgs::VM_CREATED => DirectMsgArgs::VmCreated {
1455 handle: memory_management::Handle::from([
1456 regs[3] as u32,
1457 regs[4] as u32,
1458 ]),
1459 vm_id: regs[5] as u16,
1460 },
1461 DirectMsgArgs::VM_DESTRUCTED => DirectMsgArgs::VmDestructed {
1462 handle: memory_management::Handle::from([
1463 regs[3] as u32,
1464 regs[4] as u32,
1465 ]),
1466 vm_id: regs[5] as u16,
1467 },
1468 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1469 }
1470 } else {
1471 DirectMsgArgs::Args32([
1472 regs[3] as u32,
1473 regs[4] as u32,
1474 regs[5] as u32,
1475 regs[6] as u32,
1476 regs[7] as u32,
1477 ])
1478 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001479 },
1480 FuncId::MsgSendDirectReq64 => Self::MsgSendDirectReq {
1481 src_id: (regs[1] >> 16) as u16,
1482 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001483 args: if (regs[2] & DirectMsgArgs::FWK_MSG_BITS as u64) != 0 {
1484 match regs[2] as u32 {
1485 DirectMsgArgs::POWER_PSCI_REQ => DirectMsgArgs::PowerPsciReq64 {
Tomás González67f92c72025-03-20 16:50:42 +00001486 params: [regs[3], regs[4], regs[5], regs[6]],
Tomás González4d5b0ba2025-03-03 17:15:55 +00001487 },
1488 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1489 }
1490 } else {
1491 DirectMsgArgs::Args64([regs[3], regs[4], regs[5], regs[6], regs[7]])
1492 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001493 },
1494 FuncId::MsgSendDirectResp32 => Self::MsgSendDirectResp {
1495 src_id: (regs[1] >> 16) as u16,
1496 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001497 args: if (regs[2] as u32 & DirectMsgArgs::FWK_MSG_BITS) != 0 {
1498 match regs[2] as u32 {
1499 DirectMsgArgs::VERSION_RESP => {
1500 if regs[3] as i32 == FfaError::NotSupported.into() {
1501 DirectMsgArgs::VersionResp { version: None }
1502 } else {
1503 DirectMsgArgs::VersionResp {
1504 version: Some(Version::try_from(regs[3] as u32)?),
1505 }
1506 }
1507 }
1508 DirectMsgArgs::POWER_PSCI_RESP => DirectMsgArgs::PowerPsciResp {
1509 psci_status: regs[3] as i32,
1510 },
1511 DirectMsgArgs::VM_CREATED_ACK => DirectMsgArgs::VmCreatedAck {
1512 sp_status: (regs[3] as i32).try_into()?,
1513 },
1514 DirectMsgArgs::VM_DESTRUCTED_ACK => DirectMsgArgs::VmDestructedAck {
1515 sp_status: (regs[3] as i32).try_into()?,
1516 },
1517 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1518 }
1519 } else {
1520 DirectMsgArgs::Args32([
1521 regs[3] as u32,
1522 regs[4] as u32,
1523 regs[5] as u32,
1524 regs[6] as u32,
1525 regs[7] as u32,
1526 ])
1527 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001528 },
1529 FuncId::MsgSendDirectResp64 => Self::MsgSendDirectResp {
1530 src_id: (regs[1] >> 16) as u16,
1531 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001532 args: if (regs[2] & DirectMsgArgs::FWK_MSG_BITS as u64) != 0 {
1533 return Err(Error::UnrecognisedFwkMsg(regs[2] as u32));
1534 } else {
1535 DirectMsgArgs::Args64([regs[3], regs[4], regs[5], regs[6], regs[7]])
1536 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001537 },
1538 FuncId::MemDonate32 => Self::MemDonate {
1539 total_len: regs[1] as u32,
1540 frag_len: regs[2] as u32,
1541 buf: if regs[3] != 0 && regs[4] != 0 {
1542 Some(MemOpBuf::Buf32 {
1543 addr: regs[3] as u32,
1544 page_cnt: regs[4] as u32,
1545 })
1546 } else {
1547 None
1548 },
1549 },
1550 FuncId::MemDonate64 => Self::MemDonate {
1551 total_len: regs[1] as u32,
1552 frag_len: regs[2] as u32,
1553 buf: if regs[3] != 0 && regs[4] != 0 {
1554 Some(MemOpBuf::Buf64 {
1555 addr: regs[3],
1556 page_cnt: regs[4] as u32,
1557 })
1558 } else {
1559 None
1560 },
1561 },
1562 FuncId::MemLend32 => Self::MemLend {
1563 total_len: regs[1] as u32,
1564 frag_len: regs[2] as u32,
1565 buf: if regs[3] != 0 && regs[4] != 0 {
1566 Some(MemOpBuf::Buf32 {
1567 addr: regs[3] as u32,
1568 page_cnt: regs[4] as u32,
1569 })
1570 } else {
1571 None
1572 },
1573 },
1574 FuncId::MemLend64 => Self::MemLend {
1575 total_len: regs[1] as u32,
1576 frag_len: regs[2] as u32,
1577 buf: if regs[3] != 0 && regs[4] != 0 {
1578 Some(MemOpBuf::Buf64 {
1579 addr: regs[3],
1580 page_cnt: regs[4] as u32,
1581 })
1582 } else {
1583 None
1584 },
1585 },
1586 FuncId::MemShare32 => Self::MemShare {
1587 total_len: regs[1] as u32,
1588 frag_len: regs[2] as u32,
1589 buf: if regs[3] != 0 && regs[4] != 0 {
1590 Some(MemOpBuf::Buf32 {
1591 addr: regs[3] as u32,
1592 page_cnt: regs[4] as u32,
1593 })
1594 } else {
1595 None
1596 },
1597 },
1598 FuncId::MemShare64 => Self::MemShare {
1599 total_len: regs[1] as u32,
1600 frag_len: regs[2] as u32,
1601 buf: if regs[3] != 0 && regs[4] != 0 {
1602 Some(MemOpBuf::Buf64 {
1603 addr: regs[3],
1604 page_cnt: regs[4] as u32,
1605 })
1606 } else {
1607 None
1608 },
1609 },
1610 FuncId::MemRetrieveReq32 => Self::MemRetrieveReq {
1611 total_len: regs[1] as u32,
1612 frag_len: regs[2] as u32,
1613 buf: if regs[3] != 0 && regs[4] != 0 {
1614 Some(MemOpBuf::Buf32 {
1615 addr: regs[3] as u32,
1616 page_cnt: regs[4] as u32,
1617 })
1618 } else {
1619 None
1620 },
1621 },
1622 FuncId::MemRetrieveReq64 => Self::MemRetrieveReq {
1623 total_len: regs[1] as u32,
1624 frag_len: regs[2] as u32,
1625 buf: if regs[3] != 0 && regs[4] != 0 {
1626 Some(MemOpBuf::Buf64 {
1627 addr: regs[3],
1628 page_cnt: regs[4] as u32,
1629 })
1630 } else {
1631 None
1632 },
1633 },
1634 FuncId::MemRetrieveResp => Self::MemRetrieveResp {
1635 total_len: regs[1] as u32,
1636 frag_len: regs[2] as u32,
1637 },
1638 FuncId::MemRelinquish => Self::MemRelinquish,
1639 FuncId::MemReclaim => Self::MemReclaim {
1640 handle: memory_management::Handle::from([regs[1] as u32, regs[2] as u32]),
1641 flags: regs[3] as u32,
1642 },
1643 FuncId::MemPermGet32 => Self::MemPermGet {
1644 addr: MemAddr::Addr32(regs[1] as u32),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001645 page_cnt: if version >= Version(1, 3) {
1646 Some(regs[2] as u32)
1647 } else {
1648 None
1649 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001650 },
1651 FuncId::MemPermGet64 => Self::MemPermGet {
1652 addr: MemAddr::Addr64(regs[1]),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001653 page_cnt: if version >= Version(1, 3) {
1654 Some(regs[2] as u32)
1655 } else {
1656 None
1657 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001658 },
1659 FuncId::MemPermSet32 => Self::MemPermSet {
1660 addr: MemAddr::Addr32(regs[1] as u32),
1661 page_cnt: regs[2] as u32,
1662 mem_perm: regs[3] as u32,
1663 },
1664 FuncId::MemPermSet64 => Self::MemPermSet {
1665 addr: MemAddr::Addr64(regs[1]),
1666 page_cnt: regs[2] as u32,
1667 mem_perm: regs[3] as u32,
1668 },
1669 FuncId::ConsoleLog32 => Self::ConsoleLog {
1670 char_cnt: regs[1] as u8,
1671 char_lists: ConsoleLogChars::Reg32([
1672 regs[2] as u32,
1673 regs[3] as u32,
1674 regs[4] as u32,
1675 regs[5] as u32,
1676 regs[6] as u32,
1677 regs[7] as u32,
1678 ]),
1679 },
Tomás González7ffb6132025-04-03 12:28:58 +01001680 FuncId::NotificationBitmapCreate => {
1681 let tentative_vm_id = regs[1] as u32;
1682 if (tentative_vm_id >> 16) != 0 {
1683 return Err(Error::InvalidVmId(tentative_vm_id));
1684 }
1685 Self::NotificationBitmapCreate {
1686 vm_id: tentative_vm_id as u16,
1687 vcpu_cnt: regs[2] as u32,
1688 }
1689 }
1690 FuncId::NotificationBitmapDestroy => {
1691 let tentative_vm_id = regs[1] as u32;
1692 if (tentative_vm_id >> 16) != 0 {
1693 return Err(Error::InvalidVmId(tentative_vm_id));
1694 }
1695 Self::NotificationBitmapDestroy {
1696 vm_id: tentative_vm_id as u16,
1697 }
1698 }
1699 FuncId::NotificationBind => Self::NotificationBind {
1700 sender_id: (regs[1] >> 16) as u16,
1701 receiver_id: regs[1] as u16,
1702 flags: (regs[2] as u32).into(),
1703 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1704 },
1705 FuncId::NotificationUnbind => Self::NotificationUnBind {
1706 sender_id: (regs[1] >> 16) as u16,
1707 receiver_id: regs[1] as u16,
1708 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1709 },
1710 FuncId::NotificationSet => Self::NotificationSet {
1711 sender_id: (regs[1] >> 16) as u16,
1712 receiver_id: regs[1] as u16,
1713 flags: (regs[2] as u32).try_into()?,
1714 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1715 },
1716 FuncId::NotificationGet => Self::NotificationGet {
1717 vcpu_id: (regs[1] >> 16) as u16,
1718 endpoint_id: regs[1] as u16,
1719 flags: (regs[2] as u32).into(),
1720 },
1721 FuncId::NotificationInfoGet32 => Self::NotificationInfoGet { is_32bit: true },
1722 FuncId::NotificationInfoGet64 => Self::NotificationInfoGet { is_32bit: false },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01001723 FuncId::El3IntrHandle => Self::El3IntrHandle,
Balint Dobszayde0dc802025-02-28 14:16:52 +01001724 _ => panic!("Invalid number of registers (8) for function {:#x?}", fid),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001725 };
1726
1727 Ok(msg)
1728 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01001729
Balint Dobszayde0dc802025-02-28 14:16:52 +01001730 fn unpack_regs18(version: Version, regs: &[u64; 18]) -> Result<Self, Error> {
1731 assert!(version >= Version(1, 2));
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001732
Balint Dobszayde0dc802025-02-28 14:16:52 +01001733 let fid = FuncId::try_from(regs[0] as u32)?;
1734
1735 let msg = match fid {
1736 FuncId::Success64 => Self::Success {
1737 target_info: regs[1] as u32,
Imre Kis54773b62025-04-10 13:47:39 +02001738 args: SuccessArgs::Args64_2(regs[2..18].try_into().unwrap()),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001739 },
1740 FuncId::MsgSendDirectReq64_2 => Self::MsgSendDirectReq2 {
1741 src_id: (regs[1] >> 16) as u16,
1742 dst_id: regs[1] as u16,
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00001743 uuid: Uuid::from_u64_pair(regs[2].swap_bytes(), regs[3].swap_bytes()),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001744 args: DirectMsg2Args(regs[4..18].try_into().unwrap()),
1745 },
1746 FuncId::MsgSendDirectResp64_2 => Self::MsgSendDirectResp2 {
1747 src_id: (regs[1] >> 16) as u16,
1748 dst_id: regs[1] as u16,
1749 args: DirectMsg2Args(regs[4..18].try_into().unwrap()),
1750 },
1751 FuncId::ConsoleLog64 => Self::ConsoleLog {
1752 char_cnt: regs[1] as u8,
1753 char_lists: ConsoleLogChars::Reg64(regs[2..18].try_into().unwrap()),
1754 },
Tomás González0a058bc2025-03-11 11:20:55 +00001755 FuncId::PartitionInfoGetRegs => {
1756 // Bits[15:0]: Start index
1757 let start_index = (regs[3] & 0xffff) as u16;
1758 let info_tag = ((regs[3] >> 16) & 0xffff) as u16;
1759 Self::PartitionInfoGetRegs {
1760 uuid: Uuid::from_u64_pair(regs[1].swap_bytes(), regs[2].swap_bytes()),
1761 start_index,
1762 info_tag: if start_index == 0 && info_tag != 0 {
1763 return Err(Error::InvalidInformationTag(info_tag));
1764 } else {
1765 info_tag
1766 },
1767 }
1768 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001769 _ => panic!("Invalid number of registers (18) for function {:#x?}", fid),
1770 };
1771
1772 Ok(msg)
Balint Dobszay3aad9572025-01-17 16:54:11 +01001773 }
1774
Balint Dobszaya5846852025-02-26 15:38:53 +01001775 /// Create register contents for an interface.
Balint Dobszayde0dc802025-02-28 14:16:52 +01001776 pub fn to_regs(&self, version: Version, regs: &mut [u64]) {
1777 let reg_cnt = regs.len();
1778
1779 match reg_cnt {
1780 8 => {
1781 assert!(version <= Version(1, 1));
Balint Dobszay91bea9b2025-04-09 13:16:06 +02001782 regs.fill(0);
1783
Balint Dobszayde0dc802025-02-28 14:16:52 +01001784 self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
1785 }
1786 18 => {
1787 assert!(version >= Version(1, 2));
Balint Dobszay91bea9b2025-04-09 13:16:06 +02001788 regs.fill(0);
Balint Dobszayde0dc802025-02-28 14:16:52 +01001789
1790 match self {
1791 Interface::ConsoleLog {
1792 char_lists: ConsoleLogChars::Reg64(_),
1793 ..
1794 }
1795 | Interface::Success {
Imre Kis54773b62025-04-10 13:47:39 +02001796 args: SuccessArgs::Args64_2(_),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001797 ..
1798 }
1799 | Interface::MsgSendDirectReq2 { .. }
Tomás González0a058bc2025-03-11 11:20:55 +00001800 | Interface::MsgSendDirectResp2 { .. }
1801 | Interface::PartitionInfoGetRegs { .. } => {
Balint Dobszayde0dc802025-02-28 14:16:52 +01001802 self.pack_regs18(version, regs.try_into().unwrap());
1803 }
1804 _ => {
1805 self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
1806 }
1807 }
1808 }
1809 _ => panic!("Invalid number of registers {}", reg_cnt),
1810 }
1811 }
1812
1813 fn pack_regs8(&self, version: Version, a: &mut [u64; 8]) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001814 if let Some(function_id) = self.function_id() {
1815 a[0] = function_id as u64;
1816 }
1817
1818 match *self {
1819 Interface::Error {
1820 target_info,
1821 error_code,
Balint Dobszayb727aab2025-04-07 10:24:59 +02001822 error_arg,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001823 } => {
1824 a[1] = u32::from(target_info).into();
1825 a[2] = (error_code as u32).into();
Balint Dobszayb727aab2025-04-07 10:24:59 +02001826 a[3] = error_arg.into();
Balint Dobszay3aad9572025-01-17 16:54:11 +01001827 }
1828 Interface::Success { target_info, args } => {
1829 a[1] = target_info.into();
1830 match args {
Imre Kis54773b62025-04-10 13:47:39 +02001831 SuccessArgs::Args32(regs) => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001832 a[2] = regs[0].into();
1833 a[3] = regs[1].into();
1834 a[4] = regs[2].into();
1835 a[5] = regs[3].into();
1836 a[6] = regs[4].into();
1837 a[7] = regs[5].into();
1838 }
Imre Kis54773b62025-04-10 13:47:39 +02001839 SuccessArgs::Args64(regs) => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001840 a[2] = regs[0];
1841 a[3] = regs[1];
1842 a[4] = regs[2];
1843 a[5] = regs[3];
1844 a[6] = regs[4];
1845 a[7] = regs[5];
1846 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001847 _ => panic!("{:#x?} requires 18 registers", args),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001848 }
1849 }
1850 Interface::Interrupt {
1851 target_info,
1852 interrupt_id,
1853 } => {
1854 a[1] = u32::from(target_info).into();
1855 a[2] = interrupt_id.into();
1856 }
1857 Interface::Version { input_version } => {
1858 a[1] = u32::from(input_version).into();
1859 }
1860 Interface::VersionOut { output_version } => {
1861 a[0] = u32::from(output_version).into();
1862 }
1863 Interface::Features {
1864 feat_id,
1865 input_properties,
1866 } => {
1867 a[1] = u32::from(feat_id).into();
1868 a[2] = input_properties.into();
1869 }
1870 Interface::RxAcquire { vm_id } => {
1871 a[1] = vm_id.into();
1872 }
1873 Interface::RxRelease { vm_id } => {
1874 a[1] = vm_id.into();
1875 }
1876 Interface::RxTxMap { addr, page_cnt } => {
1877 match addr {
1878 RxTxAddr::Addr32 { rx, tx } => {
1879 a[1] = tx.into();
1880 a[2] = rx.into();
1881 }
1882 RxTxAddr::Addr64 { rx, tx } => {
1883 a[1] = tx;
1884 a[2] = rx;
1885 }
1886 }
1887 a[3] = page_cnt.into();
1888 }
1889 Interface::RxTxUnmap { id } => {
1890 a[1] = id.into();
1891 }
1892 Interface::PartitionInfoGet { uuid, flags } => {
1893 let bytes = uuid.into_bytes();
1894 a[1] = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]).into();
1895 a[2] = u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]).into();
1896 a[3] = u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]).into();
1897 a[4] = u32::from_le_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]).into();
Imre Kise295adb2025-04-10 13:26:28 +02001898 a[5] = u32::from(flags).into();
Balint Dobszay3aad9572025-01-17 16:54:11 +01001899 }
Tomás González092202a2025-03-05 11:56:45 +00001900 Interface::MsgWait { flags } => {
1901 if version >= Version(1, 2) {
1902 if let Some(flags) = flags {
1903 a[2] = u32::from(flags).into();
1904 }
1905 }
1906 }
1907 Interface::IdGet | Interface::SpmIdGet | Interface::Yield => {}
Balint Dobszay3aad9572025-01-17 16:54:11 +01001908 Interface::Run { target_info } => {
1909 a[1] = u32::from(target_info).into();
1910 }
1911 Interface::NormalWorldResume => {}
Tomás González17b92442025-03-10 16:45:04 +00001912 Interface::SecondaryEpRegister { entrypoint } => match entrypoint {
1913 SecondaryEpRegisterAddr::Addr32(addr) => a[1] = addr as u64,
1914 SecondaryEpRegisterAddr::Addr64(addr) => a[1] = addr,
1915 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001916 Interface::MsgSend2 {
1917 sender_vm_id,
1918 flags,
1919 } => {
1920 a[1] = sender_vm_id.into();
1921 a[2] = flags.into();
1922 }
1923 Interface::MsgSendDirectReq {
1924 src_id,
1925 dst_id,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001926 args,
1927 } => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +01001928 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Balint Dobszay3aad9572025-01-17 16:54:11 +01001929 match args {
1930 DirectMsgArgs::Args32(args) => {
1931 a[3] = args[0].into();
1932 a[4] = args[1].into();
1933 a[5] = args[2].into();
1934 a[6] = args[3].into();
1935 a[7] = args[4].into();
1936 }
1937 DirectMsgArgs::Args64(args) => {
1938 a[3] = args[0];
1939 a[4] = args[1];
1940 a[5] = args[2];
1941 a[6] = args[3];
1942 a[7] = args[4];
1943 }
Tomás González4d5b0ba2025-03-03 17:15:55 +00001944 DirectMsgArgs::VersionReq { version } => {
1945 a[2] = DirectMsgArgs::VERSION_REQ.into();
1946 a[3] = u32::from(version).into();
1947 }
Tomás González67f92c72025-03-20 16:50:42 +00001948 DirectMsgArgs::PowerPsciReq32 { params } => {
Tomás González4d5b0ba2025-03-03 17:15:55 +00001949 a[2] = DirectMsgArgs::POWER_PSCI_REQ.into();
Tomás González67f92c72025-03-20 16:50:42 +00001950 a[3] = params[0].into();
1951 a[4] = params[1].into();
1952 a[5] = params[2].into();
1953 a[6] = params[3].into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00001954 }
Tomás González67f92c72025-03-20 16:50:42 +00001955 DirectMsgArgs::PowerPsciReq64 { params } => {
Tomás González4d5b0ba2025-03-03 17:15:55 +00001956 a[2] = DirectMsgArgs::POWER_PSCI_REQ.into();
Tomás González67f92c72025-03-20 16:50:42 +00001957 a[3] = params[0];
1958 a[4] = params[1];
1959 a[5] = params[2];
1960 a[6] = params[3];
Tomás González4d5b0ba2025-03-03 17:15:55 +00001961 }
1962 DirectMsgArgs::PowerWarmBootReq { boot_type } => {
1963 a[2] = DirectMsgArgs::POWER_WARM_BOOT_REQ.into();
1964 a[3] = u32::from(boot_type).into();
1965 }
1966 DirectMsgArgs::VmCreated { handle, vm_id } => {
1967 a[2] = DirectMsgArgs::VM_CREATED.into();
1968 let handle_regs: [u32; 2] = handle.into();
1969 a[3] = handle_regs[0].into();
1970 a[4] = handle_regs[1].into();
1971 a[5] = vm_id.into();
1972 }
1973 DirectMsgArgs::VmDestructed { handle, vm_id } => {
1974 a[2] = DirectMsgArgs::VM_DESTRUCTED.into();
1975 let handle_regs: [u32; 2] = handle.into();
1976 a[3] = handle_regs[0].into();
1977 a[4] = handle_regs[1].into();
1978 a[5] = vm_id.into();
1979 }
1980 _ => panic!("Malformed MsgSendDirectReq interface"),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001981 }
1982 }
1983 Interface::MsgSendDirectResp {
1984 src_id,
1985 dst_id,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001986 args,
1987 } => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +01001988 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Balint Dobszay3aad9572025-01-17 16:54:11 +01001989 match args {
1990 DirectMsgArgs::Args32(args) => {
1991 a[3] = args[0].into();
1992 a[4] = args[1].into();
1993 a[5] = args[2].into();
1994 a[6] = args[3].into();
1995 a[7] = args[4].into();
1996 }
1997 DirectMsgArgs::Args64(args) => {
1998 a[3] = args[0];
1999 a[4] = args[1];
2000 a[5] = args[2];
2001 a[6] = args[3];
2002 a[7] = args[4];
2003 }
Tomás González4d5b0ba2025-03-03 17:15:55 +00002004 DirectMsgArgs::VersionResp { version } => {
2005 a[2] = DirectMsgArgs::VERSION_RESP.into();
2006 match version {
Tomás González67f92c72025-03-20 16:50:42 +00002007 None => a[3] = (i32::from(FfaError::NotSupported) as u32).into(),
Tomás González4d5b0ba2025-03-03 17:15:55 +00002008 Some(ver) => a[3] = u32::from(ver).into(),
2009 }
2010 }
2011 DirectMsgArgs::PowerPsciResp { psci_status } => {
2012 a[2] = DirectMsgArgs::POWER_PSCI_RESP.into();
2013 a[3] = psci_status as u64;
2014 }
2015 DirectMsgArgs::VmCreatedAck { sp_status } => {
2016 a[2] = DirectMsgArgs::VM_CREATED_ACK.into();
Tomás González67f92c72025-03-20 16:50:42 +00002017 a[3] = (i32::from(sp_status) as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00002018 }
2019 DirectMsgArgs::VmDestructedAck { sp_status } => {
2020 a[2] = DirectMsgArgs::VM_DESTRUCTED_ACK.into();
Tomás González67f92c72025-03-20 16:50:42 +00002021 a[3] = (i32::from(sp_status) as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00002022 }
2023 _ => panic!("Malformed MsgSendDirectResp interface"),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002024 }
2025 }
2026 Interface::MemDonate {
2027 total_len,
2028 frag_len,
2029 buf,
2030 } => {
2031 a[1] = total_len.into();
2032 a[2] = frag_len.into();
2033 (a[3], a[4]) = match buf {
2034 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
2035 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
2036 None => (0, 0),
2037 };
2038 }
2039 Interface::MemLend {
2040 total_len,
2041 frag_len,
2042 buf,
2043 } => {
2044 a[1] = total_len.into();
2045 a[2] = frag_len.into();
2046 (a[3], a[4]) = match buf {
2047 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
2048 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
2049 None => (0, 0),
2050 };
2051 }
2052 Interface::MemShare {
2053 total_len,
2054 frag_len,
2055 buf,
2056 } => {
2057 a[1] = total_len.into();
2058 a[2] = frag_len.into();
2059 (a[3], a[4]) = match buf {
2060 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
2061 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
2062 None => (0, 0),
2063 };
2064 }
2065 Interface::MemRetrieveReq {
2066 total_len,
2067 frag_len,
2068 buf,
2069 } => {
2070 a[1] = total_len.into();
2071 a[2] = frag_len.into();
2072 (a[3], a[4]) = match buf {
2073 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
2074 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
2075 None => (0, 0),
2076 };
2077 }
2078 Interface::MemRetrieveResp {
2079 total_len,
2080 frag_len,
2081 } => {
2082 a[1] = total_len.into();
2083 a[2] = frag_len.into();
2084 }
2085 Interface::MemRelinquish => {}
2086 Interface::MemReclaim { handle, flags } => {
2087 let handle_regs: [u32; 2] = handle.into();
2088 a[1] = handle_regs[0].into();
2089 a[2] = handle_regs[1].into();
2090 a[3] = flags.into();
2091 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01002092 Interface::MemPermGet { addr, page_cnt } => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01002093 a[1] = match addr {
2094 MemAddr::Addr32(addr) => addr.into(),
2095 MemAddr::Addr64(addr) => addr,
2096 };
Balint Dobszayde0dc802025-02-28 14:16:52 +01002097 a[2] = if version >= Version(1, 3) {
2098 page_cnt.unwrap().into()
2099 } else {
2100 assert!(page_cnt.is_none());
2101 0
2102 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01002103 }
2104 Interface::MemPermSet {
2105 addr,
2106 page_cnt,
2107 mem_perm,
2108 } => {
2109 a[1] = match addr {
2110 MemAddr::Addr32(addr) => addr.into(),
2111 MemAddr::Addr64(addr) => addr,
2112 };
2113 a[2] = page_cnt.into();
2114 a[3] = mem_perm.into();
2115 }
2116 Interface::ConsoleLog {
2117 char_cnt,
2118 char_lists,
2119 } => {
2120 a[1] = char_cnt.into();
2121 match char_lists {
2122 ConsoleLogChars::Reg32(regs) => {
2123 a[2] = regs[0].into();
2124 a[3] = regs[1].into();
2125 a[4] = regs[2].into();
2126 a[5] = regs[3].into();
2127 a[6] = regs[4].into();
2128 a[7] = regs[5].into();
2129 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01002130 _ => panic!("{:#x?} requires 18 registers", char_lists),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002131 }
2132 }
Tomás González7ffb6132025-04-03 12:28:58 +01002133 Interface::NotificationBitmapCreate { vm_id, vcpu_cnt } => {
2134 a[1] = vm_id.into();
2135 a[2] = vcpu_cnt.into();
2136 }
2137 Interface::NotificationBitmapDestroy { vm_id } => {
2138 a[1] = vm_id.into();
2139 }
2140 Interface::NotificationBind {
2141 sender_id,
2142 receiver_id,
2143 flags,
2144 bitmap,
2145 } => {
2146 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
2147 a[2] = u32::from(flags).into();
2148 a[3] = bitmap & 0xffff_ffff;
2149 a[4] = bitmap >> 32;
2150 }
2151 Interface::NotificationUnBind {
2152 sender_id,
2153 receiver_id,
2154 bitmap,
2155 } => {
2156 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
2157 a[3] = bitmap & 0xffff_ffff;
2158 a[4] = bitmap >> 32;
2159 }
2160 Interface::NotificationSet {
2161 sender_id,
2162 receiver_id,
2163 flags,
2164 bitmap,
2165 } => {
2166 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
2167 a[2] = u32::from(flags).into();
2168 a[3] = bitmap & 0xffff_ffff;
2169 a[4] = bitmap >> 32;
2170 }
2171 Interface::NotificationGet {
2172 vcpu_id,
2173 endpoint_id,
2174 flags,
2175 } => {
2176 a[1] = (u64::from(vcpu_id) << 16) | u64::from(endpoint_id);
2177 a[2] = u32::from(flags).into();
2178 }
2179 Interface::NotificationInfoGet { .. } => {}
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01002180 Interface::El3IntrHandle => {}
Balint Dobszayde0dc802025-02-28 14:16:52 +01002181 _ => panic!("{:#x?} requires 18 registers", self),
2182 }
2183 }
2184
2185 fn pack_regs18(&self, version: Version, a: &mut [u64; 18]) {
2186 assert!(version >= Version(1, 2));
2187
Balint Dobszayde0dc802025-02-28 14:16:52 +01002188 if let Some(function_id) = self.function_id() {
2189 a[0] = function_id as u64;
2190 }
2191
2192 match *self {
2193 Interface::Success { target_info, args } => {
2194 a[1] = target_info.into();
2195 match args {
Imre Kis54773b62025-04-10 13:47:39 +02002196 SuccessArgs::Args64_2(regs) => a[2..18].copy_from_slice(&regs[..16]),
Balint Dobszayde0dc802025-02-28 14:16:52 +01002197 _ => panic!("{:#x?} requires 8 registers", args),
2198 }
2199 }
2200 Interface::MsgSendDirectReq2 {
2201 src_id,
2202 dst_id,
2203 uuid,
2204 args,
2205 } => {
2206 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00002207 let (uuid_msb, uuid_lsb) = uuid.as_u64_pair();
2208 (a[2], a[3]) = (uuid_msb.swap_bytes(), uuid_lsb.swap_bytes());
Balint Dobszayde0dc802025-02-28 14:16:52 +01002209 a[4..18].copy_from_slice(&args.0[..14]);
2210 }
2211 Interface::MsgSendDirectResp2 {
2212 src_id,
2213 dst_id,
2214 args,
2215 } => {
2216 a[1] = ((src_id as u64) << 16) | dst_id as u64;
2217 a[2] = 0;
2218 a[3] = 0;
2219 a[4..18].copy_from_slice(&args.0[..14]);
2220 }
2221 Interface::ConsoleLog {
2222 char_cnt,
2223 char_lists,
2224 } => {
2225 a[1] = char_cnt.into();
2226 match char_lists {
2227 ConsoleLogChars::Reg64(regs) => a[2..18].copy_from_slice(&regs[..16]),
2228 _ => panic!("{:#x?} requires 8 registers", char_lists),
2229 }
2230 }
Tomás González0a058bc2025-03-11 11:20:55 +00002231 Interface::PartitionInfoGetRegs {
2232 uuid,
2233 start_index,
2234 info_tag,
2235 } => {
2236 if start_index == 0 && info_tag != 0 {
2237 panic!("Information Tag MBZ if start index is 0: {:#x?}", self);
2238 }
2239 let (uuid_msb, uuid_lsb) = uuid.as_u64_pair();
2240 (a[1], a[2]) = (uuid_msb.swap_bytes(), uuid_lsb.swap_bytes());
2241 a[3] = (u64::from(info_tag) << 16) | u64::from(start_index);
2242 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01002243 _ => panic!("{:#x?} requires 8 registers", self),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002244 }
2245 }
2246
Balint Dobszaya5846852025-02-26 15:38:53 +01002247 /// Helper function to create an `FFA_SUCCESS` interface without any arguments.
Balint Dobszay3aad9572025-01-17 16:54:11 +01002248 pub fn success32_noargs() -> Self {
2249 Self::Success {
2250 target_info: 0,
Imre Kis54773b62025-04-10 13:47:39 +02002251 args: SuccessArgs::Args32([0; 6]),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002252 }
2253 }
2254
Tomás González4c8c7d22025-03-10 17:14:57 +00002255 /// Helper function to create an `FFA_SUCCESS` interface without any arguments.
2256 pub fn success64_noargs() -> Self {
2257 Self::Success {
2258 target_info: 0,
Imre Kis54773b62025-04-10 13:47:39 +02002259 args: SuccessArgs::Args64([0; 6]),
Tomás González4c8c7d22025-03-10 17:14:57 +00002260 }
2261 }
2262
Balint Dobszaya5846852025-02-26 15:38:53 +01002263 /// Helper function to create an `FFA_ERROR` interface with an error code.
Balint Dobszay3aad9572025-01-17 16:54:11 +01002264 pub fn error(error_code: FfaError) -> Self {
2265 Self::Error {
2266 target_info: TargetInfo {
2267 endpoint_id: 0,
2268 vcpu_id: 0,
2269 },
2270 error_code,
Balint Dobszayb727aab2025-04-07 10:24:59 +02002271 error_arg: 0,
Balint Dobszay3aad9572025-01-17 16:54:11 +01002272 }
2273 }
2274}
2275
Balint Dobszaya5846852025-02-26 15:38:53 +01002276/// Maximum number of characters transmitted in a single `FFA_CONSOLE_LOG32` message.
2277pub const CONSOLE_LOG_32_MAX_CHAR_CNT: u8 = 24;
Balint Dobszayde0dc802025-02-28 14:16:52 +01002278/// Maximum number of characters transmitted in a single `FFA_CONSOLE_LOG64` message.
2279pub const CONSOLE_LOG_64_MAX_CHAR_CNT: u8 = 128;
Balint Dobszay3aad9572025-01-17 16:54:11 +01002280
Balint Dobszaya5846852025-02-26 15:38:53 +01002281/// Helper function to convert the "Tightly packed list of characters" format used by the
2282/// `FFA_CONSOLE_LOG` interface into a byte slice.
Balint Dobszay3aad9572025-01-17 16:54:11 +01002283pub fn parse_console_log(
2284 char_cnt: u8,
2285 char_lists: &ConsoleLogChars,
2286 log_bytes: &mut [u8],
2287) -> Result<(), FfaError> {
2288 match char_lists {
2289 ConsoleLogChars::Reg32(regs) => {
Balint Dobszaya5846852025-02-26 15:38:53 +01002290 if !(1..=CONSOLE_LOG_32_MAX_CHAR_CNT).contains(&char_cnt) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01002291 return Err(FfaError::InvalidParameters);
2292 }
2293 for (i, reg) in regs.iter().enumerate() {
2294 log_bytes[4 * i..4 * (i + 1)].copy_from_slice(&reg.to_le_bytes());
2295 }
2296 }
2297 ConsoleLogChars::Reg64(regs) => {
Balint Dobszaya5846852025-02-26 15:38:53 +01002298 if !(1..=CONSOLE_LOG_64_MAX_CHAR_CNT).contains(&char_cnt) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01002299 return Err(FfaError::InvalidParameters);
2300 }
2301 for (i, reg) in regs.iter().enumerate() {
2302 log_bytes[8 * i..8 * (i + 1)].copy_from_slice(&reg.to_le_bytes());
2303 }
2304 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +02002305 }
2306
Balint Dobszayc8802492025-01-15 18:11:39 +01002307 Ok(())
Balint Dobszay5bf492f2024-07-29 17:21:32 +02002308}
Tomás González0a058bc2025-03-11 11:20:55 +00002309
2310#[cfg(test)]
2311mod tests {
2312 use super::*;
2313
2314 #[test]
2315 fn part_info_get_regs() {
2316 let uuid = Uuid::parse_str("a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8").unwrap();
2317 let uuid_bytes = uuid.as_bytes();
2318 let test_info_tag = 0b1101_1101;
2319 let test_start_index = 0b1101;
2320 let start_index_and_tag = (test_info_tag << 16) | test_start_index;
2321 let version = Version(1, 2);
2322
2323 // From spec:
2324 // Bytes[0...7] of UUID with byte 0 in the low-order bits.
2325 let reg_x1 = (uuid_bytes[7] as u64) << 56
2326 | (uuid_bytes[6] as u64) << 48
2327 | (uuid_bytes[5] as u64) << 40
2328 | (uuid_bytes[4] as u64) << 32
2329 | (uuid_bytes[3] as u64) << 24
2330 | (uuid_bytes[2] as u64) << 16
2331 | (uuid_bytes[1] as u64) << 8
2332 | (uuid_bytes[0] as u64);
2333
2334 // From spec:
2335 // Bytes[8...15] of UUID with byte 8 in the low-order bits.
2336 let reg_x2 = (uuid_bytes[15] as u64) << 56
2337 | (uuid_bytes[14] as u64) << 48
2338 | (uuid_bytes[13] as u64) << 40
2339 | (uuid_bytes[12] as u64) << 32
2340 | (uuid_bytes[11] as u64) << 24
2341 | (uuid_bytes[10] as u64) << 16
2342 | (uuid_bytes[9] as u64) << 8
2343 | (uuid_bytes[8] as u64);
2344
2345 // First, test for wrong tag:
2346 {
2347 let mut regs = [0u64; 18];
2348 regs[0] = FuncId::PartitionInfoGetRegs as u64;
2349 regs[1] = reg_x1;
2350 regs[2] = reg_x2;
2351 regs[3] = test_info_tag << 16;
2352
2353 assert!(Interface::from_regs(version, &regs).is_err_and(
2354 |e| e == Error::InvalidInformationTag(test_info_tag.try_into().unwrap())
2355 ));
2356 }
2357
2358 // Test for regs -> Interface -> regs
2359 {
2360 let mut orig_regs = [0u64; 18];
2361 orig_regs[0] = FuncId::PartitionInfoGetRegs as u64;
2362 orig_regs[1] = reg_x1;
2363 orig_regs[2] = reg_x2;
2364 orig_regs[3] = start_index_and_tag;
2365
2366 let mut test_regs = orig_regs.clone();
2367 let interface = Interface::from_regs(version, &mut test_regs).unwrap();
2368 match &interface {
2369 Interface::PartitionInfoGetRegs {
2370 info_tag,
2371 start_index,
2372 uuid: int_uuid,
2373 } => {
2374 assert_eq!(u64::from(*info_tag), test_info_tag);
2375 assert_eq!(u64::from(*start_index), test_start_index);
2376 assert_eq!(*int_uuid, uuid);
2377 }
2378 _ => panic!("Expecting Interface::PartitionInfoGetRegs!"),
2379 }
2380 test_regs.fill(0);
2381 interface.to_regs(version, &mut test_regs);
2382 assert_eq!(orig_regs, test_regs);
2383 }
2384
2385 // Test for Interface -> regs -> Interface
2386 {
2387 let interface = Interface::PartitionInfoGetRegs {
2388 info_tag: test_info_tag.try_into().unwrap(),
2389 start_index: test_start_index.try_into().unwrap(),
2390 uuid,
2391 };
2392
2393 let mut regs: [u64; 18] = [0; 18];
2394 interface.to_regs(version, &mut regs);
2395
2396 assert_eq!(Some(FuncId::PartitionInfoGetRegs), interface.function_id());
2397 assert_eq!(regs[0], interface.function_id().unwrap() as u64);
2398 assert_eq!(regs[1], reg_x1);
2399 assert_eq!(regs[2], reg_x2);
2400 assert_eq!(regs[3], (test_info_tag << 16) | test_start_index);
2401
2402 assert_eq!(Interface::from_regs(version, &regs).unwrap(), interface);
2403 }
2404 }
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00002405
2406 #[test]
2407 fn msg_send_direct_req2() {
2408 let uuid = Uuid::parse_str("a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8").unwrap();
2409 let uuid_bytes = uuid.as_bytes();
2410
2411 // From spec:
2412 // Bytes[0...7] of UUID with byte 0 in the low-order bits.
2413 let reg_x2 = (uuid_bytes[7] as u64) << 56
2414 | (uuid_bytes[6] as u64) << 48
2415 | (uuid_bytes[5] as u64) << 40
2416 | (uuid_bytes[4] as u64) << 32
2417 | (uuid_bytes[3] as u64) << 24
2418 | (uuid_bytes[2] as u64) << 16
2419 | (uuid_bytes[1] as u64) << 8
2420 | (uuid_bytes[0] as u64);
2421
2422 // From spec:
2423 // Bytes[8...15] of UUID with byte 8 in the low-order bits.
2424 let reg_x3 = (uuid_bytes[15] as u64) << 56
2425 | (uuid_bytes[14] as u64) << 48
2426 | (uuid_bytes[13] as u64) << 40
2427 | (uuid_bytes[12] as u64) << 32
2428 | (uuid_bytes[11] as u64) << 24
2429 | (uuid_bytes[10] as u64) << 16
2430 | (uuid_bytes[9] as u64) << 8
2431 | (uuid_bytes[8] as u64);
2432
2433 let test_sender = 0b1101_1101;
2434 let test_receiver = 0b1101;
2435 let test_sender_receiver = (test_sender << 16) | test_receiver;
2436 let version = Version(1, 2);
2437
2438 // Test for regs -> Interface -> regs
2439 {
2440 let mut orig_regs = [0u64; 18];
2441 orig_regs[0] = FuncId::MsgSendDirectReq64_2 as u64;
2442 orig_regs[1] = test_sender_receiver;
2443 orig_regs[2] = reg_x2;
2444 orig_regs[3] = reg_x3;
2445
2446 let mut test_regs = orig_regs.clone();
2447 let interface = Interface::from_regs(version, &mut test_regs).unwrap();
2448 match &interface {
2449 Interface::MsgSendDirectReq2 {
2450 dst_id,
2451 src_id,
2452 args: _,
2453 uuid: int_uuid,
2454 } => {
2455 assert_eq!(u64::from(*src_id), test_sender);
2456 assert_eq!(u64::from(*dst_id), test_receiver);
2457 assert_eq!(*int_uuid, uuid);
2458 }
2459 _ => panic!("Expecting Interface::MsgSendDirectReq2!"),
2460 }
2461 test_regs.fill(0);
2462 interface.to_regs(version, &mut test_regs);
2463 assert_eq!(orig_regs, test_regs);
2464 }
2465
2466 // Test for Interface -> regs -> Interface
2467 {
2468 let rest_of_regs: [u64; 14] = [0; 14];
2469
2470 let interface = Interface::MsgSendDirectReq2 {
2471 src_id: test_sender.try_into().unwrap(),
2472 dst_id: test_receiver.try_into().unwrap(),
2473 uuid,
2474 args: DirectMsg2Args(rest_of_regs),
2475 };
2476
2477 let mut regs: [u64; 18] = [0; 18];
2478 interface.to_regs(version, &mut regs);
2479
2480 assert_eq!(Some(FuncId::MsgSendDirectReq64_2), interface.function_id());
2481 assert_eq!(regs[0], interface.function_id().unwrap() as u64);
2482 assert_eq!(regs[1], test_sender_receiver);
2483 assert_eq!(regs[2], reg_x2);
2484 assert_eq!(regs[3], reg_x3);
2485 assert_eq!(regs[4], 0);
2486
2487 assert_eq!(Interface::from_regs(version, &regs).unwrap(), interface);
2488 }
2489 }
Tomás González6ccba0a2025-04-09 13:31:29 +01002490
2491 #[test]
2492 fn is_32bit() {
2493 let interface_64 = Interface::MsgSendDirectReq {
2494 src_id: 0,
2495 dst_id: 1,
2496 args: DirectMsgArgs::Args64([0, 0, 0, 0, 0]),
2497 };
2498 assert!(!interface_64.is_32bit());
2499
2500 let interface_32 = Interface::MsgSendDirectReq {
2501 src_id: 0,
2502 dst_id: 1,
2503 args: DirectMsgArgs::Args32([0, 0, 0, 0, 0]),
2504 };
2505 assert!(interface_32.is_32bit());
2506 }
Imre Kis787c5002025-04-10 14:25:51 +02002507
2508 #[test]
2509 fn success_args_notification_info_get32() {
2510 let mut notifications = SuccessArgsNotificationInfoGet32::default();
2511
2512 // 16.7.1.1 Example usage
2513 notifications.add_list(0x0000, &[0, 2, 3]).unwrap();
2514 notifications.add_list(0x0000, &[4, 6]).unwrap();
2515 notifications.add_list(0x0002, &[]).unwrap();
2516 notifications.add_list(0x0003, &[1]).unwrap();
2517
2518 let args: SuccessArgs = notifications.into();
2519 assert_eq!(
2520 SuccessArgs::Args32([
2521 0x0004_b200,
2522 0x0000_0000,
2523 0x0003_0002,
2524 0x0004_0000,
2525 0x0002_0006,
2526 0x0001_0003
2527 ]),
2528 args
2529 );
2530
2531 let notifications = SuccessArgsNotificationInfoGet32::try_from(args).unwrap();
2532 let mut iter = notifications.iter();
2533 assert_eq!(Some((0x0000, &[0, 2, 3][..])), iter.next());
2534 assert_eq!(Some((0x0000, &[4, 6][..])), iter.next());
2535 assert_eq!(Some((0x0002, &[][..])), iter.next());
2536 assert_eq!(Some((0x0003, &[1][..])), iter.next());
2537 }
2538
2539 #[test]
2540 fn success_args_notification_info_get64() {
2541 let mut notifications = SuccessArgsNotificationInfoGet64::default();
2542
2543 // 16.7.1.1 Example usage
2544 notifications.add_list(0x0000, &[0, 2, 3]).unwrap();
2545 notifications.add_list(0x0000, &[4, 6]).unwrap();
2546 notifications.add_list(0x0002, &[]).unwrap();
2547 notifications.add_list(0x0003, &[1]).unwrap();
2548
2549 let args: SuccessArgs = notifications.into();
2550 assert_eq!(
2551 SuccessArgs::Args64([
2552 0x0004_b200,
2553 0x0003_0002_0000_0000,
2554 0x0002_0006_0004_0000,
2555 0x0000_0000_0001_0003,
2556 0x0000_0000_0000_0000,
2557 0x0000_0000_0000_0000,
2558 ]),
2559 args
2560 );
2561
2562 let notifications = SuccessArgsNotificationInfoGet64::try_from(args).unwrap();
2563 let mut iter = notifications.iter();
2564 assert_eq!(Some((0x0000, &[0, 2, 3][..])), iter.next());
2565 assert_eq!(Some((0x0000, &[4, 6][..])), iter.next());
2566 assert_eq!(Some((0x0002, &[][..])), iter.next());
2567 assert_eq!(Some((0x0003, &[1][..])), iter.next());
2568 }
Tomás González0a058bc2025-03-11 11:20:55 +00002569}