1#![cfg_attr(not(test), no_std)]
5#![deny(clippy::undocumented_unsafe_blocks)]
6#![deny(unsafe_op_in_unsafe_fn)]
7#![doc = include_str!("../README.md")]
8
9pub mod boot_info;
10mod ffa_v1_1;
11mod ffa_v1_2;
12pub mod interface;
13pub mod interface_args;
14pub mod memory_management;
15pub mod notification;
16pub mod partition_info;
17
18use core::fmt::{self, Debug, Display, Formatter};
19pub use interface::Interface;
20use num_enum::{IntoPrimitive, TryFromPrimitive};
21use thiserror::Error;
22pub use uuid::Uuid;
23
24pub const FFA_PAGE_SIZE_4K: usize = 4096;
27
28#[derive(Debug, Error, PartialEq, Eq, Clone, Copy)]
31pub enum Error {
32 #[error("Unrecognised FF-A function ID {0}")]
33 UnrecognisedFunctionId(u32),
34 #[error("Unrecognised FF-A feature ID {0}")]
35 UnrecognisedFeatureId(u8),
36 #[error("Unrecognised FF-A error code {0}")]
37 UnrecognisedErrorCode(i32),
38 #[error("Unrecognised FF-A Framework Message {0}")]
39 UnrecognisedFwkMsg(u32),
40 #[error("Invalid FF-A Msg Wait Flag {0}")]
41 InvalidMsgWaitFlag(u32),
42 #[error("Invalid FF-A Msg Send2 Flag {0}")]
43 InvalidMsgSend2Flag(u32),
44 #[error("Unrecognised VM availability status {0}")]
45 UnrecognisedVmAvailabilityStatus(i32),
46 #[error("Unrecognised FF-A Warm Boot Type {0}")]
47 UnrecognisedWarmBootType(u32),
48 #[error("Invalid version {0}")]
49 InvalidVersion(u32),
50 #[error("Invalid Information Tag {0}")]
51 InvalidInformationTag(u16),
52 #[error("Invalid Vm ID")]
53 InvalidVmId(u32),
54 #[error("Invalid success argument variant")]
55 InvalidSuccessArgsVariant,
56 #[error("Invalid FF-A version {0} for function ID {1:?}")]
57 InvalidVersionForFunctionId(Version, FuncId),
58 #[error("Invalid character count {0}")]
59 InvalidCharacterCount(u8),
60 #[error("Invalid register count: expected {expected}, actual {actual}")]
61 InvalidRegisterCount { expected: usize, actual: usize },
62 #[error("Invalid version query type {0}")]
63 InvalidVersionQueryType(u8),
64 #[error("Invalid FF-A version flag {0}")]
65 InvalidVersionFlags(u32),
66 #[error("Memory management error")]
67 MemoryManagementError(#[from] memory_management::Error),
68 #[error("Notification error")]
69 NotificationError(#[from] notification::Error),
70 #[error("Partition info error")]
71 PartitionInfoError(#[from] partition_info::Error),
72}
73
74impl From<Error> for FfaError {
75 fn from(value: Error) -> Self {
76 match value {
77 Error::UnrecognisedFunctionId(_)
78 | Error::UnrecognisedFeatureId(_)
79 | Error::InvalidVersionForFunctionId(..)
80 | Error::InvalidRegisterCount { .. } => Self::NotSupported,
81 Error::InvalidInformationTag(_) => Self::Retry,
82 Error::UnrecognisedErrorCode(_)
83 | Error::UnrecognisedFwkMsg(_)
84 | Error::InvalidVersion(_)
85 | Error::InvalidMsgWaitFlag(_)
86 | Error::InvalidMsgSend2Flag(_)
87 | Error::UnrecognisedVmAvailabilityStatus(_)
88 | Error::InvalidVmId(_)
89 | Error::UnrecognisedWarmBootType(_)
90 | Error::InvalidSuccessArgsVariant
91 | Error::InvalidCharacterCount(_)
92 | Error::InvalidVersionQueryType(_)
93 | Error::InvalidVersionFlags(_) => Self::InvalidParameters,
94 Error::MemoryManagementError(_)
95 | Error::NotificationError(_)
96 | Error::PartitionInfoError(_) => value.into(),
97 }
98 }
99}
100
101#[derive(PartialEq, Clone, Copy)]
103pub enum Instance {
104 SecurePhysical,
106 SecureVirtual(u16),
108}
109
110#[derive(Clone, Copy, Debug, Eq, IntoPrimitive, PartialEq, TryFromPrimitive)]
112#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedFunctionId))]
113#[repr(u32)]
114pub enum FuncId {
115 Error32 = 0x84000060,
116 Error64 = 0xc4000060,
117 Success32 = 0x84000061,
118 Success64 = 0xc4000061,
119 Interrupt32 = 0x84000062,
120 Interrupt64 = 0xc4000062,
121 Version = 0x84000063,
122 Features = 0x84000064,
123 RxAcquire = 0x84000084,
124 RxRelease = 0x84000065,
125 RxTxMap32 = 0x84000066,
126 RxTxMap64 = 0xc4000066,
127 RxTxUnmap = 0x84000067,
128 PartitionInfoGet = 0x84000068,
129 PartitionInfoGetRegs = 0xc400008b,
130 IdGet = 0x84000069,
131 SpmIdGet = 0x84000085,
132 ConsoleLog32 = 0x8400008a,
133 ConsoleLog64 = 0xc400008a,
134 MsgWait32 = 0x8400006b,
135 MsgWait64 = 0xc400006b,
136 Yield32 = 0x8400006c,
137 Yield64 = 0xc400006c,
138 Run32 = 0x8400006d,
139 Run64 = 0xc400006d,
140 NormalWorldResume32 = 0x8400007c,
141 NormalWorldResume64 = 0xc400007c,
142 MsgSend2 = 0x84000086,
143 MsgSendDirectReq32 = 0x8400006f,
144 MsgSendDirectReq64 = 0xc400006f,
145 MsgSendDirectReq64_2 = 0xc400008d,
146 MsgSendDirectResp32 = 0x84000070,
147 MsgSendDirectResp64 = 0xc4000070,
148 MsgSendDirectResp64_2 = 0xc400008e,
149 NotificationBitmapCreate = 0x8400007d,
150 NotificationBitmapDestroy = 0x8400007e,
151 NotificationBind = 0x8400007f,
152 NotificationUnbind = 0x84000080,
153 NotificationSet = 0x84000081,
154 NotificationGet = 0x84000082,
155 NotificationInfoGet32 = 0x84000083,
156 NotificationInfoGet64 = 0xc4000083,
157 El3IntrHandle = 0x8400008c,
158 SecondaryEpRegister32 = 0x84000087,
159 SecondaryEpRegister64 = 0xc4000087,
160 MemDonate32 = 0x84000071,
161 MemDonate64 = 0xc4000071,
162 MemLend32 = 0x84000072,
163 MemLend64 = 0xc4000072,
164 MemShare32 = 0x84000073,
165 MemShare64 = 0xc4000073,
166 MemRetrieveReq32 = 0x84000074,
167 MemRetrieveReq64 = 0xc4000074,
168 MemRetrieveResp = 0x84000075,
169 MemRelinquish = 0x84000076,
170 MemReclaim = 0x84000077,
171 MemPermGet32 = 0x84000088,
172 MemPermGet64 = 0xc4000088,
173 MemPermSet32 = 0x84000089,
174 MemPermSet64 = 0xc4000089,
175 MemOpPause = 0x84000078,
176 MemOpResume = 0x84000079,
177 MemFragRx = 0x8400007a,
178 MemFragTx = 0x8400007b,
179}
180
181impl FuncId {
182 pub fn is_32bit(&self) -> bool {
184 u32::from(*self) & (1 << 30) == 0
185 }
186
187 pub fn minimum_ffa_version(&self) -> Version {
189 match self {
190 FuncId::Error32
191 | FuncId::Success32
192 | FuncId::Success64
193 | FuncId::Interrupt32
194 | FuncId::Version
195 | FuncId::Features
196 | FuncId::RxRelease
197 | FuncId::RxTxMap32
198 | FuncId::RxTxMap64
199 | FuncId::RxTxUnmap
200 | FuncId::PartitionInfoGet
201 | FuncId::IdGet
202 | FuncId::MsgWait32
203 | FuncId::Yield32
204 | FuncId::Run32
205 | FuncId::NormalWorldResume32
206 | FuncId::MsgSendDirectReq32
207 | FuncId::MsgSendDirectReq64
208 | FuncId::MsgSendDirectResp32
209 | FuncId::MsgSendDirectResp64
210 | FuncId::MemDonate32
211 | FuncId::MemDonate64
212 | FuncId::MemLend32
213 | FuncId::MemLend64
214 | FuncId::MemShare32
215 | FuncId::MemShare64
216 | FuncId::MemRetrieveReq32
217 | FuncId::MemRetrieveReq64
218 | FuncId::MemRetrieveResp
219 | FuncId::MemRelinquish
220 | FuncId::MemReclaim
221 | FuncId::MemOpPause
222 | FuncId::MemOpResume
223 | FuncId::MemFragRx
224 | FuncId::MemFragTx => Version(1, 0),
225
226 FuncId::RxAcquire
227 | FuncId::SpmIdGet
228 | FuncId::MsgSend2
229 | FuncId::MemPermGet32
230 | FuncId::MemPermGet64
231 | FuncId::MemPermSet32
232 | FuncId::MemPermSet64
233 | FuncId::NotificationBitmapCreate
234 | FuncId::NotificationBitmapDestroy
235 | FuncId::NotificationBind
236 | FuncId::NotificationUnbind
237 | FuncId::NotificationSet
238 | FuncId::NotificationGet
239 | FuncId::NotificationInfoGet32
240 | FuncId::NotificationInfoGet64
241 | FuncId::SecondaryEpRegister32
242 | FuncId::SecondaryEpRegister64 => Version(1, 1),
243
244 FuncId::PartitionInfoGetRegs
245 | FuncId::ConsoleLog32
246 | FuncId::ConsoleLog64
247 | FuncId::MsgSendDirectReq64_2
248 | FuncId::MsgSendDirectResp64_2
249 | FuncId::El3IntrHandle => Version(1, 2),
250
251 FuncId::Error64
252 | FuncId::Interrupt64
253 | FuncId::MsgWait64
254 | FuncId::Yield64
255 | FuncId::Run64
256 | FuncId::NormalWorldResume64 => Version(1, 3),
257 }
258 }
259}
260
261#[derive(Clone, Copy, Debug, Eq, Error, IntoPrimitive, PartialEq, TryFromPrimitive)]
263#[num_enum(error_type(name = Error, constructor = Error::UnrecognisedErrorCode))]
264#[repr(i32)]
265pub enum FfaError {
266 #[error("Not supported")]
267 NotSupported = -1,
268 #[error("Invalid parameters")]
269 InvalidParameters = -2,
270 #[error("No memory")]
271 NoMemory = -3,
272 #[error("Busy")]
273 Busy = -4,
274 #[error("Interrupted")]
275 Interrupted = -5,
276 #[error("Denied")]
277 Denied = -6,
278 #[error("Retry")]
279 Retry = -7,
280 #[error("Aborted")]
281 Aborted = -8,
282 #[error("No data")]
283 NoData = -9,
284}
285
286pub struct UuidHelper;
289
290impl UuidHelper {
291 pub fn from_bytes(value: [u8; 16]) -> Uuid {
296 Uuid::from_bytes(value)
297 }
298
299 pub fn to_bytes(value: Uuid) -> [u8; 16] {
304 value.into_bytes()
305 }
306
307 pub fn from_u32_regs(value: [u32; 4]) -> Uuid {
312 Uuid::from_u128_le(
313 value[0] as u128
314 | (value[1] as u128) << 32
315 | (value[2] as u128) << 64
316 | (value[3] as u128) << 96,
317 )
318 }
319
320 pub fn to_u32_regs(value: Uuid) -> [u32; 4] {
325 let bits = value.to_u128_le();
326
327 [
328 bits as u32,
329 (bits >> 32) as u32,
330 (bits >> 64) as u32,
331 (bits >> 96) as u32,
332 ]
333 }
334
335 pub fn from_u64_regs(value: [u64; 2]) -> Uuid {
340 Uuid::from_u128_le(value[0] as u128 | (value[1] as u128) << 64)
341 }
342
343 pub fn to_u64_regs(value: Uuid) -> [u64; 2] {
348 let bits = value.to_u128_le();
349 [bits as u64, (bits >> 64) as u64]
350 }
351}
352
353#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord)]
355pub struct Version(pub u16, pub u16);
356
357impl Version {
358 const MBZ_BITS: u32 = 1 << 31;
360
361 pub const NULL: Version = Version(0, 0);
363
364 pub fn is_compatible_to(&self, callee_version: Version) -> bool {
367 self.0 == callee_version.0 && self.1 <= callee_version.1
368 }
369}
370
371impl TryFrom<u32> for Version {
372 type Error = Error;
373
374 fn try_from(val: u32) -> Result<Self, Self::Error> {
375 if (val & Self::MBZ_BITS) != 0 {
376 Err(Error::InvalidVersion(val))
377 } else {
378 Ok(Self((val >> 16) as u16, val as u16))
379 }
380 }
381}
382
383impl From<Version> for u32 {
384 fn from(v: Version) -> Self {
385 let v_u32 = ((v.0 as u32) << 16) | v.1 as u32;
386 assert!(v_u32 & Version::MBZ_BITS == 0);
387 v_u32
388 }
389}
390
391impl Display for Version {
392 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
393 write!(f, "{}.{}", self.0, self.1)
394 }
395}
396
397impl Debug for Version {
398 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
399 Display::fmt(self, f)
400 }
401}
402
403#[derive(Debug, Eq, PartialEq, Clone, Copy)]
406pub enum VersionOut {
407 Version(Version),
408 NotSupported,
409 InvalidParameter,
410}
411
412impl VersionOut {
413 const SMCCC_NOT_SUPPORTED: i32 = -1;
415 const SMCCC_INVALID_PARAMETER: i32 = -3;
417}
418
419impl TryFrom<u32> for VersionOut {
420 type Error = Error;
421
422 fn try_from(value: u32) -> Result<Self, Self::Error> {
423 if value == i32::from(FfaError::NotSupported) as u32 {
424 Ok(Self::NotSupported)
425 } else {
426 Ok(Self::Version(Version::try_from(value)?))
427 }
428 }
429}
430
431impl From<VersionOut> for u32 {
432 fn from(value: VersionOut) -> Self {
433 match value {
435 VersionOut::Version(version) => version.into(),
436 VersionOut::NotSupported => VersionOut::SMCCC_NOT_SUPPORTED as u32,
437 VersionOut::InvalidParameter => VersionOut::SMCCC_INVALID_PARAMETER as u32,
438 }
439 }
440}
441
442#[cfg(test)]
443pub(crate) mod tests {
444 use super::*;
445 use uuid::uuid;
446
447 macro_rules! test_regs_serde {
448 ($value:expr, $bytes:expr) => {
449 let mut regs = [0u64; 18];
450 let mut bytes = [0u64; 18];
451
452 let b: &[u64] = &$bytes;
453 bytes[0..(b.len())].copy_from_slice(&b);
454
455 $value.to_regs(Version(1, 2), &mut regs);
456 assert_eq!(regs, bytes);
457
458 assert_eq!(Interface::from_regs(Version(1, 2), &bytes), Ok($value));
459 };
460 }
461 pub(crate) use test_regs_serde;
462
463 macro_rules! test_args_serde {
464 ($args:expr, $sa:expr) => {
465 assert_eq!($args.try_into(), Ok($sa));
466 assert_eq!($sa.try_into(), Ok($args));
467 };
468 ($args:expr, $sa:expr, $flags:expr) => {
469 assert_eq!($args.try_into(), Ok($sa));
470 assert_eq!(($flags, $sa).try_into(), Ok($args));
471 };
472 }
473 pub(crate) use test_args_serde;
474
475 #[test]
476 fn ffa_uuid_helpers() {
477 const UUID: Uuid = uuid!("a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8");
478
479 let bytes = [
480 0xa1, 0xa2, 0xa3, 0xa4, 0xb1, 0xb2, 0xc1, 0xc2, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6,
481 0xd7, 0xd8,
482 ];
483
484 assert_eq!(UUID, UuidHelper::from_bytes(bytes));
485 assert_eq!(bytes, UuidHelper::to_bytes(UUID));
486
487 let words = [0xa4a3a2a1, 0xc2c1b2b1, 0xd4d3d2d1, 0xd8d7d6d5];
488 assert_eq!(UUID, UuidHelper::from_u32_regs(words));
489 assert_eq!(words, UuidHelper::to_u32_regs(UUID));
490
491 let pair = [0xc2c1b2b1a4a3a2a1, 0xd8d7d6d5d4d3d2d1];
492 assert_eq!(UUID, UuidHelper::from_u64_regs(pair));
493 assert_eq!(pair, UuidHelper::to_u64_regs(UUID));
494 }
495}