blob: fb30191704a71b52df39a8a454e7b92cce02415f [file] [log] [blame]
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001// SPDX-FileCopyrightText: Copyright 2023 Arm Limited and/or its affiliates <open-source-office@arm.com>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4#![cfg_attr(not(test), no_std)]
Balint Dobszaya5846852025-02-26 15:38:53 +01005#![deny(clippy::undocumented_unsafe_blocks)]
6#![deny(unsafe_op_in_unsafe_fn)]
7#![doc = include_str!("../README.md")]
Balint Dobszay5bf492f2024-07-29 17:21:32 +02008
Andrew Walbran19970ba2024-11-25 15:35:00 +00009use core::fmt::{self, Debug, Display, Formatter};
Andrew Walbran44029a02024-11-25 15:34:31 +000010use num_enum::{IntoPrimitive, TryFromPrimitive};
11use thiserror::Error;
Imre Kisc0b062c2025-05-26 19:31:00 +020012pub use uuid::Uuid;
Imre Kis189f18c2025-05-26 19:33:05 +020013use zerocopy::{transmute, FromBytes, Immutable, IntoBytes};
Balint Dobszay5bf492f2024-07-29 17:21:32 +020014
15pub mod boot_info;
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +010016mod ffa_v1_1;
Balint Dobszayde0dc802025-02-28 14:16:52 +010017mod ffa_v1_2;
Balint Dobszay5bf492f2024-07-29 17:21:32 +020018pub mod memory_management;
19pub mod partition_info;
20
Balint Dobszaya5846852025-02-26 15:38:53 +010021/// Constant for 4K page size. On many occasions the FF-A spec defines memory size as count of 4K
22/// pages, regardless of the current translation granule.
Balint Dobszay3aad9572025-01-17 16:54:11 +010023pub const FFA_PAGE_SIZE_4K: usize = 4096;
24
Balint Dobszaya5846852025-02-26 15:38:53 +010025/// Rich error types returned by this module. Should be converted to [`crate::FfaError`] when used
26/// with the `FFA_ERROR` interface.
Tomás González0a058bc2025-03-11 11:20:55 +000027#[derive(Debug, Error, PartialEq)]
Balint Dobszay3aad9572025-01-17 16:54:11 +010028pub enum Error {
29 #[error("Unrecognised FF-A function ID {0}")]
30 UnrecognisedFunctionId(u32),
31 #[error("Unrecognised FF-A feature ID {0}")]
32 UnrecognisedFeatureId(u8),
33 #[error("Unrecognised FF-A error code {0}")]
34 UnrecognisedErrorCode(i32),
Tomás González4d5b0ba2025-03-03 17:15:55 +000035 #[error("Unrecognised FF-A Framework Message {0}")]
36 UnrecognisedFwkMsg(u32),
Tomás González092202a2025-03-05 11:56:45 +000037 #[error("Invalid FF-A Msg Wait Flag {0}")]
38 InvalidMsgWaitFlag(u32),
Tomás González4d5b0ba2025-03-03 17:15:55 +000039 #[error("Unrecognised VM availability status {0}")]
40 UnrecognisedVmAvailabilityStatus(i32),
41 #[error("Unrecognised FF-A Warm Boot Type {0}")]
42 UnrecognisedWarmBootType(u32),
43 #[error("Invalid version {0}")]
44 InvalidVersion(u32),
Tomás González0a058bc2025-03-11 11:20:55 +000045 #[error("Invalid Information Tag {0}")]
46 InvalidInformationTag(u16),
Tomás González7ffb6132025-04-03 12:28:58 +010047 #[error("Invalid Flag for Notification Set")]
48 InvalidNotificationSetFlag(u32),
49 #[error("Invalid Vm ID")]
50 InvalidVmId(u32),
Imre Kise295adb2025-04-10 13:26:28 +020051 #[error("Invalid FF-A Partition Info Get Flag {0}")]
52 InvalidPartitionInfoGetFlag(u32),
Imre Kis839eaef2025-04-11 17:38:36 +020053 #[error("Invalid success argument variant")]
54 InvalidSuccessArgsVariant,
Imre Kis787c5002025-04-10 14:25:51 +020055 #[error("Invalid notification count")]
56 InvalidNotificationCount,
Imre Kis92b663e2025-04-10 14:15:05 +020057 #[error("Invalid Partition Info Get Regs response")]
58 InvalidPartitionInfoGetRegsResponse,
Balint Dobszay82c71dd2025-04-15 10:16:44 +020059 #[error("Invalid FF-A version {0} for function ID {1:?}")]
60 InvalidVersionForFunctionId(Version, FuncId),
Imre Kis189f18c2025-05-26 19:33:05 +020061 #[error("Invalid character count {0}")]
62 InvalidCharacterCount(u8),
Imre Kisdcb7df22025-06-06 15:24:40 +020063 #[error("Memory management error")]
64 MemoryManagementError(#[from] memory_management::Error),
Balint Dobszay3aad9572025-01-17 16:54:11 +010065}
66
67impl From<Error> for FfaError {
68 fn from(value: Error) -> Self {
69 match value {
Balint Dobszay82c71dd2025-04-15 10:16:44 +020070 Error::UnrecognisedFunctionId(_)
71 | Error::UnrecognisedFeatureId(_)
72 | Error::InvalidVersionForFunctionId(..) => Self::NotSupported,
Tomás González0a058bc2025-03-11 11:20:55 +000073 Error::InvalidInformationTag(_) => Self::Retry,
Tomás González4d5b0ba2025-03-03 17:15:55 +000074 Error::UnrecognisedErrorCode(_)
75 | Error::UnrecognisedFwkMsg(_)
76 | Error::InvalidVersion(_)
Tomás González092202a2025-03-05 11:56:45 +000077 | Error::InvalidMsgWaitFlag(_)
Tomás González4d5b0ba2025-03-03 17:15:55 +000078 | Error::UnrecognisedVmAvailabilityStatus(_)
Tomás González7ffb6132025-04-03 12:28:58 +010079 | Error::InvalidNotificationSetFlag(_)
80 | Error::InvalidVmId(_)
Imre Kise295adb2025-04-10 13:26:28 +020081 | Error::UnrecognisedWarmBootType(_)
Imre Kis839eaef2025-04-11 17:38:36 +020082 | Error::InvalidPartitionInfoGetFlag(_)
Imre Kis787c5002025-04-10 14:25:51 +020083 | Error::InvalidSuccessArgsVariant
Imre Kis92b663e2025-04-10 14:15:05 +020084 | Error::InvalidNotificationCount
Imre Kis189f18c2025-05-26 19:33:05 +020085 | Error::InvalidPartitionInfoGetRegsResponse
Imre Kisdcb7df22025-06-06 15:24:40 +020086 | Error::InvalidCharacterCount(_)
87 | Error::MemoryManagementError(_) => Self::InvalidParameters,
Balint Dobszay3aad9572025-01-17 16:54:11 +010088 }
89 }
90}
Balint Dobszay5bf492f2024-07-29 17:21:32 +020091
Balint Dobszaya5846852025-02-26 15:38:53 +010092/// 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 +020093#[derive(PartialEq, Clone, Copy)]
94pub enum Instance {
Balint Dobszaya5846852025-02-26 15:38:53 +010095 /// The instance between the SPMC and SPMD.
Balint Dobszay5bf492f2024-07-29 17:21:32 +020096 SecurePhysical,
Balint Dobszaya5846852025-02-26 15:38:53 +010097 /// The instance between the SPMC and a physical SP (contains the SP's endpoint ID).
Balint Dobszay5bf492f2024-07-29 17:21:32 +020098 SecureVirtual(u16),
99}
100
Balint Dobszaya5846852025-02-26 15:38:53 +0100101/// Function IDs of the various FF-A interfaces.
Andrew Walbran969b9252024-11-25 15:35:42 +0000102#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
Balint Dobszay3aad9572025-01-17 16:54:11 +0100103#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedFunctionId))]
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200104#[repr(u32)]
105pub enum FuncId {
106 Error = 0x84000060,
107 Success32 = 0x84000061,
108 Success64 = 0xc4000061,
109 Interrupt = 0x84000062,
110 Version = 0x84000063,
111 Features = 0x84000064,
112 RxAcquire = 0x84000084,
113 RxRelease = 0x84000065,
114 RxTxMap32 = 0x84000066,
115 RxTxMap64 = 0xc4000066,
116 RxTxUnmap = 0x84000067,
117 PartitionInfoGet = 0x84000068,
Balint Dobszaye6aa4862025-02-28 16:37:56 +0100118 PartitionInfoGetRegs = 0xc400008b,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200119 IdGet = 0x84000069,
120 SpmIdGet = 0x84000085,
Balint Dobszaye6aa4862025-02-28 16:37:56 +0100121 ConsoleLog32 = 0x8400008a,
122 ConsoleLog64 = 0xc400008a,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200123 MsgWait = 0x8400006b,
124 Yield = 0x8400006c,
125 Run = 0x8400006d,
126 NormalWorldResume = 0x8400007c,
127 MsgSend2 = 0x84000086,
128 MsgSendDirectReq32 = 0x8400006f,
129 MsgSendDirectReq64 = 0xc400006f,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100130 MsgSendDirectReq64_2 = 0xc400008d,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200131 MsgSendDirectResp32 = 0x84000070,
132 MsgSendDirectResp64 = 0xc4000070,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100133 MsgSendDirectResp64_2 = 0xc400008e,
Balint Dobszaye6aa4862025-02-28 16:37:56 +0100134 NotificationBitmapCreate = 0x8400007d,
135 NotificationBitmapDestroy = 0x8400007e,
136 NotificationBind = 0x8400007f,
137 NotificationUnbind = 0x84000080,
138 NotificationSet = 0x84000081,
139 NotificationGet = 0x84000082,
140 NotificationInfoGet32 = 0x84000083,
141 NotificationInfoGet64 = 0xc4000083,
142 El3IntrHandle = 0x8400008c,
Tomás González17b92442025-03-10 16:45:04 +0000143 SecondaryEpRegister32 = 0x84000087,
144 SecondaryEpRegister64 = 0xc4000087,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200145 MemDonate32 = 0x84000071,
146 MemDonate64 = 0xc4000071,
147 MemLend32 = 0x84000072,
148 MemLend64 = 0xc4000072,
149 MemShare32 = 0x84000073,
150 MemShare64 = 0xc4000073,
151 MemRetrieveReq32 = 0x84000074,
152 MemRetrieveReq64 = 0xc4000074,
153 MemRetrieveResp = 0x84000075,
154 MemRelinquish = 0x84000076,
155 MemReclaim = 0x84000077,
156 MemPermGet32 = 0x84000088,
157 MemPermGet64 = 0xc4000088,
158 MemPermSet32 = 0x84000089,
159 MemPermSet64 = 0xc4000089,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200160}
161
Balint Dobszayde0dc802025-02-28 14:16:52 +0100162impl FuncId {
163 /// Returns true if this is a 32-bit call, or false if it is a 64-bit call.
164 pub fn is_32bit(&self) -> bool {
Tomás González6ccba0a2025-04-09 13:31:29 +0100165 u32::from(*self) & (1 << 30) == 0
Balint Dobszayde0dc802025-02-28 14:16:52 +0100166 }
Balint Dobszay82c71dd2025-04-15 10:16:44 +0200167
168 /// Returns the FF-A version that has introduced the function ID.
169 pub fn minimum_ffa_version(&self) -> Version {
170 match self {
171 FuncId::Error
172 | FuncId::Success32
173 | FuncId::Success64
174 | FuncId::Interrupt
175 | FuncId::Version
176 | FuncId::Features
177 | FuncId::RxRelease
178 | FuncId::RxTxMap32
179 | FuncId::RxTxMap64
180 | FuncId::RxTxUnmap
181 | FuncId::PartitionInfoGet
182 | FuncId::IdGet
183 | FuncId::MsgWait
184 | FuncId::Yield
185 | FuncId::Run
186 | FuncId::NormalWorldResume
187 | FuncId::MsgSendDirectReq32
188 | FuncId::MsgSendDirectReq64
189 | FuncId::MsgSendDirectResp32
190 | FuncId::MsgSendDirectResp64
191 | FuncId::MemDonate32
192 | FuncId::MemDonate64
193 | FuncId::MemLend32
194 | FuncId::MemLend64
195 | FuncId::MemShare32
196 | FuncId::MemShare64
197 | FuncId::MemRetrieveReq32
198 | FuncId::MemRetrieveReq64
199 | FuncId::MemRetrieveResp
200 | FuncId::MemRelinquish
201 | FuncId::MemReclaim => Version(1, 0),
202
203 FuncId::RxAcquire
204 | FuncId::SpmIdGet
205 | FuncId::MsgSend2
206 | FuncId::MemPermGet32
207 | FuncId::MemPermGet64
208 | FuncId::MemPermSet32
209 | FuncId::MemPermSet64
210 | FuncId::NotificationBitmapCreate
211 | FuncId::NotificationBitmapDestroy
212 | FuncId::NotificationBind
213 | FuncId::NotificationUnbind
214 | FuncId::NotificationSet
215 | FuncId::NotificationGet
216 | FuncId::NotificationInfoGet32
217 | FuncId::NotificationInfoGet64
218 | FuncId::SecondaryEpRegister32
219 | FuncId::SecondaryEpRegister64 => Version(1, 1),
220
221 FuncId::PartitionInfoGetRegs
222 | FuncId::ConsoleLog32
223 | FuncId::ConsoleLog64
224 | FuncId::MsgSendDirectReq64_2
225 | FuncId::MsgSendDirectResp64_2
226 | FuncId::El3IntrHandle => Version(1, 2),
227 }
228 }
Balint Dobszayde0dc802025-02-28 14:16:52 +0100229}
230
Balint Dobszaya5846852025-02-26 15:38:53 +0100231/// Error status codes used by the `FFA_ERROR` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100232#[derive(Clone, Copy, Debug, Eq, Error, IntoPrimitive, PartialEq, TryFromPrimitive)]
233#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedErrorCode))]
234#[repr(i32)]
235pub enum FfaError {
236 #[error("Not supported")]
237 NotSupported = -1,
238 #[error("Invalid parameters")]
239 InvalidParameters = -2,
240 #[error("No memory")]
241 NoMemory = -3,
242 #[error("Busy")]
243 Busy = -4,
244 #[error("Interrupted")]
245 Interrupted = -5,
246 #[error("Denied")]
247 Denied = -6,
248 #[error("Retry")]
249 Retry = -7,
250 #[error("Aborted")]
251 Aborted = -8,
252 #[error("No data")]
253 NoData = -9,
254}
255
Balint Dobszaya5846852025-02-26 15:38:53 +0100256/// Endpoint ID and vCPU ID pair, used by `FFA_ERROR`, `FFA_INTERRUPT` and `FFA_RUN` interfaces.
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200257#[derive(Debug, Eq, PartialEq, Clone, Copy)]
Balint Dobszay3aad9572025-01-17 16:54:11 +0100258pub struct TargetInfo {
259 pub endpoint_id: u16,
260 pub vcpu_id: u16,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200261}
262
Balint Dobszay3aad9572025-01-17 16:54:11 +0100263impl From<u32> for TargetInfo {
264 fn from(value: u32) -> Self {
265 Self {
266 endpoint_id: (value >> 16) as u16,
267 vcpu_id: value as u16,
268 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200269 }
270}
271
Balint Dobszay3aad9572025-01-17 16:54:11 +0100272impl From<TargetInfo> for u32 {
273 fn from(value: TargetInfo) -> Self {
Balint Dobszaye9a3e762025-02-26 17:29:57 +0100274 ((value.endpoint_id as u32) << 16) | value.vcpu_id as u32
Andrew Walbran0d315812024-11-25 15:36:36 +0000275 }
Balint Dobszay3aad9572025-01-17 16:54:11 +0100276}
Andrew Walbran0d315812024-11-25 15:36:36 +0000277
Imre Kis839eaef2025-04-11 17:38:36 +0200278/// Generic arguments of the `FFA_SUCCESS` interface. The interpretation of the arguments depends on
279/// the interface that initiated the request. The application code has knowledge of the request, so
280/// it has to convert `SuccessArgs` into/from a specific success args structure that matches the
281/// request.
Imre Kis4e9d8bc2025-04-10 13:48:26 +0200282///
283/// The current specialized success arguments types are:
284/// * `FFA_FEATURES` - [`SuccessArgsFeatures`]
Imre Kisbbef2872025-04-10 14:11:29 +0200285/// * `FFA_ID_GET` - [`SuccessArgsIdGet`]
286/// * `FFA_SPM_ID_GET` - [`SuccessArgsSpmIdGet`]
Imre Kis61c34092025-04-10 14:14:38 +0200287/// * `FFA_PARTITION_INFO_GET` - [`partition_info::SuccessArgsPartitionInfoGet`]
Imre Kis92b663e2025-04-10 14:15:05 +0200288/// * `FFA_PARTITION_INFO_GET_REGS` - [`partition_info::SuccessArgsPartitionInfoGetRegs`]
Imre Kis9959e062025-04-10 14:16:10 +0200289/// * `FFA_NOTIFICATION_GET` - [`SuccessArgsNotificationGet`]
Imre Kis787c5002025-04-10 14:25:51 +0200290/// * `FFA_NOTIFICATION_INFO_GET_32` - [`SuccessArgsNotificationInfoGet32`]
291/// * `FFA_NOTIFICATION_INFO_GET_64` - [`SuccessArgsNotificationInfoGet64`]
Balint Dobszay3aad9572025-01-17 16:54:11 +0100292#[derive(Debug, Eq, PartialEq, Clone, Copy)]
293pub enum SuccessArgs {
Imre Kis54773b62025-04-10 13:47:39 +0200294 Args32([u32; 6]),
295 Args64([u64; 6]),
296 Args64_2([u64; 16]),
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200297}
298
Imre Kis839eaef2025-04-11 17:38:36 +0200299impl SuccessArgs {
300 fn try_get_args32(self) -> Result<[u32; 6], Error> {
301 match self {
302 SuccessArgs::Args32(args) => Ok(args),
303 SuccessArgs::Args64(_) | SuccessArgs::Args64_2(_) => {
304 Err(Error::InvalidSuccessArgsVariant)
305 }
306 }
307 }
308
309 fn try_get_args64(self) -> Result<[u64; 6], Error> {
310 match self {
311 SuccessArgs::Args64(args) => Ok(args),
312 SuccessArgs::Args32(_) | SuccessArgs::Args64_2(_) => {
313 Err(Error::InvalidSuccessArgsVariant)
314 }
315 }
316 }
317
318 fn try_get_args64_2(self) -> Result<[u64; 16], Error> {
319 match self {
320 SuccessArgs::Args64_2(args) => Ok(args),
321 SuccessArgs::Args32(_) | SuccessArgs::Args64(_) => {
322 Err(Error::InvalidSuccessArgsVariant)
323 }
324 }
325 }
326}
327
Tomás González17b92442025-03-10 16:45:04 +0000328/// Entrypoint address argument for `FFA_SECONDARY_EP_REGISTER` interface.
329#[derive(Debug, Eq, PartialEq, Clone, Copy)]
330pub enum SecondaryEpRegisterAddr {
331 Addr32(u32),
332 Addr64(u64),
333}
334
Balint Dobszaya5846852025-02-26 15:38:53 +0100335/// Version number of the FF-A implementation, `.0` is the major, `.1` is minor the version.
Balint Dobszayde0dc802025-02-28 14:16:52 +0100336#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord)]
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200337pub struct Version(pub u16, pub u16);
338
Tomás González1f794352025-03-03 16:47:06 +0000339impl Version {
Tomás González83146af2025-03-04 11:32:41 +0000340 // The FF-A spec mandates that bit[31] of a version number must be 0
341 const MBZ_BITS: u32 = 1 << 31;
342
Tomás González1f794352025-03-03 16:47:06 +0000343 /// Returns whether the caller's version (self) is compatible with the callee's version (input
344 /// parameter)
345 pub fn is_compatible_to(&self, callee_version: &Version) -> bool {
346 self.0 == callee_version.0 && self.1 <= callee_version.1
347 }
348}
349
Tomás González83146af2025-03-04 11:32:41 +0000350impl TryFrom<u32> for Version {
351 type Error = Error;
352
353 fn try_from(val: u32) -> Result<Self, Self::Error> {
354 if (val & Self::MBZ_BITS) != 0 {
355 Err(Error::InvalidVersion(val))
356 } else {
357 Ok(Self((val >> 16) as u16, val as u16))
358 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200359 }
360}
361
362impl From<Version> for u32 {
363 fn from(v: Version) -> Self {
Tomás González83146af2025-03-04 11:32:41 +0000364 let v_u32 = ((v.0 as u32) << 16) | v.1 as u32;
365 assert!(v_u32 & Version::MBZ_BITS == 0);
366 v_u32
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200367 }
368}
369
Andrew Walbran19970ba2024-11-25 15:35:00 +0000370impl Display for Version {
371 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
372 write!(f, "{}.{}", self.0, self.1)
373 }
374}
375
376impl Debug for Version {
377 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
378 Display::fmt(self, f)
379 }
380}
381
Balint Dobszaya5846852025-02-26 15:38:53 +0100382/// Feature IDs used by the `FFA_FEATURES` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100383#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
384#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedFeatureId))]
385#[repr(u8)]
386pub enum FeatureId {
387 NotificationPendingInterrupt = 0x1,
388 ScheduleReceiverInterrupt = 0x2,
389 ManagedExitInterrupt = 0x3,
390}
Balint Dobszayc8802492025-01-15 18:11:39 +0100391
Balint Dobszaya5846852025-02-26 15:38:53 +0100392/// Arguments for the `FFA_FEATURES` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100393#[derive(Debug, Eq, PartialEq, Clone, Copy)]
394pub enum Feature {
395 FuncId(FuncId),
396 FeatureId(FeatureId),
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100397 Unknown(u32),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100398}
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200399
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100400impl From<u32> for Feature {
401 fn from(value: u32) -> Self {
402 // Bit[31] is set for all valid FF-A function IDs so we don't have to check it separately
403 if let Ok(func_id) = value.try_into() {
404 Self::FuncId(func_id)
405 } else if let Ok(feat_id) = (value as u8).try_into() {
406 Self::FeatureId(feat_id)
Balint Dobszay3aad9572025-01-17 16:54:11 +0100407 } else {
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100408 Self::Unknown(value)
409 }
Balint Dobszay3aad9572025-01-17 16:54:11 +0100410 }
411}
412
413impl From<Feature> for u32 {
414 fn from(value: Feature) -> Self {
415 match value {
416 Feature::FuncId(func_id) => (1 << 31) | func_id as u32,
417 Feature::FeatureId(feature_id) => feature_id as u32,
Imre Kis29c8ace2025-04-11 13:49:58 +0200418 Feature::Unknown(id) => id,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100419 }
420 }
421}
422
Imre Kis4e9d8bc2025-04-10 13:48:26 +0200423/// `FFA_FEATURES` specific success argument structure. This type needs further specialization based
424/// on 'FF-A function ID or Feature ID' field of the preceeding `FFA_FEATURES` request.
425#[derive(Debug, Eq, PartialEq, Clone, Copy)]
426pub struct SuccessArgsFeatures {
427 pub properties: [u32; 2],
428}
429
430impl From<SuccessArgsFeatures> for SuccessArgs {
431 fn from(value: SuccessArgsFeatures) -> Self {
432 Self::Args32([value.properties[0], value.properties[1], 0, 0, 0, 0])
433 }
434}
435
436impl TryFrom<SuccessArgs> for SuccessArgsFeatures {
437 type Error = Error;
438
439 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
440 let args = value.try_get_args32()?;
441
442 Ok(Self {
443 properties: [args[0], args[1]],
444 })
445 }
446}
447
Balint Dobszaya5846852025-02-26 15:38:53 +0100448/// RXTX buffer descriptor, used by `FFA_RXTX_MAP`.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100449#[derive(Debug, Eq, PartialEq, Clone, Copy)]
450pub enum RxTxAddr {
451 Addr32 { rx: u32, tx: u32 },
452 Addr64 { rx: u64, tx: u64 },
453}
454
Imre Kisbbef2872025-04-10 14:11:29 +0200455/// `FFA_ID_GET` specific success argument structure.
456#[derive(Debug, Eq, PartialEq, Clone, Copy)]
457pub struct SuccessArgsIdGet {
458 pub id: u16,
459}
460
461impl From<SuccessArgsIdGet> for SuccessArgs {
462 fn from(value: SuccessArgsIdGet) -> Self {
463 SuccessArgs::Args32([value.id as u32, 0, 0, 0, 0, 0])
464 }
465}
466
467impl TryFrom<SuccessArgs> for SuccessArgsIdGet {
468 type Error = Error;
469
470 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
471 let args = value.try_get_args32()?;
472 Ok(Self { id: args[0] as u16 })
473 }
474}
475
476/// `FFA_SPM_ID_GET` specific success argument structure.
477#[derive(Debug, Eq, PartialEq, Clone, Copy)]
478pub struct SuccessArgsSpmIdGet {
479 pub id: u16,
480}
481
482impl From<SuccessArgsSpmIdGet> for SuccessArgs {
483 fn from(value: SuccessArgsSpmIdGet) -> Self {
484 SuccessArgs::Args32([value.id as u32, 0, 0, 0, 0, 0])
485 }
486}
487
488impl TryFrom<SuccessArgs> for SuccessArgsSpmIdGet {
489 type Error = Error;
490
491 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
492 let args = value.try_get_args32()?;
493 Ok(Self { id: args[0] as u16 })
494 }
495}
496
Imre Kise295adb2025-04-10 13:26:28 +0200497/// Flags of the `FFA_PARTITION_INFO_GET` interface.
498#[derive(Debug, Eq, PartialEq, Clone, Copy)]
499pub struct PartitionInfoGetFlags {
500 pub count_only: bool,
501}
502
503impl PartitionInfoGetFlags {
504 const RETURN_INFORMATION_TYPE_FLAG: u32 = 1 << 0;
505 const MBZ_BITS: u32 = 0xffff_fffe;
506}
507
508impl TryFrom<u32> for PartitionInfoGetFlags {
509 type Error = Error;
510
511 fn try_from(val: u32) -> Result<Self, Self::Error> {
512 if (val & Self::MBZ_BITS) != 0 {
513 Err(Error::InvalidPartitionInfoGetFlag(val))
514 } else {
515 Ok(Self {
516 count_only: val & Self::RETURN_INFORMATION_TYPE_FLAG != 0,
517 })
518 }
519 }
520}
521
522impl From<PartitionInfoGetFlags> for u32 {
523 fn from(flags: PartitionInfoGetFlags) -> Self {
524 let mut bits: u32 = 0;
525 if flags.count_only {
526 bits |= PartitionInfoGetFlags::RETURN_INFORMATION_TYPE_FLAG;
527 }
528 bits
529 }
530}
531
Tomás González4d5b0ba2025-03-03 17:15:55 +0000532/// Composite type for capturing success and error return codes for the VM availability messages.
533///
534/// Error codes are handled by the `FfaError` type. Having a separate type for errors helps using
535/// `Result<(), FfaError>`. If a single type would include both success and error values,
536/// then `Err(FfaError::Success)` would be incomprehensible.
537#[derive(Debug, Eq, PartialEq, Clone, Copy)]
538pub enum VmAvailabilityStatus {
539 Success,
540 Error(FfaError),
541}
542
543impl TryFrom<i32> for VmAvailabilityStatus {
544 type Error = Error;
545 fn try_from(value: i32) -> Result<Self, <Self as TryFrom<i32>>::Error> {
546 Ok(match value {
547 0 => Self::Success,
548 error_code => Self::Error(FfaError::try_from(error_code)?),
549 })
550 }
551}
552
553impl From<VmAvailabilityStatus> for i32 {
554 fn from(value: VmAvailabilityStatus) -> Self {
555 match value {
556 VmAvailabilityStatus::Success => 0,
557 VmAvailabilityStatus::Error(error_code) => error_code.into(),
558 }
559 }
560}
561
562/// Arguments for the Power Warm Boot `FFA_MSG_SEND_DIRECT_REQ` interface.
563#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
564#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedWarmBootType))]
565#[repr(u32)]
566pub enum WarmBootType {
567 ExitFromSuspend = 0,
568 ExitFromLowPower = 1,
569}
570
Balint Dobszaya5846852025-02-26 15:38:53 +0100571/// Arguments for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}` interfaces.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100572#[derive(Debug, Eq, PartialEq, Clone, Copy)]
573pub enum DirectMsgArgs {
574 Args32([u32; 5]),
575 Args64([u64; 5]),
Tomás González4d5b0ba2025-03-03 17:15:55 +0000576 /// Message for forwarding FFA_VERSION call from Normal world to the SPMC
577 VersionReq {
578 version: Version,
579 },
580 /// Response message to forwarded FFA_VERSION call from the Normal world
581 /// Contains the version returned by the SPMC or None
582 VersionResp {
583 version: Option<Version>,
584 },
585 /// Message for a power management operation initiated by a PSCI function
586 PowerPsciReq32 {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000587 // params[i]: Input parameter in w[i] in PSCI function invocation at EL3.
Tomás González67f92c72025-03-20 16:50:42 +0000588 // params[0]: Function ID.
589 params: [u32; 4],
Tomás González4d5b0ba2025-03-03 17:15:55 +0000590 },
591 /// Message for a power management operation initiated by a PSCI function
592 PowerPsciReq64 {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000593 // params[i]: Input parameter in x[i] in PSCI function invocation at EL3.
Tomás González67f92c72025-03-20 16:50:42 +0000594 // params[0]: Function ID.
595 params: [u64; 4],
Tomás González4d5b0ba2025-03-03 17:15:55 +0000596 },
597 /// Message for a warm boot
598 PowerWarmBootReq {
599 boot_type: WarmBootType,
600 },
601 /// Response message to indicate return status of the last power management request message
602 /// Return error code SUCCESS or DENIED as defined in PSCI spec. Caller is left to do the
603 /// parsing of the return status.
604 PowerPsciResp {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000605 psci_status: i32,
606 },
607 /// Message to signal creation of a VM
608 VmCreated {
609 // Globally unique Handle to identify a memory region that contains IMPLEMENTATION DEFINED
610 // information associated with the created VM.
611 // The invalid memory region handle must be specified by the Hypervisor if this field is not
612 // used.
613 handle: memory_management::Handle,
614 vm_id: u16,
615 },
616 /// Message to acknowledge creation of a VM
617 VmCreatedAck {
618 sp_status: VmAvailabilityStatus,
619 },
620 /// Message to signal destruction of a VM
621 VmDestructed {
622 // Globally unique Handle to identify a memory region that contains IMPLEMENTATION DEFINED
623 // information associated with the created VM.
624 // The invalid memory region handle must be specified by the Hypervisor if this field is not
625 // used.
626 handle: memory_management::Handle,
627 vm_id: u16,
628 },
629 /// Message to acknowledge destruction of a VM
630 VmDestructedAck {
631 sp_status: VmAvailabilityStatus,
632 },
633}
634
635impl DirectMsgArgs {
636 // Flags for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}` interfaces.
637
638 const FWK_MSG_BITS: u32 = 1 << 31;
639 const VERSION_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b1000;
640 const VERSION_RESP: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b1001;
641 const POWER_PSCI_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS;
642 const POWER_WARM_BOOT_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0001;
643 const POWER_PSCI_RESP: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0010;
644 const VM_CREATED: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0100;
645 const VM_CREATED_ACK: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0101;
646 const VM_DESTRUCTED: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0110;
647 const VM_DESTRUCTED_ACK: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0111;
Balint Dobszay3aad9572025-01-17 16:54:11 +0100648}
649
Balint Dobszayde0dc802025-02-28 14:16:52 +0100650/// Arguments for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}2` interfaces.
651#[derive(Debug, Eq, PartialEq, Clone, Copy)]
Imre Kisc739e0e2025-05-30 11:49:25 +0200652pub struct DirectMsg2Args(pub [u64; 14]);
Balint Dobszayde0dc802025-02-28 14:16:52 +0100653
Tomás González092202a2025-03-05 11:56:45 +0000654#[derive(Debug, Eq, PartialEq, Clone, Copy)]
655pub struct MsgWaitFlags {
Imre Kisc739e0e2025-05-30 11:49:25 +0200656 pub retain_rx_buffer: bool,
Tomás González092202a2025-03-05 11:56:45 +0000657}
658
659impl MsgWaitFlags {
660 const RETAIN_RX_BUFFER: u32 = 0x01;
661 const MBZ_BITS: u32 = 0xfffe;
662}
663
664impl TryFrom<u32> for MsgWaitFlags {
665 type Error = Error;
666
667 fn try_from(val: u32) -> Result<Self, Self::Error> {
668 if (val & Self::MBZ_BITS) != 0 {
669 Err(Error::InvalidMsgWaitFlag(val))
670 } else {
671 Ok(MsgWaitFlags {
672 retain_rx_buffer: val & Self::RETAIN_RX_BUFFER != 0,
673 })
674 }
675 }
676}
677
678impl From<MsgWaitFlags> for u32 {
679 fn from(flags: MsgWaitFlags) -> Self {
680 let mut bits: u32 = 0;
681 if flags.retain_rx_buffer {
682 bits |= MsgWaitFlags::RETAIN_RX_BUFFER;
683 }
684 bits
685 }
686}
687
Balint Dobszaya5846852025-02-26 15:38:53 +0100688/// Descriptor for a dynamically allocated memory buffer that contains the memory transaction
Tomás Gonzálezf268e322025-03-05 11:18:11 +0000689/// descriptor.
690///
691/// Used by `FFA_MEM_{DONATE,LEND,SHARE,RETRIEVE_REQ}` interfaces, only when the TX buffer is not
692/// used to transmit the transaction descriptor.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100693#[derive(Debug, Eq, PartialEq, Clone, Copy)]
694pub enum MemOpBuf {
695 Buf32 { addr: u32, page_cnt: u32 },
696 Buf64 { addr: u64, page_cnt: u32 },
697}
698
Balint Dobszaya5846852025-02-26 15:38:53 +0100699/// Memory address argument for `FFA_MEM_PERM_{GET,SET}` interfaces.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100700#[derive(Debug, Eq, PartialEq, Clone, Copy)]
701pub enum MemAddr {
702 Addr32(u32),
703 Addr64(u64),
704}
705
Balint Dobszayde0dc802025-02-28 14:16:52 +0100706/// Argument for the `FFA_CONSOLE_LOG` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100707#[derive(Debug, Eq, PartialEq, Clone, Copy)]
708pub enum ConsoleLogChars {
Imre Kis189f18c2025-05-26 19:33:05 +0200709 Chars32(ConsoleLogChars32),
710 Chars64(ConsoleLogChars64),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100711}
712
Imre Kis189f18c2025-05-26 19:33:05 +0200713/// Generic type for storing `FFA_CONSOLE_LOG` character payload and its length in bytes.
714#[derive(Debug, Default, Eq, PartialEq, Clone, Copy)]
715pub struct LogChars<T>
716where
717 T: IntoBytes + FromBytes + Immutable,
718{
719 char_cnt: u8,
720 char_lists: T,
721}
722
723impl<T> LogChars<T>
724where
725 T: IntoBytes + FromBytes + Immutable,
726{
727 const MAX_LENGTH: u8 = core::mem::size_of::<T>() as u8;
728
729 /// Returns true if there are no characters in the structure.
730 pub fn empty(&self) -> bool {
731 self.char_cnt == 0
732 }
733
734 /// Returns true if the structure is full.
735 pub fn full(&self) -> bool {
736 self.char_cnt as usize >= core::mem::size_of::<T>()
737 }
738
739 /// Returns the payload bytes.
740 pub fn bytes(&self) -> &[u8] {
741 &self.char_lists.as_bytes()[..self.char_cnt as usize]
742 }
743
744 /// Append byte slice to the end of the characters.
745 pub fn push(&mut self, source: &[u8]) -> usize {
746 let empty_area = &mut self.char_lists.as_mut_bytes()[self.char_cnt.into()..];
747 let len = empty_area.len().min(source.len());
748
749 empty_area[..len].copy_from_slice(&source[..len]);
750 self.char_cnt += len as u8;
751
752 len
753 }
754}
755
756/// Specialized type for 32-bit `FFA_CONSOLE_LOG` payload.
757pub type ConsoleLogChars32 = LogChars<[u32; 6]>;
758
759/// Specialized type for 64-bit `FFA_CONSOLE_LOG` payload.
760pub type ConsoleLogChars64 = LogChars<[u64; 16]>;
761
Tomás González7ffb6132025-04-03 12:28:58 +0100762#[derive(Debug, Eq, PartialEq, Clone, Copy)]
763pub struct NotificationBindFlags {
Imre Kisc739e0e2025-05-30 11:49:25 +0200764 pub per_vcpu_notification: bool,
Tomás González7ffb6132025-04-03 12:28:58 +0100765}
766
767impl NotificationBindFlags {
768 const PER_VCPU_NOTIFICATION: u32 = 1;
769}
770
771impl From<NotificationBindFlags> for u32 {
772 fn from(flags: NotificationBindFlags) -> Self {
773 let mut bits: u32 = 0;
774 if flags.per_vcpu_notification {
775 bits |= NotificationBindFlags::PER_VCPU_NOTIFICATION;
776 }
777 bits
778 }
779}
780
781impl From<u32> for NotificationBindFlags {
782 fn from(flags: u32) -> Self {
783 Self {
784 per_vcpu_notification: flags & Self::PER_VCPU_NOTIFICATION != 0,
785 }
786 }
787}
788
789#[derive(Debug, Eq, PartialEq, Clone, Copy)]
790pub struct NotificationSetFlags {
Imre Kisc739e0e2025-05-30 11:49:25 +0200791 pub delay_schedule_receiver: bool,
792 pub vcpu_id: Option<u16>,
Tomás González7ffb6132025-04-03 12:28:58 +0100793}
794
795impl NotificationSetFlags {
796 const PER_VCP_NOTIFICATION: u32 = 1 << 0;
797 const DELAY_SCHEDULE_RECEIVER: u32 = 1 << 1;
798 const VCPU_ID_SHIFT: u32 = 16;
799
800 const MBZ_BITS: u32 = 0xfffc;
801}
802
803impl From<NotificationSetFlags> for u32 {
804 fn from(flags: NotificationSetFlags) -> Self {
805 let mut bits: u32 = 0;
806
807 if flags.delay_schedule_receiver {
808 bits |= NotificationSetFlags::DELAY_SCHEDULE_RECEIVER;
809 }
810 if let Some(vcpu_id) = flags.vcpu_id {
811 bits |= NotificationSetFlags::PER_VCP_NOTIFICATION;
812 bits |= u32::from(vcpu_id) << NotificationSetFlags::VCPU_ID_SHIFT;
813 }
814
815 bits
816 }
817}
818
819impl TryFrom<u32> for NotificationSetFlags {
820 type Error = Error;
821
822 fn try_from(flags: u32) -> Result<Self, Self::Error> {
823 if (flags & Self::MBZ_BITS) != 0 {
824 return Err(Error::InvalidNotificationSetFlag(flags));
825 }
826
827 let tentative_vcpu_id = (flags >> Self::VCPU_ID_SHIFT) as u16;
828
829 let vcpu_id = if (flags & Self::PER_VCP_NOTIFICATION) != 0 {
830 Some(tentative_vcpu_id)
831 } else {
832 if tentative_vcpu_id != 0 {
833 return Err(Error::InvalidNotificationSetFlag(flags));
834 }
835 None
836 };
837
838 Ok(Self {
839 delay_schedule_receiver: (flags & Self::DELAY_SCHEDULE_RECEIVER) != 0,
840 vcpu_id,
841 })
842 }
843}
844
845#[derive(Debug, Eq, PartialEq, Clone, Copy)]
846pub struct NotificationGetFlags {
Imre Kisc739e0e2025-05-30 11:49:25 +0200847 pub sp_bitmap_id: bool,
848 pub vm_bitmap_id: bool,
849 pub spm_bitmap_id: bool,
850 pub hyp_bitmap_id: bool,
Tomás González7ffb6132025-04-03 12:28:58 +0100851}
852
853impl NotificationGetFlags {
854 const SP_BITMAP_ID: u32 = 1;
855 const VM_BITMAP_ID: u32 = 1 << 1;
856 const SPM_BITMAP_ID: u32 = 1 << 2;
857 const HYP_BITMAP_ID: u32 = 1 << 3;
858}
859
860impl From<NotificationGetFlags> for u32 {
861 fn from(flags: NotificationGetFlags) -> Self {
862 let mut bits: u32 = 0;
863 if flags.sp_bitmap_id {
864 bits |= NotificationGetFlags::SP_BITMAP_ID;
865 }
866 if flags.vm_bitmap_id {
867 bits |= NotificationGetFlags::VM_BITMAP_ID;
868 }
869 if flags.spm_bitmap_id {
870 bits |= NotificationGetFlags::SPM_BITMAP_ID;
871 }
872 if flags.hyp_bitmap_id {
873 bits |= NotificationGetFlags::HYP_BITMAP_ID;
874 }
875 bits
876 }
877}
878
879impl From<u32> for NotificationGetFlags {
880 // This is a "from" instead of a "try_from" because Reserved Bits are SBZ, *not* MBZ.
881 fn from(flags: u32) -> Self {
882 Self {
883 sp_bitmap_id: (flags & Self::SP_BITMAP_ID) != 0,
884 vm_bitmap_id: (flags & Self::VM_BITMAP_ID) != 0,
885 spm_bitmap_id: (flags & Self::SPM_BITMAP_ID) != 0,
886 hyp_bitmap_id: (flags & Self::HYP_BITMAP_ID) != 0,
887 }
888 }
889}
890
Imre Kis9959e062025-04-10 14:16:10 +0200891/// `FFA_NOTIFICATION_GET` specific success argument structure.
892#[derive(Debug, Eq, PartialEq, Clone, Copy)]
893pub struct SuccessArgsNotificationGet {
894 pub sp_notifications: Option<u64>,
895 pub vm_notifications: Option<u64>,
896 pub spm_notifications: Option<u32>,
897 pub hypervisor_notifications: Option<u32>,
898}
899
900impl From<SuccessArgsNotificationGet> for SuccessArgs {
901 fn from(value: SuccessArgsNotificationGet) -> Self {
902 let mut args = [0; 6];
903
904 if let Some(bitmap) = value.sp_notifications {
905 args[0] = bitmap as u32;
906 args[1] = (bitmap >> 32) as u32;
907 }
908
909 if let Some(bitmap) = value.vm_notifications {
910 args[2] = bitmap as u32;
911 args[3] = (bitmap >> 32) as u32;
912 }
913
914 if let Some(bitmap) = value.spm_notifications {
915 args[4] = bitmap;
916 }
917
918 if let Some(bitmap) = value.hypervisor_notifications {
919 args[5] = bitmap;
920 }
921
922 Self::Args32(args)
923 }
924}
925
926impl TryFrom<(NotificationGetFlags, SuccessArgs)> for SuccessArgsNotificationGet {
927 type Error = Error;
928
929 fn try_from(value: (NotificationGetFlags, SuccessArgs)) -> Result<Self, Self::Error> {
930 let (flags, value) = value;
931 let args = value.try_get_args32()?;
932
933 let sp_notifications = if flags.sp_bitmap_id {
934 Some(u64::from(args[0]) | (u64::from(args[1]) << 32))
935 } else {
936 None
937 };
938
939 let vm_notifications = if flags.vm_bitmap_id {
940 Some(u64::from(args[2]) | (u64::from(args[3]) << 32))
941 } else {
942 None
943 };
944
945 let spm_notifications = if flags.spm_bitmap_id {
946 Some(args[4])
947 } else {
948 None
949 };
950
951 let hypervisor_notifications = if flags.hyp_bitmap_id {
952 Some(args[5])
953 } else {
954 None
955 };
956
957 Ok(Self {
958 sp_notifications,
959 vm_notifications,
960 spm_notifications,
961 hypervisor_notifications,
962 })
963 }
964}
Imre Kis787c5002025-04-10 14:25:51 +0200965
966/// `FFA_NOTIFICATION_INFO_GET` specific success argument structure. The `MAX_COUNT` parameter
967/// depends on the 32-bit or 64-bit packing.
968#[derive(Debug, Eq, PartialEq, Clone, Copy)]
969pub struct SuccessArgsNotificationInfoGet<const MAX_COUNT: usize> {
970 pub more_pending_notifications: bool,
971 list_count: usize,
972 id_counts: [u8; MAX_COUNT],
973 ids: [u16; MAX_COUNT],
974}
975
976impl<const MAX_COUNT: usize> Default for SuccessArgsNotificationInfoGet<MAX_COUNT> {
977 fn default() -> Self {
978 Self {
979 more_pending_notifications: false,
980 list_count: 0,
981 id_counts: [0; MAX_COUNT],
982 ids: [0; MAX_COUNT],
983 }
984 }
985}
986
987impl<const MAX_COUNT: usize> SuccessArgsNotificationInfoGet<MAX_COUNT> {
988 const MORE_PENDING_NOTIFICATIONS_FLAG: u64 = 1 << 0;
989 const LIST_COUNT_SHIFT: usize = 7;
990 const LIST_COUNT_MASK: u64 = 0x1f;
991 const ID_COUNT_SHIFT: usize = 12;
992 const ID_COUNT_MASK: u64 = 0x03;
993 const ID_COUNT_BITS: usize = 2;
994
995 pub fn add_list(&mut self, endpoint: u16, vcpu_ids: &[u16]) -> Result<(), Error> {
996 if self.list_count >= MAX_COUNT || vcpu_ids.len() > Self::ID_COUNT_MASK as usize {
997 return Err(Error::InvalidNotificationCount);
998 }
999
1000 // Each list contains at least one ID: the partition ID, followed by vCPU IDs. The number
1001 // of vCPU IDs is recorded in `id_counts`.
1002 let mut current_id_index = self.list_count + self.id_counts.iter().sum::<u8>() as usize;
1003 if current_id_index + 1 + vcpu_ids.len() > MAX_COUNT {
1004 // The new list does not fit into the available space for IDs.
1005 return Err(Error::InvalidNotificationCount);
1006 }
1007
1008 self.id_counts[self.list_count] = vcpu_ids.len() as u8;
1009 self.list_count += 1;
1010
1011 // The first ID is the endpoint ID.
1012 self.ids[current_id_index] = endpoint;
1013 current_id_index += 1;
1014
1015 // Insert the vCPU IDs.
1016 self.ids[current_id_index..current_id_index + vcpu_ids.len()].copy_from_slice(vcpu_ids);
1017
1018 Ok(())
1019 }
1020
1021 pub fn iter(&self) -> NotificationInfoGetIterator<'_> {
1022 NotificationInfoGetIterator {
1023 list_index: 0,
1024 id_index: 0,
1025 id_count: &self.id_counts[0..self.list_count],
1026 ids: &self.ids,
1027 }
1028 }
1029
1030 /// Pack flags field and IDs.
1031 fn pack(self) -> (u64, [u16; MAX_COUNT]) {
1032 let mut flags = if self.more_pending_notifications {
1033 Self::MORE_PENDING_NOTIFICATIONS_FLAG
1034 } else {
1035 0
1036 };
1037
1038 flags |= (self.list_count as u64) << Self::LIST_COUNT_SHIFT;
1039 for (count, shift) in self.id_counts.iter().take(self.list_count).zip(
1040 (Self::ID_COUNT_SHIFT..Self::ID_COUNT_SHIFT + Self::ID_COUNT_BITS * MAX_COUNT)
1041 .step_by(Self::ID_COUNT_BITS),
1042 ) {
1043 flags |= u64::from(*count) << shift;
1044 }
1045
1046 (flags, self.ids)
1047 }
1048
1049 /// Unpack flags field and IDs.
1050 fn unpack(flags: u64, ids: [u16; MAX_COUNT]) -> Result<Self, Error> {
1051 let count_of_lists = ((flags >> Self::LIST_COUNT_SHIFT) & Self::LIST_COUNT_MASK) as usize;
1052
1053 if count_of_lists > MAX_COUNT {
1054 return Err(Error::InvalidNotificationCount);
1055 }
1056
1057 let mut count_of_ids = [0; MAX_COUNT];
1058 let mut count_of_ids_bits = flags >> Self::ID_COUNT_SHIFT;
1059
1060 for id in count_of_ids.iter_mut().take(count_of_lists) {
1061 *id = (count_of_ids_bits & Self::ID_COUNT_MASK) as u8;
1062 count_of_ids_bits >>= Self::ID_COUNT_BITS;
1063 }
1064
Imre Kis7846c9f2025-04-15 09:45:00 +02001065 let id_field_count = count_of_lists + count_of_ids.iter().sum::<u8>() as usize;
1066 if id_field_count > MAX_COUNT {
1067 return Err(Error::InvalidNotificationCount);
1068 }
1069
Imre Kis787c5002025-04-10 14:25:51 +02001070 Ok(Self {
1071 more_pending_notifications: (flags & Self::MORE_PENDING_NOTIFICATIONS_FLAG) != 0,
1072 list_count: count_of_lists,
1073 id_counts: count_of_ids,
1074 ids,
1075 })
1076 }
1077}
1078
1079/// `FFA_NOTIFICATION_INFO_GET_32` specific success argument structure.
1080pub type SuccessArgsNotificationInfoGet32 = SuccessArgsNotificationInfoGet<10>;
1081
1082impl From<SuccessArgsNotificationInfoGet32> for SuccessArgs {
1083 fn from(value: SuccessArgsNotificationInfoGet32) -> Self {
1084 let (flags, ids) = value.pack();
1085 let id_regs: [u32; 5] = transmute!(ids);
1086
1087 let mut args = [0; 6];
1088 args[0] = flags as u32;
1089 args[1..6].copy_from_slice(&id_regs);
1090
1091 SuccessArgs::Args32(args)
1092 }
1093}
1094
1095impl TryFrom<SuccessArgs> for SuccessArgsNotificationInfoGet32 {
1096 type Error = Error;
1097
1098 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
1099 let args = value.try_get_args32()?;
1100 let flags = args[0].into();
1101 let id_regs: [u32; 5] = args[1..6].try_into().unwrap();
1102 Self::unpack(flags, transmute!(id_regs))
1103 }
1104}
1105
1106/// `FFA_NOTIFICATION_INFO_GET_64` specific success argument structure.
1107pub type SuccessArgsNotificationInfoGet64 = SuccessArgsNotificationInfoGet<20>;
1108
1109impl From<SuccessArgsNotificationInfoGet64> for SuccessArgs {
1110 fn from(value: SuccessArgsNotificationInfoGet64) -> Self {
1111 let (flags, ids) = value.pack();
1112 let id_regs: [u64; 5] = transmute!(ids);
1113
1114 let mut args = [0; 6];
1115 args[0] = flags;
1116 args[1..6].copy_from_slice(&id_regs);
1117
1118 SuccessArgs::Args64(args)
1119 }
1120}
1121
1122impl TryFrom<SuccessArgs> for SuccessArgsNotificationInfoGet64 {
1123 type Error = Error;
1124
1125 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
1126 let args = value.try_get_args64()?;
1127 let flags = args[0];
1128 let id_regs: [u64; 5] = args[1..6].try_into().unwrap();
1129 Self::unpack(flags, transmute!(id_regs))
1130 }
1131}
1132
1133pub struct NotificationInfoGetIterator<'a> {
1134 list_index: usize,
1135 id_index: usize,
1136 id_count: &'a [u8],
1137 ids: &'a [u16],
1138}
1139
1140impl<'a> Iterator for NotificationInfoGetIterator<'a> {
1141 type Item = (u16, &'a [u16]);
1142
1143 fn next(&mut self) -> Option<Self::Item> {
1144 if self.list_index < self.id_count.len() {
1145 let partition_id = self.ids[self.id_index];
1146 let id_range =
1147 (self.id_index + 1)..=(self.id_index + self.id_count[self.list_index] as usize);
1148
1149 self.id_index += 1 + self.id_count[self.list_index] as usize;
1150 self.list_index += 1;
1151
1152 Some((partition_id, &self.ids[id_range]))
1153 } else {
1154 None
1155 }
1156 }
1157}
1158
Tomás Gonzálezf268e322025-03-05 11:18:11 +00001159/// FF-A "message types", the terminology used by the spec is "interfaces".
1160///
1161/// The interfaces are used by FF-A components for communication at an FF-A instance. The spec also
1162/// describes the valid FF-A instances and conduits for each interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +01001163#[derive(Debug, Eq, PartialEq, Clone, Copy)]
1164pub enum Interface {
1165 Error {
1166 target_info: TargetInfo,
1167 error_code: FfaError,
Balint Dobszayb727aab2025-04-07 10:24:59 +02001168 error_arg: u32,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001169 },
1170 Success {
1171 target_info: u32,
1172 args: SuccessArgs,
1173 },
1174 Interrupt {
1175 target_info: TargetInfo,
1176 interrupt_id: u32,
1177 },
1178 Version {
1179 input_version: Version,
1180 },
1181 VersionOut {
1182 output_version: Version,
1183 },
1184 Features {
1185 feat_id: Feature,
1186 input_properties: u32,
1187 },
1188 RxAcquire {
1189 vm_id: u16,
1190 },
1191 RxRelease {
1192 vm_id: u16,
1193 },
1194 RxTxMap {
1195 addr: RxTxAddr,
1196 page_cnt: u32,
1197 },
1198 RxTxUnmap {
1199 id: u16,
1200 },
1201 PartitionInfoGet {
1202 uuid: Uuid,
Imre Kise295adb2025-04-10 13:26:28 +02001203 flags: PartitionInfoGetFlags,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001204 },
Tomás González0a058bc2025-03-11 11:20:55 +00001205 PartitionInfoGetRegs {
1206 uuid: Uuid,
1207 start_index: u16,
1208 info_tag: u16,
1209 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001210 IdGet,
1211 SpmIdGet,
Tomás González092202a2025-03-05 11:56:45 +00001212 MsgWait {
1213 flags: Option<MsgWaitFlags>,
1214 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001215 Yield,
1216 Run {
1217 target_info: TargetInfo,
1218 },
1219 NormalWorldResume,
Tomás González17b92442025-03-10 16:45:04 +00001220 SecondaryEpRegister {
1221 entrypoint: SecondaryEpRegisterAddr,
1222 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001223 MsgSend2 {
1224 sender_vm_id: u16,
1225 flags: u32,
1226 },
1227 MsgSendDirectReq {
1228 src_id: u16,
1229 dst_id: u16,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001230 args: DirectMsgArgs,
1231 },
1232 MsgSendDirectResp {
1233 src_id: u16,
1234 dst_id: u16,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001235 args: DirectMsgArgs,
1236 },
Balint Dobszayde0dc802025-02-28 14:16:52 +01001237 MsgSendDirectReq2 {
1238 src_id: u16,
1239 dst_id: u16,
1240 uuid: Uuid,
1241 args: DirectMsg2Args,
1242 },
1243 MsgSendDirectResp2 {
1244 src_id: u16,
1245 dst_id: u16,
1246 args: DirectMsg2Args,
1247 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001248 MemDonate {
1249 total_len: u32,
1250 frag_len: u32,
1251 buf: Option<MemOpBuf>,
1252 },
1253 MemLend {
1254 total_len: u32,
1255 frag_len: u32,
1256 buf: Option<MemOpBuf>,
1257 },
1258 MemShare {
1259 total_len: u32,
1260 frag_len: u32,
1261 buf: Option<MemOpBuf>,
1262 },
1263 MemRetrieveReq {
1264 total_len: u32,
1265 frag_len: u32,
1266 buf: Option<MemOpBuf>,
1267 },
1268 MemRetrieveResp {
1269 total_len: u32,
1270 frag_len: u32,
1271 },
1272 MemRelinquish,
1273 MemReclaim {
1274 handle: memory_management::Handle,
1275 flags: u32,
1276 },
1277 MemPermGet {
1278 addr: MemAddr,
Balint Dobszayde0dc802025-02-28 14:16:52 +01001279 page_cnt: Option<u32>,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001280 },
1281 MemPermSet {
1282 addr: MemAddr,
1283 page_cnt: u32,
Imre Kisdcb7df22025-06-06 15:24:40 +02001284 mem_perm: memory_management::MemPermissionsGetSet,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001285 },
1286 ConsoleLog {
Imre Kis189f18c2025-05-26 19:33:05 +02001287 chars: ConsoleLogChars,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001288 },
Tomás González7ffb6132025-04-03 12:28:58 +01001289 NotificationBitmapCreate {
1290 vm_id: u16,
1291 vcpu_cnt: u32,
1292 },
1293 NotificationBitmapDestroy {
1294 vm_id: u16,
1295 },
1296 NotificationBind {
1297 sender_id: u16,
1298 receiver_id: u16,
1299 flags: NotificationBindFlags,
1300 bitmap: u64,
1301 },
Imre Kis3571f2c2025-05-26 19:29:23 +02001302 NotificationUnbind {
Tomás González7ffb6132025-04-03 12:28:58 +01001303 sender_id: u16,
1304 receiver_id: u16,
1305 bitmap: u64,
1306 },
1307 NotificationSet {
1308 sender_id: u16,
1309 receiver_id: u16,
1310 flags: NotificationSetFlags,
1311 bitmap: u64,
1312 },
1313 NotificationGet {
1314 vcpu_id: u16,
1315 endpoint_id: u16,
1316 flags: NotificationGetFlags,
1317 },
1318 NotificationInfoGet {
1319 is_32bit: bool,
1320 },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01001321 El3IntrHandle,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001322}
1323
Balint Dobszayde0dc802025-02-28 14:16:52 +01001324impl Interface {
1325 /// Returns the function ID for the call, if it has one.
1326 pub fn function_id(&self) -> Option<FuncId> {
1327 match self {
1328 Interface::Error { .. } => Some(FuncId::Error),
1329 Interface::Success { args, .. } => match args {
Imre Kis54773b62025-04-10 13:47:39 +02001330 SuccessArgs::Args32(..) => Some(FuncId::Success32),
1331 SuccessArgs::Args64(..) | SuccessArgs::Args64_2(..) => Some(FuncId::Success64),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001332 },
1333 Interface::Interrupt { .. } => Some(FuncId::Interrupt),
1334 Interface::Version { .. } => Some(FuncId::Version),
1335 Interface::VersionOut { .. } => None,
1336 Interface::Features { .. } => Some(FuncId::Features),
1337 Interface::RxAcquire { .. } => Some(FuncId::RxAcquire),
1338 Interface::RxRelease { .. } => Some(FuncId::RxRelease),
1339 Interface::RxTxMap { addr, .. } => match addr {
1340 RxTxAddr::Addr32 { .. } => Some(FuncId::RxTxMap32),
1341 RxTxAddr::Addr64 { .. } => Some(FuncId::RxTxMap64),
1342 },
1343 Interface::RxTxUnmap { .. } => Some(FuncId::RxTxUnmap),
1344 Interface::PartitionInfoGet { .. } => Some(FuncId::PartitionInfoGet),
Tomás González0a058bc2025-03-11 11:20:55 +00001345 Interface::PartitionInfoGetRegs { .. } => Some(FuncId::PartitionInfoGetRegs),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001346 Interface::IdGet => Some(FuncId::IdGet),
1347 Interface::SpmIdGet => Some(FuncId::SpmIdGet),
Tomás González092202a2025-03-05 11:56:45 +00001348 Interface::MsgWait { .. } => Some(FuncId::MsgWait),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001349 Interface::Yield => Some(FuncId::Yield),
1350 Interface::Run { .. } => Some(FuncId::Run),
1351 Interface::NormalWorldResume => Some(FuncId::NormalWorldResume),
Tomás González17b92442025-03-10 16:45:04 +00001352 Interface::SecondaryEpRegister { entrypoint } => match entrypoint {
1353 SecondaryEpRegisterAddr::Addr32 { .. } => Some(FuncId::SecondaryEpRegister32),
1354 SecondaryEpRegisterAddr::Addr64 { .. } => Some(FuncId::SecondaryEpRegister64),
1355 },
Balint Dobszayde0dc802025-02-28 14:16:52 +01001356 Interface::MsgSend2 { .. } => Some(FuncId::MsgSend2),
1357 Interface::MsgSendDirectReq { args, .. } => match args {
1358 DirectMsgArgs::Args32(_) => Some(FuncId::MsgSendDirectReq32),
1359 DirectMsgArgs::Args64(_) => Some(FuncId::MsgSendDirectReq64),
Tomás González4d5b0ba2025-03-03 17:15:55 +00001360 DirectMsgArgs::VersionReq { .. } => Some(FuncId::MsgSendDirectReq32),
1361 DirectMsgArgs::PowerPsciReq32 { .. } => Some(FuncId::MsgSendDirectReq32),
1362 DirectMsgArgs::PowerPsciReq64 { .. } => Some(FuncId::MsgSendDirectReq64),
1363 DirectMsgArgs::PowerWarmBootReq { .. } => Some(FuncId::MsgSendDirectReq32),
1364 DirectMsgArgs::VmCreated { .. } => Some(FuncId::MsgSendDirectReq32),
1365 DirectMsgArgs::VmDestructed { .. } => Some(FuncId::MsgSendDirectReq32),
Balint Dobszay3c1c89a2025-04-25 17:36:46 +02001366 _ => panic!("Invalid direct request arguments: {:#?}", args),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001367 },
1368 Interface::MsgSendDirectResp { args, .. } => match args {
1369 DirectMsgArgs::Args32(_) => Some(FuncId::MsgSendDirectResp32),
1370 DirectMsgArgs::Args64(_) => Some(FuncId::MsgSendDirectResp64),
Tomás González4d5b0ba2025-03-03 17:15:55 +00001371 DirectMsgArgs::VersionResp { .. } => Some(FuncId::MsgSendDirectResp32),
1372 DirectMsgArgs::PowerPsciResp { .. } => Some(FuncId::MsgSendDirectResp32),
1373 DirectMsgArgs::VmCreatedAck { .. } => Some(FuncId::MsgSendDirectResp32),
1374 DirectMsgArgs::VmDestructedAck { .. } => Some(FuncId::MsgSendDirectResp32),
Balint Dobszay3c1c89a2025-04-25 17:36:46 +02001375 _ => panic!("Invalid direct response arguments: {:#?}", args),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001376 },
1377 Interface::MsgSendDirectReq2 { .. } => Some(FuncId::MsgSendDirectReq64_2),
1378 Interface::MsgSendDirectResp2 { .. } => Some(FuncId::MsgSendDirectResp64_2),
1379 Interface::MemDonate { buf, .. } => match buf {
1380 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemDonate64),
1381 _ => Some(FuncId::MemDonate32),
1382 },
1383 Interface::MemLend { buf, .. } => match buf {
1384 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemLend64),
1385 _ => Some(FuncId::MemLend32),
1386 },
1387 Interface::MemShare { buf, .. } => match buf {
1388 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemShare64),
1389 _ => Some(FuncId::MemShare32),
1390 },
1391 Interface::MemRetrieveReq { buf, .. } => match buf {
1392 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemRetrieveReq64),
1393 _ => Some(FuncId::MemRetrieveReq32),
1394 },
1395 Interface::MemRetrieveResp { .. } => Some(FuncId::MemRetrieveResp),
1396 Interface::MemRelinquish => Some(FuncId::MemRelinquish),
1397 Interface::MemReclaim { .. } => Some(FuncId::MemReclaim),
1398 Interface::MemPermGet { addr, .. } => match addr {
1399 MemAddr::Addr32(_) => Some(FuncId::MemPermGet32),
1400 MemAddr::Addr64(_) => Some(FuncId::MemPermGet64),
1401 },
1402 Interface::MemPermSet { addr, .. } => match addr {
1403 MemAddr::Addr32(_) => Some(FuncId::MemPermSet32),
1404 MemAddr::Addr64(_) => Some(FuncId::MemPermSet64),
1405 },
Imre Kis189f18c2025-05-26 19:33:05 +02001406 Interface::ConsoleLog { chars, .. } => match chars {
1407 ConsoleLogChars::Chars32(_) => Some(FuncId::ConsoleLog32),
1408 ConsoleLogChars::Chars64(_) => Some(FuncId::ConsoleLog64),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001409 },
Tomás González7ffb6132025-04-03 12:28:58 +01001410 Interface::NotificationBitmapCreate { .. } => Some(FuncId::NotificationBitmapCreate),
1411 Interface::NotificationBitmapDestroy { .. } => Some(FuncId::NotificationBitmapDestroy),
1412 Interface::NotificationBind { .. } => Some(FuncId::NotificationBind),
Imre Kis3571f2c2025-05-26 19:29:23 +02001413 Interface::NotificationUnbind { .. } => Some(FuncId::NotificationUnbind),
Tomás González7ffb6132025-04-03 12:28:58 +01001414 Interface::NotificationSet { .. } => Some(FuncId::NotificationSet),
1415 Interface::NotificationGet { .. } => Some(FuncId::NotificationGet),
1416 Interface::NotificationInfoGet { is_32bit } => match is_32bit {
1417 true => Some(FuncId::NotificationInfoGet32),
1418 false => Some(FuncId::NotificationInfoGet64),
1419 },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01001420 Interface::El3IntrHandle => Some(FuncId::El3IntrHandle),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001421 }
1422 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01001423
Balint Dobszayde0dc802025-02-28 14:16:52 +01001424 /// Returns true if this is a 32-bit call, or false if it is a 64-bit call.
1425 pub fn is_32bit(&self) -> bool {
Balint Dobszay3c1c89a2025-04-25 17:36:46 +02001426 if matches!(self, Self::VersionOut { .. }) {
1427 return true;
1428 }
1429
Balint Dobszayde0dc802025-02-28 14:16:52 +01001430 self.function_id().unwrap().is_32bit()
1431 }
1432
Balint Dobszay82c71dd2025-04-15 10:16:44 +02001433 /// Returns the FF-A version that has introduced the function ID.
1434 pub fn minimum_ffa_version(&self) -> Version {
Balint Dobszay3c1c89a2025-04-25 17:36:46 +02001435 if matches!(self, Self::VersionOut { .. }) {
1436 return Version(1, 0);
1437 }
1438
Balint Dobszay82c71dd2025-04-15 10:16:44 +02001439 self.function_id().unwrap().minimum_ffa_version()
1440 }
1441
Balint Dobszayde0dc802025-02-28 14:16:52 +01001442 /// Parse interface from register contents. The caller must ensure that the `regs` argument has
1443 /// the correct length: 8 registers for FF-A v1.1 and lower, 18 registers for v1.2 and higher.
1444 pub fn from_regs(version: Version, regs: &[u64]) -> Result<Self, Error> {
Balint Dobszay82c71dd2025-04-15 10:16:44 +02001445 let func_id = FuncId::try_from(regs[0] as u32)?;
1446 if version < func_id.minimum_ffa_version() {
1447 return Err(Error::InvalidVersionForFunctionId(version, func_id));
1448 }
1449
Balint Dobszayde0dc802025-02-28 14:16:52 +01001450 let reg_cnt = regs.len();
1451
1452 let msg = match reg_cnt {
1453 8 => {
1454 assert!(version <= Version(1, 1));
1455 Interface::unpack_regs8(version, regs.try_into().unwrap())?
1456 }
1457 18 => {
1458 assert!(version >= Version(1, 2));
Balint Dobszay82c71dd2025-04-15 10:16:44 +02001459 match func_id {
Balint Dobszayde0dc802025-02-28 14:16:52 +01001460 FuncId::ConsoleLog64
1461 | FuncId::Success64
1462 | FuncId::MsgSendDirectReq64_2
Tomás González0a058bc2025-03-11 11:20:55 +00001463 | FuncId::MsgSendDirectResp64_2
1464 | FuncId::PartitionInfoGetRegs => {
Balint Dobszayde0dc802025-02-28 14:16:52 +01001465 Interface::unpack_regs18(version, regs.try_into().unwrap())?
1466 }
1467 _ => Interface::unpack_regs8(version, regs[..8].try_into().unwrap())?,
1468 }
1469 }
1470 _ => panic!(
1471 "Invalid number of registers ({}) for FF-A version {}",
1472 reg_cnt, version
1473 ),
1474 };
1475
1476 Ok(msg)
1477 }
1478
1479 fn unpack_regs8(version: Version, regs: &[u64; 8]) -> Result<Self, Error> {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001480 let fid = FuncId::try_from(regs[0] as u32)?;
1481
1482 let msg = match fid {
1483 FuncId::Error => Self::Error {
1484 target_info: (regs[1] as u32).into(),
1485 error_code: FfaError::try_from(regs[2] as i32)?,
Balint Dobszayb727aab2025-04-07 10:24:59 +02001486 error_arg: regs[3] as u32,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001487 },
1488 FuncId::Success32 => Self::Success {
1489 target_info: regs[1] as u32,
Imre Kis54773b62025-04-10 13:47:39 +02001490 args: SuccessArgs::Args32([
Balint Dobszay3aad9572025-01-17 16:54:11 +01001491 regs[2] as u32,
1492 regs[3] as u32,
1493 regs[4] as u32,
1494 regs[5] as u32,
1495 regs[6] as u32,
1496 regs[7] as u32,
1497 ]),
1498 },
1499 FuncId::Success64 => Self::Success {
1500 target_info: regs[1] as u32,
Imre Kis54773b62025-04-10 13:47:39 +02001501 args: SuccessArgs::Args64([regs[2], regs[3], regs[4], regs[5], regs[6], regs[7]]),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001502 },
1503 FuncId::Interrupt => Self::Interrupt {
1504 target_info: (regs[1] as u32).into(),
1505 interrupt_id: regs[2] as u32,
1506 },
1507 FuncId::Version => Self::Version {
Tomás González83146af2025-03-04 11:32:41 +00001508 input_version: (regs[1] as u32).try_into()?,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001509 },
1510 FuncId::Features => Self::Features {
Balint Dobszayc31e0b92025-03-03 20:16:56 +01001511 feat_id: (regs[1] as u32).into(),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001512 input_properties: regs[2] as u32,
1513 },
1514 FuncId::RxAcquire => Self::RxAcquire {
1515 vm_id: regs[1] as u16,
1516 },
1517 FuncId::RxRelease => Self::RxRelease {
1518 vm_id: regs[1] as u16,
1519 },
1520 FuncId::RxTxMap32 => {
1521 let addr = RxTxAddr::Addr32 {
1522 rx: regs[2] as u32,
1523 tx: regs[1] as u32,
1524 };
1525 let page_cnt = regs[3] as u32;
1526
1527 Self::RxTxMap { addr, page_cnt }
1528 }
1529 FuncId::RxTxMap64 => {
1530 let addr = RxTxAddr::Addr64 {
1531 rx: regs[2],
1532 tx: regs[1],
1533 };
1534 let page_cnt = regs[3] as u32;
1535
1536 Self::RxTxMap { addr, page_cnt }
1537 }
1538 FuncId::RxTxUnmap => Self::RxTxUnmap { id: regs[1] as u16 },
1539 FuncId::PartitionInfoGet => {
1540 let uuid_words = [
1541 regs[1] as u32,
1542 regs[2] as u32,
1543 regs[3] as u32,
1544 regs[4] as u32,
1545 ];
1546 let mut bytes: [u8; 16] = [0; 16];
1547 for (i, b) in uuid_words.iter().flat_map(|w| w.to_le_bytes()).enumerate() {
1548 bytes[i] = b;
1549 }
1550 Self::PartitionInfoGet {
1551 uuid: Uuid::from_bytes(bytes),
Imre Kise295adb2025-04-10 13:26:28 +02001552 flags: PartitionInfoGetFlags::try_from(regs[5] as u32)?,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001553 }
1554 }
1555 FuncId::IdGet => Self::IdGet,
1556 FuncId::SpmIdGet => Self::SpmIdGet,
Tomás González092202a2025-03-05 11:56:45 +00001557 FuncId::MsgWait => Self::MsgWait {
1558 flags: if version >= Version(1, 2) {
1559 Some(MsgWaitFlags::try_from(regs[2] as u32)?)
1560 } else {
1561 None
1562 },
1563 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001564 FuncId::Yield => Self::Yield,
1565 FuncId::Run => Self::Run {
1566 target_info: (regs[1] as u32).into(),
1567 },
1568 FuncId::NormalWorldResume => Self::NormalWorldResume,
Tomás González17b92442025-03-10 16:45:04 +00001569 FuncId::SecondaryEpRegister32 => Self::SecondaryEpRegister {
1570 entrypoint: SecondaryEpRegisterAddr::Addr32(regs[1] as u32),
1571 },
1572 FuncId::SecondaryEpRegister64 => Self::SecondaryEpRegister {
1573 entrypoint: SecondaryEpRegisterAddr::Addr64(regs[1]),
1574 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001575 FuncId::MsgSend2 => Self::MsgSend2 {
1576 sender_vm_id: regs[1] as u16,
1577 flags: regs[2] as u32,
1578 },
1579 FuncId::MsgSendDirectReq32 => Self::MsgSendDirectReq {
1580 src_id: (regs[1] >> 16) as u16,
1581 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001582 args: if (regs[2] as u32 & DirectMsgArgs::FWK_MSG_BITS) != 0 {
1583 match regs[2] as u32 {
1584 DirectMsgArgs::VERSION_REQ => DirectMsgArgs::VersionReq {
1585 version: Version::try_from(regs[3] as u32)?,
1586 },
1587 DirectMsgArgs::POWER_PSCI_REQ => DirectMsgArgs::PowerPsciReq32 {
Tomás González67f92c72025-03-20 16:50:42 +00001588 params: [
1589 regs[3] as u32,
1590 regs[4] as u32,
1591 regs[5] as u32,
1592 regs[6] as u32,
1593 ],
Tomás González4d5b0ba2025-03-03 17:15:55 +00001594 },
1595 DirectMsgArgs::POWER_WARM_BOOT_REQ => DirectMsgArgs::PowerWarmBootReq {
1596 boot_type: WarmBootType::try_from(regs[3] as u32)?,
1597 },
1598 DirectMsgArgs::VM_CREATED => DirectMsgArgs::VmCreated {
1599 handle: memory_management::Handle::from([
1600 regs[3] as u32,
1601 regs[4] as u32,
1602 ]),
1603 vm_id: regs[5] as u16,
1604 },
1605 DirectMsgArgs::VM_DESTRUCTED => DirectMsgArgs::VmDestructed {
1606 handle: memory_management::Handle::from([
1607 regs[3] as u32,
1608 regs[4] as u32,
1609 ]),
1610 vm_id: regs[5] as u16,
1611 },
1612 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1613 }
1614 } else {
1615 DirectMsgArgs::Args32([
1616 regs[3] as u32,
1617 regs[4] as u32,
1618 regs[5] as u32,
1619 regs[6] as u32,
1620 regs[7] as u32,
1621 ])
1622 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001623 },
1624 FuncId::MsgSendDirectReq64 => Self::MsgSendDirectReq {
1625 src_id: (regs[1] >> 16) as u16,
1626 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001627 args: if (regs[2] & DirectMsgArgs::FWK_MSG_BITS as u64) != 0 {
1628 match regs[2] as u32 {
1629 DirectMsgArgs::POWER_PSCI_REQ => DirectMsgArgs::PowerPsciReq64 {
Tomás González67f92c72025-03-20 16:50:42 +00001630 params: [regs[3], regs[4], regs[5], regs[6]],
Tomás González4d5b0ba2025-03-03 17:15:55 +00001631 },
1632 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1633 }
1634 } else {
1635 DirectMsgArgs::Args64([regs[3], regs[4], regs[5], regs[6], regs[7]])
1636 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001637 },
1638 FuncId::MsgSendDirectResp32 => Self::MsgSendDirectResp {
1639 src_id: (regs[1] >> 16) as u16,
1640 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001641 args: if (regs[2] as u32 & DirectMsgArgs::FWK_MSG_BITS) != 0 {
1642 match regs[2] as u32 {
1643 DirectMsgArgs::VERSION_RESP => {
1644 if regs[3] as i32 == FfaError::NotSupported.into() {
1645 DirectMsgArgs::VersionResp { version: None }
1646 } else {
1647 DirectMsgArgs::VersionResp {
1648 version: Some(Version::try_from(regs[3] as u32)?),
1649 }
1650 }
1651 }
1652 DirectMsgArgs::POWER_PSCI_RESP => DirectMsgArgs::PowerPsciResp {
1653 psci_status: regs[3] as i32,
1654 },
1655 DirectMsgArgs::VM_CREATED_ACK => DirectMsgArgs::VmCreatedAck {
1656 sp_status: (regs[3] as i32).try_into()?,
1657 },
1658 DirectMsgArgs::VM_DESTRUCTED_ACK => DirectMsgArgs::VmDestructedAck {
1659 sp_status: (regs[3] as i32).try_into()?,
1660 },
1661 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1662 }
1663 } else {
1664 DirectMsgArgs::Args32([
1665 regs[3] as u32,
1666 regs[4] as u32,
1667 regs[5] as u32,
1668 regs[6] as u32,
1669 regs[7] as u32,
1670 ])
1671 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001672 },
1673 FuncId::MsgSendDirectResp64 => Self::MsgSendDirectResp {
1674 src_id: (regs[1] >> 16) as u16,
1675 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001676 args: if (regs[2] & DirectMsgArgs::FWK_MSG_BITS as u64) != 0 {
1677 return Err(Error::UnrecognisedFwkMsg(regs[2] as u32));
1678 } else {
1679 DirectMsgArgs::Args64([regs[3], regs[4], regs[5], regs[6], regs[7]])
1680 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001681 },
1682 FuncId::MemDonate32 => Self::MemDonate {
1683 total_len: regs[1] as u32,
1684 frag_len: regs[2] as u32,
1685 buf: if regs[3] != 0 && regs[4] != 0 {
1686 Some(MemOpBuf::Buf32 {
1687 addr: regs[3] as u32,
1688 page_cnt: regs[4] as u32,
1689 })
1690 } else {
1691 None
1692 },
1693 },
1694 FuncId::MemDonate64 => Self::MemDonate {
1695 total_len: regs[1] as u32,
1696 frag_len: regs[2] as u32,
1697 buf: if regs[3] != 0 && regs[4] != 0 {
1698 Some(MemOpBuf::Buf64 {
1699 addr: regs[3],
1700 page_cnt: regs[4] as u32,
1701 })
1702 } else {
1703 None
1704 },
1705 },
1706 FuncId::MemLend32 => Self::MemLend {
1707 total_len: regs[1] as u32,
1708 frag_len: regs[2] as u32,
1709 buf: if regs[3] != 0 && regs[4] != 0 {
1710 Some(MemOpBuf::Buf32 {
1711 addr: regs[3] as u32,
1712 page_cnt: regs[4] as u32,
1713 })
1714 } else {
1715 None
1716 },
1717 },
1718 FuncId::MemLend64 => Self::MemLend {
1719 total_len: regs[1] as u32,
1720 frag_len: regs[2] as u32,
1721 buf: if regs[3] != 0 && regs[4] != 0 {
1722 Some(MemOpBuf::Buf64 {
1723 addr: regs[3],
1724 page_cnt: regs[4] as u32,
1725 })
1726 } else {
1727 None
1728 },
1729 },
1730 FuncId::MemShare32 => Self::MemShare {
1731 total_len: regs[1] as u32,
1732 frag_len: regs[2] as u32,
1733 buf: if regs[3] != 0 && regs[4] != 0 {
1734 Some(MemOpBuf::Buf32 {
1735 addr: regs[3] as u32,
1736 page_cnt: regs[4] as u32,
1737 })
1738 } else {
1739 None
1740 },
1741 },
1742 FuncId::MemShare64 => Self::MemShare {
1743 total_len: regs[1] as u32,
1744 frag_len: regs[2] as u32,
1745 buf: if regs[3] != 0 && regs[4] != 0 {
1746 Some(MemOpBuf::Buf64 {
1747 addr: regs[3],
1748 page_cnt: regs[4] as u32,
1749 })
1750 } else {
1751 None
1752 },
1753 },
1754 FuncId::MemRetrieveReq32 => Self::MemRetrieveReq {
1755 total_len: regs[1] as u32,
1756 frag_len: regs[2] as u32,
1757 buf: if regs[3] != 0 && regs[4] != 0 {
1758 Some(MemOpBuf::Buf32 {
1759 addr: regs[3] as u32,
1760 page_cnt: regs[4] as u32,
1761 })
1762 } else {
1763 None
1764 },
1765 },
1766 FuncId::MemRetrieveReq64 => Self::MemRetrieveReq {
1767 total_len: regs[1] as u32,
1768 frag_len: regs[2] as u32,
1769 buf: if regs[3] != 0 && regs[4] != 0 {
1770 Some(MemOpBuf::Buf64 {
1771 addr: regs[3],
1772 page_cnt: regs[4] as u32,
1773 })
1774 } else {
1775 None
1776 },
1777 },
1778 FuncId::MemRetrieveResp => Self::MemRetrieveResp {
1779 total_len: regs[1] as u32,
1780 frag_len: regs[2] as u32,
1781 },
1782 FuncId::MemRelinquish => Self::MemRelinquish,
1783 FuncId::MemReclaim => Self::MemReclaim {
1784 handle: memory_management::Handle::from([regs[1] as u32, regs[2] as u32]),
1785 flags: regs[3] as u32,
1786 },
1787 FuncId::MemPermGet32 => Self::MemPermGet {
1788 addr: MemAddr::Addr32(regs[1] as u32),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001789 page_cnt: if version >= Version(1, 3) {
1790 Some(regs[2] as u32)
1791 } else {
1792 None
1793 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001794 },
1795 FuncId::MemPermGet64 => Self::MemPermGet {
1796 addr: MemAddr::Addr64(regs[1]),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001797 page_cnt: if version >= Version(1, 3) {
1798 Some(regs[2] as u32)
1799 } else {
1800 None
1801 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001802 },
1803 FuncId::MemPermSet32 => Self::MemPermSet {
1804 addr: MemAddr::Addr32(regs[1] as u32),
1805 page_cnt: regs[2] as u32,
Imre Kisdcb7df22025-06-06 15:24:40 +02001806 mem_perm: (regs[3] as u32).try_into()?,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001807 },
1808 FuncId::MemPermSet64 => Self::MemPermSet {
1809 addr: MemAddr::Addr64(regs[1]),
1810 page_cnt: regs[2] as u32,
Imre Kisdcb7df22025-06-06 15:24:40 +02001811 mem_perm: (regs[3] as u32).try_into()?,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001812 },
Imre Kis189f18c2025-05-26 19:33:05 +02001813 FuncId::ConsoleLog32 => {
1814 let char_cnt = regs[1] as u8;
1815 if char_cnt > ConsoleLogChars32::MAX_LENGTH {
1816 return Err(Error::InvalidCharacterCount(char_cnt));
1817 }
1818
1819 Self::ConsoleLog {
1820 chars: ConsoleLogChars::Chars32(ConsoleLogChars32 {
1821 char_cnt,
1822 char_lists: [
1823 regs[2] as u32,
1824 regs[3] as u32,
1825 regs[4] as u32,
1826 regs[5] as u32,
1827 regs[6] as u32,
1828 regs[7] as u32,
1829 ],
1830 }),
1831 }
1832 }
Tomás González7ffb6132025-04-03 12:28:58 +01001833 FuncId::NotificationBitmapCreate => {
1834 let tentative_vm_id = regs[1] as u32;
1835 if (tentative_vm_id >> 16) != 0 {
1836 return Err(Error::InvalidVmId(tentative_vm_id));
1837 }
1838 Self::NotificationBitmapCreate {
1839 vm_id: tentative_vm_id as u16,
1840 vcpu_cnt: regs[2] as u32,
1841 }
1842 }
1843 FuncId::NotificationBitmapDestroy => {
1844 let tentative_vm_id = regs[1] as u32;
1845 if (tentative_vm_id >> 16) != 0 {
1846 return Err(Error::InvalidVmId(tentative_vm_id));
1847 }
1848 Self::NotificationBitmapDestroy {
1849 vm_id: tentative_vm_id as u16,
1850 }
1851 }
1852 FuncId::NotificationBind => Self::NotificationBind {
1853 sender_id: (regs[1] >> 16) as u16,
1854 receiver_id: regs[1] as u16,
1855 flags: (regs[2] as u32).into(),
1856 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1857 },
Imre Kis3571f2c2025-05-26 19:29:23 +02001858 FuncId::NotificationUnbind => Self::NotificationUnbind {
Tomás González7ffb6132025-04-03 12:28:58 +01001859 sender_id: (regs[1] >> 16) as u16,
1860 receiver_id: regs[1] as u16,
1861 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1862 },
1863 FuncId::NotificationSet => Self::NotificationSet {
1864 sender_id: (regs[1] >> 16) as u16,
1865 receiver_id: regs[1] as u16,
1866 flags: (regs[2] as u32).try_into()?,
1867 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1868 },
1869 FuncId::NotificationGet => Self::NotificationGet {
1870 vcpu_id: (regs[1] >> 16) as u16,
1871 endpoint_id: regs[1] as u16,
1872 flags: (regs[2] as u32).into(),
1873 },
1874 FuncId::NotificationInfoGet32 => Self::NotificationInfoGet { is_32bit: true },
1875 FuncId::NotificationInfoGet64 => Self::NotificationInfoGet { is_32bit: false },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01001876 FuncId::El3IntrHandle => Self::El3IntrHandle,
Balint Dobszayde0dc802025-02-28 14:16:52 +01001877 _ => panic!("Invalid number of registers (8) for function {:#x?}", fid),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001878 };
1879
1880 Ok(msg)
1881 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01001882
Balint Dobszayde0dc802025-02-28 14:16:52 +01001883 fn unpack_regs18(version: Version, regs: &[u64; 18]) -> Result<Self, Error> {
1884 assert!(version >= Version(1, 2));
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001885
Balint Dobszayde0dc802025-02-28 14:16:52 +01001886 let fid = FuncId::try_from(regs[0] as u32)?;
1887
1888 let msg = match fid {
1889 FuncId::Success64 => Self::Success {
1890 target_info: regs[1] as u32,
Imre Kis54773b62025-04-10 13:47:39 +02001891 args: SuccessArgs::Args64_2(regs[2..18].try_into().unwrap()),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001892 },
1893 FuncId::MsgSendDirectReq64_2 => Self::MsgSendDirectReq2 {
1894 src_id: (regs[1] >> 16) as u16,
1895 dst_id: regs[1] as u16,
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00001896 uuid: Uuid::from_u64_pair(regs[2].swap_bytes(), regs[3].swap_bytes()),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001897 args: DirectMsg2Args(regs[4..18].try_into().unwrap()),
1898 },
1899 FuncId::MsgSendDirectResp64_2 => Self::MsgSendDirectResp2 {
1900 src_id: (regs[1] >> 16) as u16,
1901 dst_id: regs[1] as u16,
1902 args: DirectMsg2Args(regs[4..18].try_into().unwrap()),
1903 },
Imre Kis189f18c2025-05-26 19:33:05 +02001904 FuncId::ConsoleLog64 => {
1905 let char_cnt = regs[1] as u8;
1906 if char_cnt > ConsoleLogChars64::MAX_LENGTH {
1907 return Err(Error::InvalidCharacterCount(char_cnt));
1908 }
1909
1910 Self::ConsoleLog {
1911 chars: ConsoleLogChars::Chars64(ConsoleLogChars64 {
1912 char_cnt,
1913 char_lists: regs[2..18].try_into().unwrap(),
1914 }),
1915 }
1916 }
Tomás González0a058bc2025-03-11 11:20:55 +00001917 FuncId::PartitionInfoGetRegs => {
1918 // Bits[15:0]: Start index
1919 let start_index = (regs[3] & 0xffff) as u16;
1920 let info_tag = ((regs[3] >> 16) & 0xffff) as u16;
1921 Self::PartitionInfoGetRegs {
1922 uuid: Uuid::from_u64_pair(regs[1].swap_bytes(), regs[2].swap_bytes()),
1923 start_index,
1924 info_tag: if start_index == 0 && info_tag != 0 {
1925 return Err(Error::InvalidInformationTag(info_tag));
1926 } else {
1927 info_tag
1928 },
1929 }
1930 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001931 _ => panic!("Invalid number of registers (18) for function {:#x?}", fid),
1932 };
1933
1934 Ok(msg)
Balint Dobszay3aad9572025-01-17 16:54:11 +01001935 }
1936
Balint Dobszaya5846852025-02-26 15:38:53 +01001937 /// Create register contents for an interface.
Balint Dobszayde0dc802025-02-28 14:16:52 +01001938 pub fn to_regs(&self, version: Version, regs: &mut [u64]) {
Balint Dobszay82c71dd2025-04-15 10:16:44 +02001939 assert!(self.minimum_ffa_version() <= version);
1940
Balint Dobszayde0dc802025-02-28 14:16:52 +01001941 let reg_cnt = regs.len();
1942
1943 match reg_cnt {
1944 8 => {
1945 assert!(version <= Version(1, 1));
Balint Dobszay91bea9b2025-04-09 13:16:06 +02001946 regs.fill(0);
1947
Balint Dobszayde0dc802025-02-28 14:16:52 +01001948 self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
1949 }
1950 18 => {
1951 assert!(version >= Version(1, 2));
Balint Dobszay91bea9b2025-04-09 13:16:06 +02001952 regs.fill(0);
Balint Dobszayde0dc802025-02-28 14:16:52 +01001953
1954 match self {
1955 Interface::ConsoleLog {
Imre Kis189f18c2025-05-26 19:33:05 +02001956 chars: ConsoleLogChars::Chars64(_),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001957 ..
1958 }
1959 | Interface::Success {
Imre Kis54773b62025-04-10 13:47:39 +02001960 args: SuccessArgs::Args64_2(_),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001961 ..
1962 }
1963 | Interface::MsgSendDirectReq2 { .. }
Tomás González0a058bc2025-03-11 11:20:55 +00001964 | Interface::MsgSendDirectResp2 { .. }
1965 | Interface::PartitionInfoGetRegs { .. } => {
Balint Dobszayde0dc802025-02-28 14:16:52 +01001966 self.pack_regs18(version, regs.try_into().unwrap());
1967 }
1968 _ => {
1969 self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
1970 }
1971 }
1972 }
1973 _ => panic!("Invalid number of registers {}", reg_cnt),
1974 }
1975 }
1976
1977 fn pack_regs8(&self, version: Version, a: &mut [u64; 8]) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001978 if let Some(function_id) = self.function_id() {
1979 a[0] = function_id as u64;
1980 }
1981
1982 match *self {
1983 Interface::Error {
1984 target_info,
1985 error_code,
Balint Dobszayb727aab2025-04-07 10:24:59 +02001986 error_arg,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001987 } => {
1988 a[1] = u32::from(target_info).into();
1989 a[2] = (error_code as u32).into();
Balint Dobszayb727aab2025-04-07 10:24:59 +02001990 a[3] = error_arg.into();
Balint Dobszay3aad9572025-01-17 16:54:11 +01001991 }
1992 Interface::Success { target_info, args } => {
1993 a[1] = target_info.into();
1994 match args {
Imre Kis54773b62025-04-10 13:47:39 +02001995 SuccessArgs::Args32(regs) => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001996 a[2] = regs[0].into();
1997 a[3] = regs[1].into();
1998 a[4] = regs[2].into();
1999 a[5] = regs[3].into();
2000 a[6] = regs[4].into();
2001 a[7] = regs[5].into();
2002 }
Imre Kis54773b62025-04-10 13:47:39 +02002003 SuccessArgs::Args64(regs) => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01002004 a[2] = regs[0];
2005 a[3] = regs[1];
2006 a[4] = regs[2];
2007 a[5] = regs[3];
2008 a[6] = regs[4];
2009 a[7] = regs[5];
2010 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01002011 _ => panic!("{:#x?} requires 18 registers", args),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002012 }
2013 }
2014 Interface::Interrupt {
2015 target_info,
2016 interrupt_id,
2017 } => {
2018 a[1] = u32::from(target_info).into();
2019 a[2] = interrupt_id.into();
2020 }
2021 Interface::Version { input_version } => {
2022 a[1] = u32::from(input_version).into();
2023 }
2024 Interface::VersionOut { output_version } => {
2025 a[0] = u32::from(output_version).into();
2026 }
2027 Interface::Features {
2028 feat_id,
2029 input_properties,
2030 } => {
2031 a[1] = u32::from(feat_id).into();
2032 a[2] = input_properties.into();
2033 }
2034 Interface::RxAcquire { vm_id } => {
2035 a[1] = vm_id.into();
2036 }
2037 Interface::RxRelease { vm_id } => {
2038 a[1] = vm_id.into();
2039 }
2040 Interface::RxTxMap { addr, page_cnt } => {
2041 match addr {
2042 RxTxAddr::Addr32 { rx, tx } => {
2043 a[1] = tx.into();
2044 a[2] = rx.into();
2045 }
2046 RxTxAddr::Addr64 { rx, tx } => {
2047 a[1] = tx;
2048 a[2] = rx;
2049 }
2050 }
2051 a[3] = page_cnt.into();
2052 }
2053 Interface::RxTxUnmap { id } => {
2054 a[1] = id.into();
2055 }
2056 Interface::PartitionInfoGet { uuid, flags } => {
2057 let bytes = uuid.into_bytes();
2058 a[1] = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]).into();
2059 a[2] = u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]).into();
2060 a[3] = u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]).into();
2061 a[4] = u32::from_le_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]).into();
Imre Kise295adb2025-04-10 13:26:28 +02002062 a[5] = u32::from(flags).into();
Balint Dobszay3aad9572025-01-17 16:54:11 +01002063 }
Tomás González092202a2025-03-05 11:56:45 +00002064 Interface::MsgWait { flags } => {
2065 if version >= Version(1, 2) {
2066 if let Some(flags) = flags {
2067 a[2] = u32::from(flags).into();
2068 }
2069 }
2070 }
2071 Interface::IdGet | Interface::SpmIdGet | Interface::Yield => {}
Balint Dobszay3aad9572025-01-17 16:54:11 +01002072 Interface::Run { target_info } => {
2073 a[1] = u32::from(target_info).into();
2074 }
2075 Interface::NormalWorldResume => {}
Tomás González17b92442025-03-10 16:45:04 +00002076 Interface::SecondaryEpRegister { entrypoint } => match entrypoint {
2077 SecondaryEpRegisterAddr::Addr32(addr) => a[1] = addr as u64,
2078 SecondaryEpRegisterAddr::Addr64(addr) => a[1] = addr,
2079 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01002080 Interface::MsgSend2 {
2081 sender_vm_id,
2082 flags,
2083 } => {
2084 a[1] = sender_vm_id.into();
2085 a[2] = flags.into();
2086 }
2087 Interface::MsgSendDirectReq {
2088 src_id,
2089 dst_id,
Balint Dobszay3aad9572025-01-17 16:54:11 +01002090 args,
2091 } => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +01002092 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Balint Dobszay3aad9572025-01-17 16:54:11 +01002093 match args {
2094 DirectMsgArgs::Args32(args) => {
2095 a[3] = args[0].into();
2096 a[4] = args[1].into();
2097 a[5] = args[2].into();
2098 a[6] = args[3].into();
2099 a[7] = args[4].into();
2100 }
2101 DirectMsgArgs::Args64(args) => {
2102 a[3] = args[0];
2103 a[4] = args[1];
2104 a[5] = args[2];
2105 a[6] = args[3];
2106 a[7] = args[4];
2107 }
Tomás González4d5b0ba2025-03-03 17:15:55 +00002108 DirectMsgArgs::VersionReq { version } => {
2109 a[2] = DirectMsgArgs::VERSION_REQ.into();
2110 a[3] = u32::from(version).into();
2111 }
Tomás González67f92c72025-03-20 16:50:42 +00002112 DirectMsgArgs::PowerPsciReq32 { params } => {
Tomás González4d5b0ba2025-03-03 17:15:55 +00002113 a[2] = DirectMsgArgs::POWER_PSCI_REQ.into();
Tomás González67f92c72025-03-20 16:50:42 +00002114 a[3] = params[0].into();
2115 a[4] = params[1].into();
2116 a[5] = params[2].into();
2117 a[6] = params[3].into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00002118 }
Tomás González67f92c72025-03-20 16:50:42 +00002119 DirectMsgArgs::PowerPsciReq64 { params } => {
Tomás González4d5b0ba2025-03-03 17:15:55 +00002120 a[2] = DirectMsgArgs::POWER_PSCI_REQ.into();
Tomás González67f92c72025-03-20 16:50:42 +00002121 a[3] = params[0];
2122 a[4] = params[1];
2123 a[5] = params[2];
2124 a[6] = params[3];
Tomás González4d5b0ba2025-03-03 17:15:55 +00002125 }
2126 DirectMsgArgs::PowerWarmBootReq { boot_type } => {
2127 a[2] = DirectMsgArgs::POWER_WARM_BOOT_REQ.into();
2128 a[3] = u32::from(boot_type).into();
2129 }
2130 DirectMsgArgs::VmCreated { handle, vm_id } => {
2131 a[2] = DirectMsgArgs::VM_CREATED.into();
2132 let handle_regs: [u32; 2] = handle.into();
2133 a[3] = handle_regs[0].into();
2134 a[4] = handle_regs[1].into();
2135 a[5] = vm_id.into();
2136 }
2137 DirectMsgArgs::VmDestructed { handle, vm_id } => {
2138 a[2] = DirectMsgArgs::VM_DESTRUCTED.into();
2139 let handle_regs: [u32; 2] = handle.into();
2140 a[3] = handle_regs[0].into();
2141 a[4] = handle_regs[1].into();
2142 a[5] = vm_id.into();
2143 }
2144 _ => panic!("Malformed MsgSendDirectReq interface"),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002145 }
2146 }
2147 Interface::MsgSendDirectResp {
2148 src_id,
2149 dst_id,
Balint Dobszay3aad9572025-01-17 16:54:11 +01002150 args,
2151 } => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +01002152 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Balint Dobszay3aad9572025-01-17 16:54:11 +01002153 match args {
2154 DirectMsgArgs::Args32(args) => {
2155 a[3] = args[0].into();
2156 a[4] = args[1].into();
2157 a[5] = args[2].into();
2158 a[6] = args[3].into();
2159 a[7] = args[4].into();
2160 }
2161 DirectMsgArgs::Args64(args) => {
2162 a[3] = args[0];
2163 a[4] = args[1];
2164 a[5] = args[2];
2165 a[6] = args[3];
2166 a[7] = args[4];
2167 }
Tomás González4d5b0ba2025-03-03 17:15:55 +00002168 DirectMsgArgs::VersionResp { version } => {
2169 a[2] = DirectMsgArgs::VERSION_RESP.into();
2170 match version {
Tomás González67f92c72025-03-20 16:50:42 +00002171 None => a[3] = (i32::from(FfaError::NotSupported) as u32).into(),
Tomás González4d5b0ba2025-03-03 17:15:55 +00002172 Some(ver) => a[3] = u32::from(ver).into(),
2173 }
2174 }
2175 DirectMsgArgs::PowerPsciResp { psci_status } => {
2176 a[2] = DirectMsgArgs::POWER_PSCI_RESP.into();
Imre Kisb2d3c882025-04-11 14:19:35 +02002177 a[3] = (psci_status as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00002178 }
2179 DirectMsgArgs::VmCreatedAck { sp_status } => {
2180 a[2] = DirectMsgArgs::VM_CREATED_ACK.into();
Tomás González67f92c72025-03-20 16:50:42 +00002181 a[3] = (i32::from(sp_status) as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00002182 }
2183 DirectMsgArgs::VmDestructedAck { sp_status } => {
2184 a[2] = DirectMsgArgs::VM_DESTRUCTED_ACK.into();
Tomás González67f92c72025-03-20 16:50:42 +00002185 a[3] = (i32::from(sp_status) as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00002186 }
2187 _ => panic!("Malformed MsgSendDirectResp interface"),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002188 }
2189 }
2190 Interface::MemDonate {
2191 total_len,
2192 frag_len,
2193 buf,
2194 } => {
2195 a[1] = total_len.into();
2196 a[2] = frag_len.into();
2197 (a[3], a[4]) = match buf {
2198 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
2199 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
2200 None => (0, 0),
2201 };
2202 }
2203 Interface::MemLend {
2204 total_len,
2205 frag_len,
2206 buf,
2207 } => {
2208 a[1] = total_len.into();
2209 a[2] = frag_len.into();
2210 (a[3], a[4]) = match buf {
2211 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
2212 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
2213 None => (0, 0),
2214 };
2215 }
2216 Interface::MemShare {
2217 total_len,
2218 frag_len,
2219 buf,
2220 } => {
2221 a[1] = total_len.into();
2222 a[2] = frag_len.into();
2223 (a[3], a[4]) = match buf {
2224 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
2225 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
2226 None => (0, 0),
2227 };
2228 }
2229 Interface::MemRetrieveReq {
2230 total_len,
2231 frag_len,
2232 buf,
2233 } => {
2234 a[1] = total_len.into();
2235 a[2] = frag_len.into();
2236 (a[3], a[4]) = match buf {
2237 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
2238 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
2239 None => (0, 0),
2240 };
2241 }
2242 Interface::MemRetrieveResp {
2243 total_len,
2244 frag_len,
2245 } => {
2246 a[1] = total_len.into();
2247 a[2] = frag_len.into();
2248 }
2249 Interface::MemRelinquish => {}
2250 Interface::MemReclaim { handle, flags } => {
2251 let handle_regs: [u32; 2] = handle.into();
2252 a[1] = handle_regs[0].into();
2253 a[2] = handle_regs[1].into();
2254 a[3] = flags.into();
2255 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01002256 Interface::MemPermGet { addr, page_cnt } => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01002257 a[1] = match addr {
2258 MemAddr::Addr32(addr) => addr.into(),
2259 MemAddr::Addr64(addr) => addr,
2260 };
Balint Dobszayde0dc802025-02-28 14:16:52 +01002261 a[2] = if version >= Version(1, 3) {
2262 page_cnt.unwrap().into()
2263 } else {
2264 assert!(page_cnt.is_none());
2265 0
2266 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01002267 }
2268 Interface::MemPermSet {
2269 addr,
2270 page_cnt,
2271 mem_perm,
2272 } => {
2273 a[1] = match addr {
2274 MemAddr::Addr32(addr) => addr.into(),
2275 MemAddr::Addr64(addr) => addr,
2276 };
2277 a[2] = page_cnt.into();
Imre Kisdcb7df22025-06-06 15:24:40 +02002278 a[3] = u32::from(mem_perm).into();
Balint Dobszay3aad9572025-01-17 16:54:11 +01002279 }
Imre Kis189f18c2025-05-26 19:33:05 +02002280 Interface::ConsoleLog { chars } => match chars {
2281 ConsoleLogChars::Chars32(ConsoleLogChars32 {
2282 char_cnt,
2283 char_lists,
2284 }) => {
2285 a[1] = char_cnt.into();
2286 a[2] = char_lists[0].into();
2287 a[3] = char_lists[1].into();
2288 a[4] = char_lists[2].into();
2289 a[5] = char_lists[3].into();
2290 a[6] = char_lists[4].into();
2291 a[7] = char_lists[5].into();
Balint Dobszay3aad9572025-01-17 16:54:11 +01002292 }
Imre Kis189f18c2025-05-26 19:33:05 +02002293 _ => panic!("{:#x?} requires 18 registers", chars),
2294 },
Tomás González7ffb6132025-04-03 12:28:58 +01002295 Interface::NotificationBitmapCreate { vm_id, vcpu_cnt } => {
2296 a[1] = vm_id.into();
2297 a[2] = vcpu_cnt.into();
2298 }
2299 Interface::NotificationBitmapDestroy { vm_id } => {
2300 a[1] = vm_id.into();
2301 }
2302 Interface::NotificationBind {
2303 sender_id,
2304 receiver_id,
2305 flags,
2306 bitmap,
2307 } => {
2308 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
2309 a[2] = u32::from(flags).into();
2310 a[3] = bitmap & 0xffff_ffff;
2311 a[4] = bitmap >> 32;
2312 }
Imre Kis3571f2c2025-05-26 19:29:23 +02002313 Interface::NotificationUnbind {
Tomás González7ffb6132025-04-03 12:28:58 +01002314 sender_id,
2315 receiver_id,
2316 bitmap,
2317 } => {
2318 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
2319 a[3] = bitmap & 0xffff_ffff;
2320 a[4] = bitmap >> 32;
2321 }
2322 Interface::NotificationSet {
2323 sender_id,
2324 receiver_id,
2325 flags,
2326 bitmap,
2327 } => {
2328 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
2329 a[2] = u32::from(flags).into();
2330 a[3] = bitmap & 0xffff_ffff;
2331 a[4] = bitmap >> 32;
2332 }
2333 Interface::NotificationGet {
2334 vcpu_id,
2335 endpoint_id,
2336 flags,
2337 } => {
2338 a[1] = (u64::from(vcpu_id) << 16) | u64::from(endpoint_id);
2339 a[2] = u32::from(flags).into();
2340 }
2341 Interface::NotificationInfoGet { .. } => {}
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01002342 Interface::El3IntrHandle => {}
Balint Dobszayde0dc802025-02-28 14:16:52 +01002343 _ => panic!("{:#x?} requires 18 registers", self),
2344 }
2345 }
2346
2347 fn pack_regs18(&self, version: Version, a: &mut [u64; 18]) {
2348 assert!(version >= Version(1, 2));
2349
Balint Dobszayde0dc802025-02-28 14:16:52 +01002350 if let Some(function_id) = self.function_id() {
2351 a[0] = function_id as u64;
2352 }
2353
2354 match *self {
2355 Interface::Success { target_info, args } => {
2356 a[1] = target_info.into();
2357 match args {
Imre Kis54773b62025-04-10 13:47:39 +02002358 SuccessArgs::Args64_2(regs) => a[2..18].copy_from_slice(&regs[..16]),
Balint Dobszayde0dc802025-02-28 14:16:52 +01002359 _ => panic!("{:#x?} requires 8 registers", args),
2360 }
2361 }
2362 Interface::MsgSendDirectReq2 {
2363 src_id,
2364 dst_id,
2365 uuid,
2366 args,
2367 } => {
2368 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00002369 let (uuid_msb, uuid_lsb) = uuid.as_u64_pair();
2370 (a[2], a[3]) = (uuid_msb.swap_bytes(), uuid_lsb.swap_bytes());
Balint Dobszayde0dc802025-02-28 14:16:52 +01002371 a[4..18].copy_from_slice(&args.0[..14]);
2372 }
2373 Interface::MsgSendDirectResp2 {
2374 src_id,
2375 dst_id,
2376 args,
2377 } => {
2378 a[1] = ((src_id as u64) << 16) | dst_id as u64;
2379 a[2] = 0;
2380 a[3] = 0;
2381 a[4..18].copy_from_slice(&args.0[..14]);
2382 }
Imre Kis189f18c2025-05-26 19:33:05 +02002383 Interface::ConsoleLog { chars: char_lists } => match char_lists {
2384 ConsoleLogChars::Chars64(ConsoleLogChars64 {
2385 char_cnt,
2386 char_lists,
2387 }) => {
2388 a[1] = char_cnt.into();
2389 a[2..18].copy_from_slice(&char_lists[..16])
Balint Dobszayde0dc802025-02-28 14:16:52 +01002390 }
Imre Kis189f18c2025-05-26 19:33:05 +02002391 _ => panic!("{:#x?} requires 8 registers", char_lists),
2392 },
Tomás González0a058bc2025-03-11 11:20:55 +00002393 Interface::PartitionInfoGetRegs {
2394 uuid,
2395 start_index,
2396 info_tag,
2397 } => {
2398 if start_index == 0 && info_tag != 0 {
2399 panic!("Information Tag MBZ if start index is 0: {:#x?}", self);
2400 }
2401 let (uuid_msb, uuid_lsb) = uuid.as_u64_pair();
2402 (a[1], a[2]) = (uuid_msb.swap_bytes(), uuid_lsb.swap_bytes());
2403 a[3] = (u64::from(info_tag) << 16) | u64::from(start_index);
2404 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01002405 _ => panic!("{:#x?} requires 8 registers", self),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002406 }
2407 }
2408
Balint Dobszaya5846852025-02-26 15:38:53 +01002409 /// Helper function to create an `FFA_SUCCESS` interface without any arguments.
Balint Dobszay3aad9572025-01-17 16:54:11 +01002410 pub fn success32_noargs() -> Self {
2411 Self::Success {
2412 target_info: 0,
Imre Kis54773b62025-04-10 13:47:39 +02002413 args: SuccessArgs::Args32([0; 6]),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002414 }
2415 }
2416
Balint Dobszaya5846852025-02-26 15:38:53 +01002417 /// Helper function to create an `FFA_ERROR` interface with an error code.
Balint Dobszay3aad9572025-01-17 16:54:11 +01002418 pub fn error(error_code: FfaError) -> Self {
2419 Self::Error {
2420 target_info: TargetInfo {
2421 endpoint_id: 0,
2422 vcpu_id: 0,
2423 },
2424 error_code,
Balint Dobszayb727aab2025-04-07 10:24:59 +02002425 error_arg: 0,
Balint Dobszay3aad9572025-01-17 16:54:11 +01002426 }
2427 }
2428}
2429
Tomás González0a058bc2025-03-11 11:20:55 +00002430#[cfg(test)]
2431mod tests {
2432 use super::*;
2433
2434 #[test]
2435 fn part_info_get_regs() {
2436 let uuid = Uuid::parse_str("a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8").unwrap();
2437 let uuid_bytes = uuid.as_bytes();
2438 let test_info_tag = 0b1101_1101;
2439 let test_start_index = 0b1101;
2440 let start_index_and_tag = (test_info_tag << 16) | test_start_index;
2441 let version = Version(1, 2);
2442
2443 // From spec:
2444 // Bytes[0...7] of UUID with byte 0 in the low-order bits.
Balint Dobszayb2e9bed2025-04-15 12:57:36 +02002445 let reg_x1 = ((uuid_bytes[7] as u64) << 56)
2446 | ((uuid_bytes[6] as u64) << 48)
2447 | ((uuid_bytes[5] as u64) << 40)
2448 | ((uuid_bytes[4] as u64) << 32)
2449 | ((uuid_bytes[3] as u64) << 24)
2450 | ((uuid_bytes[2] as u64) << 16)
2451 | ((uuid_bytes[1] as u64) << 8)
Tomás González0a058bc2025-03-11 11:20:55 +00002452 | (uuid_bytes[0] as u64);
2453
2454 // From spec:
2455 // Bytes[8...15] of UUID with byte 8 in the low-order bits.
Balint Dobszayb2e9bed2025-04-15 12:57:36 +02002456 let reg_x2 = ((uuid_bytes[15] as u64) << 56)
2457 | ((uuid_bytes[14] as u64) << 48)
2458 | ((uuid_bytes[13] as u64) << 40)
2459 | ((uuid_bytes[12] as u64) << 32)
2460 | ((uuid_bytes[11] as u64) << 24)
2461 | ((uuid_bytes[10] as u64) << 16)
2462 | ((uuid_bytes[9] as u64) << 8)
Tomás González0a058bc2025-03-11 11:20:55 +00002463 | (uuid_bytes[8] as u64);
2464
2465 // First, test for wrong tag:
2466 {
2467 let mut regs = [0u64; 18];
2468 regs[0] = FuncId::PartitionInfoGetRegs as u64;
2469 regs[1] = reg_x1;
2470 regs[2] = reg_x2;
2471 regs[3] = test_info_tag << 16;
2472
2473 assert!(Interface::from_regs(version, &regs).is_err_and(
2474 |e| e == Error::InvalidInformationTag(test_info_tag.try_into().unwrap())
2475 ));
2476 }
2477
2478 // Test for regs -> Interface -> regs
2479 {
2480 let mut orig_regs = [0u64; 18];
2481 orig_regs[0] = FuncId::PartitionInfoGetRegs as u64;
2482 orig_regs[1] = reg_x1;
2483 orig_regs[2] = reg_x2;
2484 orig_regs[3] = start_index_and_tag;
2485
Balint Dobszayb2e9bed2025-04-15 12:57:36 +02002486 let mut test_regs = orig_regs;
2487 let interface = Interface::from_regs(version, &test_regs).unwrap();
Tomás González0a058bc2025-03-11 11:20:55 +00002488 match &interface {
2489 Interface::PartitionInfoGetRegs {
2490 info_tag,
2491 start_index,
2492 uuid: int_uuid,
2493 } => {
2494 assert_eq!(u64::from(*info_tag), test_info_tag);
2495 assert_eq!(u64::from(*start_index), test_start_index);
2496 assert_eq!(*int_uuid, uuid);
2497 }
2498 _ => panic!("Expecting Interface::PartitionInfoGetRegs!"),
2499 }
2500 test_regs.fill(0);
2501 interface.to_regs(version, &mut test_regs);
2502 assert_eq!(orig_regs, test_regs);
2503 }
2504
2505 // Test for Interface -> regs -> Interface
2506 {
2507 let interface = Interface::PartitionInfoGetRegs {
2508 info_tag: test_info_tag.try_into().unwrap(),
2509 start_index: test_start_index.try_into().unwrap(),
2510 uuid,
2511 };
2512
2513 let mut regs: [u64; 18] = [0; 18];
2514 interface.to_regs(version, &mut regs);
2515
2516 assert_eq!(Some(FuncId::PartitionInfoGetRegs), interface.function_id());
2517 assert_eq!(regs[0], interface.function_id().unwrap() as u64);
2518 assert_eq!(regs[1], reg_x1);
2519 assert_eq!(regs[2], reg_x2);
2520 assert_eq!(regs[3], (test_info_tag << 16) | test_start_index);
2521
2522 assert_eq!(Interface::from_regs(version, &regs).unwrap(), interface);
2523 }
2524 }
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00002525
2526 #[test]
2527 fn msg_send_direct_req2() {
2528 let uuid = Uuid::parse_str("a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8").unwrap();
2529 let uuid_bytes = uuid.as_bytes();
2530
2531 // From spec:
2532 // Bytes[0...7] of UUID with byte 0 in the low-order bits.
Balint Dobszayb2e9bed2025-04-15 12:57:36 +02002533 let reg_x2 = ((uuid_bytes[7] as u64) << 56)
2534 | ((uuid_bytes[6] as u64) << 48)
2535 | ((uuid_bytes[5] as u64) << 40)
2536 | ((uuid_bytes[4] as u64) << 32)
2537 | ((uuid_bytes[3] as u64) << 24)
2538 | ((uuid_bytes[2] as u64) << 16)
2539 | ((uuid_bytes[1] as u64) << 8)
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00002540 | (uuid_bytes[0] as u64);
2541
2542 // From spec:
2543 // Bytes[8...15] of UUID with byte 8 in the low-order bits.
Balint Dobszayb2e9bed2025-04-15 12:57:36 +02002544 let reg_x3 = ((uuid_bytes[15] as u64) << 56)
2545 | ((uuid_bytes[14] as u64) << 48)
2546 | ((uuid_bytes[13] as u64) << 40)
2547 | ((uuid_bytes[12] as u64) << 32)
2548 | ((uuid_bytes[11] as u64) << 24)
2549 | ((uuid_bytes[10] as u64) << 16)
2550 | ((uuid_bytes[9] as u64) << 8)
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00002551 | (uuid_bytes[8] as u64);
2552
2553 let test_sender = 0b1101_1101;
2554 let test_receiver = 0b1101;
2555 let test_sender_receiver = (test_sender << 16) | test_receiver;
2556 let version = Version(1, 2);
2557
2558 // Test for regs -> Interface -> regs
2559 {
2560 let mut orig_regs = [0u64; 18];
2561 orig_regs[0] = FuncId::MsgSendDirectReq64_2 as u64;
2562 orig_regs[1] = test_sender_receiver;
2563 orig_regs[2] = reg_x2;
2564 orig_regs[3] = reg_x3;
2565
Balint Dobszayb2e9bed2025-04-15 12:57:36 +02002566 let mut test_regs = orig_regs;
2567 let interface = Interface::from_regs(version, &test_regs).unwrap();
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00002568 match &interface {
2569 Interface::MsgSendDirectReq2 {
2570 dst_id,
2571 src_id,
2572 args: _,
2573 uuid: int_uuid,
2574 } => {
2575 assert_eq!(u64::from(*src_id), test_sender);
2576 assert_eq!(u64::from(*dst_id), test_receiver);
2577 assert_eq!(*int_uuid, uuid);
2578 }
2579 _ => panic!("Expecting Interface::MsgSendDirectReq2!"),
2580 }
2581 test_regs.fill(0);
2582 interface.to_regs(version, &mut test_regs);
2583 assert_eq!(orig_regs, test_regs);
2584 }
2585
2586 // Test for Interface -> regs -> Interface
2587 {
2588 let rest_of_regs: [u64; 14] = [0; 14];
2589
2590 let interface = Interface::MsgSendDirectReq2 {
2591 src_id: test_sender.try_into().unwrap(),
2592 dst_id: test_receiver.try_into().unwrap(),
2593 uuid,
2594 args: DirectMsg2Args(rest_of_regs),
2595 };
2596
2597 let mut regs: [u64; 18] = [0; 18];
2598 interface.to_regs(version, &mut regs);
2599
2600 assert_eq!(Some(FuncId::MsgSendDirectReq64_2), interface.function_id());
2601 assert_eq!(regs[0], interface.function_id().unwrap() as u64);
2602 assert_eq!(regs[1], test_sender_receiver);
2603 assert_eq!(regs[2], reg_x2);
2604 assert_eq!(regs[3], reg_x3);
2605 assert_eq!(regs[4], 0);
2606
2607 assert_eq!(Interface::from_regs(version, &regs).unwrap(), interface);
2608 }
2609 }
Tomás González6ccba0a2025-04-09 13:31:29 +01002610
2611 #[test]
2612 fn is_32bit() {
2613 let interface_64 = Interface::MsgSendDirectReq {
2614 src_id: 0,
2615 dst_id: 1,
2616 args: DirectMsgArgs::Args64([0, 0, 0, 0, 0]),
2617 };
2618 assert!(!interface_64.is_32bit());
2619
2620 let interface_32 = Interface::MsgSendDirectReq {
2621 src_id: 0,
2622 dst_id: 1,
2623 args: DirectMsgArgs::Args32([0, 0, 0, 0, 0]),
2624 };
2625 assert!(interface_32.is_32bit());
2626 }
Imre Kis787c5002025-04-10 14:25:51 +02002627
2628 #[test]
2629 fn success_args_notification_info_get32() {
2630 let mut notifications = SuccessArgsNotificationInfoGet32::default();
2631
2632 // 16.7.1.1 Example usage
2633 notifications.add_list(0x0000, &[0, 2, 3]).unwrap();
2634 notifications.add_list(0x0000, &[4, 6]).unwrap();
2635 notifications.add_list(0x0002, &[]).unwrap();
2636 notifications.add_list(0x0003, &[1]).unwrap();
2637
2638 let args: SuccessArgs = notifications.into();
2639 assert_eq!(
2640 SuccessArgs::Args32([
2641 0x0004_b200,
2642 0x0000_0000,
2643 0x0003_0002,
2644 0x0004_0000,
2645 0x0002_0006,
2646 0x0001_0003
2647 ]),
2648 args
2649 );
2650
2651 let notifications = SuccessArgsNotificationInfoGet32::try_from(args).unwrap();
2652 let mut iter = notifications.iter();
2653 assert_eq!(Some((0x0000, &[0, 2, 3][..])), iter.next());
2654 assert_eq!(Some((0x0000, &[4, 6][..])), iter.next());
2655 assert_eq!(Some((0x0002, &[][..])), iter.next());
2656 assert_eq!(Some((0x0003, &[1][..])), iter.next());
2657 }
2658
2659 #[test]
2660 fn success_args_notification_info_get64() {
2661 let mut notifications = SuccessArgsNotificationInfoGet64::default();
2662
2663 // 16.7.1.1 Example usage
2664 notifications.add_list(0x0000, &[0, 2, 3]).unwrap();
2665 notifications.add_list(0x0000, &[4, 6]).unwrap();
2666 notifications.add_list(0x0002, &[]).unwrap();
2667 notifications.add_list(0x0003, &[1]).unwrap();
2668
2669 let args: SuccessArgs = notifications.into();
2670 assert_eq!(
2671 SuccessArgs::Args64([
2672 0x0004_b200,
2673 0x0003_0002_0000_0000,
2674 0x0002_0006_0004_0000,
2675 0x0000_0000_0001_0003,
2676 0x0000_0000_0000_0000,
2677 0x0000_0000_0000_0000,
2678 ]),
2679 args
2680 );
2681
2682 let notifications = SuccessArgsNotificationInfoGet64::try_from(args).unwrap();
2683 let mut iter = notifications.iter();
2684 assert_eq!(Some((0x0000, &[0, 2, 3][..])), iter.next());
2685 assert_eq!(Some((0x0000, &[4, 6][..])), iter.next());
2686 assert_eq!(Some((0x0002, &[][..])), iter.next());
2687 assert_eq!(Some((0x0003, &[1][..])), iter.next());
2688 }
Tomás González0a058bc2025-03-11 11:20:55 +00002689}