blob: 7a93b995305a0719aa5914bcbb6ec5da29b76b8e [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),
Balint Dobszay3c1c89a2025-04-25 17:36:46 +02001312 _ => panic!("Invalid direct request arguments: {:#?}", args),
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),
Balint Dobszay3c1c89a2025-04-25 17:36:46 +02001321 _ => panic!("Invalid direct response arguments: {:#?}", args),
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 {
Balint Dobszay3c1c89a2025-04-25 17:36:46 +02001372 if matches!(self, Self::VersionOut { .. }) {
1373 return true;
1374 }
1375
Balint Dobszayde0dc802025-02-28 14:16:52 +01001376 self.function_id().unwrap().is_32bit()
1377 }
1378
Balint Dobszay82c71dd2025-04-15 10:16:44 +02001379 /// Returns the FF-A version that has introduced the function ID.
1380 pub fn minimum_ffa_version(&self) -> Version {
Balint Dobszay3c1c89a2025-04-25 17:36:46 +02001381 if matches!(self, Self::VersionOut { .. }) {
1382 return Version(1, 0);
1383 }
1384
Balint Dobszay82c71dd2025-04-15 10:16:44 +02001385 self.function_id().unwrap().minimum_ffa_version()
1386 }
1387
Balint Dobszayde0dc802025-02-28 14:16:52 +01001388 /// Parse interface from register contents. The caller must ensure that the `regs` argument has
1389 /// the correct length: 8 registers for FF-A v1.1 and lower, 18 registers for v1.2 and higher.
1390 pub fn from_regs(version: Version, regs: &[u64]) -> Result<Self, Error> {
Balint Dobszay82c71dd2025-04-15 10:16:44 +02001391 let func_id = FuncId::try_from(regs[0] as u32)?;
1392 if version < func_id.minimum_ffa_version() {
1393 return Err(Error::InvalidVersionForFunctionId(version, func_id));
1394 }
1395
Balint Dobszayde0dc802025-02-28 14:16:52 +01001396 let reg_cnt = regs.len();
1397
1398 let msg = match reg_cnt {
1399 8 => {
1400 assert!(version <= Version(1, 1));
1401 Interface::unpack_regs8(version, regs.try_into().unwrap())?
1402 }
1403 18 => {
1404 assert!(version >= Version(1, 2));
Balint Dobszay82c71dd2025-04-15 10:16:44 +02001405 match func_id {
Balint Dobszayde0dc802025-02-28 14:16:52 +01001406 FuncId::ConsoleLog64
1407 | FuncId::Success64
1408 | FuncId::MsgSendDirectReq64_2
Tomás González0a058bc2025-03-11 11:20:55 +00001409 | FuncId::MsgSendDirectResp64_2
1410 | FuncId::PartitionInfoGetRegs => {
Balint Dobszayde0dc802025-02-28 14:16:52 +01001411 Interface::unpack_regs18(version, regs.try_into().unwrap())?
1412 }
1413 _ => Interface::unpack_regs8(version, regs[..8].try_into().unwrap())?,
1414 }
1415 }
1416 _ => panic!(
1417 "Invalid number of registers ({}) for FF-A version {}",
1418 reg_cnt, version
1419 ),
1420 };
1421
1422 Ok(msg)
1423 }
1424
1425 fn unpack_regs8(version: Version, regs: &[u64; 8]) -> Result<Self, Error> {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001426 let fid = FuncId::try_from(regs[0] as u32)?;
1427
1428 let msg = match fid {
1429 FuncId::Error => Self::Error {
1430 target_info: (regs[1] as u32).into(),
1431 error_code: FfaError::try_from(regs[2] as i32)?,
Balint Dobszayb727aab2025-04-07 10:24:59 +02001432 error_arg: regs[3] as u32,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001433 },
1434 FuncId::Success32 => Self::Success {
1435 target_info: regs[1] as u32,
Imre Kis54773b62025-04-10 13:47:39 +02001436 args: SuccessArgs::Args32([
Balint Dobszay3aad9572025-01-17 16:54:11 +01001437 regs[2] as u32,
1438 regs[3] as u32,
1439 regs[4] as u32,
1440 regs[5] as u32,
1441 regs[6] as u32,
1442 regs[7] as u32,
1443 ]),
1444 },
1445 FuncId::Success64 => Self::Success {
1446 target_info: regs[1] as u32,
Imre Kis54773b62025-04-10 13:47:39 +02001447 args: SuccessArgs::Args64([regs[2], regs[3], regs[4], regs[5], regs[6], regs[7]]),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001448 },
1449 FuncId::Interrupt => Self::Interrupt {
1450 target_info: (regs[1] as u32).into(),
1451 interrupt_id: regs[2] as u32,
1452 },
1453 FuncId::Version => Self::Version {
Tomás González83146af2025-03-04 11:32:41 +00001454 input_version: (regs[1] as u32).try_into()?,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001455 },
1456 FuncId::Features => Self::Features {
Balint Dobszayc31e0b92025-03-03 20:16:56 +01001457 feat_id: (regs[1] as u32).into(),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001458 input_properties: regs[2] as u32,
1459 },
1460 FuncId::RxAcquire => Self::RxAcquire {
1461 vm_id: regs[1] as u16,
1462 },
1463 FuncId::RxRelease => Self::RxRelease {
1464 vm_id: regs[1] as u16,
1465 },
1466 FuncId::RxTxMap32 => {
1467 let addr = RxTxAddr::Addr32 {
1468 rx: regs[2] as u32,
1469 tx: regs[1] as u32,
1470 };
1471 let page_cnt = regs[3] as u32;
1472
1473 Self::RxTxMap { addr, page_cnt }
1474 }
1475 FuncId::RxTxMap64 => {
1476 let addr = RxTxAddr::Addr64 {
1477 rx: regs[2],
1478 tx: regs[1],
1479 };
1480 let page_cnt = regs[3] as u32;
1481
1482 Self::RxTxMap { addr, page_cnt }
1483 }
1484 FuncId::RxTxUnmap => Self::RxTxUnmap { id: regs[1] as u16 },
1485 FuncId::PartitionInfoGet => {
1486 let uuid_words = [
1487 regs[1] as u32,
1488 regs[2] as u32,
1489 regs[3] as u32,
1490 regs[4] as u32,
1491 ];
1492 let mut bytes: [u8; 16] = [0; 16];
1493 for (i, b) in uuid_words.iter().flat_map(|w| w.to_le_bytes()).enumerate() {
1494 bytes[i] = b;
1495 }
1496 Self::PartitionInfoGet {
1497 uuid: Uuid::from_bytes(bytes),
Imre Kise295adb2025-04-10 13:26:28 +02001498 flags: PartitionInfoGetFlags::try_from(regs[5] as u32)?,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001499 }
1500 }
1501 FuncId::IdGet => Self::IdGet,
1502 FuncId::SpmIdGet => Self::SpmIdGet,
Tomás González092202a2025-03-05 11:56:45 +00001503 FuncId::MsgWait => Self::MsgWait {
1504 flags: if version >= Version(1, 2) {
1505 Some(MsgWaitFlags::try_from(regs[2] as u32)?)
1506 } else {
1507 None
1508 },
1509 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001510 FuncId::Yield => Self::Yield,
1511 FuncId::Run => Self::Run {
1512 target_info: (regs[1] as u32).into(),
1513 },
1514 FuncId::NormalWorldResume => Self::NormalWorldResume,
Tomás González17b92442025-03-10 16:45:04 +00001515 FuncId::SecondaryEpRegister32 => Self::SecondaryEpRegister {
1516 entrypoint: SecondaryEpRegisterAddr::Addr32(regs[1] as u32),
1517 },
1518 FuncId::SecondaryEpRegister64 => Self::SecondaryEpRegister {
1519 entrypoint: SecondaryEpRegisterAddr::Addr64(regs[1]),
1520 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001521 FuncId::MsgSend2 => Self::MsgSend2 {
1522 sender_vm_id: regs[1] as u16,
1523 flags: regs[2] as u32,
1524 },
1525 FuncId::MsgSendDirectReq32 => Self::MsgSendDirectReq {
1526 src_id: (regs[1] >> 16) as u16,
1527 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001528 args: if (regs[2] as u32 & DirectMsgArgs::FWK_MSG_BITS) != 0 {
1529 match regs[2] as u32 {
1530 DirectMsgArgs::VERSION_REQ => DirectMsgArgs::VersionReq {
1531 version: Version::try_from(regs[3] as u32)?,
1532 },
1533 DirectMsgArgs::POWER_PSCI_REQ => DirectMsgArgs::PowerPsciReq32 {
Tomás González67f92c72025-03-20 16:50:42 +00001534 params: [
1535 regs[3] as u32,
1536 regs[4] as u32,
1537 regs[5] as u32,
1538 regs[6] as u32,
1539 ],
Tomás González4d5b0ba2025-03-03 17:15:55 +00001540 },
1541 DirectMsgArgs::POWER_WARM_BOOT_REQ => DirectMsgArgs::PowerWarmBootReq {
1542 boot_type: WarmBootType::try_from(regs[3] as u32)?,
1543 },
1544 DirectMsgArgs::VM_CREATED => DirectMsgArgs::VmCreated {
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 DirectMsgArgs::VM_DESTRUCTED => DirectMsgArgs::VmDestructed {
1552 handle: memory_management::Handle::from([
1553 regs[3] as u32,
1554 regs[4] as u32,
1555 ]),
1556 vm_id: regs[5] as u16,
1557 },
1558 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1559 }
1560 } else {
1561 DirectMsgArgs::Args32([
1562 regs[3] as u32,
1563 regs[4] as u32,
1564 regs[5] as u32,
1565 regs[6] as u32,
1566 regs[7] as u32,
1567 ])
1568 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001569 },
1570 FuncId::MsgSendDirectReq64 => Self::MsgSendDirectReq {
1571 src_id: (regs[1] >> 16) as u16,
1572 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001573 args: if (regs[2] & DirectMsgArgs::FWK_MSG_BITS as u64) != 0 {
1574 match regs[2] as u32 {
1575 DirectMsgArgs::POWER_PSCI_REQ => DirectMsgArgs::PowerPsciReq64 {
Tomás González67f92c72025-03-20 16:50:42 +00001576 params: [regs[3], regs[4], regs[5], regs[6]],
Tomás González4d5b0ba2025-03-03 17:15:55 +00001577 },
1578 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1579 }
1580 } else {
1581 DirectMsgArgs::Args64([regs[3], regs[4], regs[5], regs[6], regs[7]])
1582 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001583 },
1584 FuncId::MsgSendDirectResp32 => Self::MsgSendDirectResp {
1585 src_id: (regs[1] >> 16) as u16,
1586 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001587 args: if (regs[2] as u32 & DirectMsgArgs::FWK_MSG_BITS) != 0 {
1588 match regs[2] as u32 {
1589 DirectMsgArgs::VERSION_RESP => {
1590 if regs[3] as i32 == FfaError::NotSupported.into() {
1591 DirectMsgArgs::VersionResp { version: None }
1592 } else {
1593 DirectMsgArgs::VersionResp {
1594 version: Some(Version::try_from(regs[3] as u32)?),
1595 }
1596 }
1597 }
1598 DirectMsgArgs::POWER_PSCI_RESP => DirectMsgArgs::PowerPsciResp {
1599 psci_status: regs[3] as i32,
1600 },
1601 DirectMsgArgs::VM_CREATED_ACK => DirectMsgArgs::VmCreatedAck {
1602 sp_status: (regs[3] as i32).try_into()?,
1603 },
1604 DirectMsgArgs::VM_DESTRUCTED_ACK => DirectMsgArgs::VmDestructedAck {
1605 sp_status: (regs[3] as i32).try_into()?,
1606 },
1607 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1608 }
1609 } else {
1610 DirectMsgArgs::Args32([
1611 regs[3] as u32,
1612 regs[4] as u32,
1613 regs[5] as u32,
1614 regs[6] as u32,
1615 regs[7] as u32,
1616 ])
1617 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001618 },
1619 FuncId::MsgSendDirectResp64 => Self::MsgSendDirectResp {
1620 src_id: (regs[1] >> 16) as u16,
1621 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001622 args: if (regs[2] & DirectMsgArgs::FWK_MSG_BITS as u64) != 0 {
1623 return Err(Error::UnrecognisedFwkMsg(regs[2] as u32));
1624 } else {
1625 DirectMsgArgs::Args64([regs[3], regs[4], regs[5], regs[6], regs[7]])
1626 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001627 },
1628 FuncId::MemDonate32 => Self::MemDonate {
1629 total_len: regs[1] as u32,
1630 frag_len: regs[2] as u32,
1631 buf: if regs[3] != 0 && regs[4] != 0 {
1632 Some(MemOpBuf::Buf32 {
1633 addr: regs[3] as u32,
1634 page_cnt: regs[4] as u32,
1635 })
1636 } else {
1637 None
1638 },
1639 },
1640 FuncId::MemDonate64 => Self::MemDonate {
1641 total_len: regs[1] as u32,
1642 frag_len: regs[2] as u32,
1643 buf: if regs[3] != 0 && regs[4] != 0 {
1644 Some(MemOpBuf::Buf64 {
1645 addr: regs[3],
1646 page_cnt: regs[4] as u32,
1647 })
1648 } else {
1649 None
1650 },
1651 },
1652 FuncId::MemLend32 => Self::MemLend {
1653 total_len: regs[1] as u32,
1654 frag_len: regs[2] as u32,
1655 buf: if regs[3] != 0 && regs[4] != 0 {
1656 Some(MemOpBuf::Buf32 {
1657 addr: regs[3] as u32,
1658 page_cnt: regs[4] as u32,
1659 })
1660 } else {
1661 None
1662 },
1663 },
1664 FuncId::MemLend64 => Self::MemLend {
1665 total_len: regs[1] as u32,
1666 frag_len: regs[2] as u32,
1667 buf: if regs[3] != 0 && regs[4] != 0 {
1668 Some(MemOpBuf::Buf64 {
1669 addr: regs[3],
1670 page_cnt: regs[4] as u32,
1671 })
1672 } else {
1673 None
1674 },
1675 },
1676 FuncId::MemShare32 => Self::MemShare {
1677 total_len: regs[1] as u32,
1678 frag_len: regs[2] as u32,
1679 buf: if regs[3] != 0 && regs[4] != 0 {
1680 Some(MemOpBuf::Buf32 {
1681 addr: regs[3] as u32,
1682 page_cnt: regs[4] as u32,
1683 })
1684 } else {
1685 None
1686 },
1687 },
1688 FuncId::MemShare64 => Self::MemShare {
1689 total_len: regs[1] as u32,
1690 frag_len: regs[2] as u32,
1691 buf: if regs[3] != 0 && regs[4] != 0 {
1692 Some(MemOpBuf::Buf64 {
1693 addr: regs[3],
1694 page_cnt: regs[4] as u32,
1695 })
1696 } else {
1697 None
1698 },
1699 },
1700 FuncId::MemRetrieveReq32 => Self::MemRetrieveReq {
1701 total_len: regs[1] as u32,
1702 frag_len: regs[2] as u32,
1703 buf: if regs[3] != 0 && regs[4] != 0 {
1704 Some(MemOpBuf::Buf32 {
1705 addr: regs[3] as u32,
1706 page_cnt: regs[4] as u32,
1707 })
1708 } else {
1709 None
1710 },
1711 },
1712 FuncId::MemRetrieveReq64 => Self::MemRetrieveReq {
1713 total_len: regs[1] as u32,
1714 frag_len: regs[2] as u32,
1715 buf: if regs[3] != 0 && regs[4] != 0 {
1716 Some(MemOpBuf::Buf64 {
1717 addr: regs[3],
1718 page_cnt: regs[4] as u32,
1719 })
1720 } else {
1721 None
1722 },
1723 },
1724 FuncId::MemRetrieveResp => Self::MemRetrieveResp {
1725 total_len: regs[1] as u32,
1726 frag_len: regs[2] as u32,
1727 },
1728 FuncId::MemRelinquish => Self::MemRelinquish,
1729 FuncId::MemReclaim => Self::MemReclaim {
1730 handle: memory_management::Handle::from([regs[1] as u32, regs[2] as u32]),
1731 flags: regs[3] as u32,
1732 },
1733 FuncId::MemPermGet32 => Self::MemPermGet {
1734 addr: MemAddr::Addr32(regs[1] as u32),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001735 page_cnt: if version >= Version(1, 3) {
1736 Some(regs[2] as u32)
1737 } else {
1738 None
1739 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001740 },
1741 FuncId::MemPermGet64 => Self::MemPermGet {
1742 addr: MemAddr::Addr64(regs[1]),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001743 page_cnt: if version >= Version(1, 3) {
1744 Some(regs[2] as u32)
1745 } else {
1746 None
1747 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001748 },
1749 FuncId::MemPermSet32 => Self::MemPermSet {
1750 addr: MemAddr::Addr32(regs[1] as u32),
1751 page_cnt: regs[2] as u32,
1752 mem_perm: regs[3] as u32,
1753 },
1754 FuncId::MemPermSet64 => Self::MemPermSet {
1755 addr: MemAddr::Addr64(regs[1]),
1756 page_cnt: regs[2] as u32,
1757 mem_perm: regs[3] as u32,
1758 },
1759 FuncId::ConsoleLog32 => Self::ConsoleLog {
1760 char_cnt: regs[1] as u8,
1761 char_lists: ConsoleLogChars::Reg32([
1762 regs[2] as u32,
1763 regs[3] as u32,
1764 regs[4] as u32,
1765 regs[5] as u32,
1766 regs[6] as u32,
1767 regs[7] as u32,
1768 ]),
1769 },
Tomás González7ffb6132025-04-03 12:28:58 +01001770 FuncId::NotificationBitmapCreate => {
1771 let tentative_vm_id = regs[1] as u32;
1772 if (tentative_vm_id >> 16) != 0 {
1773 return Err(Error::InvalidVmId(tentative_vm_id));
1774 }
1775 Self::NotificationBitmapCreate {
1776 vm_id: tentative_vm_id as u16,
1777 vcpu_cnt: regs[2] as u32,
1778 }
1779 }
1780 FuncId::NotificationBitmapDestroy => {
1781 let tentative_vm_id = regs[1] as u32;
1782 if (tentative_vm_id >> 16) != 0 {
1783 return Err(Error::InvalidVmId(tentative_vm_id));
1784 }
1785 Self::NotificationBitmapDestroy {
1786 vm_id: tentative_vm_id as u16,
1787 }
1788 }
1789 FuncId::NotificationBind => Self::NotificationBind {
1790 sender_id: (regs[1] >> 16) as u16,
1791 receiver_id: regs[1] as u16,
1792 flags: (regs[2] as u32).into(),
1793 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1794 },
1795 FuncId::NotificationUnbind => Self::NotificationUnBind {
1796 sender_id: (regs[1] >> 16) as u16,
1797 receiver_id: regs[1] as u16,
1798 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1799 },
1800 FuncId::NotificationSet => Self::NotificationSet {
1801 sender_id: (regs[1] >> 16) as u16,
1802 receiver_id: regs[1] as u16,
1803 flags: (regs[2] as u32).try_into()?,
1804 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1805 },
1806 FuncId::NotificationGet => Self::NotificationGet {
1807 vcpu_id: (regs[1] >> 16) as u16,
1808 endpoint_id: regs[1] as u16,
1809 flags: (regs[2] as u32).into(),
1810 },
1811 FuncId::NotificationInfoGet32 => Self::NotificationInfoGet { is_32bit: true },
1812 FuncId::NotificationInfoGet64 => Self::NotificationInfoGet { is_32bit: false },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01001813 FuncId::El3IntrHandle => Self::El3IntrHandle,
Balint Dobszayde0dc802025-02-28 14:16:52 +01001814 _ => panic!("Invalid number of registers (8) for function {:#x?}", fid),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001815 };
1816
1817 Ok(msg)
1818 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01001819
Balint Dobszayde0dc802025-02-28 14:16:52 +01001820 fn unpack_regs18(version: Version, regs: &[u64; 18]) -> Result<Self, Error> {
1821 assert!(version >= Version(1, 2));
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001822
Balint Dobszayde0dc802025-02-28 14:16:52 +01001823 let fid = FuncId::try_from(regs[0] as u32)?;
1824
1825 let msg = match fid {
1826 FuncId::Success64 => Self::Success {
1827 target_info: regs[1] as u32,
Imre Kis54773b62025-04-10 13:47:39 +02001828 args: SuccessArgs::Args64_2(regs[2..18].try_into().unwrap()),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001829 },
1830 FuncId::MsgSendDirectReq64_2 => Self::MsgSendDirectReq2 {
1831 src_id: (regs[1] >> 16) as u16,
1832 dst_id: regs[1] as u16,
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00001833 uuid: Uuid::from_u64_pair(regs[2].swap_bytes(), regs[3].swap_bytes()),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001834 args: DirectMsg2Args(regs[4..18].try_into().unwrap()),
1835 },
1836 FuncId::MsgSendDirectResp64_2 => Self::MsgSendDirectResp2 {
1837 src_id: (regs[1] >> 16) as u16,
1838 dst_id: regs[1] as u16,
1839 args: DirectMsg2Args(regs[4..18].try_into().unwrap()),
1840 },
1841 FuncId::ConsoleLog64 => Self::ConsoleLog {
1842 char_cnt: regs[1] as u8,
1843 char_lists: ConsoleLogChars::Reg64(regs[2..18].try_into().unwrap()),
1844 },
Tomás González0a058bc2025-03-11 11:20:55 +00001845 FuncId::PartitionInfoGetRegs => {
1846 // Bits[15:0]: Start index
1847 let start_index = (regs[3] & 0xffff) as u16;
1848 let info_tag = ((regs[3] >> 16) & 0xffff) as u16;
1849 Self::PartitionInfoGetRegs {
1850 uuid: Uuid::from_u64_pair(regs[1].swap_bytes(), regs[2].swap_bytes()),
1851 start_index,
1852 info_tag: if start_index == 0 && info_tag != 0 {
1853 return Err(Error::InvalidInformationTag(info_tag));
1854 } else {
1855 info_tag
1856 },
1857 }
1858 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001859 _ => panic!("Invalid number of registers (18) for function {:#x?}", fid),
1860 };
1861
1862 Ok(msg)
Balint Dobszay3aad9572025-01-17 16:54:11 +01001863 }
1864
Balint Dobszaya5846852025-02-26 15:38:53 +01001865 /// Create register contents for an interface.
Balint Dobszayde0dc802025-02-28 14:16:52 +01001866 pub fn to_regs(&self, version: Version, regs: &mut [u64]) {
Balint Dobszay82c71dd2025-04-15 10:16:44 +02001867 assert!(self.minimum_ffa_version() <= version);
1868
Balint Dobszayde0dc802025-02-28 14:16:52 +01001869 let reg_cnt = regs.len();
1870
1871 match reg_cnt {
1872 8 => {
1873 assert!(version <= Version(1, 1));
Balint Dobszay91bea9b2025-04-09 13:16:06 +02001874 regs.fill(0);
1875
Balint Dobszayde0dc802025-02-28 14:16:52 +01001876 self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
1877 }
1878 18 => {
1879 assert!(version >= Version(1, 2));
Balint Dobszay91bea9b2025-04-09 13:16:06 +02001880 regs.fill(0);
Balint Dobszayde0dc802025-02-28 14:16:52 +01001881
1882 match self {
1883 Interface::ConsoleLog {
1884 char_lists: ConsoleLogChars::Reg64(_),
1885 ..
1886 }
1887 | Interface::Success {
Imre Kis54773b62025-04-10 13:47:39 +02001888 args: SuccessArgs::Args64_2(_),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001889 ..
1890 }
1891 | Interface::MsgSendDirectReq2 { .. }
Tomás González0a058bc2025-03-11 11:20:55 +00001892 | Interface::MsgSendDirectResp2 { .. }
1893 | Interface::PartitionInfoGetRegs { .. } => {
Balint Dobszayde0dc802025-02-28 14:16:52 +01001894 self.pack_regs18(version, regs.try_into().unwrap());
1895 }
1896 _ => {
1897 self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
1898 }
1899 }
1900 }
1901 _ => panic!("Invalid number of registers {}", reg_cnt),
1902 }
1903 }
1904
1905 fn pack_regs8(&self, version: Version, a: &mut [u64; 8]) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001906 if let Some(function_id) = self.function_id() {
1907 a[0] = function_id as u64;
1908 }
1909
1910 match *self {
1911 Interface::Error {
1912 target_info,
1913 error_code,
Balint Dobszayb727aab2025-04-07 10:24:59 +02001914 error_arg,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001915 } => {
1916 a[1] = u32::from(target_info).into();
1917 a[2] = (error_code as u32).into();
Balint Dobszayb727aab2025-04-07 10:24:59 +02001918 a[3] = error_arg.into();
Balint Dobszay3aad9572025-01-17 16:54:11 +01001919 }
1920 Interface::Success { target_info, args } => {
1921 a[1] = target_info.into();
1922 match args {
Imre Kis54773b62025-04-10 13:47:39 +02001923 SuccessArgs::Args32(regs) => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001924 a[2] = regs[0].into();
1925 a[3] = regs[1].into();
1926 a[4] = regs[2].into();
1927 a[5] = regs[3].into();
1928 a[6] = regs[4].into();
1929 a[7] = regs[5].into();
1930 }
Imre Kis54773b62025-04-10 13:47:39 +02001931 SuccessArgs::Args64(regs) => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001932 a[2] = regs[0];
1933 a[3] = regs[1];
1934 a[4] = regs[2];
1935 a[5] = regs[3];
1936 a[6] = regs[4];
1937 a[7] = regs[5];
1938 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001939 _ => panic!("{:#x?} requires 18 registers", args),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001940 }
1941 }
1942 Interface::Interrupt {
1943 target_info,
1944 interrupt_id,
1945 } => {
1946 a[1] = u32::from(target_info).into();
1947 a[2] = interrupt_id.into();
1948 }
1949 Interface::Version { input_version } => {
1950 a[1] = u32::from(input_version).into();
1951 }
1952 Interface::VersionOut { output_version } => {
1953 a[0] = u32::from(output_version).into();
1954 }
1955 Interface::Features {
1956 feat_id,
1957 input_properties,
1958 } => {
1959 a[1] = u32::from(feat_id).into();
1960 a[2] = input_properties.into();
1961 }
1962 Interface::RxAcquire { vm_id } => {
1963 a[1] = vm_id.into();
1964 }
1965 Interface::RxRelease { vm_id } => {
1966 a[1] = vm_id.into();
1967 }
1968 Interface::RxTxMap { addr, page_cnt } => {
1969 match addr {
1970 RxTxAddr::Addr32 { rx, tx } => {
1971 a[1] = tx.into();
1972 a[2] = rx.into();
1973 }
1974 RxTxAddr::Addr64 { rx, tx } => {
1975 a[1] = tx;
1976 a[2] = rx;
1977 }
1978 }
1979 a[3] = page_cnt.into();
1980 }
1981 Interface::RxTxUnmap { id } => {
1982 a[1] = id.into();
1983 }
1984 Interface::PartitionInfoGet { uuid, flags } => {
1985 let bytes = uuid.into_bytes();
1986 a[1] = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]).into();
1987 a[2] = u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]).into();
1988 a[3] = u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]).into();
1989 a[4] = u32::from_le_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]).into();
Imre Kise295adb2025-04-10 13:26:28 +02001990 a[5] = u32::from(flags).into();
Balint Dobszay3aad9572025-01-17 16:54:11 +01001991 }
Tomás González092202a2025-03-05 11:56:45 +00001992 Interface::MsgWait { flags } => {
1993 if version >= Version(1, 2) {
1994 if let Some(flags) = flags {
1995 a[2] = u32::from(flags).into();
1996 }
1997 }
1998 }
1999 Interface::IdGet | Interface::SpmIdGet | Interface::Yield => {}
Balint Dobszay3aad9572025-01-17 16:54:11 +01002000 Interface::Run { target_info } => {
2001 a[1] = u32::from(target_info).into();
2002 }
2003 Interface::NormalWorldResume => {}
Tomás González17b92442025-03-10 16:45:04 +00002004 Interface::SecondaryEpRegister { entrypoint } => match entrypoint {
2005 SecondaryEpRegisterAddr::Addr32(addr) => a[1] = addr as u64,
2006 SecondaryEpRegisterAddr::Addr64(addr) => a[1] = addr,
2007 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01002008 Interface::MsgSend2 {
2009 sender_vm_id,
2010 flags,
2011 } => {
2012 a[1] = sender_vm_id.into();
2013 a[2] = flags.into();
2014 }
2015 Interface::MsgSendDirectReq {
2016 src_id,
2017 dst_id,
Balint Dobszay3aad9572025-01-17 16:54:11 +01002018 args,
2019 } => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +01002020 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Balint Dobszay3aad9572025-01-17 16:54:11 +01002021 match args {
2022 DirectMsgArgs::Args32(args) => {
2023 a[3] = args[0].into();
2024 a[4] = args[1].into();
2025 a[5] = args[2].into();
2026 a[6] = args[3].into();
2027 a[7] = args[4].into();
2028 }
2029 DirectMsgArgs::Args64(args) => {
2030 a[3] = args[0];
2031 a[4] = args[1];
2032 a[5] = args[2];
2033 a[6] = args[3];
2034 a[7] = args[4];
2035 }
Tomás González4d5b0ba2025-03-03 17:15:55 +00002036 DirectMsgArgs::VersionReq { version } => {
2037 a[2] = DirectMsgArgs::VERSION_REQ.into();
2038 a[3] = u32::from(version).into();
2039 }
Tomás González67f92c72025-03-20 16:50:42 +00002040 DirectMsgArgs::PowerPsciReq32 { 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].into();
2043 a[4] = params[1].into();
2044 a[5] = params[2].into();
2045 a[6] = params[3].into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00002046 }
Tomás González67f92c72025-03-20 16:50:42 +00002047 DirectMsgArgs::PowerPsciReq64 { params } => {
Tomás González4d5b0ba2025-03-03 17:15:55 +00002048 a[2] = DirectMsgArgs::POWER_PSCI_REQ.into();
Tomás González67f92c72025-03-20 16:50:42 +00002049 a[3] = params[0];
2050 a[4] = params[1];
2051 a[5] = params[2];
2052 a[6] = params[3];
Tomás González4d5b0ba2025-03-03 17:15:55 +00002053 }
2054 DirectMsgArgs::PowerWarmBootReq { boot_type } => {
2055 a[2] = DirectMsgArgs::POWER_WARM_BOOT_REQ.into();
2056 a[3] = u32::from(boot_type).into();
2057 }
2058 DirectMsgArgs::VmCreated { handle, vm_id } => {
2059 a[2] = DirectMsgArgs::VM_CREATED.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 DirectMsgArgs::VmDestructed { handle, vm_id } => {
2066 a[2] = DirectMsgArgs::VM_DESTRUCTED.into();
2067 let handle_regs: [u32; 2] = handle.into();
2068 a[3] = handle_regs[0].into();
2069 a[4] = handle_regs[1].into();
2070 a[5] = vm_id.into();
2071 }
2072 _ => panic!("Malformed MsgSendDirectReq interface"),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002073 }
2074 }
2075 Interface::MsgSendDirectResp {
2076 src_id,
2077 dst_id,
Balint Dobszay3aad9572025-01-17 16:54:11 +01002078 args,
2079 } => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +01002080 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Balint Dobszay3aad9572025-01-17 16:54:11 +01002081 match args {
2082 DirectMsgArgs::Args32(args) => {
2083 a[3] = args[0].into();
2084 a[4] = args[1].into();
2085 a[5] = args[2].into();
2086 a[6] = args[3].into();
2087 a[7] = args[4].into();
2088 }
2089 DirectMsgArgs::Args64(args) => {
2090 a[3] = args[0];
2091 a[4] = args[1];
2092 a[5] = args[2];
2093 a[6] = args[3];
2094 a[7] = args[4];
2095 }
Tomás González4d5b0ba2025-03-03 17:15:55 +00002096 DirectMsgArgs::VersionResp { version } => {
2097 a[2] = DirectMsgArgs::VERSION_RESP.into();
2098 match version {
Tomás González67f92c72025-03-20 16:50:42 +00002099 None => a[3] = (i32::from(FfaError::NotSupported) as u32).into(),
Tomás González4d5b0ba2025-03-03 17:15:55 +00002100 Some(ver) => a[3] = u32::from(ver).into(),
2101 }
2102 }
2103 DirectMsgArgs::PowerPsciResp { psci_status } => {
2104 a[2] = DirectMsgArgs::POWER_PSCI_RESP.into();
Imre Kisb2d3c882025-04-11 14:19:35 +02002105 a[3] = (psci_status as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00002106 }
2107 DirectMsgArgs::VmCreatedAck { sp_status } => {
2108 a[2] = DirectMsgArgs::VM_CREATED_ACK.into();
Tomás González67f92c72025-03-20 16:50:42 +00002109 a[3] = (i32::from(sp_status) as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00002110 }
2111 DirectMsgArgs::VmDestructedAck { sp_status } => {
2112 a[2] = DirectMsgArgs::VM_DESTRUCTED_ACK.into();
Tomás González67f92c72025-03-20 16:50:42 +00002113 a[3] = (i32::from(sp_status) as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00002114 }
2115 _ => panic!("Malformed MsgSendDirectResp interface"),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002116 }
2117 }
2118 Interface::MemDonate {
2119 total_len,
2120 frag_len,
2121 buf,
2122 } => {
2123 a[1] = total_len.into();
2124 a[2] = frag_len.into();
2125 (a[3], a[4]) = match buf {
2126 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
2127 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
2128 None => (0, 0),
2129 };
2130 }
2131 Interface::MemLend {
2132 total_len,
2133 frag_len,
2134 buf,
2135 } => {
2136 a[1] = total_len.into();
2137 a[2] = frag_len.into();
2138 (a[3], a[4]) = match buf {
2139 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
2140 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
2141 None => (0, 0),
2142 };
2143 }
2144 Interface::MemShare {
2145 total_len,
2146 frag_len,
2147 buf,
2148 } => {
2149 a[1] = total_len.into();
2150 a[2] = frag_len.into();
2151 (a[3], a[4]) = match buf {
2152 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
2153 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
2154 None => (0, 0),
2155 };
2156 }
2157 Interface::MemRetrieveReq {
2158 total_len,
2159 frag_len,
2160 buf,
2161 } => {
2162 a[1] = total_len.into();
2163 a[2] = frag_len.into();
2164 (a[3], a[4]) = match buf {
2165 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
2166 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
2167 None => (0, 0),
2168 };
2169 }
2170 Interface::MemRetrieveResp {
2171 total_len,
2172 frag_len,
2173 } => {
2174 a[1] = total_len.into();
2175 a[2] = frag_len.into();
2176 }
2177 Interface::MemRelinquish => {}
2178 Interface::MemReclaim { handle, flags } => {
2179 let handle_regs: [u32; 2] = handle.into();
2180 a[1] = handle_regs[0].into();
2181 a[2] = handle_regs[1].into();
2182 a[3] = flags.into();
2183 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01002184 Interface::MemPermGet { addr, page_cnt } => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01002185 a[1] = match addr {
2186 MemAddr::Addr32(addr) => addr.into(),
2187 MemAddr::Addr64(addr) => addr,
2188 };
Balint Dobszayde0dc802025-02-28 14:16:52 +01002189 a[2] = if version >= Version(1, 3) {
2190 page_cnt.unwrap().into()
2191 } else {
2192 assert!(page_cnt.is_none());
2193 0
2194 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01002195 }
2196 Interface::MemPermSet {
2197 addr,
2198 page_cnt,
2199 mem_perm,
2200 } => {
2201 a[1] = match addr {
2202 MemAddr::Addr32(addr) => addr.into(),
2203 MemAddr::Addr64(addr) => addr,
2204 };
2205 a[2] = page_cnt.into();
2206 a[3] = mem_perm.into();
2207 }
2208 Interface::ConsoleLog {
2209 char_cnt,
2210 char_lists,
2211 } => {
2212 a[1] = char_cnt.into();
2213 match char_lists {
2214 ConsoleLogChars::Reg32(regs) => {
2215 a[2] = regs[0].into();
2216 a[3] = regs[1].into();
2217 a[4] = regs[2].into();
2218 a[5] = regs[3].into();
2219 a[6] = regs[4].into();
2220 a[7] = regs[5].into();
2221 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01002222 _ => panic!("{:#x?} requires 18 registers", char_lists),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002223 }
2224 }
Tomás González7ffb6132025-04-03 12:28:58 +01002225 Interface::NotificationBitmapCreate { vm_id, vcpu_cnt } => {
2226 a[1] = vm_id.into();
2227 a[2] = vcpu_cnt.into();
2228 }
2229 Interface::NotificationBitmapDestroy { vm_id } => {
2230 a[1] = vm_id.into();
2231 }
2232 Interface::NotificationBind {
2233 sender_id,
2234 receiver_id,
2235 flags,
2236 bitmap,
2237 } => {
2238 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
2239 a[2] = u32::from(flags).into();
2240 a[3] = bitmap & 0xffff_ffff;
2241 a[4] = bitmap >> 32;
2242 }
2243 Interface::NotificationUnBind {
2244 sender_id,
2245 receiver_id,
2246 bitmap,
2247 } => {
2248 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
2249 a[3] = bitmap & 0xffff_ffff;
2250 a[4] = bitmap >> 32;
2251 }
2252 Interface::NotificationSet {
2253 sender_id,
2254 receiver_id,
2255 flags,
2256 bitmap,
2257 } => {
2258 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
2259 a[2] = u32::from(flags).into();
2260 a[3] = bitmap & 0xffff_ffff;
2261 a[4] = bitmap >> 32;
2262 }
2263 Interface::NotificationGet {
2264 vcpu_id,
2265 endpoint_id,
2266 flags,
2267 } => {
2268 a[1] = (u64::from(vcpu_id) << 16) | u64::from(endpoint_id);
2269 a[2] = u32::from(flags).into();
2270 }
2271 Interface::NotificationInfoGet { .. } => {}
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01002272 Interface::El3IntrHandle => {}
Balint Dobszayde0dc802025-02-28 14:16:52 +01002273 _ => panic!("{:#x?} requires 18 registers", self),
2274 }
2275 }
2276
2277 fn pack_regs18(&self, version: Version, a: &mut [u64; 18]) {
2278 assert!(version >= Version(1, 2));
2279
Balint Dobszayde0dc802025-02-28 14:16:52 +01002280 if let Some(function_id) = self.function_id() {
2281 a[0] = function_id as u64;
2282 }
2283
2284 match *self {
2285 Interface::Success { target_info, args } => {
2286 a[1] = target_info.into();
2287 match args {
Imre Kis54773b62025-04-10 13:47:39 +02002288 SuccessArgs::Args64_2(regs) => a[2..18].copy_from_slice(&regs[..16]),
Balint Dobszayde0dc802025-02-28 14:16:52 +01002289 _ => panic!("{:#x?} requires 8 registers", args),
2290 }
2291 }
2292 Interface::MsgSendDirectReq2 {
2293 src_id,
2294 dst_id,
2295 uuid,
2296 args,
2297 } => {
2298 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00002299 let (uuid_msb, uuid_lsb) = uuid.as_u64_pair();
2300 (a[2], a[3]) = (uuid_msb.swap_bytes(), uuid_lsb.swap_bytes());
Balint Dobszayde0dc802025-02-28 14:16:52 +01002301 a[4..18].copy_from_slice(&args.0[..14]);
2302 }
2303 Interface::MsgSendDirectResp2 {
2304 src_id,
2305 dst_id,
2306 args,
2307 } => {
2308 a[1] = ((src_id as u64) << 16) | dst_id as u64;
2309 a[2] = 0;
2310 a[3] = 0;
2311 a[4..18].copy_from_slice(&args.0[..14]);
2312 }
2313 Interface::ConsoleLog {
2314 char_cnt,
2315 char_lists,
2316 } => {
2317 a[1] = char_cnt.into();
2318 match char_lists {
2319 ConsoleLogChars::Reg64(regs) => a[2..18].copy_from_slice(&regs[..16]),
2320 _ => panic!("{:#x?} requires 8 registers", char_lists),
2321 }
2322 }
Tomás González0a058bc2025-03-11 11:20:55 +00002323 Interface::PartitionInfoGetRegs {
2324 uuid,
2325 start_index,
2326 info_tag,
2327 } => {
2328 if start_index == 0 && info_tag != 0 {
2329 panic!("Information Tag MBZ if start index is 0: {:#x?}", self);
2330 }
2331 let (uuid_msb, uuid_lsb) = uuid.as_u64_pair();
2332 (a[1], a[2]) = (uuid_msb.swap_bytes(), uuid_lsb.swap_bytes());
2333 a[3] = (u64::from(info_tag) << 16) | u64::from(start_index);
2334 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01002335 _ => panic!("{:#x?} requires 8 registers", self),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002336 }
2337 }
2338
Balint Dobszaya5846852025-02-26 15:38:53 +01002339 /// Helper function to create an `FFA_SUCCESS` interface without any arguments.
Balint Dobszay3aad9572025-01-17 16:54:11 +01002340 pub fn success32_noargs() -> Self {
2341 Self::Success {
2342 target_info: 0,
Imre Kis54773b62025-04-10 13:47:39 +02002343 args: SuccessArgs::Args32([0; 6]),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002344 }
2345 }
2346
Balint Dobszaya5846852025-02-26 15:38:53 +01002347 /// Helper function to create an `FFA_ERROR` interface with an error code.
Balint Dobszay3aad9572025-01-17 16:54:11 +01002348 pub fn error(error_code: FfaError) -> Self {
2349 Self::Error {
2350 target_info: TargetInfo {
2351 endpoint_id: 0,
2352 vcpu_id: 0,
2353 },
2354 error_code,
Balint Dobszayb727aab2025-04-07 10:24:59 +02002355 error_arg: 0,
Balint Dobszay3aad9572025-01-17 16:54:11 +01002356 }
2357 }
2358}
2359
Balint Dobszaya5846852025-02-26 15:38:53 +01002360/// Maximum number of characters transmitted in a single `FFA_CONSOLE_LOG32` message.
2361pub const CONSOLE_LOG_32_MAX_CHAR_CNT: u8 = 24;
Balint Dobszayde0dc802025-02-28 14:16:52 +01002362/// Maximum number of characters transmitted in a single `FFA_CONSOLE_LOG64` message.
2363pub const CONSOLE_LOG_64_MAX_CHAR_CNT: u8 = 128;
Balint Dobszay3aad9572025-01-17 16:54:11 +01002364
Balint Dobszaya5846852025-02-26 15:38:53 +01002365/// Helper function to convert the "Tightly packed list of characters" format used by the
2366/// `FFA_CONSOLE_LOG` interface into a byte slice.
Balint Dobszay3aad9572025-01-17 16:54:11 +01002367pub fn parse_console_log(
2368 char_cnt: u8,
2369 char_lists: &ConsoleLogChars,
2370 log_bytes: &mut [u8],
2371) -> Result<(), FfaError> {
2372 match char_lists {
2373 ConsoleLogChars::Reg32(regs) => {
Balint Dobszaya5846852025-02-26 15:38:53 +01002374 if !(1..=CONSOLE_LOG_32_MAX_CHAR_CNT).contains(&char_cnt) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01002375 return Err(FfaError::InvalidParameters);
2376 }
2377 for (i, reg) in regs.iter().enumerate() {
2378 log_bytes[4 * i..4 * (i + 1)].copy_from_slice(&reg.to_le_bytes());
2379 }
2380 }
2381 ConsoleLogChars::Reg64(regs) => {
Balint Dobszaya5846852025-02-26 15:38:53 +01002382 if !(1..=CONSOLE_LOG_64_MAX_CHAR_CNT).contains(&char_cnt) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01002383 return Err(FfaError::InvalidParameters);
2384 }
2385 for (i, reg) in regs.iter().enumerate() {
2386 log_bytes[8 * i..8 * (i + 1)].copy_from_slice(&reg.to_le_bytes());
2387 }
2388 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +02002389 }
2390
Balint Dobszayc8802492025-01-15 18:11:39 +01002391 Ok(())
Balint Dobszay5bf492f2024-07-29 17:21:32 +02002392}
Tomás González0a058bc2025-03-11 11:20:55 +00002393
2394#[cfg(test)]
2395mod tests {
2396 use super::*;
2397
2398 #[test]
2399 fn part_info_get_regs() {
2400 let uuid = Uuid::parse_str("a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8").unwrap();
2401 let uuid_bytes = uuid.as_bytes();
2402 let test_info_tag = 0b1101_1101;
2403 let test_start_index = 0b1101;
2404 let start_index_and_tag = (test_info_tag << 16) | test_start_index;
2405 let version = Version(1, 2);
2406
2407 // From spec:
2408 // Bytes[0...7] of UUID with byte 0 in the low-order bits.
Balint Dobszayb2e9bed2025-04-15 12:57:36 +02002409 let reg_x1 = ((uuid_bytes[7] as u64) << 56)
2410 | ((uuid_bytes[6] as u64) << 48)
2411 | ((uuid_bytes[5] as u64) << 40)
2412 | ((uuid_bytes[4] as u64) << 32)
2413 | ((uuid_bytes[3] as u64) << 24)
2414 | ((uuid_bytes[2] as u64) << 16)
2415 | ((uuid_bytes[1] as u64) << 8)
Tomás González0a058bc2025-03-11 11:20:55 +00002416 | (uuid_bytes[0] as u64);
2417
2418 // From spec:
2419 // Bytes[8...15] of UUID with byte 8 in the low-order bits.
Balint Dobszayb2e9bed2025-04-15 12:57:36 +02002420 let reg_x2 = ((uuid_bytes[15] as u64) << 56)
2421 | ((uuid_bytes[14] as u64) << 48)
2422 | ((uuid_bytes[13] as u64) << 40)
2423 | ((uuid_bytes[12] as u64) << 32)
2424 | ((uuid_bytes[11] as u64) << 24)
2425 | ((uuid_bytes[10] as u64) << 16)
2426 | ((uuid_bytes[9] as u64) << 8)
Tomás González0a058bc2025-03-11 11:20:55 +00002427 | (uuid_bytes[8] as u64);
2428
2429 // First, test for wrong tag:
2430 {
2431 let mut regs = [0u64; 18];
2432 regs[0] = FuncId::PartitionInfoGetRegs as u64;
2433 regs[1] = reg_x1;
2434 regs[2] = reg_x2;
2435 regs[3] = test_info_tag << 16;
2436
2437 assert!(Interface::from_regs(version, &regs).is_err_and(
2438 |e| e == Error::InvalidInformationTag(test_info_tag.try_into().unwrap())
2439 ));
2440 }
2441
2442 // Test for regs -> Interface -> regs
2443 {
2444 let mut orig_regs = [0u64; 18];
2445 orig_regs[0] = FuncId::PartitionInfoGetRegs as u64;
2446 orig_regs[1] = reg_x1;
2447 orig_regs[2] = reg_x2;
2448 orig_regs[3] = start_index_and_tag;
2449
Balint Dobszayb2e9bed2025-04-15 12:57:36 +02002450 let mut test_regs = orig_regs;
2451 let interface = Interface::from_regs(version, &test_regs).unwrap();
Tomás González0a058bc2025-03-11 11:20:55 +00002452 match &interface {
2453 Interface::PartitionInfoGetRegs {
2454 info_tag,
2455 start_index,
2456 uuid: int_uuid,
2457 } => {
2458 assert_eq!(u64::from(*info_tag), test_info_tag);
2459 assert_eq!(u64::from(*start_index), test_start_index);
2460 assert_eq!(*int_uuid, uuid);
2461 }
2462 _ => panic!("Expecting Interface::PartitionInfoGetRegs!"),
2463 }
2464 test_regs.fill(0);
2465 interface.to_regs(version, &mut test_regs);
2466 assert_eq!(orig_regs, test_regs);
2467 }
2468
2469 // Test for Interface -> regs -> Interface
2470 {
2471 let interface = Interface::PartitionInfoGetRegs {
2472 info_tag: test_info_tag.try_into().unwrap(),
2473 start_index: test_start_index.try_into().unwrap(),
2474 uuid,
2475 };
2476
2477 let mut regs: [u64; 18] = [0; 18];
2478 interface.to_regs(version, &mut regs);
2479
2480 assert_eq!(Some(FuncId::PartitionInfoGetRegs), interface.function_id());
2481 assert_eq!(regs[0], interface.function_id().unwrap() as u64);
2482 assert_eq!(regs[1], reg_x1);
2483 assert_eq!(regs[2], reg_x2);
2484 assert_eq!(regs[3], (test_info_tag << 16) | test_start_index);
2485
2486 assert_eq!(Interface::from_regs(version, &regs).unwrap(), interface);
2487 }
2488 }
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00002489
2490 #[test]
2491 fn msg_send_direct_req2() {
2492 let uuid = Uuid::parse_str("a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8").unwrap();
2493 let uuid_bytes = uuid.as_bytes();
2494
2495 // From spec:
2496 // Bytes[0...7] of UUID with byte 0 in the low-order bits.
Balint Dobszayb2e9bed2025-04-15 12:57:36 +02002497 let reg_x2 = ((uuid_bytes[7] as u64) << 56)
2498 | ((uuid_bytes[6] as u64) << 48)
2499 | ((uuid_bytes[5] as u64) << 40)
2500 | ((uuid_bytes[4] as u64) << 32)
2501 | ((uuid_bytes[3] as u64) << 24)
2502 | ((uuid_bytes[2] as u64) << 16)
2503 | ((uuid_bytes[1] as u64) << 8)
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00002504 | (uuid_bytes[0] as u64);
2505
2506 // From spec:
2507 // Bytes[8...15] of UUID with byte 8 in the low-order bits.
Balint Dobszayb2e9bed2025-04-15 12:57:36 +02002508 let reg_x3 = ((uuid_bytes[15] as u64) << 56)
2509 | ((uuid_bytes[14] as u64) << 48)
2510 | ((uuid_bytes[13] as u64) << 40)
2511 | ((uuid_bytes[12] as u64) << 32)
2512 | ((uuid_bytes[11] as u64) << 24)
2513 | ((uuid_bytes[10] as u64) << 16)
2514 | ((uuid_bytes[9] as u64) << 8)
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00002515 | (uuid_bytes[8] as u64);
2516
2517 let test_sender = 0b1101_1101;
2518 let test_receiver = 0b1101;
2519 let test_sender_receiver = (test_sender << 16) | test_receiver;
2520 let version = Version(1, 2);
2521
2522 // Test for regs -> Interface -> regs
2523 {
2524 let mut orig_regs = [0u64; 18];
2525 orig_regs[0] = FuncId::MsgSendDirectReq64_2 as u64;
2526 orig_regs[1] = test_sender_receiver;
2527 orig_regs[2] = reg_x2;
2528 orig_regs[3] = reg_x3;
2529
Balint Dobszayb2e9bed2025-04-15 12:57:36 +02002530 let mut test_regs = orig_regs;
2531 let interface = Interface::from_regs(version, &test_regs).unwrap();
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00002532 match &interface {
2533 Interface::MsgSendDirectReq2 {
2534 dst_id,
2535 src_id,
2536 args: _,
2537 uuid: int_uuid,
2538 } => {
2539 assert_eq!(u64::from(*src_id), test_sender);
2540 assert_eq!(u64::from(*dst_id), test_receiver);
2541 assert_eq!(*int_uuid, uuid);
2542 }
2543 _ => panic!("Expecting Interface::MsgSendDirectReq2!"),
2544 }
2545 test_regs.fill(0);
2546 interface.to_regs(version, &mut test_regs);
2547 assert_eq!(orig_regs, test_regs);
2548 }
2549
2550 // Test for Interface -> regs -> Interface
2551 {
2552 let rest_of_regs: [u64; 14] = [0; 14];
2553
2554 let interface = Interface::MsgSendDirectReq2 {
2555 src_id: test_sender.try_into().unwrap(),
2556 dst_id: test_receiver.try_into().unwrap(),
2557 uuid,
2558 args: DirectMsg2Args(rest_of_regs),
2559 };
2560
2561 let mut regs: [u64; 18] = [0; 18];
2562 interface.to_regs(version, &mut regs);
2563
2564 assert_eq!(Some(FuncId::MsgSendDirectReq64_2), interface.function_id());
2565 assert_eq!(regs[0], interface.function_id().unwrap() as u64);
2566 assert_eq!(regs[1], test_sender_receiver);
2567 assert_eq!(regs[2], reg_x2);
2568 assert_eq!(regs[3], reg_x3);
2569 assert_eq!(regs[4], 0);
2570
2571 assert_eq!(Interface::from_regs(version, &regs).unwrap(), interface);
2572 }
2573 }
Tomás González6ccba0a2025-04-09 13:31:29 +01002574
2575 #[test]
2576 fn is_32bit() {
2577 let interface_64 = Interface::MsgSendDirectReq {
2578 src_id: 0,
2579 dst_id: 1,
2580 args: DirectMsgArgs::Args64([0, 0, 0, 0, 0]),
2581 };
2582 assert!(!interface_64.is_32bit());
2583
2584 let interface_32 = Interface::MsgSendDirectReq {
2585 src_id: 0,
2586 dst_id: 1,
2587 args: DirectMsgArgs::Args32([0, 0, 0, 0, 0]),
2588 };
2589 assert!(interface_32.is_32bit());
2590 }
Imre Kis787c5002025-04-10 14:25:51 +02002591
2592 #[test]
2593 fn success_args_notification_info_get32() {
2594 let mut notifications = SuccessArgsNotificationInfoGet32::default();
2595
2596 // 16.7.1.1 Example usage
2597 notifications.add_list(0x0000, &[0, 2, 3]).unwrap();
2598 notifications.add_list(0x0000, &[4, 6]).unwrap();
2599 notifications.add_list(0x0002, &[]).unwrap();
2600 notifications.add_list(0x0003, &[1]).unwrap();
2601
2602 let args: SuccessArgs = notifications.into();
2603 assert_eq!(
2604 SuccessArgs::Args32([
2605 0x0004_b200,
2606 0x0000_0000,
2607 0x0003_0002,
2608 0x0004_0000,
2609 0x0002_0006,
2610 0x0001_0003
2611 ]),
2612 args
2613 );
2614
2615 let notifications = SuccessArgsNotificationInfoGet32::try_from(args).unwrap();
2616 let mut iter = notifications.iter();
2617 assert_eq!(Some((0x0000, &[0, 2, 3][..])), iter.next());
2618 assert_eq!(Some((0x0000, &[4, 6][..])), iter.next());
2619 assert_eq!(Some((0x0002, &[][..])), iter.next());
2620 assert_eq!(Some((0x0003, &[1][..])), iter.next());
2621 }
2622
2623 #[test]
2624 fn success_args_notification_info_get64() {
2625 let mut notifications = SuccessArgsNotificationInfoGet64::default();
2626
2627 // 16.7.1.1 Example usage
2628 notifications.add_list(0x0000, &[0, 2, 3]).unwrap();
2629 notifications.add_list(0x0000, &[4, 6]).unwrap();
2630 notifications.add_list(0x0002, &[]).unwrap();
2631 notifications.add_list(0x0003, &[1]).unwrap();
2632
2633 let args: SuccessArgs = notifications.into();
2634 assert_eq!(
2635 SuccessArgs::Args64([
2636 0x0004_b200,
2637 0x0003_0002_0000_0000,
2638 0x0002_0006_0004_0000,
2639 0x0000_0000_0001_0003,
2640 0x0000_0000_0000_0000,
2641 0x0000_0000_0000_0000,
2642 ]),
2643 args
2644 );
2645
2646 let notifications = SuccessArgsNotificationInfoGet64::try_from(args).unwrap();
2647 let mut iter = notifications.iter();
2648 assert_eq!(Some((0x0000, &[0, 2, 3][..])), iter.next());
2649 assert_eq!(Some((0x0000, &[4, 6][..])), iter.next());
2650 assert_eq!(Some((0x0002, &[][..])), iter.next());
2651 assert_eq!(Some((0x0003, &[1][..])), iter.next());
2652 }
Tomás González0a058bc2025-03-11 11:20:55 +00002653}