blob: ad1d784e85e5e918143a4dd48c74d66459f83a4e [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.
Imre Kise521a282025-06-13 13:29:24 +0200257#[derive(Debug, Default, 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 }
Balint Dobszay5ded5922025-06-13 12:06:53 +0200348
349 /// Returns true if the specified FF-A version uses 18 registers for calls, false if it uses 8.
350 pub fn needs_18_regs(&self) -> bool {
351 *self >= Version(1, 2)
352 }
Tomás González1f794352025-03-03 16:47:06 +0000353}
354
Tomás González83146af2025-03-04 11:32:41 +0000355impl TryFrom<u32> for Version {
356 type Error = Error;
357
358 fn try_from(val: u32) -> Result<Self, Self::Error> {
359 if (val & Self::MBZ_BITS) != 0 {
360 Err(Error::InvalidVersion(val))
361 } else {
362 Ok(Self((val >> 16) as u16, val as u16))
363 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200364 }
365}
366
367impl From<Version> for u32 {
368 fn from(v: Version) -> Self {
Tomás González83146af2025-03-04 11:32:41 +0000369 let v_u32 = ((v.0 as u32) << 16) | v.1 as u32;
370 assert!(v_u32 & Version::MBZ_BITS == 0);
371 v_u32
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200372 }
373}
374
Andrew Walbran19970ba2024-11-25 15:35:00 +0000375impl Display for Version {
376 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
377 write!(f, "{}.{}", self.0, self.1)
378 }
379}
380
381impl Debug for Version {
382 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
383 Display::fmt(self, f)
384 }
385}
386
Balint Dobszaya5846852025-02-26 15:38:53 +0100387/// Feature IDs used by the `FFA_FEATURES` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100388#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
389#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedFeatureId))]
390#[repr(u8)]
391pub enum FeatureId {
392 NotificationPendingInterrupt = 0x1,
393 ScheduleReceiverInterrupt = 0x2,
394 ManagedExitInterrupt = 0x3,
395}
Balint Dobszayc8802492025-01-15 18:11:39 +0100396
Balint Dobszaya5846852025-02-26 15:38:53 +0100397/// Arguments for the `FFA_FEATURES` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100398#[derive(Debug, Eq, PartialEq, Clone, Copy)]
399pub enum Feature {
400 FuncId(FuncId),
401 FeatureId(FeatureId),
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100402 Unknown(u32),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100403}
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200404
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100405impl From<u32> for Feature {
406 fn from(value: u32) -> Self {
407 // Bit[31] is set for all valid FF-A function IDs so we don't have to check it separately
408 if let Ok(func_id) = value.try_into() {
409 Self::FuncId(func_id)
410 } else if let Ok(feat_id) = (value as u8).try_into() {
411 Self::FeatureId(feat_id)
Balint Dobszay3aad9572025-01-17 16:54:11 +0100412 } else {
Balint Dobszayc31e0b92025-03-03 20:16:56 +0100413 Self::Unknown(value)
414 }
Balint Dobszay3aad9572025-01-17 16:54:11 +0100415 }
416}
417
418impl From<Feature> for u32 {
419 fn from(value: Feature) -> Self {
420 match value {
421 Feature::FuncId(func_id) => (1 << 31) | func_id as u32,
422 Feature::FeatureId(feature_id) => feature_id as u32,
Imre Kis29c8ace2025-04-11 13:49:58 +0200423 Feature::Unknown(id) => id,
Balint Dobszay3aad9572025-01-17 16:54:11 +0100424 }
425 }
426}
427
Imre Kis4e9d8bc2025-04-10 13:48:26 +0200428/// `FFA_FEATURES` specific success argument structure. This type needs further specialization based
429/// on 'FF-A function ID or Feature ID' field of the preceeding `FFA_FEATURES` request.
430#[derive(Debug, Eq, PartialEq, Clone, Copy)]
431pub struct SuccessArgsFeatures {
432 pub properties: [u32; 2],
433}
434
435impl From<SuccessArgsFeatures> for SuccessArgs {
436 fn from(value: SuccessArgsFeatures) -> Self {
437 Self::Args32([value.properties[0], value.properties[1], 0, 0, 0, 0])
438 }
439}
440
441impl TryFrom<SuccessArgs> for SuccessArgsFeatures {
442 type Error = Error;
443
444 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
445 let args = value.try_get_args32()?;
446
447 Ok(Self {
448 properties: [args[0], args[1]],
449 })
450 }
451}
452
Balint Dobszaya5846852025-02-26 15:38:53 +0100453/// RXTX buffer descriptor, used by `FFA_RXTX_MAP`.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100454#[derive(Debug, Eq, PartialEq, Clone, Copy)]
455pub enum RxTxAddr {
456 Addr32 { rx: u32, tx: u32 },
457 Addr64 { rx: u64, tx: u64 },
458}
459
Imre Kisbbef2872025-04-10 14:11:29 +0200460/// `FFA_ID_GET` specific success argument structure.
461#[derive(Debug, Eq, PartialEq, Clone, Copy)]
462pub struct SuccessArgsIdGet {
463 pub id: u16,
464}
465
466impl From<SuccessArgsIdGet> for SuccessArgs {
467 fn from(value: SuccessArgsIdGet) -> Self {
468 SuccessArgs::Args32([value.id as u32, 0, 0, 0, 0, 0])
469 }
470}
471
472impl TryFrom<SuccessArgs> for SuccessArgsIdGet {
473 type Error = Error;
474
475 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
476 let args = value.try_get_args32()?;
477 Ok(Self { id: args[0] as u16 })
478 }
479}
480
481/// `FFA_SPM_ID_GET` specific success argument structure.
482#[derive(Debug, Eq, PartialEq, Clone, Copy)]
483pub struct SuccessArgsSpmIdGet {
484 pub id: u16,
485}
486
487impl From<SuccessArgsSpmIdGet> for SuccessArgs {
488 fn from(value: SuccessArgsSpmIdGet) -> Self {
489 SuccessArgs::Args32([value.id as u32, 0, 0, 0, 0, 0])
490 }
491}
492
493impl TryFrom<SuccessArgs> for SuccessArgsSpmIdGet {
494 type Error = Error;
495
496 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
497 let args = value.try_get_args32()?;
498 Ok(Self { id: args[0] as u16 })
499 }
500}
501
Imre Kise295adb2025-04-10 13:26:28 +0200502/// Flags of the `FFA_PARTITION_INFO_GET` interface.
503#[derive(Debug, Eq, PartialEq, Clone, Copy)]
504pub struct PartitionInfoGetFlags {
505 pub count_only: bool,
506}
507
508impl PartitionInfoGetFlags {
509 const RETURN_INFORMATION_TYPE_FLAG: u32 = 1 << 0;
510 const MBZ_BITS: u32 = 0xffff_fffe;
511}
512
513impl TryFrom<u32> for PartitionInfoGetFlags {
514 type Error = Error;
515
516 fn try_from(val: u32) -> Result<Self, Self::Error> {
517 if (val & Self::MBZ_BITS) != 0 {
518 Err(Error::InvalidPartitionInfoGetFlag(val))
519 } else {
520 Ok(Self {
521 count_only: val & Self::RETURN_INFORMATION_TYPE_FLAG != 0,
522 })
523 }
524 }
525}
526
527impl From<PartitionInfoGetFlags> for u32 {
528 fn from(flags: PartitionInfoGetFlags) -> Self {
529 let mut bits: u32 = 0;
530 if flags.count_only {
531 bits |= PartitionInfoGetFlags::RETURN_INFORMATION_TYPE_FLAG;
532 }
533 bits
534 }
535}
536
Tomás González4d5b0ba2025-03-03 17:15:55 +0000537/// Composite type for capturing success and error return codes for the VM availability messages.
538///
539/// Error codes are handled by the `FfaError` type. Having a separate type for errors helps using
540/// `Result<(), FfaError>`. If a single type would include both success and error values,
541/// then `Err(FfaError::Success)` would be incomprehensible.
542#[derive(Debug, Eq, PartialEq, Clone, Copy)]
543pub enum VmAvailabilityStatus {
544 Success,
545 Error(FfaError),
546}
547
548impl TryFrom<i32> for VmAvailabilityStatus {
549 type Error = Error;
550 fn try_from(value: i32) -> Result<Self, <Self as TryFrom<i32>>::Error> {
551 Ok(match value {
552 0 => Self::Success,
553 error_code => Self::Error(FfaError::try_from(error_code)?),
554 })
555 }
556}
557
558impl From<VmAvailabilityStatus> for i32 {
559 fn from(value: VmAvailabilityStatus) -> Self {
560 match value {
561 VmAvailabilityStatus::Success => 0,
562 VmAvailabilityStatus::Error(error_code) => error_code.into(),
563 }
564 }
565}
566
567/// Arguments for the Power Warm Boot `FFA_MSG_SEND_DIRECT_REQ` interface.
568#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
569#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedWarmBootType))]
570#[repr(u32)]
571pub enum WarmBootType {
572 ExitFromSuspend = 0,
573 ExitFromLowPower = 1,
574}
575
Balint Dobszaya5846852025-02-26 15:38:53 +0100576/// Arguments for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}` interfaces.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100577#[derive(Debug, Eq, PartialEq, Clone, Copy)]
578pub enum DirectMsgArgs {
579 Args32([u32; 5]),
580 Args64([u64; 5]),
Tomás González4d5b0ba2025-03-03 17:15:55 +0000581 /// Message for forwarding FFA_VERSION call from Normal world to the SPMC
582 VersionReq {
583 version: Version,
584 },
585 /// Response message to forwarded FFA_VERSION call from the Normal world
586 /// Contains the version returned by the SPMC or None
587 VersionResp {
588 version: Option<Version>,
589 },
590 /// Message for a power management operation initiated by a PSCI function
591 PowerPsciReq32 {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000592 // params[i]: Input parameter in w[i] in PSCI function invocation at EL3.
Tomás González67f92c72025-03-20 16:50:42 +0000593 // params[0]: Function ID.
594 params: [u32; 4],
Tomás González4d5b0ba2025-03-03 17:15:55 +0000595 },
596 /// Message for a power management operation initiated by a PSCI function
597 PowerPsciReq64 {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000598 // params[i]: Input parameter in x[i] in PSCI function invocation at EL3.
Tomás González67f92c72025-03-20 16:50:42 +0000599 // params[0]: Function ID.
600 params: [u64; 4],
Tomás González4d5b0ba2025-03-03 17:15:55 +0000601 },
602 /// Message for a warm boot
603 PowerWarmBootReq {
604 boot_type: WarmBootType,
605 },
606 /// Response message to indicate return status of the last power management request message
607 /// Return error code SUCCESS or DENIED as defined in PSCI spec. Caller is left to do the
608 /// parsing of the return status.
609 PowerPsciResp {
Tomás González4d5b0ba2025-03-03 17:15:55 +0000610 psci_status: i32,
611 },
612 /// Message to signal creation of a VM
613 VmCreated {
614 // Globally unique Handle to identify a memory region that contains IMPLEMENTATION DEFINED
615 // information associated with the created VM.
616 // The invalid memory region handle must be specified by the Hypervisor if this field is not
617 // used.
618 handle: memory_management::Handle,
619 vm_id: u16,
620 },
621 /// Message to acknowledge creation of a VM
622 VmCreatedAck {
623 sp_status: VmAvailabilityStatus,
624 },
625 /// Message to signal destruction of a VM
626 VmDestructed {
627 // Globally unique Handle to identify a memory region that contains IMPLEMENTATION DEFINED
628 // information associated with the created VM.
629 // The invalid memory region handle must be specified by the Hypervisor if this field is not
630 // used.
631 handle: memory_management::Handle,
632 vm_id: u16,
633 },
634 /// Message to acknowledge destruction of a VM
635 VmDestructedAck {
636 sp_status: VmAvailabilityStatus,
637 },
638}
639
640impl DirectMsgArgs {
641 // Flags for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}` interfaces.
642
643 const FWK_MSG_BITS: u32 = 1 << 31;
644 const VERSION_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b1000;
645 const VERSION_RESP: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b1001;
646 const POWER_PSCI_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS;
647 const POWER_WARM_BOOT_REQ: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0001;
648 const POWER_PSCI_RESP: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0010;
649 const VM_CREATED: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0100;
650 const VM_CREATED_ACK: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0101;
651 const VM_DESTRUCTED: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0110;
652 const VM_DESTRUCTED_ACK: u32 = DirectMsgArgs::FWK_MSG_BITS | 0b0111;
Balint Dobszay3aad9572025-01-17 16:54:11 +0100653}
654
Balint Dobszayde0dc802025-02-28 14:16:52 +0100655/// Arguments for the `FFA_MSG_SEND_DIRECT_{REQ,RESP}2` interfaces.
656#[derive(Debug, Eq, PartialEq, Clone, Copy)]
Imre Kisc739e0e2025-05-30 11:49:25 +0200657pub struct DirectMsg2Args(pub [u64; 14]);
Balint Dobszayde0dc802025-02-28 14:16:52 +0100658
Tomás González092202a2025-03-05 11:56:45 +0000659#[derive(Debug, Eq, PartialEq, Clone, Copy)]
660pub struct MsgWaitFlags {
Imre Kisc739e0e2025-05-30 11:49:25 +0200661 pub retain_rx_buffer: bool,
Tomás González092202a2025-03-05 11:56:45 +0000662}
663
664impl MsgWaitFlags {
665 const RETAIN_RX_BUFFER: u32 = 0x01;
666 const MBZ_BITS: u32 = 0xfffe;
667}
668
669impl TryFrom<u32> for MsgWaitFlags {
670 type Error = Error;
671
672 fn try_from(val: u32) -> Result<Self, Self::Error> {
673 if (val & Self::MBZ_BITS) != 0 {
674 Err(Error::InvalidMsgWaitFlag(val))
675 } else {
676 Ok(MsgWaitFlags {
677 retain_rx_buffer: val & Self::RETAIN_RX_BUFFER != 0,
678 })
679 }
680 }
681}
682
683impl From<MsgWaitFlags> for u32 {
684 fn from(flags: MsgWaitFlags) -> Self {
685 let mut bits: u32 = 0;
686 if flags.retain_rx_buffer {
687 bits |= MsgWaitFlags::RETAIN_RX_BUFFER;
688 }
689 bits
690 }
691}
692
Balint Dobszaya5846852025-02-26 15:38:53 +0100693/// Descriptor for a dynamically allocated memory buffer that contains the memory transaction
Tomás Gonzálezf268e322025-03-05 11:18:11 +0000694/// descriptor.
695///
696/// Used by `FFA_MEM_{DONATE,LEND,SHARE,RETRIEVE_REQ}` interfaces, only when the TX buffer is not
697/// used to transmit the transaction descriptor.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100698#[derive(Debug, Eq, PartialEq, Clone, Copy)]
699pub enum MemOpBuf {
700 Buf32 { addr: u32, page_cnt: u32 },
701 Buf64 { addr: u64, page_cnt: u32 },
702}
703
Balint Dobszaya5846852025-02-26 15:38:53 +0100704/// Memory address argument for `FFA_MEM_PERM_{GET,SET}` interfaces.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100705#[derive(Debug, Eq, PartialEq, Clone, Copy)]
706pub enum MemAddr {
707 Addr32(u32),
708 Addr64(u64),
709}
710
Balint Dobszayde0dc802025-02-28 14:16:52 +0100711/// Argument for the `FFA_CONSOLE_LOG` interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +0100712#[derive(Debug, Eq, PartialEq, Clone, Copy)]
713pub enum ConsoleLogChars {
Imre Kis189f18c2025-05-26 19:33:05 +0200714 Chars32(ConsoleLogChars32),
715 Chars64(ConsoleLogChars64),
Balint Dobszay3aad9572025-01-17 16:54:11 +0100716}
717
Imre Kis189f18c2025-05-26 19:33:05 +0200718/// Generic type for storing `FFA_CONSOLE_LOG` character payload and its length in bytes.
719#[derive(Debug, Default, Eq, PartialEq, Clone, Copy)]
720pub struct LogChars<T>
721where
722 T: IntoBytes + FromBytes + Immutable,
723{
724 char_cnt: u8,
725 char_lists: T,
726}
727
728impl<T> LogChars<T>
729where
730 T: IntoBytes + FromBytes + Immutable,
731{
732 const MAX_LENGTH: u8 = core::mem::size_of::<T>() as u8;
733
734 /// Returns true if there are no characters in the structure.
735 pub fn empty(&self) -> bool {
736 self.char_cnt == 0
737 }
738
739 /// Returns true if the structure is full.
740 pub fn full(&self) -> bool {
741 self.char_cnt as usize >= core::mem::size_of::<T>()
742 }
743
744 /// Returns the payload bytes.
745 pub fn bytes(&self) -> &[u8] {
746 &self.char_lists.as_bytes()[..self.char_cnt as usize]
747 }
748
749 /// Append byte slice to the end of the characters.
750 pub fn push(&mut self, source: &[u8]) -> usize {
751 let empty_area = &mut self.char_lists.as_mut_bytes()[self.char_cnt.into()..];
752 let len = empty_area.len().min(source.len());
753
754 empty_area[..len].copy_from_slice(&source[..len]);
755 self.char_cnt += len as u8;
756
757 len
758 }
759}
760
761/// Specialized type for 32-bit `FFA_CONSOLE_LOG` payload.
762pub type ConsoleLogChars32 = LogChars<[u32; 6]>;
763
764/// Specialized type for 64-bit `FFA_CONSOLE_LOG` payload.
765pub type ConsoleLogChars64 = LogChars<[u64; 16]>;
766
Tomás González7ffb6132025-04-03 12:28:58 +0100767#[derive(Debug, Eq, PartialEq, Clone, Copy)]
768pub struct NotificationBindFlags {
Imre Kisc739e0e2025-05-30 11:49:25 +0200769 pub per_vcpu_notification: bool,
Tomás González7ffb6132025-04-03 12:28:58 +0100770}
771
772impl NotificationBindFlags {
773 const PER_VCPU_NOTIFICATION: u32 = 1;
774}
775
776impl From<NotificationBindFlags> for u32 {
777 fn from(flags: NotificationBindFlags) -> Self {
778 let mut bits: u32 = 0;
779 if flags.per_vcpu_notification {
780 bits |= NotificationBindFlags::PER_VCPU_NOTIFICATION;
781 }
782 bits
783 }
784}
785
786impl From<u32> for NotificationBindFlags {
787 fn from(flags: u32) -> Self {
788 Self {
789 per_vcpu_notification: flags & Self::PER_VCPU_NOTIFICATION != 0,
790 }
791 }
792}
793
794#[derive(Debug, Eq, PartialEq, Clone, Copy)]
795pub struct NotificationSetFlags {
Imre Kisc739e0e2025-05-30 11:49:25 +0200796 pub delay_schedule_receiver: bool,
797 pub vcpu_id: Option<u16>,
Tomás González7ffb6132025-04-03 12:28:58 +0100798}
799
800impl NotificationSetFlags {
801 const PER_VCP_NOTIFICATION: u32 = 1 << 0;
802 const DELAY_SCHEDULE_RECEIVER: u32 = 1 << 1;
803 const VCPU_ID_SHIFT: u32 = 16;
804
805 const MBZ_BITS: u32 = 0xfffc;
806}
807
808impl From<NotificationSetFlags> for u32 {
809 fn from(flags: NotificationSetFlags) -> Self {
810 let mut bits: u32 = 0;
811
812 if flags.delay_schedule_receiver {
813 bits |= NotificationSetFlags::DELAY_SCHEDULE_RECEIVER;
814 }
815 if let Some(vcpu_id) = flags.vcpu_id {
816 bits |= NotificationSetFlags::PER_VCP_NOTIFICATION;
817 bits |= u32::from(vcpu_id) << NotificationSetFlags::VCPU_ID_SHIFT;
818 }
819
820 bits
821 }
822}
823
824impl TryFrom<u32> for NotificationSetFlags {
825 type Error = Error;
826
827 fn try_from(flags: u32) -> Result<Self, Self::Error> {
828 if (flags & Self::MBZ_BITS) != 0 {
829 return Err(Error::InvalidNotificationSetFlag(flags));
830 }
831
832 let tentative_vcpu_id = (flags >> Self::VCPU_ID_SHIFT) as u16;
833
834 let vcpu_id = if (flags & Self::PER_VCP_NOTIFICATION) != 0 {
835 Some(tentative_vcpu_id)
836 } else {
837 if tentative_vcpu_id != 0 {
838 return Err(Error::InvalidNotificationSetFlag(flags));
839 }
840 None
841 };
842
843 Ok(Self {
844 delay_schedule_receiver: (flags & Self::DELAY_SCHEDULE_RECEIVER) != 0,
845 vcpu_id,
846 })
847 }
848}
849
850#[derive(Debug, Eq, PartialEq, Clone, Copy)]
851pub struct NotificationGetFlags {
Imre Kisc739e0e2025-05-30 11:49:25 +0200852 pub sp_bitmap_id: bool,
853 pub vm_bitmap_id: bool,
854 pub spm_bitmap_id: bool,
855 pub hyp_bitmap_id: bool,
Tomás González7ffb6132025-04-03 12:28:58 +0100856}
857
858impl NotificationGetFlags {
859 const SP_BITMAP_ID: u32 = 1;
860 const VM_BITMAP_ID: u32 = 1 << 1;
861 const SPM_BITMAP_ID: u32 = 1 << 2;
862 const HYP_BITMAP_ID: u32 = 1 << 3;
863}
864
865impl From<NotificationGetFlags> for u32 {
866 fn from(flags: NotificationGetFlags) -> Self {
867 let mut bits: u32 = 0;
868 if flags.sp_bitmap_id {
869 bits |= NotificationGetFlags::SP_BITMAP_ID;
870 }
871 if flags.vm_bitmap_id {
872 bits |= NotificationGetFlags::VM_BITMAP_ID;
873 }
874 if flags.spm_bitmap_id {
875 bits |= NotificationGetFlags::SPM_BITMAP_ID;
876 }
877 if flags.hyp_bitmap_id {
878 bits |= NotificationGetFlags::HYP_BITMAP_ID;
879 }
880 bits
881 }
882}
883
884impl From<u32> for NotificationGetFlags {
885 // This is a "from" instead of a "try_from" because Reserved Bits are SBZ, *not* MBZ.
886 fn from(flags: u32) -> Self {
887 Self {
888 sp_bitmap_id: (flags & Self::SP_BITMAP_ID) != 0,
889 vm_bitmap_id: (flags & Self::VM_BITMAP_ID) != 0,
890 spm_bitmap_id: (flags & Self::SPM_BITMAP_ID) != 0,
891 hyp_bitmap_id: (flags & Self::HYP_BITMAP_ID) != 0,
892 }
893 }
894}
895
Imre Kis9959e062025-04-10 14:16:10 +0200896/// `FFA_NOTIFICATION_GET` specific success argument structure.
897#[derive(Debug, Eq, PartialEq, Clone, Copy)]
898pub struct SuccessArgsNotificationGet {
899 pub sp_notifications: Option<u64>,
900 pub vm_notifications: Option<u64>,
901 pub spm_notifications: Option<u32>,
902 pub hypervisor_notifications: Option<u32>,
903}
904
905impl From<SuccessArgsNotificationGet> for SuccessArgs {
906 fn from(value: SuccessArgsNotificationGet) -> Self {
907 let mut args = [0; 6];
908
909 if let Some(bitmap) = value.sp_notifications {
910 args[0] = bitmap as u32;
911 args[1] = (bitmap >> 32) as u32;
912 }
913
914 if let Some(bitmap) = value.vm_notifications {
915 args[2] = bitmap as u32;
916 args[3] = (bitmap >> 32) as u32;
917 }
918
919 if let Some(bitmap) = value.spm_notifications {
920 args[4] = bitmap;
921 }
922
923 if let Some(bitmap) = value.hypervisor_notifications {
924 args[5] = bitmap;
925 }
926
927 Self::Args32(args)
928 }
929}
930
931impl TryFrom<(NotificationGetFlags, SuccessArgs)> for SuccessArgsNotificationGet {
932 type Error = Error;
933
934 fn try_from(value: (NotificationGetFlags, SuccessArgs)) -> Result<Self, Self::Error> {
935 let (flags, value) = value;
936 let args = value.try_get_args32()?;
937
938 let sp_notifications = if flags.sp_bitmap_id {
939 Some(u64::from(args[0]) | (u64::from(args[1]) << 32))
940 } else {
941 None
942 };
943
944 let vm_notifications = if flags.vm_bitmap_id {
945 Some(u64::from(args[2]) | (u64::from(args[3]) << 32))
946 } else {
947 None
948 };
949
950 let spm_notifications = if flags.spm_bitmap_id {
951 Some(args[4])
952 } else {
953 None
954 };
955
956 let hypervisor_notifications = if flags.hyp_bitmap_id {
957 Some(args[5])
958 } else {
959 None
960 };
961
962 Ok(Self {
963 sp_notifications,
964 vm_notifications,
965 spm_notifications,
966 hypervisor_notifications,
967 })
968 }
969}
Imre Kis787c5002025-04-10 14:25:51 +0200970
971/// `FFA_NOTIFICATION_INFO_GET` specific success argument structure. The `MAX_COUNT` parameter
972/// depends on the 32-bit or 64-bit packing.
973#[derive(Debug, Eq, PartialEq, Clone, Copy)]
974pub struct SuccessArgsNotificationInfoGet<const MAX_COUNT: usize> {
975 pub more_pending_notifications: bool,
976 list_count: usize,
977 id_counts: [u8; MAX_COUNT],
978 ids: [u16; MAX_COUNT],
979}
980
981impl<const MAX_COUNT: usize> Default for SuccessArgsNotificationInfoGet<MAX_COUNT> {
982 fn default() -> Self {
983 Self {
984 more_pending_notifications: false,
985 list_count: 0,
986 id_counts: [0; MAX_COUNT],
987 ids: [0; MAX_COUNT],
988 }
989 }
990}
991
992impl<const MAX_COUNT: usize> SuccessArgsNotificationInfoGet<MAX_COUNT> {
993 const MORE_PENDING_NOTIFICATIONS_FLAG: u64 = 1 << 0;
994 const LIST_COUNT_SHIFT: usize = 7;
995 const LIST_COUNT_MASK: u64 = 0x1f;
996 const ID_COUNT_SHIFT: usize = 12;
997 const ID_COUNT_MASK: u64 = 0x03;
998 const ID_COUNT_BITS: usize = 2;
999
1000 pub fn add_list(&mut self, endpoint: u16, vcpu_ids: &[u16]) -> Result<(), Error> {
1001 if self.list_count >= MAX_COUNT || vcpu_ids.len() > Self::ID_COUNT_MASK as usize {
1002 return Err(Error::InvalidNotificationCount);
1003 }
1004
1005 // Each list contains at least one ID: the partition ID, followed by vCPU IDs. The number
1006 // of vCPU IDs is recorded in `id_counts`.
1007 let mut current_id_index = self.list_count + self.id_counts.iter().sum::<u8>() as usize;
1008 if current_id_index + 1 + vcpu_ids.len() > MAX_COUNT {
1009 // The new list does not fit into the available space for IDs.
1010 return Err(Error::InvalidNotificationCount);
1011 }
1012
1013 self.id_counts[self.list_count] = vcpu_ids.len() as u8;
1014 self.list_count += 1;
1015
1016 // The first ID is the endpoint ID.
1017 self.ids[current_id_index] = endpoint;
1018 current_id_index += 1;
1019
1020 // Insert the vCPU IDs.
1021 self.ids[current_id_index..current_id_index + vcpu_ids.len()].copy_from_slice(vcpu_ids);
1022
1023 Ok(())
1024 }
1025
1026 pub fn iter(&self) -> NotificationInfoGetIterator<'_> {
1027 NotificationInfoGetIterator {
1028 list_index: 0,
1029 id_index: 0,
1030 id_count: &self.id_counts[0..self.list_count],
1031 ids: &self.ids,
1032 }
1033 }
1034
1035 /// Pack flags field and IDs.
1036 fn pack(self) -> (u64, [u16; MAX_COUNT]) {
1037 let mut flags = if self.more_pending_notifications {
1038 Self::MORE_PENDING_NOTIFICATIONS_FLAG
1039 } else {
1040 0
1041 };
1042
1043 flags |= (self.list_count as u64) << Self::LIST_COUNT_SHIFT;
1044 for (count, shift) in self.id_counts.iter().take(self.list_count).zip(
1045 (Self::ID_COUNT_SHIFT..Self::ID_COUNT_SHIFT + Self::ID_COUNT_BITS * MAX_COUNT)
1046 .step_by(Self::ID_COUNT_BITS),
1047 ) {
1048 flags |= u64::from(*count) << shift;
1049 }
1050
1051 (flags, self.ids)
1052 }
1053
1054 /// Unpack flags field and IDs.
1055 fn unpack(flags: u64, ids: [u16; MAX_COUNT]) -> Result<Self, Error> {
1056 let count_of_lists = ((flags >> Self::LIST_COUNT_SHIFT) & Self::LIST_COUNT_MASK) as usize;
1057
1058 if count_of_lists > MAX_COUNT {
1059 return Err(Error::InvalidNotificationCount);
1060 }
1061
1062 let mut count_of_ids = [0; MAX_COUNT];
1063 let mut count_of_ids_bits = flags >> Self::ID_COUNT_SHIFT;
1064
1065 for id in count_of_ids.iter_mut().take(count_of_lists) {
1066 *id = (count_of_ids_bits & Self::ID_COUNT_MASK) as u8;
1067 count_of_ids_bits >>= Self::ID_COUNT_BITS;
1068 }
1069
Imre Kis7846c9f2025-04-15 09:45:00 +02001070 let id_field_count = count_of_lists + count_of_ids.iter().sum::<u8>() as usize;
1071 if id_field_count > MAX_COUNT {
1072 return Err(Error::InvalidNotificationCount);
1073 }
1074
Imre Kis787c5002025-04-10 14:25:51 +02001075 Ok(Self {
1076 more_pending_notifications: (flags & Self::MORE_PENDING_NOTIFICATIONS_FLAG) != 0,
1077 list_count: count_of_lists,
1078 id_counts: count_of_ids,
1079 ids,
1080 })
1081 }
1082}
1083
1084/// `FFA_NOTIFICATION_INFO_GET_32` specific success argument structure.
1085pub type SuccessArgsNotificationInfoGet32 = SuccessArgsNotificationInfoGet<10>;
1086
1087impl From<SuccessArgsNotificationInfoGet32> for SuccessArgs {
1088 fn from(value: SuccessArgsNotificationInfoGet32) -> Self {
1089 let (flags, ids) = value.pack();
1090 let id_regs: [u32; 5] = transmute!(ids);
1091
1092 let mut args = [0; 6];
1093 args[0] = flags as u32;
1094 args[1..6].copy_from_slice(&id_regs);
1095
1096 SuccessArgs::Args32(args)
1097 }
1098}
1099
1100impl TryFrom<SuccessArgs> for SuccessArgsNotificationInfoGet32 {
1101 type Error = Error;
1102
1103 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
1104 let args = value.try_get_args32()?;
1105 let flags = args[0].into();
1106 let id_regs: [u32; 5] = args[1..6].try_into().unwrap();
1107 Self::unpack(flags, transmute!(id_regs))
1108 }
1109}
1110
1111/// `FFA_NOTIFICATION_INFO_GET_64` specific success argument structure.
1112pub type SuccessArgsNotificationInfoGet64 = SuccessArgsNotificationInfoGet<20>;
1113
1114impl From<SuccessArgsNotificationInfoGet64> for SuccessArgs {
1115 fn from(value: SuccessArgsNotificationInfoGet64) -> Self {
1116 let (flags, ids) = value.pack();
1117 let id_regs: [u64; 5] = transmute!(ids);
1118
1119 let mut args = [0; 6];
1120 args[0] = flags;
1121 args[1..6].copy_from_slice(&id_regs);
1122
1123 SuccessArgs::Args64(args)
1124 }
1125}
1126
1127impl TryFrom<SuccessArgs> for SuccessArgsNotificationInfoGet64 {
1128 type Error = Error;
1129
1130 fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
1131 let args = value.try_get_args64()?;
1132 let flags = args[0];
1133 let id_regs: [u64; 5] = args[1..6].try_into().unwrap();
1134 Self::unpack(flags, transmute!(id_regs))
1135 }
1136}
1137
1138pub struct NotificationInfoGetIterator<'a> {
1139 list_index: usize,
1140 id_index: usize,
1141 id_count: &'a [u8],
1142 ids: &'a [u16],
1143}
1144
1145impl<'a> Iterator for NotificationInfoGetIterator<'a> {
1146 type Item = (u16, &'a [u16]);
1147
1148 fn next(&mut self) -> Option<Self::Item> {
1149 if self.list_index < self.id_count.len() {
1150 let partition_id = self.ids[self.id_index];
1151 let id_range =
1152 (self.id_index + 1)..=(self.id_index + self.id_count[self.list_index] as usize);
1153
1154 self.id_index += 1 + self.id_count[self.list_index] as usize;
1155 self.list_index += 1;
1156
1157 Some((partition_id, &self.ids[id_range]))
1158 } else {
1159 None
1160 }
1161 }
1162}
1163
Tomás Gonzálezf268e322025-03-05 11:18:11 +00001164/// FF-A "message types", the terminology used by the spec is "interfaces".
1165///
1166/// The interfaces are used by FF-A components for communication at an FF-A instance. The spec also
1167/// describes the valid FF-A instances and conduits for each interface.
Balint Dobszay3aad9572025-01-17 16:54:11 +01001168#[derive(Debug, Eq, PartialEq, Clone, Copy)]
1169pub enum Interface {
1170 Error {
1171 target_info: TargetInfo,
1172 error_code: FfaError,
Balint Dobszayb727aab2025-04-07 10:24:59 +02001173 error_arg: u32,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001174 },
1175 Success {
Imre Kise521a282025-06-13 13:29:24 +02001176 target_info: TargetInfo,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001177 args: SuccessArgs,
1178 },
1179 Interrupt {
1180 target_info: TargetInfo,
1181 interrupt_id: u32,
1182 },
1183 Version {
1184 input_version: Version,
1185 },
1186 VersionOut {
1187 output_version: Version,
1188 },
1189 Features {
1190 feat_id: Feature,
1191 input_properties: u32,
1192 },
1193 RxAcquire {
1194 vm_id: u16,
1195 },
1196 RxRelease {
1197 vm_id: u16,
1198 },
1199 RxTxMap {
1200 addr: RxTxAddr,
1201 page_cnt: u32,
1202 },
1203 RxTxUnmap {
1204 id: u16,
1205 },
1206 PartitionInfoGet {
1207 uuid: Uuid,
Imre Kise295adb2025-04-10 13:26:28 +02001208 flags: PartitionInfoGetFlags,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001209 },
Tomás González0a058bc2025-03-11 11:20:55 +00001210 PartitionInfoGetRegs {
1211 uuid: Uuid,
1212 start_index: u16,
1213 info_tag: u16,
1214 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001215 IdGet,
1216 SpmIdGet,
Tomás González092202a2025-03-05 11:56:45 +00001217 MsgWait {
1218 flags: Option<MsgWaitFlags>,
1219 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001220 Yield,
1221 Run {
1222 target_info: TargetInfo,
1223 },
1224 NormalWorldResume,
Tomás González17b92442025-03-10 16:45:04 +00001225 SecondaryEpRegister {
1226 entrypoint: SecondaryEpRegisterAddr,
1227 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001228 MsgSend2 {
1229 sender_vm_id: u16,
1230 flags: u32,
1231 },
1232 MsgSendDirectReq {
1233 src_id: u16,
1234 dst_id: u16,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001235 args: DirectMsgArgs,
1236 },
1237 MsgSendDirectResp {
1238 src_id: u16,
1239 dst_id: u16,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001240 args: DirectMsgArgs,
1241 },
Balint Dobszayde0dc802025-02-28 14:16:52 +01001242 MsgSendDirectReq2 {
1243 src_id: u16,
1244 dst_id: u16,
1245 uuid: Uuid,
1246 args: DirectMsg2Args,
1247 },
1248 MsgSendDirectResp2 {
1249 src_id: u16,
1250 dst_id: u16,
1251 args: DirectMsg2Args,
1252 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001253 MemDonate {
1254 total_len: u32,
1255 frag_len: u32,
1256 buf: Option<MemOpBuf>,
1257 },
1258 MemLend {
1259 total_len: u32,
1260 frag_len: u32,
1261 buf: Option<MemOpBuf>,
1262 },
1263 MemShare {
1264 total_len: u32,
1265 frag_len: u32,
1266 buf: Option<MemOpBuf>,
1267 },
1268 MemRetrieveReq {
1269 total_len: u32,
1270 frag_len: u32,
1271 buf: Option<MemOpBuf>,
1272 },
1273 MemRetrieveResp {
1274 total_len: u32,
1275 frag_len: u32,
1276 },
1277 MemRelinquish,
1278 MemReclaim {
1279 handle: memory_management::Handle,
1280 flags: u32,
1281 },
1282 MemPermGet {
1283 addr: MemAddr,
Balint Dobszayde0dc802025-02-28 14:16:52 +01001284 page_cnt: Option<u32>,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001285 },
1286 MemPermSet {
1287 addr: MemAddr,
1288 page_cnt: u32,
Imre Kisdcb7df22025-06-06 15:24:40 +02001289 mem_perm: memory_management::MemPermissionsGetSet,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001290 },
1291 ConsoleLog {
Imre Kis189f18c2025-05-26 19:33:05 +02001292 chars: ConsoleLogChars,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001293 },
Tomás González7ffb6132025-04-03 12:28:58 +01001294 NotificationBitmapCreate {
1295 vm_id: u16,
1296 vcpu_cnt: u32,
1297 },
1298 NotificationBitmapDestroy {
1299 vm_id: u16,
1300 },
1301 NotificationBind {
1302 sender_id: u16,
1303 receiver_id: u16,
1304 flags: NotificationBindFlags,
1305 bitmap: u64,
1306 },
Imre Kis3571f2c2025-05-26 19:29:23 +02001307 NotificationUnbind {
Tomás González7ffb6132025-04-03 12:28:58 +01001308 sender_id: u16,
1309 receiver_id: u16,
1310 bitmap: u64,
1311 },
1312 NotificationSet {
1313 sender_id: u16,
1314 receiver_id: u16,
1315 flags: NotificationSetFlags,
1316 bitmap: u64,
1317 },
1318 NotificationGet {
1319 vcpu_id: u16,
1320 endpoint_id: u16,
1321 flags: NotificationGetFlags,
1322 },
1323 NotificationInfoGet {
1324 is_32bit: bool,
1325 },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01001326 El3IntrHandle,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001327}
1328
Balint Dobszayde0dc802025-02-28 14:16:52 +01001329impl Interface {
1330 /// Returns the function ID for the call, if it has one.
1331 pub fn function_id(&self) -> Option<FuncId> {
1332 match self {
1333 Interface::Error { .. } => Some(FuncId::Error),
1334 Interface::Success { args, .. } => match args {
Imre Kis54773b62025-04-10 13:47:39 +02001335 SuccessArgs::Args32(..) => Some(FuncId::Success32),
1336 SuccessArgs::Args64(..) | SuccessArgs::Args64_2(..) => Some(FuncId::Success64),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001337 },
1338 Interface::Interrupt { .. } => Some(FuncId::Interrupt),
1339 Interface::Version { .. } => Some(FuncId::Version),
1340 Interface::VersionOut { .. } => None,
1341 Interface::Features { .. } => Some(FuncId::Features),
1342 Interface::RxAcquire { .. } => Some(FuncId::RxAcquire),
1343 Interface::RxRelease { .. } => Some(FuncId::RxRelease),
1344 Interface::RxTxMap { addr, .. } => match addr {
1345 RxTxAddr::Addr32 { .. } => Some(FuncId::RxTxMap32),
1346 RxTxAddr::Addr64 { .. } => Some(FuncId::RxTxMap64),
1347 },
1348 Interface::RxTxUnmap { .. } => Some(FuncId::RxTxUnmap),
1349 Interface::PartitionInfoGet { .. } => Some(FuncId::PartitionInfoGet),
Tomás González0a058bc2025-03-11 11:20:55 +00001350 Interface::PartitionInfoGetRegs { .. } => Some(FuncId::PartitionInfoGetRegs),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001351 Interface::IdGet => Some(FuncId::IdGet),
1352 Interface::SpmIdGet => Some(FuncId::SpmIdGet),
Tomás González092202a2025-03-05 11:56:45 +00001353 Interface::MsgWait { .. } => Some(FuncId::MsgWait),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001354 Interface::Yield => Some(FuncId::Yield),
1355 Interface::Run { .. } => Some(FuncId::Run),
1356 Interface::NormalWorldResume => Some(FuncId::NormalWorldResume),
Tomás González17b92442025-03-10 16:45:04 +00001357 Interface::SecondaryEpRegister { entrypoint } => match entrypoint {
1358 SecondaryEpRegisterAddr::Addr32 { .. } => Some(FuncId::SecondaryEpRegister32),
1359 SecondaryEpRegisterAddr::Addr64 { .. } => Some(FuncId::SecondaryEpRegister64),
1360 },
Balint Dobszayde0dc802025-02-28 14:16:52 +01001361 Interface::MsgSend2 { .. } => Some(FuncId::MsgSend2),
1362 Interface::MsgSendDirectReq { args, .. } => match args {
1363 DirectMsgArgs::Args32(_) => Some(FuncId::MsgSendDirectReq32),
1364 DirectMsgArgs::Args64(_) => Some(FuncId::MsgSendDirectReq64),
Tomás González4d5b0ba2025-03-03 17:15:55 +00001365 DirectMsgArgs::VersionReq { .. } => Some(FuncId::MsgSendDirectReq32),
1366 DirectMsgArgs::PowerPsciReq32 { .. } => Some(FuncId::MsgSendDirectReq32),
1367 DirectMsgArgs::PowerPsciReq64 { .. } => Some(FuncId::MsgSendDirectReq64),
1368 DirectMsgArgs::PowerWarmBootReq { .. } => Some(FuncId::MsgSendDirectReq32),
1369 DirectMsgArgs::VmCreated { .. } => Some(FuncId::MsgSendDirectReq32),
1370 DirectMsgArgs::VmDestructed { .. } => Some(FuncId::MsgSendDirectReq32),
Balint Dobszay3c1c89a2025-04-25 17:36:46 +02001371 _ => panic!("Invalid direct request arguments: {:#?}", args),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001372 },
1373 Interface::MsgSendDirectResp { args, .. } => match args {
1374 DirectMsgArgs::Args32(_) => Some(FuncId::MsgSendDirectResp32),
1375 DirectMsgArgs::Args64(_) => Some(FuncId::MsgSendDirectResp64),
Tomás González4d5b0ba2025-03-03 17:15:55 +00001376 DirectMsgArgs::VersionResp { .. } => Some(FuncId::MsgSendDirectResp32),
1377 DirectMsgArgs::PowerPsciResp { .. } => Some(FuncId::MsgSendDirectResp32),
1378 DirectMsgArgs::VmCreatedAck { .. } => Some(FuncId::MsgSendDirectResp32),
1379 DirectMsgArgs::VmDestructedAck { .. } => Some(FuncId::MsgSendDirectResp32),
Balint Dobszay3c1c89a2025-04-25 17:36:46 +02001380 _ => panic!("Invalid direct response arguments: {:#?}", args),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001381 },
1382 Interface::MsgSendDirectReq2 { .. } => Some(FuncId::MsgSendDirectReq64_2),
1383 Interface::MsgSendDirectResp2 { .. } => Some(FuncId::MsgSendDirectResp64_2),
1384 Interface::MemDonate { buf, .. } => match buf {
1385 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemDonate64),
1386 _ => Some(FuncId::MemDonate32),
1387 },
1388 Interface::MemLend { buf, .. } => match buf {
1389 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemLend64),
1390 _ => Some(FuncId::MemLend32),
1391 },
1392 Interface::MemShare { buf, .. } => match buf {
1393 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemShare64),
1394 _ => Some(FuncId::MemShare32),
1395 },
1396 Interface::MemRetrieveReq { buf, .. } => match buf {
1397 Some(MemOpBuf::Buf64 { .. }) => Some(FuncId::MemRetrieveReq64),
1398 _ => Some(FuncId::MemRetrieveReq32),
1399 },
1400 Interface::MemRetrieveResp { .. } => Some(FuncId::MemRetrieveResp),
1401 Interface::MemRelinquish => Some(FuncId::MemRelinquish),
1402 Interface::MemReclaim { .. } => Some(FuncId::MemReclaim),
1403 Interface::MemPermGet { addr, .. } => match addr {
1404 MemAddr::Addr32(_) => Some(FuncId::MemPermGet32),
1405 MemAddr::Addr64(_) => Some(FuncId::MemPermGet64),
1406 },
1407 Interface::MemPermSet { addr, .. } => match addr {
1408 MemAddr::Addr32(_) => Some(FuncId::MemPermSet32),
1409 MemAddr::Addr64(_) => Some(FuncId::MemPermSet64),
1410 },
Imre Kis189f18c2025-05-26 19:33:05 +02001411 Interface::ConsoleLog { chars, .. } => match chars {
1412 ConsoleLogChars::Chars32(_) => Some(FuncId::ConsoleLog32),
1413 ConsoleLogChars::Chars64(_) => Some(FuncId::ConsoleLog64),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001414 },
Tomás González7ffb6132025-04-03 12:28:58 +01001415 Interface::NotificationBitmapCreate { .. } => Some(FuncId::NotificationBitmapCreate),
1416 Interface::NotificationBitmapDestroy { .. } => Some(FuncId::NotificationBitmapDestroy),
1417 Interface::NotificationBind { .. } => Some(FuncId::NotificationBind),
Imre Kis3571f2c2025-05-26 19:29:23 +02001418 Interface::NotificationUnbind { .. } => Some(FuncId::NotificationUnbind),
Tomás González7ffb6132025-04-03 12:28:58 +01001419 Interface::NotificationSet { .. } => Some(FuncId::NotificationSet),
1420 Interface::NotificationGet { .. } => Some(FuncId::NotificationGet),
1421 Interface::NotificationInfoGet { is_32bit } => match is_32bit {
1422 true => Some(FuncId::NotificationInfoGet32),
1423 false => Some(FuncId::NotificationInfoGet64),
1424 },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01001425 Interface::El3IntrHandle => Some(FuncId::El3IntrHandle),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001426 }
1427 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01001428
Balint Dobszayde0dc802025-02-28 14:16:52 +01001429 /// Returns true if this is a 32-bit call, or false if it is a 64-bit call.
1430 pub fn is_32bit(&self) -> bool {
Balint Dobszay3c1c89a2025-04-25 17:36:46 +02001431 if matches!(self, Self::VersionOut { .. }) {
1432 return true;
1433 }
1434
Balint Dobszayde0dc802025-02-28 14:16:52 +01001435 self.function_id().unwrap().is_32bit()
1436 }
1437
Balint Dobszay82c71dd2025-04-15 10:16:44 +02001438 /// Returns the FF-A version that has introduced the function ID.
1439 pub fn minimum_ffa_version(&self) -> Version {
Balint Dobszay3c1c89a2025-04-25 17:36:46 +02001440 if matches!(self, Self::VersionOut { .. }) {
1441 return Version(1, 0);
1442 }
1443
Balint Dobszay82c71dd2025-04-15 10:16:44 +02001444 self.function_id().unwrap().minimum_ffa_version()
1445 }
1446
Balint Dobszayde0dc802025-02-28 14:16:52 +01001447 /// Parse interface from register contents. The caller must ensure that the `regs` argument has
1448 /// the correct length: 8 registers for FF-A v1.1 and lower, 18 registers for v1.2 and higher.
1449 pub fn from_regs(version: Version, regs: &[u64]) -> Result<Self, Error> {
Balint Dobszay82c71dd2025-04-15 10:16:44 +02001450 let func_id = FuncId::try_from(regs[0] as u32)?;
1451 if version < func_id.minimum_ffa_version() {
1452 return Err(Error::InvalidVersionForFunctionId(version, func_id));
1453 }
1454
Balint Dobszayde0dc802025-02-28 14:16:52 +01001455 let reg_cnt = regs.len();
1456
1457 let msg = match reg_cnt {
1458 8 => {
1459 assert!(version <= Version(1, 1));
1460 Interface::unpack_regs8(version, regs.try_into().unwrap())?
1461 }
1462 18 => {
1463 assert!(version >= Version(1, 2));
Balint Dobszay82c71dd2025-04-15 10:16:44 +02001464 match func_id {
Balint Dobszayde0dc802025-02-28 14:16:52 +01001465 FuncId::ConsoleLog64
1466 | FuncId::Success64
1467 | FuncId::MsgSendDirectReq64_2
Tomás González0a058bc2025-03-11 11:20:55 +00001468 | FuncId::MsgSendDirectResp64_2
1469 | FuncId::PartitionInfoGetRegs => {
Balint Dobszayde0dc802025-02-28 14:16:52 +01001470 Interface::unpack_regs18(version, regs.try_into().unwrap())?
1471 }
1472 _ => Interface::unpack_regs8(version, regs[..8].try_into().unwrap())?,
1473 }
1474 }
1475 _ => panic!(
1476 "Invalid number of registers ({}) for FF-A version {}",
1477 reg_cnt, version
1478 ),
1479 };
1480
1481 Ok(msg)
1482 }
1483
1484 fn unpack_regs8(version: Version, regs: &[u64; 8]) -> Result<Self, Error> {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001485 let fid = FuncId::try_from(regs[0] as u32)?;
1486
1487 let msg = match fid {
1488 FuncId::Error => Self::Error {
1489 target_info: (regs[1] as u32).into(),
1490 error_code: FfaError::try_from(regs[2] as i32)?,
Balint Dobszayb727aab2025-04-07 10:24:59 +02001491 error_arg: regs[3] as u32,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001492 },
1493 FuncId::Success32 => Self::Success {
Imre Kise521a282025-06-13 13:29:24 +02001494 target_info: (regs[1] as u32).into(),
Imre Kis54773b62025-04-10 13:47:39 +02001495 args: SuccessArgs::Args32([
Balint Dobszay3aad9572025-01-17 16:54:11 +01001496 regs[2] as u32,
1497 regs[3] as u32,
1498 regs[4] as u32,
1499 regs[5] as u32,
1500 regs[6] as u32,
1501 regs[7] as u32,
1502 ]),
1503 },
1504 FuncId::Success64 => Self::Success {
Imre Kise521a282025-06-13 13:29:24 +02001505 target_info: (regs[1] as u32).into(),
Imre Kis54773b62025-04-10 13:47:39 +02001506 args: SuccessArgs::Args64([regs[2], regs[3], regs[4], regs[5], regs[6], regs[7]]),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001507 },
1508 FuncId::Interrupt => Self::Interrupt {
1509 target_info: (regs[1] as u32).into(),
1510 interrupt_id: regs[2] as u32,
1511 },
1512 FuncId::Version => Self::Version {
Tomás González83146af2025-03-04 11:32:41 +00001513 input_version: (regs[1] as u32).try_into()?,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001514 },
1515 FuncId::Features => Self::Features {
Balint Dobszayc31e0b92025-03-03 20:16:56 +01001516 feat_id: (regs[1] as u32).into(),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001517 input_properties: regs[2] as u32,
1518 },
1519 FuncId::RxAcquire => Self::RxAcquire {
1520 vm_id: regs[1] as u16,
1521 },
1522 FuncId::RxRelease => Self::RxRelease {
1523 vm_id: regs[1] as u16,
1524 },
1525 FuncId::RxTxMap32 => {
1526 let addr = RxTxAddr::Addr32 {
1527 rx: regs[2] as u32,
1528 tx: regs[1] as u32,
1529 };
1530 let page_cnt = regs[3] as u32;
1531
1532 Self::RxTxMap { addr, page_cnt }
1533 }
1534 FuncId::RxTxMap64 => {
1535 let addr = RxTxAddr::Addr64 {
1536 rx: regs[2],
1537 tx: regs[1],
1538 };
1539 let page_cnt = regs[3] as u32;
1540
1541 Self::RxTxMap { addr, page_cnt }
1542 }
1543 FuncId::RxTxUnmap => Self::RxTxUnmap { id: regs[1] as u16 },
1544 FuncId::PartitionInfoGet => {
1545 let uuid_words = [
1546 regs[1] as u32,
1547 regs[2] as u32,
1548 regs[3] as u32,
1549 regs[4] as u32,
1550 ];
1551 let mut bytes: [u8; 16] = [0; 16];
1552 for (i, b) in uuid_words.iter().flat_map(|w| w.to_le_bytes()).enumerate() {
1553 bytes[i] = b;
1554 }
1555 Self::PartitionInfoGet {
1556 uuid: Uuid::from_bytes(bytes),
Imre Kise295adb2025-04-10 13:26:28 +02001557 flags: PartitionInfoGetFlags::try_from(regs[5] as u32)?,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001558 }
1559 }
1560 FuncId::IdGet => Self::IdGet,
1561 FuncId::SpmIdGet => Self::SpmIdGet,
Tomás González092202a2025-03-05 11:56:45 +00001562 FuncId::MsgWait => Self::MsgWait {
1563 flags: if version >= Version(1, 2) {
1564 Some(MsgWaitFlags::try_from(regs[2] as u32)?)
1565 } else {
1566 None
1567 },
1568 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001569 FuncId::Yield => Self::Yield,
1570 FuncId::Run => Self::Run {
1571 target_info: (regs[1] as u32).into(),
1572 },
1573 FuncId::NormalWorldResume => Self::NormalWorldResume,
Tomás González17b92442025-03-10 16:45:04 +00001574 FuncId::SecondaryEpRegister32 => Self::SecondaryEpRegister {
1575 entrypoint: SecondaryEpRegisterAddr::Addr32(regs[1] as u32),
1576 },
1577 FuncId::SecondaryEpRegister64 => Self::SecondaryEpRegister {
1578 entrypoint: SecondaryEpRegisterAddr::Addr64(regs[1]),
1579 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001580 FuncId::MsgSend2 => Self::MsgSend2 {
1581 sender_vm_id: regs[1] as u16,
1582 flags: regs[2] as u32,
1583 },
1584 FuncId::MsgSendDirectReq32 => Self::MsgSendDirectReq {
1585 src_id: (regs[1] >> 16) as u16,
1586 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001587 args: if (regs[2] as u32 & DirectMsgArgs::FWK_MSG_BITS) != 0 {
1588 match regs[2] as u32 {
1589 DirectMsgArgs::VERSION_REQ => DirectMsgArgs::VersionReq {
1590 version: Version::try_from(regs[3] as u32)?,
1591 },
1592 DirectMsgArgs::POWER_PSCI_REQ => DirectMsgArgs::PowerPsciReq32 {
Tomás González67f92c72025-03-20 16:50:42 +00001593 params: [
1594 regs[3] as u32,
1595 regs[4] as u32,
1596 regs[5] as u32,
1597 regs[6] as u32,
1598 ],
Tomás González4d5b0ba2025-03-03 17:15:55 +00001599 },
1600 DirectMsgArgs::POWER_WARM_BOOT_REQ => DirectMsgArgs::PowerWarmBootReq {
1601 boot_type: WarmBootType::try_from(regs[3] as u32)?,
1602 },
1603 DirectMsgArgs::VM_CREATED => DirectMsgArgs::VmCreated {
1604 handle: memory_management::Handle::from([
1605 regs[3] as u32,
1606 regs[4] as u32,
1607 ]),
1608 vm_id: regs[5] as u16,
1609 },
1610 DirectMsgArgs::VM_DESTRUCTED => DirectMsgArgs::VmDestructed {
1611 handle: memory_management::Handle::from([
1612 regs[3] as u32,
1613 regs[4] as u32,
1614 ]),
1615 vm_id: regs[5] as u16,
1616 },
1617 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1618 }
1619 } else {
1620 DirectMsgArgs::Args32([
1621 regs[3] as u32,
1622 regs[4] as u32,
1623 regs[5] as u32,
1624 regs[6] as u32,
1625 regs[7] as u32,
1626 ])
1627 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001628 },
1629 FuncId::MsgSendDirectReq64 => Self::MsgSendDirectReq {
1630 src_id: (regs[1] >> 16) as u16,
1631 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001632 args: if (regs[2] & DirectMsgArgs::FWK_MSG_BITS as u64) != 0 {
1633 match regs[2] as u32 {
1634 DirectMsgArgs::POWER_PSCI_REQ => DirectMsgArgs::PowerPsciReq64 {
Tomás González67f92c72025-03-20 16:50:42 +00001635 params: [regs[3], regs[4], regs[5], regs[6]],
Tomás González4d5b0ba2025-03-03 17:15:55 +00001636 },
1637 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1638 }
1639 } else {
1640 DirectMsgArgs::Args64([regs[3], regs[4], regs[5], regs[6], regs[7]])
1641 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001642 },
1643 FuncId::MsgSendDirectResp32 => Self::MsgSendDirectResp {
1644 src_id: (regs[1] >> 16) as u16,
1645 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001646 args: if (regs[2] as u32 & DirectMsgArgs::FWK_MSG_BITS) != 0 {
1647 match regs[2] as u32 {
1648 DirectMsgArgs::VERSION_RESP => {
1649 if regs[3] as i32 == FfaError::NotSupported.into() {
1650 DirectMsgArgs::VersionResp { version: None }
1651 } else {
1652 DirectMsgArgs::VersionResp {
1653 version: Some(Version::try_from(regs[3] as u32)?),
1654 }
1655 }
1656 }
1657 DirectMsgArgs::POWER_PSCI_RESP => DirectMsgArgs::PowerPsciResp {
1658 psci_status: regs[3] as i32,
1659 },
1660 DirectMsgArgs::VM_CREATED_ACK => DirectMsgArgs::VmCreatedAck {
1661 sp_status: (regs[3] as i32).try_into()?,
1662 },
1663 DirectMsgArgs::VM_DESTRUCTED_ACK => DirectMsgArgs::VmDestructedAck {
1664 sp_status: (regs[3] as i32).try_into()?,
1665 },
1666 _ => return Err(Error::UnrecognisedFwkMsg(regs[2] as u32)),
1667 }
1668 } else {
1669 DirectMsgArgs::Args32([
1670 regs[3] as u32,
1671 regs[4] as u32,
1672 regs[5] as u32,
1673 regs[6] as u32,
1674 regs[7] as u32,
1675 ])
1676 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001677 },
1678 FuncId::MsgSendDirectResp64 => Self::MsgSendDirectResp {
1679 src_id: (regs[1] >> 16) as u16,
1680 dst_id: regs[1] as u16,
Tomás González4d5b0ba2025-03-03 17:15:55 +00001681 args: if (regs[2] & DirectMsgArgs::FWK_MSG_BITS as u64) != 0 {
1682 return Err(Error::UnrecognisedFwkMsg(regs[2] as u32));
1683 } else {
1684 DirectMsgArgs::Args64([regs[3], regs[4], regs[5], regs[6], regs[7]])
1685 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001686 },
1687 FuncId::MemDonate32 => Self::MemDonate {
1688 total_len: regs[1] as u32,
1689 frag_len: regs[2] as u32,
1690 buf: if regs[3] != 0 && regs[4] != 0 {
1691 Some(MemOpBuf::Buf32 {
1692 addr: regs[3] as u32,
1693 page_cnt: regs[4] as u32,
1694 })
1695 } else {
1696 None
1697 },
1698 },
1699 FuncId::MemDonate64 => Self::MemDonate {
1700 total_len: regs[1] as u32,
1701 frag_len: regs[2] as u32,
1702 buf: if regs[3] != 0 && regs[4] != 0 {
1703 Some(MemOpBuf::Buf64 {
1704 addr: regs[3],
1705 page_cnt: regs[4] as u32,
1706 })
1707 } else {
1708 None
1709 },
1710 },
1711 FuncId::MemLend32 => Self::MemLend {
1712 total_len: regs[1] as u32,
1713 frag_len: regs[2] as u32,
1714 buf: if regs[3] != 0 && regs[4] != 0 {
1715 Some(MemOpBuf::Buf32 {
1716 addr: regs[3] as u32,
1717 page_cnt: regs[4] as u32,
1718 })
1719 } else {
1720 None
1721 },
1722 },
1723 FuncId::MemLend64 => Self::MemLend {
1724 total_len: regs[1] as u32,
1725 frag_len: regs[2] as u32,
1726 buf: if regs[3] != 0 && regs[4] != 0 {
1727 Some(MemOpBuf::Buf64 {
1728 addr: regs[3],
1729 page_cnt: regs[4] as u32,
1730 })
1731 } else {
1732 None
1733 },
1734 },
1735 FuncId::MemShare32 => Self::MemShare {
1736 total_len: regs[1] as u32,
1737 frag_len: regs[2] as u32,
1738 buf: if regs[3] != 0 && regs[4] != 0 {
1739 Some(MemOpBuf::Buf32 {
1740 addr: regs[3] as u32,
1741 page_cnt: regs[4] as u32,
1742 })
1743 } else {
1744 None
1745 },
1746 },
1747 FuncId::MemShare64 => Self::MemShare {
1748 total_len: regs[1] as u32,
1749 frag_len: regs[2] as u32,
1750 buf: if regs[3] != 0 && regs[4] != 0 {
1751 Some(MemOpBuf::Buf64 {
1752 addr: regs[3],
1753 page_cnt: regs[4] as u32,
1754 })
1755 } else {
1756 None
1757 },
1758 },
1759 FuncId::MemRetrieveReq32 => Self::MemRetrieveReq {
1760 total_len: regs[1] as u32,
1761 frag_len: regs[2] as u32,
1762 buf: if regs[3] != 0 && regs[4] != 0 {
1763 Some(MemOpBuf::Buf32 {
1764 addr: regs[3] as u32,
1765 page_cnt: regs[4] as u32,
1766 })
1767 } else {
1768 None
1769 },
1770 },
1771 FuncId::MemRetrieveReq64 => Self::MemRetrieveReq {
1772 total_len: regs[1] as u32,
1773 frag_len: regs[2] as u32,
1774 buf: if regs[3] != 0 && regs[4] != 0 {
1775 Some(MemOpBuf::Buf64 {
1776 addr: regs[3],
1777 page_cnt: regs[4] as u32,
1778 })
1779 } else {
1780 None
1781 },
1782 },
1783 FuncId::MemRetrieveResp => Self::MemRetrieveResp {
1784 total_len: regs[1] as u32,
1785 frag_len: regs[2] as u32,
1786 },
1787 FuncId::MemRelinquish => Self::MemRelinquish,
1788 FuncId::MemReclaim => Self::MemReclaim {
1789 handle: memory_management::Handle::from([regs[1] as u32, regs[2] as u32]),
1790 flags: regs[3] as u32,
1791 },
1792 FuncId::MemPermGet32 => Self::MemPermGet {
1793 addr: MemAddr::Addr32(regs[1] as u32),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001794 page_cnt: if version >= Version(1, 3) {
1795 Some(regs[2] as u32)
1796 } else {
1797 None
1798 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001799 },
1800 FuncId::MemPermGet64 => Self::MemPermGet {
1801 addr: MemAddr::Addr64(regs[1]),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001802 page_cnt: if version >= Version(1, 3) {
1803 Some(regs[2] as u32)
1804 } else {
1805 None
1806 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01001807 },
1808 FuncId::MemPermSet32 => Self::MemPermSet {
1809 addr: MemAddr::Addr32(regs[1] as u32),
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 },
1813 FuncId::MemPermSet64 => Self::MemPermSet {
1814 addr: MemAddr::Addr64(regs[1]),
1815 page_cnt: regs[2] as u32,
Imre Kisdcb7df22025-06-06 15:24:40 +02001816 mem_perm: (regs[3] as u32).try_into()?,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001817 },
Imre Kis189f18c2025-05-26 19:33:05 +02001818 FuncId::ConsoleLog32 => {
1819 let char_cnt = regs[1] as u8;
1820 if char_cnt > ConsoleLogChars32::MAX_LENGTH {
1821 return Err(Error::InvalidCharacterCount(char_cnt));
1822 }
1823
1824 Self::ConsoleLog {
1825 chars: ConsoleLogChars::Chars32(ConsoleLogChars32 {
1826 char_cnt,
1827 char_lists: [
1828 regs[2] as u32,
1829 regs[3] as u32,
1830 regs[4] as u32,
1831 regs[5] as u32,
1832 regs[6] as u32,
1833 regs[7] as u32,
1834 ],
1835 }),
1836 }
1837 }
Tomás González7ffb6132025-04-03 12:28:58 +01001838 FuncId::NotificationBitmapCreate => {
1839 let tentative_vm_id = regs[1] as u32;
1840 if (tentative_vm_id >> 16) != 0 {
1841 return Err(Error::InvalidVmId(tentative_vm_id));
1842 }
1843 Self::NotificationBitmapCreate {
1844 vm_id: tentative_vm_id as u16,
1845 vcpu_cnt: regs[2] as u32,
1846 }
1847 }
1848 FuncId::NotificationBitmapDestroy => {
1849 let tentative_vm_id = regs[1] as u32;
1850 if (tentative_vm_id >> 16) != 0 {
1851 return Err(Error::InvalidVmId(tentative_vm_id));
1852 }
1853 Self::NotificationBitmapDestroy {
1854 vm_id: tentative_vm_id as u16,
1855 }
1856 }
1857 FuncId::NotificationBind => Self::NotificationBind {
1858 sender_id: (regs[1] >> 16) as u16,
1859 receiver_id: regs[1] as u16,
1860 flags: (regs[2] as u32).into(),
1861 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1862 },
Imre Kis3571f2c2025-05-26 19:29:23 +02001863 FuncId::NotificationUnbind => Self::NotificationUnbind {
Tomás González7ffb6132025-04-03 12:28:58 +01001864 sender_id: (regs[1] >> 16) as u16,
1865 receiver_id: regs[1] as u16,
1866 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1867 },
1868 FuncId::NotificationSet => Self::NotificationSet {
1869 sender_id: (regs[1] >> 16) as u16,
1870 receiver_id: regs[1] as u16,
1871 flags: (regs[2] as u32).try_into()?,
1872 bitmap: (regs[4] << 32) | (regs[3] & 0xffff_ffff),
1873 },
1874 FuncId::NotificationGet => Self::NotificationGet {
1875 vcpu_id: (regs[1] >> 16) as u16,
1876 endpoint_id: regs[1] as u16,
1877 flags: (regs[2] as u32).into(),
1878 },
1879 FuncId::NotificationInfoGet32 => Self::NotificationInfoGet { is_32bit: true },
1880 FuncId::NotificationInfoGet64 => Self::NotificationInfoGet { is_32bit: false },
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01001881 FuncId::El3IntrHandle => Self::El3IntrHandle,
Balint Dobszayde0dc802025-02-28 14:16:52 +01001882 _ => panic!("Invalid number of registers (8) for function {:#x?}", fid),
Balint Dobszay3aad9572025-01-17 16:54:11 +01001883 };
1884
1885 Ok(msg)
1886 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01001887
Balint Dobszayde0dc802025-02-28 14:16:52 +01001888 fn unpack_regs18(version: Version, regs: &[u64; 18]) -> Result<Self, Error> {
1889 assert!(version >= Version(1, 2));
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001890
Balint Dobszayde0dc802025-02-28 14:16:52 +01001891 let fid = FuncId::try_from(regs[0] as u32)?;
1892
1893 let msg = match fid {
1894 FuncId::Success64 => Self::Success {
Imre Kise521a282025-06-13 13:29:24 +02001895 target_info: (regs[1] as u32).into(),
Imre Kis54773b62025-04-10 13:47:39 +02001896 args: SuccessArgs::Args64_2(regs[2..18].try_into().unwrap()),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001897 },
1898 FuncId::MsgSendDirectReq64_2 => Self::MsgSendDirectReq2 {
1899 src_id: (regs[1] >> 16) as u16,
1900 dst_id: regs[1] as u16,
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00001901 uuid: Uuid::from_u64_pair(regs[2].swap_bytes(), regs[3].swap_bytes()),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001902 args: DirectMsg2Args(regs[4..18].try_into().unwrap()),
1903 },
1904 FuncId::MsgSendDirectResp64_2 => Self::MsgSendDirectResp2 {
1905 src_id: (regs[1] >> 16) as u16,
1906 dst_id: regs[1] as u16,
1907 args: DirectMsg2Args(regs[4..18].try_into().unwrap()),
1908 },
Imre Kis189f18c2025-05-26 19:33:05 +02001909 FuncId::ConsoleLog64 => {
1910 let char_cnt = regs[1] as u8;
1911 if char_cnt > ConsoleLogChars64::MAX_LENGTH {
1912 return Err(Error::InvalidCharacterCount(char_cnt));
1913 }
1914
1915 Self::ConsoleLog {
1916 chars: ConsoleLogChars::Chars64(ConsoleLogChars64 {
1917 char_cnt,
1918 char_lists: regs[2..18].try_into().unwrap(),
1919 }),
1920 }
1921 }
Tomás González0a058bc2025-03-11 11:20:55 +00001922 FuncId::PartitionInfoGetRegs => {
1923 // Bits[15:0]: Start index
1924 let start_index = (regs[3] & 0xffff) as u16;
1925 let info_tag = ((regs[3] >> 16) & 0xffff) as u16;
1926 Self::PartitionInfoGetRegs {
1927 uuid: Uuid::from_u64_pair(regs[1].swap_bytes(), regs[2].swap_bytes()),
1928 start_index,
1929 info_tag: if start_index == 0 && info_tag != 0 {
1930 return Err(Error::InvalidInformationTag(info_tag));
1931 } else {
1932 info_tag
1933 },
1934 }
1935 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01001936 _ => panic!("Invalid number of registers (18) for function {:#x?}", fid),
1937 };
1938
1939 Ok(msg)
Balint Dobszay3aad9572025-01-17 16:54:11 +01001940 }
1941
Balint Dobszaya5846852025-02-26 15:38:53 +01001942 /// Create register contents for an interface.
Balint Dobszayde0dc802025-02-28 14:16:52 +01001943 pub fn to_regs(&self, version: Version, regs: &mut [u64]) {
Balint Dobszay82c71dd2025-04-15 10:16:44 +02001944 assert!(self.minimum_ffa_version() <= version);
1945
Balint Dobszayde0dc802025-02-28 14:16:52 +01001946 let reg_cnt = regs.len();
1947
1948 match reg_cnt {
1949 8 => {
1950 assert!(version <= Version(1, 1));
Balint Dobszay91bea9b2025-04-09 13:16:06 +02001951 regs.fill(0);
1952
Balint Dobszayde0dc802025-02-28 14:16:52 +01001953 self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
1954 }
1955 18 => {
1956 assert!(version >= Version(1, 2));
Balint Dobszay91bea9b2025-04-09 13:16:06 +02001957 regs.fill(0);
Balint Dobszayde0dc802025-02-28 14:16:52 +01001958
1959 match self {
1960 Interface::ConsoleLog {
Imre Kis189f18c2025-05-26 19:33:05 +02001961 chars: ConsoleLogChars::Chars64(_),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001962 ..
1963 }
1964 | Interface::Success {
Imre Kis54773b62025-04-10 13:47:39 +02001965 args: SuccessArgs::Args64_2(_),
Balint Dobszayde0dc802025-02-28 14:16:52 +01001966 ..
1967 }
1968 | Interface::MsgSendDirectReq2 { .. }
Tomás González0a058bc2025-03-11 11:20:55 +00001969 | Interface::MsgSendDirectResp2 { .. }
1970 | Interface::PartitionInfoGetRegs { .. } => {
Balint Dobszayde0dc802025-02-28 14:16:52 +01001971 self.pack_regs18(version, regs.try_into().unwrap());
1972 }
1973 _ => {
1974 self.pack_regs8(version, (&mut regs[..8]).try_into().unwrap());
1975 }
1976 }
1977 }
1978 _ => panic!("Invalid number of registers {}", reg_cnt),
1979 }
1980 }
1981
1982 fn pack_regs8(&self, version: Version, a: &mut [u64; 8]) {
Balint Dobszay3aad9572025-01-17 16:54:11 +01001983 if let Some(function_id) = self.function_id() {
1984 a[0] = function_id as u64;
1985 }
1986
1987 match *self {
1988 Interface::Error {
1989 target_info,
1990 error_code,
Balint Dobszayb727aab2025-04-07 10:24:59 +02001991 error_arg,
Balint Dobszay3aad9572025-01-17 16:54:11 +01001992 } => {
1993 a[1] = u32::from(target_info).into();
1994 a[2] = (error_code as u32).into();
Balint Dobszayb727aab2025-04-07 10:24:59 +02001995 a[3] = error_arg.into();
Balint Dobszay3aad9572025-01-17 16:54:11 +01001996 }
1997 Interface::Success { target_info, args } => {
Imre Kise521a282025-06-13 13:29:24 +02001998 a[1] = u32::from(target_info).into();
Balint Dobszay3aad9572025-01-17 16:54:11 +01001999 match args {
Imre Kis54773b62025-04-10 13:47:39 +02002000 SuccessArgs::Args32(regs) => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01002001 a[2] = regs[0].into();
2002 a[3] = regs[1].into();
2003 a[4] = regs[2].into();
2004 a[5] = regs[3].into();
2005 a[6] = regs[4].into();
2006 a[7] = regs[5].into();
2007 }
Imre Kis54773b62025-04-10 13:47:39 +02002008 SuccessArgs::Args64(regs) => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01002009 a[2] = regs[0];
2010 a[3] = regs[1];
2011 a[4] = regs[2];
2012 a[5] = regs[3];
2013 a[6] = regs[4];
2014 a[7] = regs[5];
2015 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01002016 _ => panic!("{:#x?} requires 18 registers", args),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002017 }
2018 }
2019 Interface::Interrupt {
2020 target_info,
2021 interrupt_id,
2022 } => {
2023 a[1] = u32::from(target_info).into();
2024 a[2] = interrupt_id.into();
2025 }
2026 Interface::Version { input_version } => {
2027 a[1] = u32::from(input_version).into();
2028 }
2029 Interface::VersionOut { output_version } => {
2030 a[0] = u32::from(output_version).into();
2031 }
2032 Interface::Features {
2033 feat_id,
2034 input_properties,
2035 } => {
2036 a[1] = u32::from(feat_id).into();
2037 a[2] = input_properties.into();
2038 }
2039 Interface::RxAcquire { vm_id } => {
2040 a[1] = vm_id.into();
2041 }
2042 Interface::RxRelease { vm_id } => {
2043 a[1] = vm_id.into();
2044 }
2045 Interface::RxTxMap { addr, page_cnt } => {
2046 match addr {
2047 RxTxAddr::Addr32 { rx, tx } => {
2048 a[1] = tx.into();
2049 a[2] = rx.into();
2050 }
2051 RxTxAddr::Addr64 { rx, tx } => {
2052 a[1] = tx;
2053 a[2] = rx;
2054 }
2055 }
2056 a[3] = page_cnt.into();
2057 }
2058 Interface::RxTxUnmap { id } => {
2059 a[1] = id.into();
2060 }
2061 Interface::PartitionInfoGet { uuid, flags } => {
2062 let bytes = uuid.into_bytes();
2063 a[1] = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]).into();
2064 a[2] = u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]).into();
2065 a[3] = u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]).into();
2066 a[4] = u32::from_le_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]).into();
Imre Kise295adb2025-04-10 13:26:28 +02002067 a[5] = u32::from(flags).into();
Balint Dobszay3aad9572025-01-17 16:54:11 +01002068 }
Tomás González092202a2025-03-05 11:56:45 +00002069 Interface::MsgWait { flags } => {
2070 if version >= Version(1, 2) {
2071 if let Some(flags) = flags {
2072 a[2] = u32::from(flags).into();
2073 }
2074 }
2075 }
2076 Interface::IdGet | Interface::SpmIdGet | Interface::Yield => {}
Balint Dobszay3aad9572025-01-17 16:54:11 +01002077 Interface::Run { target_info } => {
2078 a[1] = u32::from(target_info).into();
2079 }
2080 Interface::NormalWorldResume => {}
Tomás González17b92442025-03-10 16:45:04 +00002081 Interface::SecondaryEpRegister { entrypoint } => match entrypoint {
2082 SecondaryEpRegisterAddr::Addr32(addr) => a[1] = addr as u64,
2083 SecondaryEpRegisterAddr::Addr64(addr) => a[1] = addr,
2084 },
Balint Dobszay3aad9572025-01-17 16:54:11 +01002085 Interface::MsgSend2 {
2086 sender_vm_id,
2087 flags,
2088 } => {
2089 a[1] = sender_vm_id.into();
2090 a[2] = flags.into();
2091 }
2092 Interface::MsgSendDirectReq {
2093 src_id,
2094 dst_id,
Balint Dobszay3aad9572025-01-17 16:54:11 +01002095 args,
2096 } => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +01002097 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Balint Dobszay3aad9572025-01-17 16:54:11 +01002098 match args {
2099 DirectMsgArgs::Args32(args) => {
2100 a[3] = args[0].into();
2101 a[4] = args[1].into();
2102 a[5] = args[2].into();
2103 a[6] = args[3].into();
2104 a[7] = args[4].into();
2105 }
2106 DirectMsgArgs::Args64(args) => {
2107 a[3] = args[0];
2108 a[4] = args[1];
2109 a[5] = args[2];
2110 a[6] = args[3];
2111 a[7] = args[4];
2112 }
Tomás González4d5b0ba2025-03-03 17:15:55 +00002113 DirectMsgArgs::VersionReq { version } => {
2114 a[2] = DirectMsgArgs::VERSION_REQ.into();
2115 a[3] = u32::from(version).into();
2116 }
Tomás González67f92c72025-03-20 16:50:42 +00002117 DirectMsgArgs::PowerPsciReq32 { params } => {
Tomás González4d5b0ba2025-03-03 17:15:55 +00002118 a[2] = DirectMsgArgs::POWER_PSCI_REQ.into();
Tomás González67f92c72025-03-20 16:50:42 +00002119 a[3] = params[0].into();
2120 a[4] = params[1].into();
2121 a[5] = params[2].into();
2122 a[6] = params[3].into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00002123 }
Tomás González67f92c72025-03-20 16:50:42 +00002124 DirectMsgArgs::PowerPsciReq64 { params } => {
Tomás González4d5b0ba2025-03-03 17:15:55 +00002125 a[2] = DirectMsgArgs::POWER_PSCI_REQ.into();
Tomás González67f92c72025-03-20 16:50:42 +00002126 a[3] = params[0];
2127 a[4] = params[1];
2128 a[5] = params[2];
2129 a[6] = params[3];
Tomás González4d5b0ba2025-03-03 17:15:55 +00002130 }
2131 DirectMsgArgs::PowerWarmBootReq { boot_type } => {
2132 a[2] = DirectMsgArgs::POWER_WARM_BOOT_REQ.into();
2133 a[3] = u32::from(boot_type).into();
2134 }
2135 DirectMsgArgs::VmCreated { handle, vm_id } => {
2136 a[2] = DirectMsgArgs::VM_CREATED.into();
2137 let handle_regs: [u32; 2] = handle.into();
2138 a[3] = handle_regs[0].into();
2139 a[4] = handle_regs[1].into();
2140 a[5] = vm_id.into();
2141 }
2142 DirectMsgArgs::VmDestructed { handle, vm_id } => {
2143 a[2] = DirectMsgArgs::VM_DESTRUCTED.into();
2144 let handle_regs: [u32; 2] = handle.into();
2145 a[3] = handle_regs[0].into();
2146 a[4] = handle_regs[1].into();
2147 a[5] = vm_id.into();
2148 }
2149 _ => panic!("Malformed MsgSendDirectReq interface"),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002150 }
2151 }
2152 Interface::MsgSendDirectResp {
2153 src_id,
2154 dst_id,
Balint Dobszay3aad9572025-01-17 16:54:11 +01002155 args,
2156 } => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +01002157 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Balint Dobszay3aad9572025-01-17 16:54:11 +01002158 match args {
2159 DirectMsgArgs::Args32(args) => {
2160 a[3] = args[0].into();
2161 a[4] = args[1].into();
2162 a[5] = args[2].into();
2163 a[6] = args[3].into();
2164 a[7] = args[4].into();
2165 }
2166 DirectMsgArgs::Args64(args) => {
2167 a[3] = args[0];
2168 a[4] = args[1];
2169 a[5] = args[2];
2170 a[6] = args[3];
2171 a[7] = args[4];
2172 }
Tomás González4d5b0ba2025-03-03 17:15:55 +00002173 DirectMsgArgs::VersionResp { version } => {
2174 a[2] = DirectMsgArgs::VERSION_RESP.into();
2175 match version {
Tomás González67f92c72025-03-20 16:50:42 +00002176 None => a[3] = (i32::from(FfaError::NotSupported) as u32).into(),
Tomás González4d5b0ba2025-03-03 17:15:55 +00002177 Some(ver) => a[3] = u32::from(ver).into(),
2178 }
2179 }
2180 DirectMsgArgs::PowerPsciResp { psci_status } => {
2181 a[2] = DirectMsgArgs::POWER_PSCI_RESP.into();
Imre Kisb2d3c882025-04-11 14:19:35 +02002182 a[3] = (psci_status as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00002183 }
2184 DirectMsgArgs::VmCreatedAck { sp_status } => {
2185 a[2] = DirectMsgArgs::VM_CREATED_ACK.into();
Tomás González67f92c72025-03-20 16:50:42 +00002186 a[3] = (i32::from(sp_status) as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00002187 }
2188 DirectMsgArgs::VmDestructedAck { sp_status } => {
2189 a[2] = DirectMsgArgs::VM_DESTRUCTED_ACK.into();
Tomás González67f92c72025-03-20 16:50:42 +00002190 a[3] = (i32::from(sp_status) as u32).into();
Tomás González4d5b0ba2025-03-03 17:15:55 +00002191 }
2192 _ => panic!("Malformed MsgSendDirectResp interface"),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002193 }
2194 }
2195 Interface::MemDonate {
2196 total_len,
2197 frag_len,
2198 buf,
2199 } => {
2200 a[1] = total_len.into();
2201 a[2] = frag_len.into();
2202 (a[3], a[4]) = match buf {
2203 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
2204 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
2205 None => (0, 0),
2206 };
2207 }
2208 Interface::MemLend {
2209 total_len,
2210 frag_len,
2211 buf,
2212 } => {
2213 a[1] = total_len.into();
2214 a[2] = frag_len.into();
2215 (a[3], a[4]) = match buf {
2216 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
2217 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
2218 None => (0, 0),
2219 };
2220 }
2221 Interface::MemShare {
2222 total_len,
2223 frag_len,
2224 buf,
2225 } => {
2226 a[1] = total_len.into();
2227 a[2] = frag_len.into();
2228 (a[3], a[4]) = match buf {
2229 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
2230 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
2231 None => (0, 0),
2232 };
2233 }
2234 Interface::MemRetrieveReq {
2235 total_len,
2236 frag_len,
2237 buf,
2238 } => {
2239 a[1] = total_len.into();
2240 a[2] = frag_len.into();
2241 (a[3], a[4]) = match buf {
2242 Some(MemOpBuf::Buf32 { addr, page_cnt }) => (addr.into(), page_cnt.into()),
2243 Some(MemOpBuf::Buf64 { addr, page_cnt }) => (addr, page_cnt.into()),
2244 None => (0, 0),
2245 };
2246 }
2247 Interface::MemRetrieveResp {
2248 total_len,
2249 frag_len,
2250 } => {
2251 a[1] = total_len.into();
2252 a[2] = frag_len.into();
2253 }
2254 Interface::MemRelinquish => {}
2255 Interface::MemReclaim { handle, flags } => {
2256 let handle_regs: [u32; 2] = handle.into();
2257 a[1] = handle_regs[0].into();
2258 a[2] = handle_regs[1].into();
2259 a[3] = flags.into();
2260 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01002261 Interface::MemPermGet { addr, page_cnt } => {
Balint Dobszay3aad9572025-01-17 16:54:11 +01002262 a[1] = match addr {
2263 MemAddr::Addr32(addr) => addr.into(),
2264 MemAddr::Addr64(addr) => addr,
2265 };
Balint Dobszayde0dc802025-02-28 14:16:52 +01002266 a[2] = if version >= Version(1, 3) {
2267 page_cnt.unwrap().into()
2268 } else {
2269 assert!(page_cnt.is_none());
2270 0
2271 }
Balint Dobszay3aad9572025-01-17 16:54:11 +01002272 }
2273 Interface::MemPermSet {
2274 addr,
2275 page_cnt,
2276 mem_perm,
2277 } => {
2278 a[1] = match addr {
2279 MemAddr::Addr32(addr) => addr.into(),
2280 MemAddr::Addr64(addr) => addr,
2281 };
2282 a[2] = page_cnt.into();
Imre Kisdcb7df22025-06-06 15:24:40 +02002283 a[3] = u32::from(mem_perm).into();
Balint Dobszay3aad9572025-01-17 16:54:11 +01002284 }
Imre Kis189f18c2025-05-26 19:33:05 +02002285 Interface::ConsoleLog { chars } => match chars {
2286 ConsoleLogChars::Chars32(ConsoleLogChars32 {
2287 char_cnt,
2288 char_lists,
2289 }) => {
2290 a[1] = char_cnt.into();
2291 a[2] = char_lists[0].into();
2292 a[3] = char_lists[1].into();
2293 a[4] = char_lists[2].into();
2294 a[5] = char_lists[3].into();
2295 a[6] = char_lists[4].into();
2296 a[7] = char_lists[5].into();
Balint Dobszay3aad9572025-01-17 16:54:11 +01002297 }
Imre Kis189f18c2025-05-26 19:33:05 +02002298 _ => panic!("{:#x?} requires 18 registers", chars),
2299 },
Tomás González7ffb6132025-04-03 12:28:58 +01002300 Interface::NotificationBitmapCreate { vm_id, vcpu_cnt } => {
2301 a[1] = vm_id.into();
2302 a[2] = vcpu_cnt.into();
2303 }
2304 Interface::NotificationBitmapDestroy { vm_id } => {
2305 a[1] = vm_id.into();
2306 }
2307 Interface::NotificationBind {
2308 sender_id,
2309 receiver_id,
2310 flags,
2311 bitmap,
2312 } => {
2313 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
2314 a[2] = u32::from(flags).into();
2315 a[3] = bitmap & 0xffff_ffff;
2316 a[4] = bitmap >> 32;
2317 }
Imre Kis3571f2c2025-05-26 19:29:23 +02002318 Interface::NotificationUnbind {
Tomás González7ffb6132025-04-03 12:28:58 +01002319 sender_id,
2320 receiver_id,
2321 bitmap,
2322 } => {
2323 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
2324 a[3] = bitmap & 0xffff_ffff;
2325 a[4] = bitmap >> 32;
2326 }
2327 Interface::NotificationSet {
2328 sender_id,
2329 receiver_id,
2330 flags,
2331 bitmap,
2332 } => {
2333 a[1] = (u64::from(sender_id) << 16) | u64::from(receiver_id);
2334 a[2] = u32::from(flags).into();
2335 a[3] = bitmap & 0xffff_ffff;
2336 a[4] = bitmap >> 32;
2337 }
2338 Interface::NotificationGet {
2339 vcpu_id,
2340 endpoint_id,
2341 flags,
2342 } => {
2343 a[1] = (u64::from(vcpu_id) << 16) | u64::from(endpoint_id);
2344 a[2] = u32::from(flags).into();
2345 }
2346 Interface::NotificationInfoGet { .. } => {}
Tomás Gonzáleze6fe75f2025-04-04 09:46:50 +01002347 Interface::El3IntrHandle => {}
Balint Dobszayde0dc802025-02-28 14:16:52 +01002348 _ => panic!("{:#x?} requires 18 registers", self),
2349 }
2350 }
2351
2352 fn pack_regs18(&self, version: Version, a: &mut [u64; 18]) {
2353 assert!(version >= Version(1, 2));
2354
Balint Dobszayde0dc802025-02-28 14:16:52 +01002355 if let Some(function_id) = self.function_id() {
2356 a[0] = function_id as u64;
2357 }
2358
2359 match *self {
2360 Interface::Success { target_info, args } => {
Imre Kise521a282025-06-13 13:29:24 +02002361 a[1] = u32::from(target_info).into();
Balint Dobszayde0dc802025-02-28 14:16:52 +01002362 match args {
Imre Kis54773b62025-04-10 13:47:39 +02002363 SuccessArgs::Args64_2(regs) => a[2..18].copy_from_slice(&regs[..16]),
Balint Dobszayde0dc802025-02-28 14:16:52 +01002364 _ => panic!("{:#x?} requires 8 registers", args),
2365 }
2366 }
2367 Interface::MsgSendDirectReq2 {
2368 src_id,
2369 dst_id,
2370 uuid,
2371 args,
2372 } => {
2373 a[1] = ((src_id as u64) << 16) | dst_id as u64;
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00002374 let (uuid_msb, uuid_lsb) = uuid.as_u64_pair();
2375 (a[2], a[3]) = (uuid_msb.swap_bytes(), uuid_lsb.swap_bytes());
Balint Dobszayde0dc802025-02-28 14:16:52 +01002376 a[4..18].copy_from_slice(&args.0[..14]);
2377 }
2378 Interface::MsgSendDirectResp2 {
2379 src_id,
2380 dst_id,
2381 args,
2382 } => {
2383 a[1] = ((src_id as u64) << 16) | dst_id as u64;
2384 a[2] = 0;
2385 a[3] = 0;
2386 a[4..18].copy_from_slice(&args.0[..14]);
2387 }
Imre Kis189f18c2025-05-26 19:33:05 +02002388 Interface::ConsoleLog { chars: char_lists } => match char_lists {
2389 ConsoleLogChars::Chars64(ConsoleLogChars64 {
2390 char_cnt,
2391 char_lists,
2392 }) => {
2393 a[1] = char_cnt.into();
2394 a[2..18].copy_from_slice(&char_lists[..16])
Balint Dobszayde0dc802025-02-28 14:16:52 +01002395 }
Imre Kis189f18c2025-05-26 19:33:05 +02002396 _ => panic!("{:#x?} requires 8 registers", char_lists),
2397 },
Tomás González0a058bc2025-03-11 11:20:55 +00002398 Interface::PartitionInfoGetRegs {
2399 uuid,
2400 start_index,
2401 info_tag,
2402 } => {
2403 if start_index == 0 && info_tag != 0 {
2404 panic!("Information Tag MBZ if start index is 0: {:#x?}", self);
2405 }
2406 let (uuid_msb, uuid_lsb) = uuid.as_u64_pair();
2407 (a[1], a[2]) = (uuid_msb.swap_bytes(), uuid_lsb.swap_bytes());
2408 a[3] = (u64::from(info_tag) << 16) | u64::from(start_index);
2409 }
Balint Dobszayde0dc802025-02-28 14:16:52 +01002410 _ => panic!("{:#x?} requires 8 registers", self),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002411 }
2412 }
2413
Balint Dobszaya5846852025-02-26 15:38:53 +01002414 /// Helper function to create an `FFA_SUCCESS` interface without any arguments.
Balint Dobszay3aad9572025-01-17 16:54:11 +01002415 pub fn success32_noargs() -> Self {
2416 Self::Success {
Imre Kise521a282025-06-13 13:29:24 +02002417 target_info: TargetInfo::default(),
Imre Kis54773b62025-04-10 13:47:39 +02002418 args: SuccessArgs::Args32([0; 6]),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002419 }
2420 }
2421
Balint Dobszaya5846852025-02-26 15:38:53 +01002422 /// Helper function to create an `FFA_ERROR` interface with an error code.
Balint Dobszay3aad9572025-01-17 16:54:11 +01002423 pub fn error(error_code: FfaError) -> Self {
2424 Self::Error {
Imre Kise521a282025-06-13 13:29:24 +02002425 target_info: TargetInfo::default(),
Balint Dobszay3aad9572025-01-17 16:54:11 +01002426 error_code,
Balint Dobszayb727aab2025-04-07 10:24:59 +02002427 error_arg: 0,
Balint Dobszay3aad9572025-01-17 16:54:11 +01002428 }
2429 }
2430}
2431
Tomás González0a058bc2025-03-11 11:20:55 +00002432#[cfg(test)]
2433mod tests {
2434 use super::*;
2435
2436 #[test]
Balint Dobszay5ded5922025-06-13 12:06:53 +02002437 fn version_reg_count() {
2438 assert!(!Version(1, 1).needs_18_regs());
2439 assert!(Version(1, 2).needs_18_regs())
2440 }
2441
2442 #[test]
Tomás González0a058bc2025-03-11 11:20:55 +00002443 fn part_info_get_regs() {
2444 let uuid = Uuid::parse_str("a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8").unwrap();
2445 let uuid_bytes = uuid.as_bytes();
2446 let test_info_tag = 0b1101_1101;
2447 let test_start_index = 0b1101;
2448 let start_index_and_tag = (test_info_tag << 16) | test_start_index;
2449 let version = Version(1, 2);
2450
2451 // From spec:
2452 // Bytes[0...7] of UUID with byte 0 in the low-order bits.
Balint Dobszayb2e9bed2025-04-15 12:57:36 +02002453 let reg_x1 = ((uuid_bytes[7] as u64) << 56)
2454 | ((uuid_bytes[6] as u64) << 48)
2455 | ((uuid_bytes[5] as u64) << 40)
2456 | ((uuid_bytes[4] as u64) << 32)
2457 | ((uuid_bytes[3] as u64) << 24)
2458 | ((uuid_bytes[2] as u64) << 16)
2459 | ((uuid_bytes[1] as u64) << 8)
Tomás González0a058bc2025-03-11 11:20:55 +00002460 | (uuid_bytes[0] as u64);
2461
2462 // From spec:
2463 // Bytes[8...15] of UUID with byte 8 in the low-order bits.
Balint Dobszayb2e9bed2025-04-15 12:57:36 +02002464 let reg_x2 = ((uuid_bytes[15] as u64) << 56)
2465 | ((uuid_bytes[14] as u64) << 48)
2466 | ((uuid_bytes[13] as u64) << 40)
2467 | ((uuid_bytes[12] as u64) << 32)
2468 | ((uuid_bytes[11] as u64) << 24)
2469 | ((uuid_bytes[10] as u64) << 16)
2470 | ((uuid_bytes[9] as u64) << 8)
Tomás González0a058bc2025-03-11 11:20:55 +00002471 | (uuid_bytes[8] as u64);
2472
2473 // First, test for wrong tag:
2474 {
2475 let mut regs = [0u64; 18];
2476 regs[0] = FuncId::PartitionInfoGetRegs as u64;
2477 regs[1] = reg_x1;
2478 regs[2] = reg_x2;
2479 regs[3] = test_info_tag << 16;
2480
2481 assert!(Interface::from_regs(version, &regs).is_err_and(
2482 |e| e == Error::InvalidInformationTag(test_info_tag.try_into().unwrap())
2483 ));
2484 }
2485
2486 // Test for regs -> Interface -> regs
2487 {
2488 let mut orig_regs = [0u64; 18];
2489 orig_regs[0] = FuncId::PartitionInfoGetRegs as u64;
2490 orig_regs[1] = reg_x1;
2491 orig_regs[2] = reg_x2;
2492 orig_regs[3] = start_index_and_tag;
2493
Balint Dobszayb2e9bed2025-04-15 12:57:36 +02002494 let mut test_regs = orig_regs;
2495 let interface = Interface::from_regs(version, &test_regs).unwrap();
Tomás González0a058bc2025-03-11 11:20:55 +00002496 match &interface {
2497 Interface::PartitionInfoGetRegs {
2498 info_tag,
2499 start_index,
2500 uuid: int_uuid,
2501 } => {
2502 assert_eq!(u64::from(*info_tag), test_info_tag);
2503 assert_eq!(u64::from(*start_index), test_start_index);
2504 assert_eq!(*int_uuid, uuid);
2505 }
2506 _ => panic!("Expecting Interface::PartitionInfoGetRegs!"),
2507 }
2508 test_regs.fill(0);
2509 interface.to_regs(version, &mut test_regs);
2510 assert_eq!(orig_regs, test_regs);
2511 }
2512
2513 // Test for Interface -> regs -> Interface
2514 {
2515 let interface = Interface::PartitionInfoGetRegs {
2516 info_tag: test_info_tag.try_into().unwrap(),
2517 start_index: test_start_index.try_into().unwrap(),
2518 uuid,
2519 };
2520
2521 let mut regs: [u64; 18] = [0; 18];
2522 interface.to_regs(version, &mut regs);
2523
2524 assert_eq!(Some(FuncId::PartitionInfoGetRegs), interface.function_id());
2525 assert_eq!(regs[0], interface.function_id().unwrap() as u64);
2526 assert_eq!(regs[1], reg_x1);
2527 assert_eq!(regs[2], reg_x2);
2528 assert_eq!(regs[3], (test_info_tag << 16) | test_start_index);
2529
2530 assert_eq!(Interface::from_regs(version, &regs).unwrap(), interface);
2531 }
2532 }
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00002533
2534 #[test]
2535 fn msg_send_direct_req2() {
2536 let uuid = Uuid::parse_str("a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8").unwrap();
2537 let uuid_bytes = uuid.as_bytes();
2538
2539 // From spec:
2540 // Bytes[0...7] of UUID with byte 0 in the low-order bits.
Balint Dobszayb2e9bed2025-04-15 12:57:36 +02002541 let reg_x2 = ((uuid_bytes[7] as u64) << 56)
2542 | ((uuid_bytes[6] as u64) << 48)
2543 | ((uuid_bytes[5] as u64) << 40)
2544 | ((uuid_bytes[4] as u64) << 32)
2545 | ((uuid_bytes[3] as u64) << 24)
2546 | ((uuid_bytes[2] as u64) << 16)
2547 | ((uuid_bytes[1] as u64) << 8)
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00002548 | (uuid_bytes[0] as u64);
2549
2550 // From spec:
2551 // Bytes[8...15] of UUID with byte 8 in the low-order bits.
Balint Dobszayb2e9bed2025-04-15 12:57:36 +02002552 let reg_x3 = ((uuid_bytes[15] as u64) << 56)
2553 | ((uuid_bytes[14] as u64) << 48)
2554 | ((uuid_bytes[13] as u64) << 40)
2555 | ((uuid_bytes[12] as u64) << 32)
2556 | ((uuid_bytes[11] as u64) << 24)
2557 | ((uuid_bytes[10] as u64) << 16)
2558 | ((uuid_bytes[9] as u64) << 8)
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00002559 | (uuid_bytes[8] as u64);
2560
2561 let test_sender = 0b1101_1101;
2562 let test_receiver = 0b1101;
2563 let test_sender_receiver = (test_sender << 16) | test_receiver;
2564 let version = Version(1, 2);
2565
2566 // Test for regs -> Interface -> regs
2567 {
2568 let mut orig_regs = [0u64; 18];
2569 orig_regs[0] = FuncId::MsgSendDirectReq64_2 as u64;
2570 orig_regs[1] = test_sender_receiver;
2571 orig_regs[2] = reg_x2;
2572 orig_regs[3] = reg_x3;
2573
Balint Dobszayb2e9bed2025-04-15 12:57:36 +02002574 let mut test_regs = orig_regs;
2575 let interface = Interface::from_regs(version, &test_regs).unwrap();
Tomás Gonzálezce3bc222025-03-25 14:30:42 +00002576 match &interface {
2577 Interface::MsgSendDirectReq2 {
2578 dst_id,
2579 src_id,
2580 args: _,
2581 uuid: int_uuid,
2582 } => {
2583 assert_eq!(u64::from(*src_id), test_sender);
2584 assert_eq!(u64::from(*dst_id), test_receiver);
2585 assert_eq!(*int_uuid, uuid);
2586 }
2587 _ => panic!("Expecting Interface::MsgSendDirectReq2!"),
2588 }
2589 test_regs.fill(0);
2590 interface.to_regs(version, &mut test_regs);
2591 assert_eq!(orig_regs, test_regs);
2592 }
2593
2594 // Test for Interface -> regs -> Interface
2595 {
2596 let rest_of_regs: [u64; 14] = [0; 14];
2597
2598 let interface = Interface::MsgSendDirectReq2 {
2599 src_id: test_sender.try_into().unwrap(),
2600 dst_id: test_receiver.try_into().unwrap(),
2601 uuid,
2602 args: DirectMsg2Args(rest_of_regs),
2603 };
2604
2605 let mut regs: [u64; 18] = [0; 18];
2606 interface.to_regs(version, &mut regs);
2607
2608 assert_eq!(Some(FuncId::MsgSendDirectReq64_2), interface.function_id());
2609 assert_eq!(regs[0], interface.function_id().unwrap() as u64);
2610 assert_eq!(regs[1], test_sender_receiver);
2611 assert_eq!(regs[2], reg_x2);
2612 assert_eq!(regs[3], reg_x3);
2613 assert_eq!(regs[4], 0);
2614
2615 assert_eq!(Interface::from_regs(version, &regs).unwrap(), interface);
2616 }
2617 }
Tomás González6ccba0a2025-04-09 13:31:29 +01002618
2619 #[test]
2620 fn is_32bit() {
2621 let interface_64 = Interface::MsgSendDirectReq {
2622 src_id: 0,
2623 dst_id: 1,
2624 args: DirectMsgArgs::Args64([0, 0, 0, 0, 0]),
2625 };
2626 assert!(!interface_64.is_32bit());
2627
2628 let interface_32 = Interface::MsgSendDirectReq {
2629 src_id: 0,
2630 dst_id: 1,
2631 args: DirectMsgArgs::Args32([0, 0, 0, 0, 0]),
2632 };
2633 assert!(interface_32.is_32bit());
2634 }
Imre Kis787c5002025-04-10 14:25:51 +02002635
2636 #[test]
2637 fn success_args_notification_info_get32() {
2638 let mut notifications = SuccessArgsNotificationInfoGet32::default();
2639
2640 // 16.7.1.1 Example usage
2641 notifications.add_list(0x0000, &[0, 2, 3]).unwrap();
2642 notifications.add_list(0x0000, &[4, 6]).unwrap();
2643 notifications.add_list(0x0002, &[]).unwrap();
2644 notifications.add_list(0x0003, &[1]).unwrap();
2645
2646 let args: SuccessArgs = notifications.into();
2647 assert_eq!(
2648 SuccessArgs::Args32([
2649 0x0004_b200,
2650 0x0000_0000,
2651 0x0003_0002,
2652 0x0004_0000,
2653 0x0002_0006,
2654 0x0001_0003
2655 ]),
2656 args
2657 );
2658
2659 let notifications = SuccessArgsNotificationInfoGet32::try_from(args).unwrap();
2660 let mut iter = notifications.iter();
2661 assert_eq!(Some((0x0000, &[0, 2, 3][..])), iter.next());
2662 assert_eq!(Some((0x0000, &[4, 6][..])), iter.next());
2663 assert_eq!(Some((0x0002, &[][..])), iter.next());
2664 assert_eq!(Some((0x0003, &[1][..])), iter.next());
2665 }
2666
2667 #[test]
2668 fn success_args_notification_info_get64() {
2669 let mut notifications = SuccessArgsNotificationInfoGet64::default();
2670
2671 // 16.7.1.1 Example usage
2672 notifications.add_list(0x0000, &[0, 2, 3]).unwrap();
2673 notifications.add_list(0x0000, &[4, 6]).unwrap();
2674 notifications.add_list(0x0002, &[]).unwrap();
2675 notifications.add_list(0x0003, &[1]).unwrap();
2676
2677 let args: SuccessArgs = notifications.into();
2678 assert_eq!(
2679 SuccessArgs::Args64([
2680 0x0004_b200,
2681 0x0003_0002_0000_0000,
2682 0x0002_0006_0004_0000,
2683 0x0000_0000_0001_0003,
2684 0x0000_0000_0000_0000,
2685 0x0000_0000_0000_0000,
2686 ]),
2687 args
2688 );
2689
2690 let notifications = SuccessArgsNotificationInfoGet64::try_from(args).unwrap();
2691 let mut iter = notifications.iter();
2692 assert_eq!(Some((0x0000, &[0, 2, 3][..])), iter.next());
2693 assert_eq!(Some((0x0000, &[4, 6][..])), iter.next());
2694 assert_eq!(Some((0x0002, &[][..])), iter.next());
2695 assert_eq!(Some((0x0003, &[1][..])), iter.next());
2696 }
Tomás González0a058bc2025-03-11 11:20:55 +00002697}