blob: bfba56f8668cfb1da514a63f1db964b7042d86b4 [file] [log] [blame]
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001// SPDX-FileCopyrightText: Copyright 2023 Arm Limited and/or its affiliates <open-source-office@arm.com>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4#![cfg_attr(not(test), no_std)]
Balint Dobszaya5846852025-02-26 15:38:53 +01005#![deny(clippy::undocumented_unsafe_blocks)]
6#![deny(unsafe_op_in_unsafe_fn)]
7#![doc = include_str!("../README.md")]
Balint Dobszay5bf492f2024-07-29 17:21:32 +02008
Andrew Walbran19970ba2024-11-25 15:35:00 +00009use core::fmt::{self, Debug, Display, Formatter};
Andrew Walbran44029a02024-11-25 15:34:31 +000010use num_enum::{IntoPrimitive, TryFromPrimitive};
11use thiserror::Error;
Balint Dobszay5bf492f2024-07-29 17:21:32 +020012use uuid::Uuid;
Imre Kis787c5002025-04-10 14:25:51 +020013use zerocopy::transmute;
Balint Dobszay5bf492f2024-07-29 17:21:32 +020014
15pub mod boot_info;
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +010016mod ffa_v1_1;
Balint Dobszayde0dc802025-02-28 14:16:52 +010017mod ffa_v1_2;
Balint Dobszay5bf492f2024-07-29 17:21:32 +020018pub mod memory_management;
19pub mod partition_info;
20
Balint Dobszaya5846852025-02-26 15:38:53 +010021/// Constant for 4K page size. On many occasions the FF-A spec defines memory size as count of 4K
22/// pages, regardless of the current translation granule.
Balint Dobszay3aad9572025-01-17 16:54:11 +010023pub const FFA_PAGE_SIZE_4K: usize = 4096;
24
Balint Dobszaya5846852025-02-26 15:38:53 +010025/// Rich error types returned by this module. Should be converted to [`crate::FfaError`] when used
26/// with the `FFA_ERROR` interface.
Tomás González0a058bc2025-03-11 11:20:55 +000027#[derive(Debug, Error, PartialEq)]
Balint Dobszay3aad9572025-01-17 16:54:11 +010028pub enum Error {
29 #[error("Unrecognised FF-A function ID {0}")]
30 UnrecognisedFunctionId(u32),
31 #[error("Unrecognised FF-A feature ID {0}")]
32 UnrecognisedFeatureId(u8),
33 #[error("Unrecognised FF-A error code {0}")]
34 UnrecognisedErrorCode(i32),
Tomás González4d5b0ba2025-03-03 17:15:55 +000035 #[error("Unrecognised FF-A Framework Message {0}")]
36 UnrecognisedFwkMsg(u32),
Tomás González092202a2025-03-05 11:56:45 +000037 #[error("Invalid FF-A Msg Wait Flag {0}")]
38 InvalidMsgWaitFlag(u32),
Tomás González4d5b0ba2025-03-03 17:15:55 +000039 #[error("Unrecognised VM availability status {0}")]
40 UnrecognisedVmAvailabilityStatus(i32),
41 #[error("Unrecognised FF-A Warm Boot Type {0}")]
42 UnrecognisedWarmBootType(u32),
43 #[error("Invalid version {0}")]
44 InvalidVersion(u32),
Tomás González0a058bc2025-03-11 11:20:55 +000045 #[error("Invalid Information Tag {0}")]
46 InvalidInformationTag(u16),
Tomás González7ffb6132025-04-03 12:28:58 +010047 #[error("Invalid Flag for Notification Set")]
48 InvalidNotificationSetFlag(u32),
49 #[error("Invalid Vm ID")]
50 InvalidVmId(u32),
Imre Kise295adb2025-04-10 13:26:28 +020051 #[error("Invalid FF-A Partition Info Get Flag {0}")]
52 InvalidPartitionInfoGetFlag(u32),
Imre Kis839eaef2025-04-11 17:38:36 +020053 #[error("Invalid success argument variant")]
54 InvalidSuccessArgsVariant,
Imre Kis787c5002025-04-10 14:25:51 +020055 #[error("Invalid notification count")]
56 InvalidNotificationCount,
Imre Kis92b663e2025-04-10 14:15:05 +020057 #[error("Invalid Partition Info Get Regs response")]
58 InvalidPartitionInfoGetRegsResponse,
Balint Dobszay82c71dd2025-04-15 10:16:44 +020059 #[error("Invalid FF-A version {0} for function ID {1:?}")]
60 InvalidVersionForFunctionId(Version, FuncId),
Balint Dobszay3aad9572025-01-17 16:54:11 +010061}
62
63impl From<Error> for FfaError {
64 fn from(value: Error) -> Self {
65 match value {
Balint Dobszay82c71dd2025-04-15 10:16:44 +020066 Error::UnrecognisedFunctionId(_)
67 | Error::UnrecognisedFeatureId(_)
68 | Error::InvalidVersionForFunctionId(..) => Self::NotSupported,
Tomás González0a058bc2025-03-11 11:20:55 +000069 Error::InvalidInformationTag(_) => Self::Retry,
Tomás González4d5b0ba2025-03-03 17:15:55 +000070 Error::UnrecognisedErrorCode(_)
71 | Error::UnrecognisedFwkMsg(_)
72 | Error::InvalidVersion(_)
Tomás González092202a2025-03-05 11:56:45 +000073 | Error::InvalidMsgWaitFlag(_)
Tomás González4d5b0ba2025-03-03 17:15:55 +000074 | Error::UnrecognisedVmAvailabilityStatus(_)
Tomás González7ffb6132025-04-03 12:28:58 +010075 | Error::InvalidNotificationSetFlag(_)
76 | Error::InvalidVmId(_)
Imre Kise295adb2025-04-10 13:26:28 +020077 | Error::UnrecognisedWarmBootType(_)
Imre Kis839eaef2025-04-11 17:38:36 +020078 | Error::InvalidPartitionInfoGetFlag(_)
Imre Kis787c5002025-04-10 14:25:51 +020079 | Error::InvalidSuccessArgsVariant
Imre Kis92b663e2025-04-10 14:15:05 +020080 | Error::InvalidNotificationCount
81 | Error::InvalidPartitionInfoGetRegsResponse => Self::InvalidParameters,
Balint Dobszay3aad9572025-01-17 16:54:11 +010082 }
83 }
84}
Balint Dobszay5bf492f2024-07-29 17:21:32 +020085
Balint Dobszaya5846852025-02-26 15:38:53 +010086/// 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 +020087#[derive(PartialEq, Clone, Copy)]
88pub enum Instance {
Balint Dobszaya5846852025-02-26 15:38:53 +010089 /// The instance between the SPMC and SPMD.
Balint Dobszay5bf492f2024-07-29 17:21:32 +020090 SecurePhysical,
Balint Dobszaya5846852025-02-26 15:38:53 +010091 /// The instance between the SPMC and a physical SP (contains the SP's endpoint ID).
Balint Dobszay5bf492f2024-07-29 17:21:32 +020092 SecureVirtual(u16),
93}
94
Balint Dobszaya5846852025-02-26 15:38:53 +010095/// Function IDs of the various FF-A interfaces.
Andrew Walbran969b9252024-11-25 15:35:42 +000096#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
Balint Dobszay3aad9572025-01-17 16:54:11 +010097#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedFunctionId))]
Balint Dobszay5bf492f2024-07-29 17:21:32 +020098#[repr(u32)]
99pub enum FuncId {
100 Error = 0x84000060,
101 Success32 = 0x84000061,
102 Success64 = 0xc4000061,
103 Interrupt = 0x84000062,
104 Version = 0x84000063,
105 Features = 0x84000064,
106 RxAcquire = 0x84000084,
107 RxRelease = 0x84000065,
108 RxTxMap32 = 0x84000066,
109 RxTxMap64 = 0xc4000066,
110 RxTxUnmap = 0x84000067,
111 PartitionInfoGet = 0x84000068,
Balint Dobszaye6aa4862025-02-28 16:37:56 +0100112 PartitionInfoGetRegs = 0xc400008b,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200113 IdGet = 0x84000069,
114 SpmIdGet = 0x84000085,
Balint Dobszaye6aa4862025-02-28 16:37:56 +0100115 ConsoleLog32 = 0x8400008a,
116 ConsoleLog64 = 0xc400008a,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200117 MsgWait = 0x8400006b,
118 Yield = 0x8400006c,
119 Run = 0x8400006d,
120 NormalWorldResume = 0x8400007c,
121 MsgSend2 = 0x84000086,
122 MsgSendDirectReq32 = 0x8400006f,
123 MsgSendDirectReq64 = 0xc400006f,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100124 MsgSendDirectReq64_2 = 0xc400008d,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200125 MsgSendDirectResp32 = 0x84000070,
126 MsgSendDirectResp64 = 0xc4000070,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100127 MsgSendDirectResp64_2 = 0xc400008e,
Balint Dobszaye6aa4862025-02-28 16:37:56 +0100128 NotificationBitmapCreate = 0x8400007d,
129 NotificationBitmapDestroy = 0x8400007e,
130 NotificationBind = 0x8400007f,
131 NotificationUnbind = 0x84000080,
132 NotificationSet = 0x84000081,
133 NotificationGet = 0x84000082,
134 NotificationInfoGet32 = 0x84000083,
135 NotificationInfoGet64 = 0xc4000083,
136 El3IntrHandle = 0x8400008c,
Tomás González17b92442025-03-10 16:45:04 +0000137 SecondaryEpRegister32 = 0x84000087,
138 SecondaryEpRegister64 = 0xc4000087,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200139 MemDonate32 = 0x84000071,
140 MemDonate64 = 0xc4000071,
141 MemLend32 = 0x84000072,
142 MemLend64 = 0xc4000072,
143 MemShare32 = 0x84000073,
144 MemShare64 = 0xc4000073,
145 MemRetrieveReq32 = 0x84000074,
146 MemRetrieveReq64 = 0xc4000074,
147 MemRetrieveResp = 0x84000075,
148 MemRelinquish = 0x84000076,
149 MemReclaim = 0x84000077,
150 MemPermGet32 = 0x84000088,
151 MemPermGet64 = 0xc4000088,
152 MemPermSet32 = 0x84000089,
153 MemPermSet64 = 0xc4000089,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200154}
155
Balint Dobszayde0dc802025-02-28 14:16:52 +0100156impl FuncId {
157 /// Returns true if this is a 32-bit call, or false if it is a 64-bit call.
158 pub fn is_32bit(&self) -> bool {
Tomás González6ccba0a2025-04-09 13:31:29 +0100159 u32::from(*self) & (1 << 30) == 0
Balint Dobszayde0dc802025-02-28 14:16:52 +0100160 }
Balint Dobszay82c71dd2025-04-15 10:16:44 +0200161
162 /// Returns the FF-A version that has introduced the function ID.
163 pub fn minimum_ffa_version(&self) -> Version {
164 match self {
165 FuncId::Error
166 | FuncId::Success32
167 | FuncId::Success64
168 | FuncId::Interrupt
169 | FuncId::Version
170 | FuncId::Features
171 | FuncId::RxRelease
172 | FuncId::RxTxMap32
173 | FuncId::RxTxMap64
174 | FuncId::RxTxUnmap
175 | FuncId::PartitionInfoGet
176 | FuncId::IdGet
177 | FuncId::MsgWait
178 | FuncId::Yield
179 | FuncId::Run
180 | FuncId::NormalWorldResume
181 | FuncId::MsgSendDirectReq32
182 | FuncId::MsgSendDirectReq64
183 | FuncId::MsgSendDirectResp32
184 | FuncId::MsgSendDirectResp64
185 | FuncId::MemDonate32
186 | FuncId::MemDonate64
187 | FuncId::MemLend32
188 | FuncId::MemLend64
189 | FuncId::MemShare32
190 | FuncId::MemShare64
191 | FuncId::MemRetrieveReq32
192 | FuncId::MemRetrieveReq64
193 | FuncId::MemRetrieveResp
194 | FuncId::MemRelinquish
195 | FuncId::MemReclaim => Version(1, 0),
196
197 FuncId::RxAcquire
198 | FuncId::SpmIdGet
199 | FuncId::MsgSend2
200 | FuncId::MemPermGet32
201 | FuncId::MemPermGet64
202 | FuncId::MemPermSet32
203 | FuncId::MemPermSet64
204 | FuncId::NotificationBitmapCreate
205 | FuncId::NotificationBitmapDestroy
206 | FuncId::NotificationBind
207 | FuncId::NotificationUnbind
208 | FuncId::NotificationSet
209 | FuncId::NotificationGet
210 | FuncId::NotificationInfoGet32
211 | FuncId::NotificationInfoGet64
212 | FuncId::SecondaryEpRegister32
213 | FuncId::SecondaryEpRegister64 => Version(1, 1),
214
215 FuncId::PartitionInfoGetRegs
216 | FuncId::ConsoleLog32
217 | FuncId::ConsoleLog64
218 | FuncId::MsgSendDirectReq64_2
219 | FuncId::MsgSendDirectResp64_2
220 | FuncId::El3IntrHandle => Version(1, 2),
221 }
222 }
Balint Dobszayde0dc802025-02-28 14:16:52 +0100223}
224
Balint Dobszaya5846852025-02-26 15:38:53 +0100225/// Error status codes used by the `FFA_ERROR` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100226#[derive(Clone, Copy, Debug, Eq, Error, IntoPrimitive, PartialEq, TryFromPrimitive)]
227#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedErrorCode))]
228#[repr(i32)]
229pub enum FfaError {
230 #[error("Not supported")]
231 NotSupported = -1,
232 #[error("Invalid parameters")]
233 InvalidParameters = -2,
234 #[error("No memory")]
235 NoMemory = -3,
236 #[error("Busy")]
237 Busy = -4,
238 #[error("Interrupted")]
239 Interrupted = -5,
240 #[error("Denied")]
241 Denied = -6,
242 #[error("Retry")]
243 Retry = -7,
244 #[error("Aborted")]
245 Aborted = -8,
246 #[error("No data")]
247 NoData = -9,
248}
249
Balint Dobszaya5846852025-02-26 15:38:53 +0100250/// Endpoint ID and vCPU ID pair, used by `FFA_ERROR`, `FFA_INTERRUPT` and `FFA_RUN` interfaces.
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200251#[derive(Debug, Eq, PartialEq, Clone, Copy)]
Balint Dobszay3aad9572025-01-17 16:54:11 +0100252pub struct TargetInfo {
253 pub endpoint_id: u16,
254 pub vcpu_id: u16,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200255}
256
Balint Dobszay3aad9572025-01-17 16:54:11 +0100257impl From<u32> for TargetInfo {
258 fn from(value: u32) -> Self {
259 Self {
260 endpoint_id: (value >> 16) as u16,
261 vcpu_id: value as u16,
262 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200263 }
264}
265
Balint Dobszay3aad9572025-01-17 16:54:11 +0100266impl From<TargetInfo> for u32 {
267 fn from(value: TargetInfo) -> Self {
Balint Dobszaye9a3e762025-02-26 17:29:57 +0100268 ((value.endpoint_id as u32) << 16) | value.vcpu_id as u32
Andrew Walbran0d315812024-11-25 15:36:36 +0000269 }
Balint Dobszay3aad9572025-01-17 16:54:11 +0100270}
Andrew Walbran0d315812024-11-25 15:36:36 +0000271
Imre Kis839eaef2025-04-11 17:38:36 +0200272/// Generic arguments of the `FFA_SUCCESS` interface. The interpretation of the arguments depends on
273/// the interface that initiated the request. The application code has knowledge of the request, so
274/// it has to convert `SuccessArgs` into/from a specific success args structure that matches the
275/// request.
Imre Kis4e9d8bc2025-04-10 13:48:26 +0200276///
277/// The current specialized success arguments types are:
278/// * `FFA_FEATURES` - [`SuccessArgsFeatures`]
Imre Kisbbef2872025-04-10 14:11:29 +0200279/// * `FFA_ID_GET` - [`SuccessArgsIdGet`]
280/// * `FFA_SPM_ID_GET` - [`SuccessArgsSpmIdGet`]
Imre Kis61c34092025-04-10 14:14:38 +0200281/// * `FFA_PARTITION_INFO_GET` - [`partition_info::SuccessArgsPartitionInfoGet`]
Imre Kis92b663e2025-04-10 14:15:05 +0200282/// * `FFA_PARTITION_INFO_GET_REGS` - [`partition_info::SuccessArgsPartitionInfoGetRegs`]
Imre Kis9959e062025-04-10 14:16:10 +0200283/// * `FFA_NOTIFICATION_GET` - [`SuccessArgsNotificationGet`]
Imre Kis787c5002025-04-10 14:25:51 +0200284/// * `FFA_NOTIFICATION_INFO_GET_32` - [`SuccessArgsNotificationInfoGet32`]
285/// * `FFA_NOTIFICATION_INFO_GET_64` - [`SuccessArgsNotificationInfoGet64`]
Balint Dobszay3aad9572025-01-17 16:54:11 +0100286#[derive(Debug, Eq, PartialEq, Clone, Copy)]
287pub enum SuccessArgs {
Imre Kis54773b62025-04-10 13:47:39 +0200288 Args32([u32; 6]),
289 Args64([u64; 6]),
290 Args64_2([u64; 16]),
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200291}
292
Imre Kis839eaef2025-04-11 17:38:36 +0200293impl SuccessArgs {
294 fn try_get_args32(self) -> Result<[u32; 6], Error> {
295 match self {
296 SuccessArgs::Args32(args) => Ok(args),
297 SuccessArgs::Args64(_) | SuccessArgs::Args64_2(_) => {
298 Err(Error::InvalidSuccessArgsVariant)
299 }
300 }
301 }
302
303 fn try_get_args64(self) -> Result<[u64; 6], Error> {
304 match self {
305 SuccessArgs::Args64(args) => Ok(args),
306 SuccessArgs::Args32(_) | SuccessArgs::Args64_2(_) => {
307 Err(Error::InvalidSuccessArgsVariant)
308 }
309 }
310 }
311
312 fn try_get_args64_2(self) -> Result<[u64; 16], Error> {
313 match self {
314 SuccessArgs::Args64_2(args) => Ok(args),
315 SuccessArgs::Args32(_) | SuccessArgs::Args64(_) => {
316 Err(Error::InvalidSuccessArgsVariant)
317 }
318 }
319 }
320}
321
Tomás González17b92442025-03-10 16:45:04 +0000322/// Entrypoint address argument for `FFA_SECONDARY_EP_REGISTER` interface.
323#[derive(Debug, Eq, PartialEq, Clone, Copy)]
324pub enum SecondaryEpRegisterAddr {
325 Addr32(u32),
326 Addr64(u64),
327}
328
Balint Dobszaya5846852025-02-26 15:38:53 +0100329/// Version number of the FF-A implementation, `.0` is the major, `.1` is minor the version.
Balint Dobszayde0dc802025-02-28 14:16:52 +0100330#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord)]
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200331pub struct Version(pub u16, pub u16);
332
Tomás González1f794352025-03-03 16:47:06 +0000333impl Version {
Tomás González83146af2025-03-04 11:32:41 +0000334 // The FF-A spec mandates that bit[31] of a version number must be 0
335 const MBZ_BITS: u32 = 1 << 31;
336
Tomás González1f794352025-03-03 16:47:06 +0000337 /// Returns whether the caller's version (self) is compatible with the callee's version (input
338 /// parameter)
339 pub fn is_compatible_to(&self, callee_version: &Version) -> bool {
340 self.0 == callee_version.0 && self.1 <= callee_version.1
341 }
342}
343
Tomás González83146af2025-03-04 11:32:41 +0000344impl TryFrom<u32> for Version {
345 type Error = Error;
346
347 fn try_from(val: u32) -> Result<Self, Self::Error> {
348 if (val & Self::MBZ_BITS) != 0 {
349 Err(Error::InvalidVersion(val))
350 } else {
351 Ok(Self((val >> 16) as u16, val as u16))
352 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200353 }
354}
355
356impl From<Version> for u32 {
357 fn from(v: Version) -> Self {
Tomás González83146af2025-03-04 11:32:41 +0000358 let v_u32 = ((v.0 as u32) << 16) | v.1 as u32;
359 assert!(v_u32 & Version::MBZ_BITS == 0);
360 v_u32
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200361 }
362}
363
Andrew Walbran19970ba2024-11-25 15:35:00 +0000364impl Display for Version {
365 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
366 write!(f, "{}.{}", self.0, self.1)
367 }
368}
369
370impl Debug for Version {
371 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
372 Display::fmt(self, f)
373 }
374}
375
Balint Dobszaya5846852025-02-26 15:38:53 +0100376/// Feature IDs used by the `FFA_FEATURES` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100377#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
378#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedFeatureId))]
379#[repr(u8)]
380pub enum FeatureId {
381 NotificationPendingInterrupt = 0x1,
382 ScheduleReceiverInterrupt = 0x2,
383 ManagedExitInterrupt = 0x3,
384}
Balint Dobszayc8802492025-01-15 18:11:39 +0100385
Balint Dobszaya5846852025-02-26 15:38:53 +0100386/// Arguments for the `FFA_FEATURES` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100387#[derive(Debug, Eq, PartialEq, Clone, Copy)]
388pub enum Feature {
389 FuncId(FuncId),
390 FeatureId(FeatureId),
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100391 Unknown(u32),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100392}
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200393
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100394impl From<u32> for Feature {
395 fn from(value: u32) -> Self {
396 // Bit[31] is set for all valid FF-A function IDs so we don't have to check it separately
397 if let Ok(func_id) = value.try_into() {
398 Self::FuncId(func_id)
399 } else if let Ok(feat_id) = (value as u8).try_into() {
400 Self::FeatureId(feat_id)
Balint Dobszay3aad9572025-01-17 16:54:11 +0100401 } else {
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100402 Self::Unknown(value)
403 }
Balint Dobszay3aad9572025-01-17 16:54:11 +0100404 }
405}
406
407impl From<Feature> for u32 {
408 fn from(value: Feature) -> Self {
409 match value {
410 Feature::FuncId(func_id) => (1 << 31) | func_id as u32,
411 Feature::FeatureId(feature_id) => feature_id as u32,
Imre Kis29c8ace2025-04-11 13:49:58 +0200412 Feature::Unknown(id) => id,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100413 }
414 }
415}
416
Imre Kis4e9d8bc2025-04-10 13:48:26 +0200417/// `FFA_FEATURES` specific success argument structure. This type needs further specialization based
418/// on 'FF-A function ID or Feature ID' field of the preceeding `FFA_FEATURES` request.
419#[derive(Debug, Eq, PartialEq, Clone, Copy)]
420pub struct SuccessArgsFeatures {
421 pub properties: [u32; 2],
422}
423
424impl From<SuccessArgsFeatures> for SuccessArgs {
425 fn from(value: SuccessArgsFeatures) -> Self {
426 Self::Args32([value.properties[0], value.properties[1], 0, 0, 0, 0])
427 }
428}
429
430impl TryFrom<SuccessArgs> for SuccessArgsFeatures {
431 type Error = Error;
432
433 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
434 let args = value.try_get_args32()?;
435
436 Ok(Self {
437 properties: [args[0], args[1]],
438 })
439 }
440}
441
Balint Dobszaya5846852025-02-26 15:38:53 +0100442/// RXTX buffer descriptor, used by `FFA_RXTX_MAP`.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100443#[derive(Debug, Eq, PartialEq, Clone, Copy)]
444pub enum RxTxAddr {
445 Addr32 { rx: u32, tx: u32 },
446 Addr64 { rx: u64, tx: u64 },
447}
448
Imre Kisbbef2872025-04-10 14:11:29 +0200449/// `FFA_ID_GET` specific success argument structure.
450#[derive(Debug, Eq, PartialEq, Clone, Copy)]
451pub struct SuccessArgsIdGet {
452 pub id: u16,
453}
454
455impl From<SuccessArgsIdGet> for SuccessArgs {
456 fn from(value: SuccessArgsIdGet) -> Self {
457 SuccessArgs::Args32([value.id as u32, 0, 0, 0, 0, 0])
458 }
459}
460
461impl TryFrom<SuccessArgs> for SuccessArgsIdGet {
462 type Error = Error;
463
464 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
465 let args = value.try_get_args32()?;
466 Ok(Self { id: args[0] as u16 })
467 }
468}
469
470/// `FFA_SPM_ID_GET` specific success argument structure.
471#[derive(Debug, Eq, PartialEq, Clone, Copy)]
472pub struct SuccessArgsSpmIdGet {
473 pub id: u16,
474}
475
476impl From<SuccessArgsSpmIdGet> for SuccessArgs {
477 fn from(value: SuccessArgsSpmIdGet) -> Self {
478 SuccessArgs::Args32([value.id as u32, 0, 0, 0, 0, 0])
479 }
480}
481
482impl TryFrom<SuccessArgs> for SuccessArgsSpmIdGet {
483 type Error = Error;
484
485 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
486 let args = value.try_get_args32()?;
487 Ok(Self { id: args[0] as u16 })
488 }
489}
490
Imre Kise295adb2025-04-10 13:26:28 +0200491/// Flags of the `FFA_PARTITION_INFO_GET` interface.
492#[derive(Debug, Eq, PartialEq, Clone, Copy)]
493pub struct PartitionInfoGetFlags {
494 pub count_only: bool,
495}
496
497impl PartitionInfoGetFlags {
498 const RETURN_INFORMATION_TYPE_FLAG: u32 = 1 << 0;
499 const MBZ_BITS: u32 = 0xffff_fffe;
500}
501
502impl TryFrom<u32> for PartitionInfoGetFlags {
503 type Error = Error;
504
505 fn try_from(val: u32) -> Result<Self, Self::Error> {
506 if (val & Self::MBZ_BITS) != 0 {
507 Err(Error::InvalidPartitionInfoGetFlag(val))
508 } else {
509 Ok(Self {
510 count_only: val & Self::RETURN_INFORMATION_TYPE_FLAG != 0,
511 })
512 }
513 }
514}
515
516impl From<PartitionInfoGetFlags> for u32 {
517 fn from(flags: PartitionInfoGetFlags) -> Self {
518 let mut bits: u32 = 0;
519 if flags.count_only {
520 bits |= PartitionInfoGetFlags::RETURN_INFORMATION_TYPE_FLAG;
521 }
522 bits
523 }
524}
525
Tomás González4d5b0ba2025-03-03 17:15:55 +0000526/// Composite type for capturing success and error return codes for the VM availability messages.
527///
528/// Error codes are handled by the `FfaError` type. Having a separate type for errors helps using
529/// `Result<(), FfaError>`. If a single type would include both success and error values,
530/// then `Err(FfaError::Success)` would be incomprehensible.
531#[derive(Debug, Eq, PartialEq, Clone, Copy)]
532pub enum VmAvailabilityStatus {
533 Success,
534 Error(FfaError),
535}
536
537impl TryFrom<i32> for VmAvailabilityStatus {
538 type Error = Error;
539 fn try_from(value: i32) -> Result<Self, <Self as TryFrom<i32>>::Error> {
540 Ok(match value {
541 0 => Self::Success,
542 error_code => Self::Error(FfaError::try_from(error_code)?),
543 })
544 }
545}
546
547impl From<VmAvailabilityStatus> for i32 {
548 fn from(value: VmAvailabilityStatus) -> Self {
549 match value {
550 VmAvailabilityStatus::Success => 0,
551 VmAvailabilityStatus::Error(error_code) => error_code.into(),
552 }
553 }
554}
555
556/// Arguments for the Power Warm Boot `FFA_MSG_SEND_DIRECT_REQ` interface.
557#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
558#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedWarmBootType))]
559#[repr(u32)]
560pub enum WarmBootType {
561 ExitFromSuspend = 0,
562 ExitFromLowPower = 1,
563}
564
Balint Dobszaya5846852025-02-26 15:38:53 +0100565/// Arguments for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}` interfaces.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100566#[derive(Debug, Eq, PartialEq, Clone, Copy)]
567pub enum DirectMsgArgs {
568 Args32([u32; 5]),
569 Args64([u64; 5]),
Tomás González4d5b0ba2025-03-03 17:15:55 +0000570 /// Message for forwarding FFA_VERSION call from Normal world to the SPMC
571 VersionReq {
572 version: Version,
573 },
574 /// Response message to forwarded FFA_VERSION call from the Normal world
575 /// Contains the version returned by the SPMC or None
576 VersionResp {
577 version: Option<Version>,
578 },
579 /// Message for a power management operation initiated by a PSCI function
580 PowerPsciReq32 {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000581 // params[i]: Input parameter in w[i] in PSCI function invocation at EL3.
Tomás González67f92c72025-03-20 16:50:42 +0000582 // params[0]: Function ID.
583 params: [u32; 4],
Tomás González4d5b0ba2025-03-03 17:15:55 +0000584 },
585 /// Message for a power management operation initiated by a PSCI function
586 PowerPsciReq64 {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000587 // params[i]: Input parameter in x[i] in PSCI function invocation at EL3.
Tomás González67f92c72025-03-20 16:50:42 +0000588 // params[0]: Function ID.
589 params: [u64; 4],
Tomás González4d5b0ba2025-03-03 17:15:55 +0000590 },
591 /// Message for a warm boot
592 PowerWarmBootReq {
593 boot_type: WarmBootType,
594 },
595 /// Response message to indicate return status of the last power management request message
596 /// Return error code SUCCESS or DENIED as defined in PSCI spec. Caller is left to do the
597 /// parsing of the return status.
598 PowerPsciResp {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000599 psci_status: i32,
600 },
601 /// Message to signal creation of a VM
602 VmCreated {
603 // Globally unique Handle to identify a memory region that contains IMPLEMENTATION DEFINED
604 // information associated with the created VM.
605 // The invalid memory region handle must be specified by the Hypervisor if this field is not
606 // used.
607 handle: memory_management::Handle,
608 vm_id: u16,
609 },
610 /// Message to acknowledge creation of a VM
611 VmCreatedAck {
612 sp_status: VmAvailabilityStatus,
613 },
614 /// Message to signal destruction of a VM
615 VmDestructed {
616 // Globally unique Handle to identify a memory region that contains IMPLEMENTATION DEFINED
617 // information associated with the created VM.
618 // The invalid memory region handle must be specified by the Hypervisor if this field is not
619 // used.
620 handle: memory_management::Handle,
621 vm_id: u16,
622 },
623 /// Message to acknowledge destruction of a VM
624 VmDestructedAck {
625 sp_status: VmAvailabilityStatus,
626 },
627}
628
629impl DirectMsgArgs {
630 // Flags for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}` interfaces.
631
632 const FWK_MSG_BITS: u32 = 1 << 31;
633 const VERSION_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b1000;
634 const VERSION_RESP: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b1001;
635 const POWER_PSCI_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS;
636 const POWER_WARM_BOOT_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0001;
637 const POWER_PSCI_RESP: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0010;
638 const VM_CREATED: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0100;
639 const VM_CREATED_ACK: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0101;
640 const VM_DESTRUCTED: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0110;
641 const VM_DESTRUCTED_ACK: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0111;
Balint Dobszay3aad9572025-01-17 16:54:11 +0100642}
643
Balint Dobszayde0dc802025-02-28 14:16:52 +0100644/// Arguments for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}2` interfaces.
645#[derive(Debug, Eq, PartialEq, Clone, Copy)]
646pub struct DirectMsg2Args([u64; 14]);
647
Tomás González092202a2025-03-05 11:56:45 +0000648#[derive(Debug, Eq, PartialEq, Clone, Copy)]
649pub struct MsgWaitFlags {
650 retain_rx_buffer: bool,
651}
652
653impl MsgWaitFlags {
654 const RETAIN_RX_BUFFER: u32 = 0x01;
655 const MBZ_BITS: u32 = 0xfffe;
656}
657
658impl TryFrom<u32> for MsgWaitFlags {
659 type Error = Error;
660
661 fn try_from(val: u32) -> Result<Self, Self::Error> {
662 if (val & Self::MBZ_BITS) != 0 {
663 Err(Error::InvalidMsgWaitFlag(val))
664 } else {
665 Ok(MsgWaitFlags {
666 retain_rx_buffer: val & Self::RETAIN_RX_BUFFER != 0,
667 })
668 }
669 }
670}
671
672impl From<MsgWaitFlags> for u32 {
673 fn from(flags: MsgWaitFlags) -> Self {
674 let mut bits: u32 = 0;
675 if flags.retain_rx_buffer {
676 bits |= MsgWaitFlags::RETAIN_RX_BUFFER;
677 }
678 bits
679 }
680}
681
Balint Dobszaya5846852025-02-26 15:38:53 +0100682/// Descriptor for a dynamically allocated memory buffer that contains the memory transaction
Tomás Gonzálezf268e322025-03-05 11:18:11 +0000683/// descriptor.
684///
685/// Used by `FFA_MEM_{DONATE,LEND,SHARE,RETRIEVE_REQ}` interfaces, only when the TX buffer is not
686/// used to transmit the transaction descriptor.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100687#[derive(Debug, Eq, PartialEq, Clone, Copy)]
688pub enum MemOpBuf {
689 Buf32 { addr: u32, page_cnt: u32 },
690 Buf64 { addr: u64, page_cnt: u32 },
691}
692
Balint Dobszaya5846852025-02-26 15:38:53 +0100693/// Memory address argument for `FFA_MEM_PERM_{GET,SET}` interfaces.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100694#[derive(Debug, Eq, PartialEq, Clone, Copy)]
695pub enum MemAddr {
696 Addr32(u32),
697 Addr64(u64),
698}
699
Balint Dobszayde0dc802025-02-28 14:16:52 +0100700/// Argument for the `FFA_CONSOLE_LOG` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100701#[derive(Debug, Eq, PartialEq, Clone, Copy)]
702pub enum ConsoleLogChars {
703 Reg32([u32; 6]),
Balint Dobszayde0dc802025-02-28 14:16:52 +0100704 Reg64([u64; 16]),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100705}
706
Tomás González7ffb6132025-04-03 12:28:58 +0100707#[derive(Debug, Eq, PartialEq, Clone, Copy)]
708pub struct NotificationBindFlags {
709 per_vcpu_notification: bool,
710}
711
712impl NotificationBindFlags {
713 const PER_VCPU_NOTIFICATION: u32 = 1;
714}
715
716impl From<NotificationBindFlags> for u32 {
717 fn from(flags: NotificationBindFlags) -> Self {
718 let mut bits: u32 = 0;
719 if flags.per_vcpu_notification {
720 bits |= NotificationBindFlags::PER_VCPU_NOTIFICATION;
721 }
722 bits
723 }
724}
725
726impl From<u32> for NotificationBindFlags {
727 fn from(flags: u32) -> Self {
728 Self {
729 per_vcpu_notification: flags & Self::PER_VCPU_NOTIFICATION != 0,
730 }
731 }
732}
733
734#[derive(Debug, Eq, PartialEq, Clone, Copy)]
735pub struct NotificationSetFlags {
736 delay_schedule_receiver: bool,
737 vcpu_id: Option<u16>,
738}
739
740impl NotificationSetFlags {
741 const PER_VCP_NOTIFICATION: u32 = 1 << 0;
742 const DELAY_SCHEDULE_RECEIVER: u32 = 1 << 1;
743 const VCPU_ID_SHIFT: u32 = 16;
744
745 const MBZ_BITS: u32 = 0xfffc;
746}
747
748impl From<NotificationSetFlags> for u32 {
749 fn from(flags: NotificationSetFlags) -> Self {
750 let mut bits: u32 = 0;
751
752 if flags.delay_schedule_receiver {
753 bits |= NotificationSetFlags::DELAY_SCHEDULE_RECEIVER;
754 }
755 if let Some(vcpu_id) = flags.vcpu_id {
756 bits |= NotificationSetFlags::PER_VCP_NOTIFICATION;
757 bits |= u32::from(vcpu_id) << NotificationSetFlags::VCPU_ID_SHIFT;
758 }
759
760 bits
761 }
762}
763
764impl TryFrom<u32> for NotificationSetFlags {
765 type Error = Error;
766
767 fn try_from(flags: u32) -> Result<Self, Self::Error> {
768 if (flags & Self::MBZ_BITS) != 0 {
769 return Err(Error::InvalidNotificationSetFlag(flags));
770 }
771
772 let tentative_vcpu_id = (flags >> Self::VCPU_ID_SHIFT) as u16;
773
774 let vcpu_id = if (flags & Self::PER_VCP_NOTIFICATION) != 0 {
775 Some(tentative_vcpu_id)
776 } else {
777 if tentative_vcpu_id != 0 {
778 return Err(Error::InvalidNotificationSetFlag(flags));
779 }
780 None
781 };
782
783 Ok(Self {
784 delay_schedule_receiver: (flags & Self::DELAY_SCHEDULE_RECEIVER) != 0,
785 vcpu_id,
786 })
787 }
788}
789
790#[derive(Debug, Eq, PartialEq, Clone, Copy)]
791pub struct NotificationGetFlags {
792 sp_bitmap_id: bool,
793 vm_bitmap_id: bool,
794 spm_bitmap_id: bool,
795 hyp_bitmap_id: bool,
796}
797
798impl NotificationGetFlags {
799 const SP_BITMAP_ID: u32 = 1;
800 const VM_BITMAP_ID: u32 = 1 << 1;
801 const SPM_BITMAP_ID: u32 = 1 << 2;
802 const HYP_BITMAP_ID: u32 = 1 << 3;
803}
804
805impl From<NotificationGetFlags> for u32 {
806 fn from(flags: NotificationGetFlags) -> Self {
807 let mut bits: u32 = 0;
808 if flags.sp_bitmap_id {
809 bits |= NotificationGetFlags::SP_BITMAP_ID;
810 }
811 if flags.vm_bitmap_id {
812 bits |= NotificationGetFlags::VM_BITMAP_ID;
813 }
814 if flags.spm_bitmap_id {
815 bits |= NotificationGetFlags::SPM_BITMAP_ID;
816 }
817 if flags.hyp_bitmap_id {
818 bits |= NotificationGetFlags::HYP_BITMAP_ID;
819 }
820 bits
821 }
822}
823
824impl From<u32> for NotificationGetFlags {
825 // This is a "from" instead of a "try_from" because Reserved Bits are SBZ, *not* MBZ.
826 fn from(flags: u32) -> Self {
827 Self {
828 sp_bitmap_id: (flags & Self::SP_BITMAP_ID) != 0,
829 vm_bitmap_id: (flags & Self::VM_BITMAP_ID) != 0,
830 spm_bitmap_id: (flags & Self::SPM_BITMAP_ID) != 0,
831 hyp_bitmap_id: (flags & Self::HYP_BITMAP_ID) != 0,
832 }
833 }
834}
835
Imre Kis9959e062025-04-10 14:16:10 +0200836/// `FFA_NOTIFICATION_GET` specific success argument structure.
837#[derive(Debug, Eq, PartialEq, Clone, Copy)]
838pub struct SuccessArgsNotificationGet {
839 pub sp_notifications: Option<u64>,
840 pub vm_notifications: Option<u64>,
841 pub spm_notifications: Option<u32>,
842 pub hypervisor_notifications: Option<u32>,
843}
844
845impl From<SuccessArgsNotificationGet> for SuccessArgs {
846 fn from(value: SuccessArgsNotificationGet) -> Self {
847 let mut args = [0; 6];
848
849 if let Some(bitmap) = value.sp_notifications {
850 args[0] = bitmap as u32;
851 args[1] = (bitmap >> 32) as u32;
852 }
853
854 if let Some(bitmap) = value.vm_notifications {
855 args[2] = bitmap as u32;
856 args[3] = (bitmap >> 32) as u32;
857 }
858
859 if let Some(bitmap) = value.spm_notifications {
860 args[4] = bitmap;
861 }
862
863 if let Some(bitmap) = value.hypervisor_notifications {
864 args[5] = bitmap;
865 }
866
867 Self::Args32(args)
868 }
869}
870
871impl TryFrom<(NotificationGetFlags, SuccessArgs)> for SuccessArgsNotificationGet {
872 type Error = Error;
873
874 fn try_from(value: (NotificationGetFlags, SuccessArgs)) -> Result<Self, Self::Error> {
875 let (flags, value) = value;
876 let args = value.try_get_args32()?;
877
878 let sp_notifications = if flags.sp_bitmap_id {
879 Some(u64::from(args[0]) | (u64::from(args[1]) << 32))
880 } else {
881 None
882 };
883
884 let vm_notifications = if flags.vm_bitmap_id {
885 Some(u64::from(args[2]) | (u64::from(args[3]) << 32))
886 } else {
887 None
888 };
889
890 let spm_notifications = if flags.spm_bitmap_id {
891 Some(args[4])
892 } else {
893 None
894 };
895
896 let hypervisor_notifications = if flags.hyp_bitmap_id {
897 Some(args[5])
898 } else {
899 None
900 };
901
902 Ok(Self {
903 sp_notifications,
904 vm_notifications,
905 spm_notifications,
906 hypervisor_notifications,
907 })
908 }
909}
Imre Kis787c5002025-04-10 14:25:51 +0200910
911/// `FFA_NOTIFICATION_INFO_GET` specific success argument structure. The `MAX_COUNT` parameter
912/// depends on the 32-bit or 64-bit packing.
913#[derive(Debug, Eq, PartialEq, Clone, Copy)]
914pub struct SuccessArgsNotificationInfoGet<const MAX_COUNT: usize> {
915 pub more_pending_notifications: bool,
916 list_count: usize,
917 id_counts: [u8; MAX_COUNT],
918 ids: [u16; MAX_COUNT],
919}
920
921impl<const MAX_COUNT: usize> Default for SuccessArgsNotificationInfoGet<MAX_COUNT> {
922 fn default() -> Self {
923 Self {
924 more_pending_notifications: false,
925 list_count: 0,
926 id_counts: [0; MAX_COUNT],
927 ids: [0; MAX_COUNT],
928 }
929 }
930}
931
932impl<const MAX_COUNT: usize> SuccessArgsNotificationInfoGet<MAX_COUNT> {
933 const MORE_PENDING_NOTIFICATIONS_FLAG: u64 = 1 << 0;
934 const LIST_COUNT_SHIFT: usize = 7;
935 const LIST_COUNT_MASK: u64 = 0x1f;
936 const ID_COUNT_SHIFT: usize = 12;
937 const ID_COUNT_MASK: u64 = 0x03;
938 const ID_COUNT_BITS: usize = 2;
939
940 pub fn add_list(&mut self, endpoint: u16, vcpu_ids: &[u16]) -> Result<(), Error> {
941 if self.list_count >= MAX_COUNT || vcpu_ids.len() > Self::ID_COUNT_MASK as usize {
942 return Err(Error::InvalidNotificationCount);
943 }
944
945 // Each list contains at least one ID: the partition ID, followed by vCPU IDs. The number
946 // of vCPU IDs is recorded in `id_counts`.
947 let mut current_id_index = self.list_count + self.id_counts.iter().sum::<u8>() as usize;
948 if current_id_index + 1 + vcpu_ids.len() > MAX_COUNT {
949 // The new list does not fit into the available space for IDs.
950 return Err(Error::InvalidNotificationCount);
951 }
952
953 self.id_counts[self.list_count] = vcpu_ids.len() as u8;
954 self.list_count += 1;
955
956 // The first ID is the endpoint ID.
957 self.ids[current_id_index] = endpoint;
958 current_id_index += 1;
959
960 // Insert the vCPU IDs.
961 self.ids[current_id_index..current_id_index + vcpu_ids.len()].copy_from_slice(vcpu_ids);
962
963 Ok(())
964 }
965
966 pub fn iter(&self) -> NotificationInfoGetIterator<'_> {
967 NotificationInfoGetIterator {
968 list_index: 0,
969 id_index: 0,
970 id_count: &self.id_counts[0..self.list_count],
971 ids: &self.ids,
972 }
973 }
974
975 /// Pack flags field and IDs.
976 fn pack(self) -> (u64, [u16; MAX_COUNT]) {
977 let mut flags = if self.more_pending_notifications {
978 Self::MORE_PENDING_NOTIFICATIONS_FLAG
979 } else {
980 0
981 };
982
983 flags |= (self.list_count as u64) << Self::LIST_COUNT_SHIFT;
984 for (count, shift) in self.id_counts.iter().take(self.list_count).zip(
985 (Self::ID_COUNT_SHIFT..Self::ID_COUNT_SHIFT + Self::ID_COUNT_BITS * MAX_COUNT)
986 .step_by(Self::ID_COUNT_BITS),
987 ) {
988 flags |= u64::from(*count) << shift;
989 }
990
991 (flags, self.ids)
992 }
993
994 /// Unpack flags field and IDs.
995 fn unpack(flags: u64, ids: [u16; MAX_COUNT]) -> Result<Self, Error> {
996 let count_of_lists = ((flags >> Self::LIST_COUNT_SHIFT) & Self::LIST_COUNT_MASK) as usize;
997
998 if count_of_lists > MAX_COUNT {
999 return Err(Error::InvalidNotificationCount);
1000 }
1001
1002 let mut count_of_ids = [0; MAX_COUNT];
1003 let mut count_of_ids_bits = flags >> Self::ID_COUNT_SHIFT;
1004
1005 for id in count_of_ids.iter_mut().take(count_of_lists) {
1006 *id = (count_of_ids_bits & Self::ID_COUNT_MASK) as u8;
1007 count_of_ids_bits >>= Self::ID_COUNT_BITS;
1008 }
1009
Imre Kis7846c9f2025-04-15 09:45:00 +02001010 let id_field_count = count_of_lists + count_of_ids.iter().sum::<u8>() as usize;
1011 if id_field_count > MAX_COUNT {
1012 return Err(Error::InvalidNotificationCount);
1013 }
1014
Imre Kis787c5002025-04-10 14:25:51 +02001015 Ok(Self {
1016 more_pending_notifications: (flags & Self::MORE_PENDING_NOTIFICATIONS_FLAG) != 0,
1017 list_count: count_of_lists,
1018 id_counts: count_of_ids,
1019 ids,
1020 })
1021 }
1022}
1023
1024/// `FFA_NOTIFICATION_INFO_GET_32` specific success argument structure.
1025pub type SuccessArgsNotificationInfoGet32 = SuccessArgsNotificationInfoGet<10>;
1026
1027impl From<SuccessArgsNotificationInfoGet32> for SuccessArgs {
1028 fn from(value: SuccessArgsNotificationInfoGet32) -> Self {
1029 let (flags, ids) = value.pack();
1030 let id_regs: [u32; 5] = transmute!(ids);
1031
1032 let mut args = [0; 6];
1033 args[0] = flags as u32;
1034 args[1..6].copy_from_slice(&id_regs);
1035
1036 SuccessArgs::Args32(args)
1037 }
1038}
1039
1040impl TryFrom<SuccessArgs> for SuccessArgsNotificationInfoGet32 {
1041 type Error = Error;
1042
1043 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
1044 let args = value.try_get_args32()?;
1045 let flags = args[0].into();
1046 let id_regs: [u32; 5] = args[1..6].try_into().unwrap();
1047 Self::unpack(flags, transmute!(id_regs))
1048 }
1049}
1050
1051/// `FFA_NOTIFICATION_INFO_GET_64` specific success argument structure.
1052pub type SuccessArgsNotificationInfoGet64 = SuccessArgsNotificationInfoGet<20>;
1053
1054impl From<SuccessArgsNotificationInfoGet64> for SuccessArgs {
1055 fn from(value: SuccessArgsNotificationInfoGet64) -> Self {
1056 let (flags, ids) = value.pack();
1057 let id_regs: [u64; 5] = transmute!(ids);
1058
1059 let mut args = [0; 6];
1060 args[0] = flags;
1061 args[1..6].copy_from_slice(&id_regs);
1062
1063 SuccessArgs::Args64(args)
1064 }
1065}
1066
1067impl TryFrom<SuccessArgs> for SuccessArgsNotificationInfoGet64 {
1068 type Error = Error;
1069
1070 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
1071 let args = value.try_get_args64()?;
1072 let flags = args[0];
1073 let id_regs: [u64; 5] = args[1..6].try_into().unwrap();
1074 Self::unpack(flags, transmute!(id_regs))
1075 }
1076}
1077
1078pub struct NotificationInfoGetIterator<'a> {
1079 list_index: usize,
1080 id_index: usize,
1081 id_count: &'a [u8],
1082 ids: &'a [u16],
1083}
1084
1085impl<'a> Iterator for NotificationInfoGetIterator<'a> {
1086 type Item = (u16, &'a [u16]);
1087
1088 fn next(&mut self) -> Option<Self::Item> {
1089 if self.list_index < self.id_count.len() {
1090 let partition_id = self.ids[self.id_index];
1091 let id_range =
1092 (self.id_index + 1)..=(self.id_index + self.id_count[self.list_index] as usize);
1093
1094 self.id_index += 1 + self.id_count[self.list_index] as usize;
1095 self.list_index += 1;
1096
1097 Some((partition_id, &self.ids[id_range]))
1098 } else {
1099 None
1100 }
1101 }
1102}
1103
Tomás Gonzálezf268e322025-03-05 11:18:11 +00001104/// FF-A "message types", the terminology used by the spec is "interfaces".
1105///
1106/// The interfaces are used by FF-A components for communication at an FF-A instance. The spec also
1107/// describes the valid FF-A instances and conduits for each interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +01001108#[derive(Debug, Eq, PartialEq, Clone, Copy)]
1109pub enum Interface {
1110 Error {
1111 target_info: TargetInfo,
1112 error_code: FfaError,
Balint Dobszayb727aab2025-04-07 10:24:59 +02001113 error_arg: u32,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001114 },
1115 Success {
1116 target_info: u32,
1117 args: SuccessArgs,
1118 },
1119 Interrupt {
1120 target_info: TargetInfo,
1121 interrupt_id: u32,
1122 },
1123 Version {
1124 input_version: Version,
1125 },
1126 VersionOut {
1127 output_version: Version,
1128 },
1129 Features {
1130 feat_id: Feature,
1131 input_properties: u32,
1132 },
1133 RxAcquire {
1134 vm_id: u16,
1135 },
1136 RxRelease {
1137 vm_id: u16,
1138 },
1139 RxTxMap {
1140 addr: RxTxAddr,
1141 page_cnt: u32,
1142 },
1143 RxTxUnmap {
1144 id: u16,
1145 },
1146 PartitionInfoGet {
1147 uuid: Uuid,
Imre Kise295adb2025-04-10 13:26:28 +02001148 flags: PartitionInfoGetFlags,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001149 },
Tomás González0a058bc2025-03-11 11:20:55 +00001150 PartitionInfoGetRegs {
1151 uuid: Uuid,
1152 start_index: u16,
1153 info_tag: u16,
1154 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001155 IdGet,
1156 SpmIdGet,
Tomás González092202a2025-03-05 11:56:45 +00001157 MsgWait {
1158 flags: Option<MsgWaitFlags>,
1159 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001160 Yield,
1161 Run {
1162 target_info: TargetInfo,
1163 },
1164 NormalWorldResume,
Tomás González17b92442025-03-10 16:45:04 +00001165 SecondaryEpRegister {
1166 entrypoint: SecondaryEpRegisterAddr,
1167 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001168 MsgSend2 {
1169 sender_vm_id: u16,
1170 flags: u32,
1171 },
1172 MsgSendDirectReq {
1173 src_id: u16,
1174 dst_id: u16,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001175 args: DirectMsgArgs,
1176 },
1177 MsgSendDirectResp {
1178 src_id: u16,
1179 dst_id: u16,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001180 args: DirectMsgArgs,
1181 },
Balint Dobszayde0dc802025-02-28 14:16:52 +01001182 MsgSendDirectReq2 {
1183 src_id: u16,
1184 dst_id: u16,
1185 uuid: Uuid,
1186 args: DirectMsg2Args,
1187 },
1188 MsgSendDirectResp2 {
1189 src_id: u16,
1190 dst_id: u16,
1191 args: DirectMsg2Args,
1192 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001193 MemDonate {
1194 total_len: u32,
1195 frag_len: u32,
1196 buf: Option<MemOpBuf>,
1197 },
1198 MemLend {
1199 total_len: u32,
1200 frag_len: u32,
1201 buf: Option<MemOpBuf>,
1202 },
1203 MemShare {
1204 total_len: u32,
1205 frag_len: u32,
1206 buf: Option<MemOpBuf>,
1207 },
1208 MemRetrieveReq {
1209 total_len: u32,
1210 frag_len: u32,
1211 buf: Option<MemOpBuf>,
1212 },
1213 MemRetrieveResp {
1214 total_len: u32,
1215 frag_len: u32,
1216 },
1217 MemRelinquish,
1218 MemReclaim {
1219 handle: memory_management::Handle,
1220 flags: u32,
1221 },
1222 MemPermGet {
1223 addr: MemAddr,
Balint Dobszayde0dc802025-02-28 14:16:52 +01001224 page_cnt: Option<u32>,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001225 },
1226 MemPermSet {
1227 addr: MemAddr,
1228 page_cnt: u32,
1229 mem_perm: u32,
1230 },
1231 ConsoleLog {
1232 char_cnt: u8,
1233 char_lists: ConsoleLogChars,
1234 },
Tomás González7ffb6132025-04-03 12:28:58 +01001235 NotificationBitmapCreate {
1236 vm_id: u16,
1237 vcpu_cnt: u32,
1238 },
1239 NotificationBitmapDestroy {
1240 vm_id: u16,
1241 },
1242 NotificationBind {
1243 sender_id: u16,
1244 receiver_id: u16,
1245 flags: NotificationBindFlags,
1246 bitmap: u64,
1247 },
1248 NotificationUnBind {
1249 sender_id: u16,
1250 receiver_id: u16,
1251 bitmap: u64,
1252 },
1253 NotificationSet {
1254 sender_id: u16,
1255 receiver_id: u16,
1256 flags: NotificationSetFlags,
1257 bitmap: u64,
1258 },
1259 NotificationGet {
1260 vcpu_id: u16,
1261 endpoint_id: u16,
1262 flags: NotificationGetFlags,
1263 },
1264 NotificationInfoGet {
1265 is_32bit: bool,
1266 },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01001267 El3IntrHandle,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001268}
1269
Balint Dobszayde0dc802025-02-28 14:16:52 +01001270impl Interface {
1271 /// Returns the function ID for the call, if it has one.
1272 pub fn function_id(&self) -> Option<FuncId> {
1273 match self {
1274 Interface::Error { .. } => Some(FuncId::Error),
1275 Interface::Success { args, .. } => match args {
Imre Kis54773b62025-04-10 13:47:39 +02001276 SuccessArgs::Args32(..) => Some(FuncId::Success32),
1277 SuccessArgs::Args64(..) | SuccessArgs::Args64_2(..) => Some(FuncId::Success64),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001278 },
1279 Interface::Interrupt { .. } => Some(FuncId::Interrupt),
1280 Interface::Version { .. } => Some(FuncId::Version),
1281 Interface::VersionOut { .. } => None,
1282 Interface::Features { .. } => Some(FuncId::Features),
1283 Interface::RxAcquire { .. } => Some(FuncId::RxAcquire),
1284 Interface::RxRelease { .. } => Some(FuncId::RxRelease),
1285 Interface::RxTxMap { addr, .. } => match addr {
1286 RxTxAddr::Addr32 { .. } => Some(FuncId::RxTxMap32),
1287 RxTxAddr::Addr64 { .. } => Some(FuncId::RxTxMap64),
1288 },
1289 Interface::RxTxUnmap { .. } => Some(FuncId::RxTxUnmap),
1290 Interface::PartitionInfoGet { .. } => Some(FuncId::PartitionInfoGet),
Tomás González0a058bc2025-03-11 11:20:55 +00001291 Interface::PartitionInfoGetRegs { .. } => Some(FuncId::PartitionInfoGetRegs),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001292 Interface::IdGet => Some(FuncId::IdGet),
1293 Interface::SpmIdGet => Some(FuncId::SpmIdGet),
Tomás González092202a2025-03-05 11:56:45 +00001294 Interface::MsgWait { .. } => Some(FuncId::MsgWait),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001295 Interface::Yield => Some(FuncId::Yield),
1296 Interface::Run { .. } => Some(FuncId::Run),
1297 Interface::NormalWorldResume => Some(FuncId::NormalWorldResume),
Tomás González17b92442025-03-10 16:45:04 +00001298 Interface::SecondaryEpRegister { entrypoint } => match entrypoint {
1299 SecondaryEpRegisterAddr::Addr32 { .. } => Some(FuncId::SecondaryEpRegister32),
1300 SecondaryEpRegisterAddr::Addr64 { .. } => Some(FuncId::SecondaryEpRegister64),
1301 },
Balint Dobszayde0dc802025-02-28 14:16:52 +01001302 Interface::MsgSend2 { .. } => Some(FuncId::MsgSend2),
1303 Interface::MsgSendDirectReq { args, .. } => match args {
1304 DirectMsgArgs::Args32(_) => Some(FuncId::MsgSendDirectReq32),
1305 DirectMsgArgs::Args64(_) => Some(FuncId::MsgSendDirectReq64),
Tomás González4d5b0ba2025-03-03 17:15:55 +00001306 DirectMsgArgs::VersionReq { .. } => Some(FuncId::MsgSendDirectReq32),
1307 DirectMsgArgs::PowerPsciReq32 { .. } => Some(FuncId::MsgSendDirectReq32),
1308 DirectMsgArgs::PowerPsciReq64 { .. } => Some(FuncId::MsgSendDirectReq64),
1309 DirectMsgArgs::PowerWarmBootReq { .. } => Some(FuncId::MsgSendDirectReq32),
1310 DirectMsgArgs::VmCreated { .. } => Some(FuncId::MsgSendDirectReq32),
1311 DirectMsgArgs::VmDestructed { .. } => Some(FuncId::MsgSendDirectReq32),
1312 _ => None,
Balint Dobszayde0dc802025-02-28 14:16:52 +01001313 },
1314 Interface::MsgSendDirectResp { args, .. } => match args {
1315 DirectMsgArgs::Args32(_) => Some(FuncId::MsgSendDirectResp32),
1316 DirectMsgArgs::Args64(_) => Some(FuncId::MsgSendDirectResp64),
Tomás González4d5b0ba2025-03-03 17:15:55 +00001317 DirectMsgArgs::VersionResp { .. } => Some(FuncId::MsgSendDirectResp32),
1318 DirectMsgArgs::PowerPsciResp { .. } => Some(FuncId::MsgSendDirectResp32),
1319 DirectMsgArgs::VmCreatedAck { .. } => Some(FuncId::MsgSendDirectResp32),
1320 DirectMsgArgs::VmDestructedAck { .. } => Some(FuncId::MsgSendDirectResp32),
1321 _ => None,
Balint Dobszayde0dc802025-02-28 14:16:52 +01001322 },
1323 Interface::MsgSendDirectReq2 { .. } => Some(FuncId::MsgSendDirectReq64_2),
1324 Interface::MsgSendDirectResp2 { .. } => Some(FuncId::MsgSendDirectResp64_2),
1325 Interface::MemDonate { buf, .. } => match buf {
1326 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemDonate64),
1327 _ => Some(FuncId::MemDonate32),
1328 },
1329 Interface::MemLend { buf, .. } => match buf {
1330 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemLend64),
1331 _ => Some(FuncId::MemLend32),
1332 },
1333 Interface::MemShare { buf, .. } => match buf {
1334 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemShare64),
1335 _ => Some(FuncId::MemShare32),
1336 },
1337 Interface::MemRetrieveReq { buf, .. } => match buf {
1338 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemRetrieveReq64),
1339 _ => Some(FuncId::MemRetrieveReq32),
1340 },
1341 Interface::MemRetrieveResp { .. } => Some(FuncId::MemRetrieveResp),
1342 Interface::MemRelinquish => Some(FuncId::MemRelinquish),
1343 Interface::MemReclaim { .. } => Some(FuncId::MemReclaim),
1344 Interface::MemPermGet { addr, .. } => match addr {
1345 MemAddr::Addr32(_) => Some(FuncId::MemPermGet32),
1346 MemAddr::Addr64(_) => Some(FuncId::MemPermGet64),
1347 },
1348 Interface::MemPermSet { addr, .. } => match addr {
1349 MemAddr::Addr32(_) => Some(FuncId::MemPermSet32),
1350 MemAddr::Addr64(_) => Some(FuncId::MemPermSet64),
1351 },
1352 Interface::ConsoleLog { char_lists, .. } => match char_lists {
1353 ConsoleLogChars::Reg32(_) => Some(FuncId::ConsoleLog32),
1354 ConsoleLogChars::Reg64(_) => Some(FuncId::ConsoleLog64),
1355 },
Tomás González7ffb6132025-04-03 12:28:58 +01001356 Interface::NotificationBitmapCreate { .. } => Some(FuncId::NotificationBitmapCreate),
1357 Interface::NotificationBitmapDestroy { .. } => Some(FuncId::NotificationBitmapDestroy),
1358 Interface::NotificationBind { .. } => Some(FuncId::NotificationBind),
1359 Interface::NotificationUnBind { .. } => Some(FuncId::NotificationUnbind),
1360 Interface::NotificationSet { .. } => Some(FuncId::NotificationSet),
1361 Interface::NotificationGet { .. } => Some(FuncId::NotificationGet),
1362 Interface::NotificationInfoGet { is_32bit } => match is_32bit {
1363 true => Some(FuncId::NotificationInfoGet32),
1364 false => Some(FuncId::NotificationInfoGet64),
1365 },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01001366 Interface::El3IntrHandle => Some(FuncId::El3IntrHandle),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001367 }
1368 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01001369
Balint Dobszayde0dc802025-02-28 14:16:52 +01001370 /// Returns true if this is a 32-bit call, or false if it is a 64-bit call.
1371 pub fn is_32bit(&self) -> bool {
1372 // TODO: self should always have a function ID?
1373 self.function_id().unwrap().is_32bit()
1374 }
1375
Balint Dobszay82c71dd2025-04-15 10:16:44 +02001376 /// Returns the FF-A version that has introduced the function ID.
1377 pub fn minimum_ffa_version(&self) -> Version {
1378 self.function_id().unwrap().minimum_ffa_version()
1379 }
1380
Balint Dobszayde0dc802025-02-28 14:16:52 +01001381 /// Parse interface from register contents. The caller must ensure that the `regs` argument has
1382 /// the correct length: 8 registers for FF-A v1.1 and lower, 18 registers for v1.2 and higher.
1383 pub fn from_regs(version: Version, regs: &[u64]) -> Result<Self, Error> {
Balint Dobszay82c71dd2025-04-15 10:16:44 +02001384 let func_id = FuncId::try_from(regs[0] as u32)?;
1385 if version < func_id.minimum_ffa_version() {
1386 return Err(Error::InvalidVersionForFunctionId(version, func_id));
1387 }
1388
Balint Dobszayde0dc802025-02-28 14:16:52 +01001389 let reg_cnt = regs.len();
1390
1391 let msg = match reg_cnt {
1392 8 => {
1393 assert!(version <= Version(1, 1));
1394 Interface::unpack_regs8(version, regs.try_into().unwrap())?
1395 }
1396 18 => {
1397 assert!(version >= Version(1, 2));
Balint Dobszay82c71dd2025-04-15 10:16:44 +02001398 match func_id {
Balint Dobszayde0dc802025-02-28 14:16:52 +01001399 FuncId::ConsoleLog64
1400 | FuncId::Success64
1401 | FuncId::MsgSendDirectReq64_2
Tomás González0a058bc2025-03-11 11:20:55 +00001402 | FuncId::MsgSendDirectResp64_2
1403 | FuncId::PartitionInfoGetRegs => {
Balint Dobszayde0dc802025-02-28 14:16:52 +01001404 Interface::unpack_regs18(version, regs.try_into().unwrap())?
1405 }
1406 _ => Interface::unpack_regs8(version, regs[..8].try_into().unwrap())?,
1407 }
1408 }
1409 _ => panic!(
1410 "Invalid number of registers ({}) for FF-A version {}",
1411 reg_cnt, version
1412 ),
1413 };
1414
1415 Ok(msg)
1416 }
1417
1418 fn unpack_regs8(version: Version, regs: &[u64; 8]) -> Result<Self, Error> {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001419 let fid = FuncId::try_from(regs[0] as u32)?;
1420
1421 let msg = match fid {
1422 FuncId::Error => Self::Error {
1423 target_info: (regs[1] as u32).into(),
1424 error_code: FfaError::try_from(regs[2] as i32)?,
Balint Dobszayb727aab2025-04-07 10:24:59 +02001425 error_arg: regs[3] as u32,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001426 },
1427 FuncId::Success32 => Self::Success {
1428 target_info: regs[1] as u32,
Imre Kis54773b62025-04-10 13:47:39 +02001429 args: SuccessArgs::Args32([
Balint Dobszay3aad9572025-01-17 16:54:11 +01001430 regs[2] as u32,
1431 regs[3] as u32,
1432 regs[4] as u32,
1433 regs[5] as u32,
1434 regs[6] as u32,
1435 regs[7] as u32,
1436 ]),
1437 },
1438 FuncId::Success64 => Self::Success {
1439 target_info: regs[1] as u32,
Imre Kis54773b62025-04-10 13:47:39 +02001440 args: SuccessArgs::Args64([regs[2], regs[3], regs[4], regs[5], regs[6], regs[7]]),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001441 },
1442 FuncId::Interrupt => Self::Interrupt {
1443 target_info: (regs[1] as u32).into(),
1444 interrupt_id: regs[2] as u32,
1445 },
1446 FuncId::Version => Self::Version {
Tomás González83146af2025-03-04 11:32:41 +00001447 input_version: (regs[1] as u32).try_into()?,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001448 },
1449 FuncId::Features => Self::Features {
Balint Dobszayc31e0b92025-03-03 20:16:56 +01001450 feat_id: (regs[1] as u32).into(),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001451 input_properties: regs[2] as u32,
1452 },
1453 FuncId::RxAcquire => Self::RxAcquire {
1454 vm_id: regs[1] as u16,
1455 },
1456 FuncId::RxRelease => Self::RxRelease {
1457 vm_id: regs[1] as u16,
1458 },
1459 FuncId::RxTxMap32 => {
1460 let addr = RxTxAddr::Addr32 {
1461 rx: regs[2] as u32,
1462 tx: regs[1] as u32,
1463 };
1464 let page_cnt = regs[3] as u32;
1465
1466 Self::RxTxMap { addr, page_cnt }
1467 }
1468 FuncId::RxTxMap64 => {
1469 let addr = RxTxAddr::Addr64 {
1470 rx: regs[2],
1471 tx: regs[1],
1472 };
1473 let page_cnt = regs[3] as u32;
1474
1475 Self::RxTxMap { addr, page_cnt }
1476 }
1477 FuncId::RxTxUnmap => Self::RxTxUnmap { id: regs[1] as u16 },
1478 FuncId::PartitionInfoGet => {
1479 let uuid_words = [
1480 regs[1] as u32,
1481 regs[2] as u32,
1482 regs[3] as u32,
1483 regs[4] as u32,
1484 ];
1485 let mut bytes: [u8; 16] = [0; 16];
1486 for (i, b) in uuid_words.iter().flat_map(|w| w.to_le_bytes()).enumerate() {
1487 bytes[i] = b;
1488 }
1489 Self::PartitionInfoGet {
1490 uuid: Uuid::from_bytes(bytes),
Imre Kise295adb2025-04-10 13:26:28 +02001491 flags: PartitionInfoGetFlags::try_from(regs[5] as u32)?,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001492 }
1493 }
1494 FuncId::IdGet => Self::IdGet,
1495 FuncId::SpmIdGet => Self::SpmIdGet,
Tomás González092202a2025-03-05 11:56:45 +00001496 FuncId::MsgWait => Self::MsgWait {
1497 flags: if version >= Version(1, 2) {
1498 Some(MsgWaitFlags::try_from(regs[2] as u32)?)
1499 } else {
1500 None
1501 },
1502 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001503 FuncId::Yield => Self::Yield,
1504 FuncId::Run => Self::Run {
1505 target_info: (regs[1] as u32).into(),
1506 },
1507 FuncId::NormalWorldResume => Self::NormalWorldResume,
Tomás González17b92442025-03-10 16:45:04 +00001508 FuncId::SecondaryEpRegister32 => Self::SecondaryEpRegister {
1509 entrypoint: SecondaryEpRegisterAddr::Addr32(regs[1] as u32),
1510 },
1511 FuncId::SecondaryEpRegister64 => Self::SecondaryEpRegister {
1512 entrypoint: SecondaryEpRegisterAddr::Addr64(regs[1]),
1513 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001514 FuncId::MsgSend2 => Self::MsgSend2 {
1515 sender_vm_id: regs[1] as u16,
1516 flags: regs[2] as u32,
1517 },
1518 FuncId::MsgSendDirectReq32 => Self::MsgSendDirectReq {
1519 src_id: (regs[1] >> 16) as u16,
1520 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001521 args: if (regs[2] as u32 & DirectMsgArgs::FWK_MSG_BITS) != 0 {
1522 match regs[2] as u32 {
1523 DirectMsgArgs::VERSION_REQ => DirectMsgArgs::VersionReq {
1524 version: Version::try_from(regs[3] as u32)?,
1525 },
1526 DirectMsgArgs::POWER_PSCI_REQ => DirectMsgArgs::PowerPsciReq32 {
Tomás González67f92c72025-03-20 16:50:42 +00001527 params: [
1528 regs[3] as u32,
1529 regs[4] as u32,
1530 regs[5] as u32,
1531 regs[6] as u32,
1532 ],
Tomás González4d5b0ba2025-03-03 17:15:55 +00001533 },
1534 DirectMsgArgs::POWER_WARM_BOOT_REQ => DirectMsgArgs::PowerWarmBootReq {
1535 boot_type: WarmBootType::try_from(regs[3] as u32)?,
1536 },
1537 DirectMsgArgs::VM_CREATED => DirectMsgArgs::VmCreated {
1538 handle: memory_management::Handle::from([
1539 regs[3] as u32,
1540 regs[4] as u32,
1541 ]),
1542 vm_id: regs[5] as u16,
1543 },
1544 DirectMsgArgs::VM_DESTRUCTED => DirectMsgArgs::VmDestructed {
1545 handle: memory_management::Handle::from([
1546 regs[3] as u32,
1547 regs[4] as u32,
1548 ]),
1549 vm_id: regs[5] as u16,
1550 },
1551 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1552 }
1553 } else {
1554 DirectMsgArgs::Args32([
1555 regs[3] as u32,
1556 regs[4] as u32,
1557 regs[5] as u32,
1558 regs[6] as u32,
1559 regs[7] as u32,
1560 ])
1561 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001562 },
1563 FuncId::MsgSendDirectReq64 => Self::MsgSendDirectReq {
1564 src_id: (regs[1] >> 16) as u16,
1565 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001566 args: if (regs[2] & DirectMsgArgs::FWK_MSG_BITS as u64) != 0 {
1567 match regs[2] as u32 {
1568 DirectMsgArgs::POWER_PSCI_REQ => DirectMsgArgs::PowerPsciReq64 {
Tomás González67f92c72025-03-20 16:50:42 +00001569 params: [regs[3], regs[4], regs[5], regs[6]],
Tomás González4d5b0ba2025-03-03 17:15:55 +00001570 },
1571 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1572 }
1573 } else {
1574 DirectMsgArgs::Args64([regs[3], regs[4], regs[5], regs[6], regs[7]])
1575 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001576 },
1577 FuncId::MsgSendDirectResp32 => Self::MsgSendDirectResp {
1578 src_id: (regs[1] >> 16) as u16,
1579 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001580 args: if (regs[2] as u32 & DirectMsgArgs::FWK_MSG_BITS) != 0 {
1581 match regs[2] as u32 {
1582 DirectMsgArgs::VERSION_RESP => {
1583 if regs[3] as i32 == FfaError::NotSupported.into() {
1584 DirectMsgArgs::VersionResp { version: None }
1585 } else {
1586 DirectMsgArgs::VersionResp {
1587 version: Some(Version::try_from(regs[3] as u32)?),
1588 }
1589 }
1590 }
1591 DirectMsgArgs::POWER_PSCI_RESP => DirectMsgArgs::PowerPsciResp {
1592 psci_status: regs[3] as i32,
1593 },
1594 DirectMsgArgs::VM_CREATED_ACK => DirectMsgArgs::VmCreatedAck {
1595 sp_status: (regs[3] as i32).try_into()?,
1596 },
1597 DirectMsgArgs::VM_DESTRUCTED_ACK => DirectMsgArgs::VmDestructedAck {
1598 sp_status: (regs[3] as i32).try_into()?,
1599 },
1600 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1601 }
1602 } else {
1603 DirectMsgArgs::Args32([
1604 regs[3] as u32,
1605 regs[4] as u32,
1606 regs[5] as u32,
1607 regs[6] as u32,
1608 regs[7] as u32,
1609 ])
1610 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001611 },
1612 FuncId::MsgSendDirectResp64 => Self::MsgSendDirectResp {
1613 src_id: (regs[1] >> 16) as u16,
1614 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001615 args: if (regs[2] & DirectMsgArgs::FWK_MSG_BITS as u64) != 0 {
1616 return Err(Error::UnrecognisedFwkMsg(regs[2] as u32));
1617 } else {
1618 DirectMsgArgs::Args64([regs[3], regs[4], regs[5], regs[6], regs[7]])
1619 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001620 },
1621 FuncId::MemDonate32 => Self::MemDonate {
1622 total_len: regs[1] as u32,
1623 frag_len: regs[2] as u32,
1624 buf: if regs[3] != 0 && regs[4] != 0 {
1625 Some(MemOpBuf::Buf32 {
1626 addr: regs[3] as u32,
1627 page_cnt: regs[4] as u32,
1628 })
1629 } else {
1630 None
1631 },
1632 },
1633 FuncId::MemDonate64 => Self::MemDonate {
1634 total_len: regs[1] as u32,
1635 frag_len: regs[2] as u32,
1636 buf: if regs[3] != 0 && regs[4] != 0 {
1637 Some(MemOpBuf::Buf64 {
1638 addr: regs[3],
1639 page_cnt: regs[4] as u32,
1640 })
1641 } else {
1642 None
1643 },
1644 },
1645 FuncId::MemLend32 => Self::MemLend {
1646 total_len: regs[1] as u32,
1647 frag_len: regs[2] as u32,
1648 buf: if regs[3] != 0 && regs[4] != 0 {
1649 Some(MemOpBuf::Buf32 {
1650 addr: regs[3] as u32,
1651 page_cnt: regs[4] as u32,
1652 })
1653 } else {
1654 None
1655 },
1656 },
1657 FuncId::MemLend64 => Self::MemLend {
1658 total_len: regs[1] as u32,
1659 frag_len: regs[2] as u32,
1660 buf: if regs[3] != 0 && regs[4] != 0 {
1661 Some(MemOpBuf::Buf64 {
1662 addr: regs[3],
1663 page_cnt: regs[4] as u32,
1664 })
1665 } else {
1666 None
1667 },
1668 },
1669 FuncId::MemShare32 => Self::MemShare {
1670 total_len: regs[1] as u32,
1671 frag_len: regs[2] as u32,
1672 buf: if regs[3] != 0 && regs[4] != 0 {
1673 Some(MemOpBuf::Buf32 {
1674 addr: regs[3] as u32,
1675 page_cnt: regs[4] as u32,
1676 })
1677 } else {
1678 None
1679 },
1680 },
1681 FuncId::MemShare64 => Self::MemShare {
1682 total_len: regs[1] as u32,
1683 frag_len: regs[2] as u32,
1684 buf: if regs[3] != 0 && regs[4] != 0 {
1685 Some(MemOpBuf::Buf64 {
1686 addr: regs[3],
1687 page_cnt: regs[4] as u32,
1688 })
1689 } else {
1690 None
1691 },
1692 },
1693 FuncId::MemRetrieveReq32 => Self::MemRetrieveReq {
1694 total_len: regs[1] as u32,
1695 frag_len: regs[2] as u32,
1696 buf: if regs[3] != 0 && regs[4] != 0 {
1697 Some(MemOpBuf::Buf32 {
1698 addr: regs[3] as u32,
1699 page_cnt: regs[4] as u32,
1700 })
1701 } else {
1702 None
1703 },
1704 },
1705 FuncId::MemRetrieveReq64 => Self::MemRetrieveReq {
1706 total_len: regs[1] as u32,
1707 frag_len: regs[2] as u32,
1708 buf: if regs[3] != 0 && regs[4] != 0 {
1709 Some(MemOpBuf::Buf64 {
1710 addr: regs[3],
1711 page_cnt: regs[4] as u32,
1712 })
1713 } else {
1714 None
1715 },
1716 },
1717 FuncId::MemRetrieveResp => Self::MemRetrieveResp {
1718 total_len: regs[1] as u32,
1719 frag_len: regs[2] as u32,
1720 },
1721 FuncId::MemRelinquish => Self::MemRelinquish,
1722 FuncId::MemReclaim => Self::MemReclaim {
1723 handle: memory_management::Handle::from([regs[1] as u32, regs[2] as u32]),
1724 flags: regs[3] as u32,
1725 },
1726 FuncId::MemPermGet32 => Self::MemPermGet {
1727 addr: MemAddr::Addr32(regs[1] as u32),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001728 page_cnt: if version >= Version(1, 3) {
1729 Some(regs[2] as u32)
1730 } else {
1731 None
1732 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001733 },
1734 FuncId::MemPermGet64 => Self::MemPermGet {
1735 addr: MemAddr::Addr64(regs[1]),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001736 page_cnt: if version >= Version(1, 3) {
1737 Some(regs[2] as u32)
1738 } else {
1739 None
1740 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001741 },
1742 FuncId::MemPermSet32 => Self::MemPermSet {
1743 addr: MemAddr::Addr32(regs[1] as u32),
1744 page_cnt: regs[2] as u32,
1745 mem_perm: regs[3] as u32,
1746 },
1747 FuncId::MemPermSet64 => Self::MemPermSet {
1748 addr: MemAddr::Addr64(regs[1]),
1749 page_cnt: regs[2] as u32,
1750 mem_perm: regs[3] as u32,
1751 },
1752 FuncId::ConsoleLog32 => Self::ConsoleLog {
1753 char_cnt: regs[1] as u8,
1754 char_lists: ConsoleLogChars::Reg32([
1755 regs[2] as u32,
1756 regs[3] as u32,
1757 regs[4] as u32,
1758 regs[5] as u32,
1759 regs[6] as u32,
1760 regs[7] as u32,
1761 ]),
1762 },
Tomás González7ffb6132025-04-03 12:28:58 +01001763 FuncId::NotificationBitmapCreate => {
1764 let tentative_vm_id = regs[1] as u32;
1765 if (tentative_vm_id >> 16) != 0 {
1766 return Err(Error::InvalidVmId(tentative_vm_id));
1767 }
1768 Self::NotificationBitmapCreate {
1769 vm_id: tentative_vm_id as u16,
1770 vcpu_cnt: regs[2] as u32,
1771 }
1772 }
1773 FuncId::NotificationBitmapDestroy => {
1774 let tentative_vm_id = regs[1] as u32;
1775 if (tentative_vm_id >> 16) != 0 {
1776 return Err(Error::InvalidVmId(tentative_vm_id));
1777 }
1778 Self::NotificationBitmapDestroy {
1779 vm_id: tentative_vm_id as u16,
1780 }
1781 }
1782 FuncId::NotificationBind => Self::NotificationBind {
1783 sender_id: (regs[1] >> 16) as u16,
1784 receiver_id: regs[1] as u16,
1785 flags: (regs[2] as u32).into(),
1786 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1787 },
1788 FuncId::NotificationUnbind => Self::NotificationUnBind {
1789 sender_id: (regs[1] >> 16) as u16,
1790 receiver_id: regs[1] as u16,
1791 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1792 },
1793 FuncId::NotificationSet => Self::NotificationSet {
1794 sender_id: (regs[1] >> 16) as u16,
1795 receiver_id: regs[1] as u16,
1796 flags: (regs[2] as u32).try_into()?,
1797 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1798 },
1799 FuncId::NotificationGet => Self::NotificationGet {
1800 vcpu_id: (regs[1] >> 16) as u16,
1801 endpoint_id: regs[1] as u16,
1802 flags: (regs[2] as u32).into(),
1803 },
1804 FuncId::NotificationInfoGet32 => Self::NotificationInfoGet { is_32bit: true },
1805 FuncId::NotificationInfoGet64 => Self::NotificationInfoGet { is_32bit: false },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01001806 FuncId::El3IntrHandle => Self::El3IntrHandle,
Balint Dobszayde0dc802025-02-28 14:16:52 +01001807 _ => panic!("Invalid number of registers (8) for function {:#x?}", fid),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001808 };
1809
1810 Ok(msg)
1811 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01001812
Balint Dobszayde0dc802025-02-28 14:16:52 +01001813 fn unpack_regs18(version: Version, regs: &[u64; 18]) -> Result<Self, Error> {
1814 assert!(version >= Version(1, 2));
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001815
Balint Dobszayde0dc802025-02-28 14:16:52 +01001816 let fid = FuncId::try_from(regs[0] as u32)?;
1817
1818 let msg = match fid {
1819 FuncId::Success64 => Self::Success {
1820 target_info: regs[1] as u32,
Imre Kis54773b62025-04-10 13:47:39 +02001821 args: SuccessArgs::Args64_2(regs[2..18].try_into().unwrap()),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001822 },
1823 FuncId::MsgSendDirectReq64_2 => Self::MsgSendDirectReq2 {
1824 src_id: (regs[1] >> 16) as u16,
1825 dst_id: regs[1] as u16,
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00001826 uuid: Uuid::from_u64_pair(regs[2].swap_bytes(), regs[3].swap_bytes()),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001827 args: DirectMsg2Args(regs[4..18].try_into().unwrap()),
1828 },
1829 FuncId::MsgSendDirectResp64_2 => Self::MsgSendDirectResp2 {
1830 src_id: (regs[1] >> 16) as u16,
1831 dst_id: regs[1] as u16,
1832 args: DirectMsg2Args(regs[4..18].try_into().unwrap()),
1833 },
1834 FuncId::ConsoleLog64 => Self::ConsoleLog {
1835 char_cnt: regs[1] as u8,
1836 char_lists: ConsoleLogChars::Reg64(regs[2..18].try_into().unwrap()),
1837 },
Tomás González0a058bc2025-03-11 11:20:55 +00001838 FuncId::PartitionInfoGetRegs => {
1839 // Bits[15:0]: Start index
1840 let start_index = (regs[3] & 0xffff) as u16;
1841 let info_tag = ((regs[3] >> 16) & 0xffff) as u16;
1842 Self::PartitionInfoGetRegs {
1843 uuid: Uuid::from_u64_pair(regs[1].swap_bytes(), regs[2].swap_bytes()),
1844 start_index,
1845 info_tag: if start_index == 0 && info_tag != 0 {
1846 return Err(Error::InvalidInformationTag(info_tag));
1847 } else {
1848 info_tag
1849 },
1850 }
1851 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001852 _ => panic!("Invalid number of registers (18) for function {:#x?}", fid),
1853 };
1854
1855 Ok(msg)
Balint Dobszay3aad9572025-01-17 16:54:11 +01001856 }
1857
Balint Dobszaya5846852025-02-26 15:38:53 +01001858 /// Create register contents for an interface.
Balint Dobszayde0dc802025-02-28 14:16:52 +01001859 pub fn to_regs(&self, version: Version, regs: &mut [u64]) {
Balint Dobszay82c71dd2025-04-15 10:16:44 +02001860 assert!(self.minimum_ffa_version() <= version);
1861
Balint Dobszayde0dc802025-02-28 14:16:52 +01001862 let reg_cnt = regs.len();
1863
1864 match reg_cnt {
1865 8 => {
1866 assert!(version <= Version(1, 1));
Balint Dobszay91bea9b2025-04-09 13:16:06 +02001867 regs.fill(0);
1868
Balint Dobszayde0dc802025-02-28 14:16:52 +01001869 self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
1870 }
1871 18 => {
1872 assert!(version >= Version(1, 2));
Balint Dobszay91bea9b2025-04-09 13:16:06 +02001873 regs.fill(0);
Balint Dobszayde0dc802025-02-28 14:16:52 +01001874
1875 match self {
1876 Interface::ConsoleLog {
1877 char_lists: ConsoleLogChars::Reg64(_),
1878 ..
1879 }
1880 | Interface::Success {
Imre Kis54773b62025-04-10 13:47:39 +02001881 args: SuccessArgs::Args64_2(_),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001882 ..
1883 }
1884 | Interface::MsgSendDirectReq2 { .. }
Tomás González0a058bc2025-03-11 11:20:55 +00001885 | Interface::MsgSendDirectResp2 { .. }
1886 | Interface::PartitionInfoGetRegs { .. } => {
Balint Dobszayde0dc802025-02-28 14:16:52 +01001887 self.pack_regs18(version, regs.try_into().unwrap());
1888 }
1889 _ => {
1890 self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
1891 }
1892 }
1893 }
1894 _ => panic!("Invalid number of registers {}", reg_cnt),
1895 }
1896 }
1897
1898 fn pack_regs8(&self, version: Version, a: &mut [u64; 8]) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001899 if let Some(function_id) = self.function_id() {
1900 a[0] = function_id as u64;
1901 }
1902
1903 match *self {
1904 Interface::Error {
1905 target_info,
1906 error_code,
Balint Dobszayb727aab2025-04-07 10:24:59 +02001907 error_arg,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001908 } => {
1909 a[1] = u32::from(target_info).into();
1910 a[2] = (error_code as u32).into();
Balint Dobszayb727aab2025-04-07 10:24:59 +02001911 a[3] = error_arg.into();
Balint Dobszay3aad9572025-01-17 16:54:11 +01001912 }
1913 Interface::Success { target_info, args } => {
1914 a[1] = target_info.into();
1915 match args {
Imre Kis54773b62025-04-10 13:47:39 +02001916 SuccessArgs::Args32(regs) => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001917 a[2] = regs[0].into();
1918 a[3] = regs[1].into();
1919 a[4] = regs[2].into();
1920 a[5] = regs[3].into();
1921 a[6] = regs[4].into();
1922 a[7] = regs[5].into();
1923 }
Imre Kis54773b62025-04-10 13:47:39 +02001924 SuccessArgs::Args64(regs) => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001925 a[2] = regs[0];
1926 a[3] = regs[1];
1927 a[4] = regs[2];
1928 a[5] = regs[3];
1929 a[6] = regs[4];
1930 a[7] = regs[5];
1931 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001932 _ => panic!("{:#x?} requires 18 registers", args),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001933 }
1934 }
1935 Interface::Interrupt {
1936 target_info,
1937 interrupt_id,
1938 } => {
1939 a[1] = u32::from(target_info).into();
1940 a[2] = interrupt_id.into();
1941 }
1942 Interface::Version { input_version } => {
1943 a[1] = u32::from(input_version).into();
1944 }
1945 Interface::VersionOut { output_version } => {
1946 a[0] = u32::from(output_version).into();
1947 }
1948 Interface::Features {
1949 feat_id,
1950 input_properties,
1951 } => {
1952 a[1] = u32::from(feat_id).into();
1953 a[2] = input_properties.into();
1954 }
1955 Interface::RxAcquire { vm_id } => {
1956 a[1] = vm_id.into();
1957 }
1958 Interface::RxRelease { vm_id } => {
1959 a[1] = vm_id.into();
1960 }
1961 Interface::RxTxMap { addr, page_cnt } => {
1962 match addr {
1963 RxTxAddr::Addr32 { rx, tx } => {
1964 a[1] = tx.into();
1965 a[2] = rx.into();
1966 }
1967 RxTxAddr::Addr64 { rx, tx } => {
1968 a[1] = tx;
1969 a[2] = rx;
1970 }
1971 }
1972 a[3] = page_cnt.into();
1973 }
1974 Interface::RxTxUnmap { id } => {
1975 a[1] = id.into();
1976 }
1977 Interface::PartitionInfoGet { uuid, flags } => {
1978 let bytes = uuid.into_bytes();
1979 a[1] = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]).into();
1980 a[2] = u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]).into();
1981 a[3] = u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]).into();
1982 a[4] = u32::from_le_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]).into();
Imre Kise295adb2025-04-10 13:26:28 +02001983 a[5] = u32::from(flags).into();
Balint Dobszay3aad9572025-01-17 16:54:11 +01001984 }
Tomás González092202a2025-03-05 11:56:45 +00001985 Interface::MsgWait { flags } => {
1986 if version >= Version(1, 2) {
1987 if let Some(flags) = flags {
1988 a[2] = u32::from(flags).into();
1989 }
1990 }
1991 }
1992 Interface::IdGet | Interface::SpmIdGet | Interface::Yield => {}
Balint Dobszay3aad9572025-01-17 16:54:11 +01001993 Interface::Run { target_info } => {
1994 a[1] = u32::from(target_info).into();
1995 }
1996 Interface::NormalWorldResume => {}
Tomás González17b92442025-03-10 16:45:04 +00001997 Interface::SecondaryEpRegister { entrypoint } => match entrypoint {
1998 SecondaryEpRegisterAddr::Addr32(addr) => a[1] = addr as u64,
1999 SecondaryEpRegisterAddr::Addr64(addr) => a[1] = addr,
2000 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01002001 Interface::MsgSend2 {
2002 sender_vm_id,
2003 flags,
2004 } => {
2005 a[1] = sender_vm_id.into();
2006 a[2] = flags.into();
2007 }
2008 Interface::MsgSendDirectReq {
2009 src_id,
2010 dst_id,
Balint Dobszay3aad9572025-01-17 16:54:11 +01002011 args,
2012 } => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +01002013 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Balint Dobszay3aad9572025-01-17 16:54:11 +01002014 match args {
2015 DirectMsgArgs::Args32(args) => {
2016 a[3] = args[0].into();
2017 a[4] = args[1].into();
2018 a[5] = args[2].into();
2019 a[6] = args[3].into();
2020 a[7] = args[4].into();
2021 }
2022 DirectMsgArgs::Args64(args) => {
2023 a[3] = args[0];
2024 a[4] = args[1];
2025 a[5] = args[2];
2026 a[6] = args[3];
2027 a[7] = args[4];
2028 }
Tomás González4d5b0ba2025-03-03 17:15:55 +00002029 DirectMsgArgs::VersionReq { version } => {
2030 a[2] = DirectMsgArgs::VERSION_REQ.into();
2031 a[3] = u32::from(version).into();
2032 }
Tomás González67f92c72025-03-20 16:50:42 +00002033 DirectMsgArgs::PowerPsciReq32 { params } => {
Tomás González4d5b0ba2025-03-03 17:15:55 +00002034 a[2] = DirectMsgArgs::POWER_PSCI_REQ.into();
Tomás González67f92c72025-03-20 16:50:42 +00002035 a[3] = params[0].into();
2036 a[4] = params[1].into();
2037 a[5] = params[2].into();
2038 a[6] = params[3].into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00002039 }
Tomás González67f92c72025-03-20 16:50:42 +00002040 DirectMsgArgs::PowerPsciReq64 { params } => {
Tomás González4d5b0ba2025-03-03 17:15:55 +00002041 a[2] = DirectMsgArgs::POWER_PSCI_REQ.into();
Tomás González67f92c72025-03-20 16:50:42 +00002042 a[3] = params[0];
2043 a[4] = params[1];
2044 a[5] = params[2];
2045 a[6] = params[3];
Tomás González4d5b0ba2025-03-03 17:15:55 +00002046 }
2047 DirectMsgArgs::PowerWarmBootReq { boot_type } => {
2048 a[2] = DirectMsgArgs::POWER_WARM_BOOT_REQ.into();
2049 a[3] = u32::from(boot_type).into();
2050 }
2051 DirectMsgArgs::VmCreated { handle, vm_id } => {
2052 a[2] = DirectMsgArgs::VM_CREATED.into();
2053 let handle_regs: [u32; 2] = handle.into();
2054 a[3] = handle_regs[0].into();
2055 a[4] = handle_regs[1].into();
2056 a[5] = vm_id.into();
2057 }
2058 DirectMsgArgs::VmDestructed { handle, vm_id } => {
2059 a[2] = DirectMsgArgs::VM_DESTRUCTED.into();
2060 let handle_regs: [u32; 2] = handle.into();
2061 a[3] = handle_regs[0].into();
2062 a[4] = handle_regs[1].into();
2063 a[5] = vm_id.into();
2064 }
2065 _ => panic!("Malformed MsgSendDirectReq interface"),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002066 }
2067 }
2068 Interface::MsgSendDirectResp {
2069 src_id,
2070 dst_id,
Balint Dobszay3aad9572025-01-17 16:54:11 +01002071 args,
2072 } => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +01002073 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Balint Dobszay3aad9572025-01-17 16:54:11 +01002074 match args {
2075 DirectMsgArgs::Args32(args) => {
2076 a[3] = args[0].into();
2077 a[4] = args[1].into();
2078 a[5] = args[2].into();
2079 a[6] = args[3].into();
2080 a[7] = args[4].into();
2081 }
2082 DirectMsgArgs::Args64(args) => {
2083 a[3] = args[0];
2084 a[4] = args[1];
2085 a[5] = args[2];
2086 a[6] = args[3];
2087 a[7] = args[4];
2088 }
Tomás González4d5b0ba2025-03-03 17:15:55 +00002089 DirectMsgArgs::VersionResp { version } => {
2090 a[2] = DirectMsgArgs::VERSION_RESP.into();
2091 match version {
Tomás González67f92c72025-03-20 16:50:42 +00002092 None => a[3] = (i32::from(FfaError::NotSupported) as u32).into(),
Tomás González4d5b0ba2025-03-03 17:15:55 +00002093 Some(ver) => a[3] = u32::from(ver).into(),
2094 }
2095 }
2096 DirectMsgArgs::PowerPsciResp { psci_status } => {
2097 a[2] = DirectMsgArgs::POWER_PSCI_RESP.into();
Imre Kisb2d3c882025-04-11 14:19:35 +02002098 a[3] = (psci_status as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00002099 }
2100 DirectMsgArgs::VmCreatedAck { sp_status } => {
2101 a[2] = DirectMsgArgs::VM_CREATED_ACK.into();
Tomás González67f92c72025-03-20 16:50:42 +00002102 a[3] = (i32::from(sp_status) as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00002103 }
2104 DirectMsgArgs::VmDestructedAck { sp_status } => {
2105 a[2] = DirectMsgArgs::VM_DESTRUCTED_ACK.into();
Tomás González67f92c72025-03-20 16:50:42 +00002106 a[3] = (i32::from(sp_status) as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00002107 }
2108 _ => panic!("Malformed MsgSendDirectResp interface"),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002109 }
2110 }
2111 Interface::MemDonate {
2112 total_len,
2113 frag_len,
2114 buf,
2115 } => {
2116 a[1] = total_len.into();
2117 a[2] = frag_len.into();
2118 (a[3], a[4]) = match buf {
2119 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
2120 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
2121 None => (0, 0),
2122 };
2123 }
2124 Interface::MemLend {
2125 total_len,
2126 frag_len,
2127 buf,
2128 } => {
2129 a[1] = total_len.into();
2130 a[2] = frag_len.into();
2131 (a[3], a[4]) = match buf {
2132 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
2133 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
2134 None => (0, 0),
2135 };
2136 }
2137 Interface::MemShare {
2138 total_len,
2139 frag_len,
2140 buf,
2141 } => {
2142 a[1] = total_len.into();
2143 a[2] = frag_len.into();
2144 (a[3], a[4]) = match buf {
2145 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
2146 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
2147 None => (0, 0),
2148 };
2149 }
2150 Interface::MemRetrieveReq {
2151 total_len,
2152 frag_len,
2153 buf,
2154 } => {
2155 a[1] = total_len.into();
2156 a[2] = frag_len.into();
2157 (a[3], a[4]) = match buf {
2158 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
2159 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
2160 None => (0, 0),
2161 };
2162 }
2163 Interface::MemRetrieveResp {
2164 total_len,
2165 frag_len,
2166 } => {
2167 a[1] = total_len.into();
2168 a[2] = frag_len.into();
2169 }
2170 Interface::MemRelinquish => {}
2171 Interface::MemReclaim { handle, flags } => {
2172 let handle_regs: [u32; 2] = handle.into();
2173 a[1] = handle_regs[0].into();
2174 a[2] = handle_regs[1].into();
2175 a[3] = flags.into();
2176 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01002177 Interface::MemPermGet { addr, page_cnt } => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01002178 a[1] = match addr {
2179 MemAddr::Addr32(addr) => addr.into(),
2180 MemAddr::Addr64(addr) => addr,
2181 };
Balint Dobszayde0dc802025-02-28 14:16:52 +01002182 a[2] = if version >= Version(1, 3) {
2183 page_cnt.unwrap().into()
2184 } else {
2185 assert!(page_cnt.is_none());
2186 0
2187 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01002188 }
2189 Interface::MemPermSet {
2190 addr,
2191 page_cnt,
2192 mem_perm,
2193 } => {
2194 a[1] = match addr {
2195 MemAddr::Addr32(addr) => addr.into(),
2196 MemAddr::Addr64(addr) => addr,
2197 };
2198 a[2] = page_cnt.into();
2199 a[3] = mem_perm.into();
2200 }
2201 Interface::ConsoleLog {
2202 char_cnt,
2203 char_lists,
2204 } => {
2205 a[1] = char_cnt.into();
2206 match char_lists {
2207 ConsoleLogChars::Reg32(regs) => {
2208 a[2] = regs[0].into();
2209 a[3] = regs[1].into();
2210 a[4] = regs[2].into();
2211 a[5] = regs[3].into();
2212 a[6] = regs[4].into();
2213 a[7] = regs[5].into();
2214 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01002215 _ => panic!("{:#x?} requires 18 registers", char_lists),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002216 }
2217 }
Tomás González7ffb6132025-04-03 12:28:58 +01002218 Interface::NotificationBitmapCreate { vm_id, vcpu_cnt } => {
2219 a[1] = vm_id.into();
2220 a[2] = vcpu_cnt.into();
2221 }
2222 Interface::NotificationBitmapDestroy { vm_id } => {
2223 a[1] = vm_id.into();
2224 }
2225 Interface::NotificationBind {
2226 sender_id,
2227 receiver_id,
2228 flags,
2229 bitmap,
2230 } => {
2231 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
2232 a[2] = u32::from(flags).into();
2233 a[3] = bitmap & 0xffff_ffff;
2234 a[4] = bitmap >> 32;
2235 }
2236 Interface::NotificationUnBind {
2237 sender_id,
2238 receiver_id,
2239 bitmap,
2240 } => {
2241 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
2242 a[3] = bitmap & 0xffff_ffff;
2243 a[4] = bitmap >> 32;
2244 }
2245 Interface::NotificationSet {
2246 sender_id,
2247 receiver_id,
2248 flags,
2249 bitmap,
2250 } => {
2251 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
2252 a[2] = u32::from(flags).into();
2253 a[3] = bitmap & 0xffff_ffff;
2254 a[4] = bitmap >> 32;
2255 }
2256 Interface::NotificationGet {
2257 vcpu_id,
2258 endpoint_id,
2259 flags,
2260 } => {
2261 a[1] = (u64::from(vcpu_id) << 16) | u64::from(endpoint_id);
2262 a[2] = u32::from(flags).into();
2263 }
2264 Interface::NotificationInfoGet { .. } => {}
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01002265 Interface::El3IntrHandle => {}
Balint Dobszayde0dc802025-02-28 14:16:52 +01002266 _ => panic!("{:#x?} requires 18 registers", self),
2267 }
2268 }
2269
2270 fn pack_regs18(&self, version: Version, a: &mut [u64; 18]) {
2271 assert!(version >= Version(1, 2));
2272
Balint Dobszayde0dc802025-02-28 14:16:52 +01002273 if let Some(function_id) = self.function_id() {
2274 a[0] = function_id as u64;
2275 }
2276
2277 match *self {
2278 Interface::Success { target_info, args } => {
2279 a[1] = target_info.into();
2280 match args {
Imre Kis54773b62025-04-10 13:47:39 +02002281 SuccessArgs::Args64_2(regs) => a[2..18].copy_from_slice(&regs[..16]),
Balint Dobszayde0dc802025-02-28 14:16:52 +01002282 _ => panic!("{:#x?} requires 8 registers", args),
2283 }
2284 }
2285 Interface::MsgSendDirectReq2 {
2286 src_id,
2287 dst_id,
2288 uuid,
2289 args,
2290 } => {
2291 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00002292 let (uuid_msb, uuid_lsb) = uuid.as_u64_pair();
2293 (a[2], a[3]) = (uuid_msb.swap_bytes(), uuid_lsb.swap_bytes());
Balint Dobszayde0dc802025-02-28 14:16:52 +01002294 a[4..18].copy_from_slice(&args.0[..14]);
2295 }
2296 Interface::MsgSendDirectResp2 {
2297 src_id,
2298 dst_id,
2299 args,
2300 } => {
2301 a[1] = ((src_id as u64) << 16) | dst_id as u64;
2302 a[2] = 0;
2303 a[3] = 0;
2304 a[4..18].copy_from_slice(&args.0[..14]);
2305 }
2306 Interface::ConsoleLog {
2307 char_cnt,
2308 char_lists,
2309 } => {
2310 a[1] = char_cnt.into();
2311 match char_lists {
2312 ConsoleLogChars::Reg64(regs) => a[2..18].copy_from_slice(&regs[..16]),
2313 _ => panic!("{:#x?} requires 8 registers", char_lists),
2314 }
2315 }
Tomás González0a058bc2025-03-11 11:20:55 +00002316 Interface::PartitionInfoGetRegs {
2317 uuid,
2318 start_index,
2319 info_tag,
2320 } => {
2321 if start_index == 0 && info_tag != 0 {
2322 panic!("Information Tag MBZ if start index is 0: {:#x?}", self);
2323 }
2324 let (uuid_msb, uuid_lsb) = uuid.as_u64_pair();
2325 (a[1], a[2]) = (uuid_msb.swap_bytes(), uuid_lsb.swap_bytes());
2326 a[3] = (u64::from(info_tag) << 16) | u64::from(start_index);
2327 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01002328 _ => panic!("{:#x?} requires 8 registers", self),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002329 }
2330 }
2331
Balint Dobszaya5846852025-02-26 15:38:53 +01002332 /// Helper function to create an `FFA_SUCCESS` interface without any arguments.
Balint Dobszay3aad9572025-01-17 16:54:11 +01002333 pub fn success32_noargs() -> Self {
2334 Self::Success {
2335 target_info: 0,
Imre Kis54773b62025-04-10 13:47:39 +02002336 args: SuccessArgs::Args32([0; 6]),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002337 }
2338 }
2339
Tomás González4c8c7d22025-03-10 17:14:57 +00002340 /// Helper function to create an `FFA_SUCCESS` interface without any arguments.
2341 pub fn success64_noargs() -> Self {
2342 Self::Success {
2343 target_info: 0,
Imre Kis54773b62025-04-10 13:47:39 +02002344 args: SuccessArgs::Args64([0; 6]),
Tomás González4c8c7d22025-03-10 17:14:57 +00002345 }
2346 }
2347
Balint Dobszaya5846852025-02-26 15:38:53 +01002348 /// Helper function to create an `FFA_ERROR` interface with an error code.
Balint Dobszay3aad9572025-01-17 16:54:11 +01002349 pub fn error(error_code: FfaError) -> Self {
2350 Self::Error {
2351 target_info: TargetInfo {
2352 endpoint_id: 0,
2353 vcpu_id: 0,
2354 },
2355 error_code,
Balint Dobszayb727aab2025-04-07 10:24:59 +02002356 error_arg: 0,
Balint Dobszay3aad9572025-01-17 16:54:11 +01002357 }
2358 }
2359}
2360
Balint Dobszaya5846852025-02-26 15:38:53 +01002361/// Maximum number of characters transmitted in a single `FFA_CONSOLE_LOG32` message.
2362pub const CONSOLE_LOG_32_MAX_CHAR_CNT: u8 = 24;
Balint Dobszayde0dc802025-02-28 14:16:52 +01002363/// Maximum number of characters transmitted in a single `FFA_CONSOLE_LOG64` message.
2364pub const CONSOLE_LOG_64_MAX_CHAR_CNT: u8 = 128;
Balint Dobszay3aad9572025-01-17 16:54:11 +01002365
Balint Dobszaya5846852025-02-26 15:38:53 +01002366/// Helper function to convert the "Tightly packed list of characters" format used by the
2367/// `FFA_CONSOLE_LOG` interface into a byte slice.
Balint Dobszay3aad9572025-01-17 16:54:11 +01002368pub fn parse_console_log(
2369 char_cnt: u8,
2370 char_lists: &ConsoleLogChars,
2371 log_bytes: &mut [u8],
2372) -> Result<(), FfaError> {
2373 match char_lists {
2374 ConsoleLogChars::Reg32(regs) => {
Balint Dobszaya5846852025-02-26 15:38:53 +01002375 if !(1..=CONSOLE_LOG_32_MAX_CHAR_CNT).contains(&char_cnt) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01002376 return Err(FfaError::InvalidParameters);
2377 }
2378 for (i, reg) in regs.iter().enumerate() {
2379 log_bytes[4 * i..4 * (i + 1)].copy_from_slice(&reg.to_le_bytes());
2380 }
2381 }
2382 ConsoleLogChars::Reg64(regs) => {
Balint Dobszaya5846852025-02-26 15:38:53 +01002383 if !(1..=CONSOLE_LOG_64_MAX_CHAR_CNT).contains(&char_cnt) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01002384 return Err(FfaError::InvalidParameters);
2385 }
2386 for (i, reg) in regs.iter().enumerate() {
2387 log_bytes[8 * i..8 * (i + 1)].copy_from_slice(&reg.to_le_bytes());
2388 }
2389 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +02002390 }
2391
Balint Dobszayc8802492025-01-15 18:11:39 +01002392 Ok(())
Balint Dobszay5bf492f2024-07-29 17:21:32 +02002393}
Tomás González0a058bc2025-03-11 11:20:55 +00002394
2395#[cfg(test)]
2396mod tests {
2397 use super::*;
2398
2399 #[test]
2400 fn part_info_get_regs() {
2401 let uuid = Uuid::parse_str("a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8").unwrap();
2402 let uuid_bytes = uuid.as_bytes();
2403 let test_info_tag = 0b1101_1101;
2404 let test_start_index = 0b1101;
2405 let start_index_and_tag = (test_info_tag << 16) | test_start_index;
2406 let version = Version(1, 2);
2407
2408 // From spec:
2409 // Bytes[0...7] of UUID with byte 0 in the low-order bits.
2410 let reg_x1 = (uuid_bytes[7] as u64) << 56
2411 | (uuid_bytes[6] as u64) << 48
2412 | (uuid_bytes[5] as u64) << 40
2413 | (uuid_bytes[4] as u64) << 32
2414 | (uuid_bytes[3] as u64) << 24
2415 | (uuid_bytes[2] as u64) << 16
2416 | (uuid_bytes[1] as u64) << 8
2417 | (uuid_bytes[0] as u64);
2418
2419 // From spec:
2420 // Bytes[8...15] of UUID with byte 8 in the low-order bits.
2421 let reg_x2 = (uuid_bytes[15] as u64) << 56
2422 | (uuid_bytes[14] as u64) << 48
2423 | (uuid_bytes[13] as u64) << 40
2424 | (uuid_bytes[12] as u64) << 32
2425 | (uuid_bytes[11] as u64) << 24
2426 | (uuid_bytes[10] as u64) << 16
2427 | (uuid_bytes[9] as u64) << 8
2428 | (uuid_bytes[8] as u64);
2429
2430 // First, test for wrong tag:
2431 {
2432 let mut regs = [0u64; 18];
2433 regs[0] = FuncId::PartitionInfoGetRegs as u64;
2434 regs[1] = reg_x1;
2435 regs[2] = reg_x2;
2436 regs[3] = test_info_tag << 16;
2437
2438 assert!(Interface::from_regs(version, &regs).is_err_and(
2439 |e| e == Error::InvalidInformationTag(test_info_tag.try_into().unwrap())
2440 ));
2441 }
2442
2443 // Test for regs -> Interface -> regs
2444 {
2445 let mut orig_regs = [0u64; 18];
2446 orig_regs[0] = FuncId::PartitionInfoGetRegs as u64;
2447 orig_regs[1] = reg_x1;
2448 orig_regs[2] = reg_x2;
2449 orig_regs[3] = start_index_and_tag;
2450
2451 let mut test_regs = orig_regs.clone();
2452 let interface = Interface::from_regs(version, &mut test_regs).unwrap();
2453 match &interface {
2454 Interface::PartitionInfoGetRegs {
2455 info_tag,
2456 start_index,
2457 uuid: int_uuid,
2458 } => {
2459 assert_eq!(u64::from(*info_tag), test_info_tag);
2460 assert_eq!(u64::from(*start_index), test_start_index);
2461 assert_eq!(*int_uuid, uuid);
2462 }
2463 _ => panic!("Expecting Interface::PartitionInfoGetRegs!"),
2464 }
2465 test_regs.fill(0);
2466 interface.to_regs(version, &mut test_regs);
2467 assert_eq!(orig_regs, test_regs);
2468 }
2469
2470 // Test for Interface -> regs -> Interface
2471 {
2472 let interface = Interface::PartitionInfoGetRegs {
2473 info_tag: test_info_tag.try_into().unwrap(),
2474 start_index: test_start_index.try_into().unwrap(),
2475 uuid,
2476 };
2477
2478 let mut regs: [u64; 18] = [0; 18];
2479 interface.to_regs(version, &mut regs);
2480
2481 assert_eq!(Some(FuncId::PartitionInfoGetRegs), interface.function_id());
2482 assert_eq!(regs[0], interface.function_id().unwrap() as u64);
2483 assert_eq!(regs[1], reg_x1);
2484 assert_eq!(regs[2], reg_x2);
2485 assert_eq!(regs[3], (test_info_tag << 16) | test_start_index);
2486
2487 assert_eq!(Interface::from_regs(version, &regs).unwrap(), interface);
2488 }
2489 }
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00002490
2491 #[test]
2492 fn msg_send_direct_req2() {
2493 let uuid = Uuid::parse_str("a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8").unwrap();
2494 let uuid_bytes = uuid.as_bytes();
2495
2496 // From spec:
2497 // Bytes[0...7] of UUID with byte 0 in the low-order bits.
2498 let reg_x2 = (uuid_bytes[7] as u64) << 56
2499 | (uuid_bytes[6] as u64) << 48
2500 | (uuid_bytes[5] as u64) << 40
2501 | (uuid_bytes[4] as u64) << 32
2502 | (uuid_bytes[3] as u64) << 24
2503 | (uuid_bytes[2] as u64) << 16
2504 | (uuid_bytes[1] as u64) << 8
2505 | (uuid_bytes[0] as u64);
2506
2507 // From spec:
2508 // Bytes[8...15] of UUID with byte 8 in the low-order bits.
2509 let reg_x3 = (uuid_bytes[15] as u64) << 56
2510 | (uuid_bytes[14] as u64) << 48
2511 | (uuid_bytes[13] as u64) << 40
2512 | (uuid_bytes[12] as u64) << 32
2513 | (uuid_bytes[11] as u64) << 24
2514 | (uuid_bytes[10] as u64) << 16
2515 | (uuid_bytes[9] as u64) << 8
2516 | (uuid_bytes[8] as u64);
2517
2518 let test_sender = 0b1101_1101;
2519 let test_receiver = 0b1101;
2520 let test_sender_receiver = (test_sender << 16) | test_receiver;
2521 let version = Version(1, 2);
2522
2523 // Test for regs -> Interface -> regs
2524 {
2525 let mut orig_regs = [0u64; 18];
2526 orig_regs[0] = FuncId::MsgSendDirectReq64_2 as u64;
2527 orig_regs[1] = test_sender_receiver;
2528 orig_regs[2] = reg_x2;
2529 orig_regs[3] = reg_x3;
2530
2531 let mut test_regs = orig_regs.clone();
2532 let interface = Interface::from_regs(version, &mut test_regs).unwrap();
2533 match &interface {
2534 Interface::MsgSendDirectReq2 {
2535 dst_id,
2536 src_id,
2537 args: _,
2538 uuid: int_uuid,
2539 } => {
2540 assert_eq!(u64::from(*src_id), test_sender);
2541 assert_eq!(u64::from(*dst_id), test_receiver);
2542 assert_eq!(*int_uuid, uuid);
2543 }
2544 _ => panic!("Expecting Interface::MsgSendDirectReq2!"),
2545 }
2546 test_regs.fill(0);
2547 interface.to_regs(version, &mut test_regs);
2548 assert_eq!(orig_regs, test_regs);
2549 }
2550
2551 // Test for Interface -> regs -> Interface
2552 {
2553 let rest_of_regs: [u64; 14] = [0; 14];
2554
2555 let interface = Interface::MsgSendDirectReq2 {
2556 src_id: test_sender.try_into().unwrap(),
2557 dst_id: test_receiver.try_into().unwrap(),
2558 uuid,
2559 args: DirectMsg2Args(rest_of_regs),
2560 };
2561
2562 let mut regs: [u64; 18] = [0; 18];
2563 interface.to_regs(version, &mut regs);
2564
2565 assert_eq!(Some(FuncId::MsgSendDirectReq64_2), interface.function_id());
2566 assert_eq!(regs[0], interface.function_id().unwrap() as u64);
2567 assert_eq!(regs[1], test_sender_receiver);
2568 assert_eq!(regs[2], reg_x2);
2569 assert_eq!(regs[3], reg_x3);
2570 assert_eq!(regs[4], 0);
2571
2572 assert_eq!(Interface::from_regs(version, &regs).unwrap(), interface);
2573 }
2574 }
Tomás González6ccba0a2025-04-09 13:31:29 +01002575
2576 #[test]
2577 fn is_32bit() {
2578 let interface_64 = Interface::MsgSendDirectReq {
2579 src_id: 0,
2580 dst_id: 1,
2581 args: DirectMsgArgs::Args64([0, 0, 0, 0, 0]),
2582 };
2583 assert!(!interface_64.is_32bit());
2584
2585 let interface_32 = Interface::MsgSendDirectReq {
2586 src_id: 0,
2587 dst_id: 1,
2588 args: DirectMsgArgs::Args32([0, 0, 0, 0, 0]),
2589 };
2590 assert!(interface_32.is_32bit());
2591 }
Imre Kis787c5002025-04-10 14:25:51 +02002592
2593 #[test]
2594 fn success_args_notification_info_get32() {
2595 let mut notifications = SuccessArgsNotificationInfoGet32::default();
2596
2597 // 16.7.1.1 Example usage
2598 notifications.add_list(0x0000, &[0, 2, 3]).unwrap();
2599 notifications.add_list(0x0000, &[4, 6]).unwrap();
2600 notifications.add_list(0x0002, &[]).unwrap();
2601 notifications.add_list(0x0003, &[1]).unwrap();
2602
2603 let args: SuccessArgs = notifications.into();
2604 assert_eq!(
2605 SuccessArgs::Args32([
2606 0x0004_b200,
2607 0x0000_0000,
2608 0x0003_0002,
2609 0x0004_0000,
2610 0x0002_0006,
2611 0x0001_0003
2612 ]),
2613 args
2614 );
2615
2616 let notifications = SuccessArgsNotificationInfoGet32::try_from(args).unwrap();
2617 let mut iter = notifications.iter();
2618 assert_eq!(Some((0x0000, &[0, 2, 3][..])), iter.next());
2619 assert_eq!(Some((0x0000, &[4, 6][..])), iter.next());
2620 assert_eq!(Some((0x0002, &[][..])), iter.next());
2621 assert_eq!(Some((0x0003, &[1][..])), iter.next());
2622 }
2623
2624 #[test]
2625 fn success_args_notification_info_get64() {
2626 let mut notifications = SuccessArgsNotificationInfoGet64::default();
2627
2628 // 16.7.1.1 Example usage
2629 notifications.add_list(0x0000, &[0, 2, 3]).unwrap();
2630 notifications.add_list(0x0000, &[4, 6]).unwrap();
2631 notifications.add_list(0x0002, &[]).unwrap();
2632 notifications.add_list(0x0003, &[1]).unwrap();
2633
2634 let args: SuccessArgs = notifications.into();
2635 assert_eq!(
2636 SuccessArgs::Args64([
2637 0x0004_b200,
2638 0x0003_0002_0000_0000,
2639 0x0002_0006_0004_0000,
2640 0x0000_0000_0001_0003,
2641 0x0000_0000_0000_0000,
2642 0x0000_0000_0000_0000,
2643 ]),
2644 args
2645 );
2646
2647 let notifications = SuccessArgsNotificationInfoGet64::try_from(args).unwrap();
2648 let mut iter = notifications.iter();
2649 assert_eq!(Some((0x0000, &[0, 2, 3][..])), iter.next());
2650 assert_eq!(Some((0x0000, &[4, 6][..])), iter.next());
2651 assert_eq!(Some((0x0002, &[][..])), iter.next());
2652 assert_eq!(Some((0x0003, &[1][..])), iter.next());
2653 }
Tomás González0a058bc2025-03-11 11:20:55 +00002654}