Skip to main content

arm_ffa/
lib.rs

1// SPDX-FileCopyrightText: Copyright The arm-ffa Contributors.
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4#![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
24/// Constant for 4K page size. On many occasions the FF-A spec defines memory size as count of 4K
25/// pages, regardless of the current translation granule.
26pub const FFA_PAGE_SIZE_4K: usize = 4096;
27
28/// Rich error types returned by this module. Should be converted to [`crate::FfaError`] when used
29/// with the `FFA_ERROR` interface.
30#[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/// An FF-A instance is a valid combination of two FF-A components at an exception level boundary.
102#[derive(PartialEq, Clone, Copy)]
103pub enum Instance {
104    /// The instance between the SPMC and SPMD.
105    SecurePhysical,
106    /// The instance between the SPMC and a physical SP (contains the SP's endpoint ID).
107    SecureVirtual(u16),
108}
109
110/// Function IDs of the various FF-A interfaces.
111#[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    /// Returns true if this is a 32-bit call, or false if it is a 64-bit call.
183    pub fn is_32bit(&self) -> bool {
184        u32::from(*self) & (1 << 30) == 0
185    }
186
187    /// Returns the FF-A version that has introduced the function ID.
188    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/// Error status codes used by the `FFA_ERROR` interface.
262#[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
286/// Collection of helper functions for converting between `Uuid` type and its representations in
287/// various FF-A containers.
288pub struct UuidHelper;
289
290impl UuidHelper {
291    /// Converts byte array into `Uuid`.
292    /// Example:
293    /// * Input `[a1, a2, a3, a4, b1, b2, c1, c2, d1, d2, d3, d4, d5, d6, d7, d8]`
294    /// * Output: `a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8`
295    pub fn from_bytes(value: [u8; 16]) -> Uuid {
296        Uuid::from_bytes(value)
297    }
298
299    /// Converts `Uuid` into byte array.
300    /// Example:
301    /// * Input: `a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8`
302    /// * Output `[a1, a2, a3, a4, b1, b2, c1, c2, d1, d2, d3, d4, d5, d6, d7, d8]`
303    pub fn to_bytes(value: Uuid) -> [u8; 16] {
304        value.into_bytes()
305    }
306
307    /// Creates `Uuid` from four 32 bit register values.
308    /// Example:
309    /// * Input `[a4a3a2a1, c2c1b2b1, d4d3d2d1, d8d7d6d5]`
310    /// * Output: `a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8`
311    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    /// Converts `Uuid` into four 32 bit register values.
321    /// Example:
322    /// * Input: `a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8`
323    /// * Output `[a4a3a2a1, c2c1b2b1, d4d3d2d1, d8d7d6d5]`
324    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    /// Creates `Uuid` from a 64 bit register pair.
336    /// Example:
337    /// * Input `[c2c1b2b1a4a3a2a1, d8d7d6d5d4d3d2d1]`
338    /// * Output: `a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8`
339    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    /// Converts `Uuid` into a 64 bit register pair.
344    /// Example:
345    /// * Input `[c2c1b2b1a4a3a2a1, d8d7d6d5d4d3d2d1]`
346    /// * Output: `a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8`
347    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/// Version number of the FF-A implementation, `.0` is the major, `.1` is minor the version.
354#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord)]
355pub struct Version(pub u16, pub u16);
356
357impl Version {
358    // The FF-A spec mandates that bit[31] of a version number must be 0
359    const MBZ_BITS: u32 = 1 << 31;
360
361    /// The encoding used if no version is negotiated between a caller and the callee.
362    pub const NULL: Version = Version(0, 0);
363
364    /// Returns whether the caller's version (self) is compatible with the callee's version (input
365    /// parameter)
366    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/// Enum for storing the response of an FFA_VERSION request. It can either contain a `Version` or
404/// a `NOT_SUPPORTED` error code.
405#[derive(Debug, Eq, PartialEq, Clone, Copy)]
406pub enum VersionOut {
407    Version(Version),
408    NotSupported,
409    InvalidParameter,
410}
411
412impl VersionOut {
413    /// SMCCC return code: The call is not supported by the implementation.
414    const SMCCC_NOT_SUPPORTED: i32 = -1;
415    /// SMCCC return code: One of the call parameters has a non-supported value.
416    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        // Note: in case of error we return the SMCCC error codes, not the FF-A ones
434        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}