Initial version
Add some common FF-A functionality. Supported features (roughly):
- define FF-A function IDs and error codes
- create boot info descriptor
- create partition info descriptor
- create and parse memory transaction descriptor
- parse memory relinquish descriptor
- parse console log message
Limitations apply, code quality is not production ready.
Signed-off-by: Balint Dobszay <balint.dobszay@arm.com>
Change-Id: Ibb3fde9d4c0d0d9b8823bb84cdcf23eb3f9d087a
diff --git a/src/boot_info.rs b/src/boot_info.rs
new file mode 100644
index 0000000..553c7c5
--- /dev/null
+++ b/src/boot_info.rs
@@ -0,0 +1,182 @@
+// SPDX-FileCopyrightText: Copyright 2023 Arm Limited and/or its affiliates <open-source-office@arm.com>
+// SPDX-License-Identifier: MIT OR Apache-2.0
+
+use uuid::Uuid;
+
+pub enum BootInfoName {
+ NullTermString(&'static str),
+ Uuid(Uuid),
+}
+
+#[derive(Clone, Copy)]
+pub enum BootInfoStdType {
+ Fdt = 0,
+ Hob = 1,
+}
+
+pub enum BootInfoType {
+ Std(BootInfoStdType),
+ Impdef(u8),
+}
+
+pub enum BootInfoContents {
+ Address(usize),
+ Value(u64),
+}
+
+pub struct BootInfoDescriptor {
+ /// Name of boot information passed to the consumer
+ pub name: BootInfoName,
+
+ /// Type of boot information passed to the consumer
+ pub typ: BootInfoType,
+
+ /// Size (in bytes) of boot information identified by the Name and Type fields
+ pub size: u32,
+
+ pub contents: BootInfoContents,
+}
+
+impl BootInfoDescriptor {
+ pub fn create(descriptors: &[BootInfoDescriptor], buf: &mut [u8]) {
+ // Offset from the base of the header to the first element in the boot info descriptor array
+ // Must be 8 byte aligned
+ const DESC_ARRAY_OFFSET: usize = 32;
+
+ /// In FF-A v1.1, Table 5.8: Boot information descriptor is 32 bytes long
+ const DESC_SIZE: usize = 32;
+
+ // assert!(descriptors.len() <= u32::MAX as usize);
+ let desc_cnt = descriptors.len();
+
+ // Add the already known fields, later we have to add the sizes referenced by the individual descriptors
+ let mut total_size = 0usize;
+ total_size = total_size.checked_add(DESC_ARRAY_OFFSET).unwrap();
+ total_size = total_size
+ .checked_add(desc_cnt.checked_mul(DESC_SIZE).unwrap())
+ .unwrap();
+
+ // Create the boot info header starting at offset 0 in the buffer
+ // Offset 0, length 4: Hexadecimal value 0x0FFA to identify the header
+ buf[0..4].copy_from_slice(&0x0ffa_u32.to_le_bytes());
+
+ // Offset 4, length 4: Version of the boot information blob encoded as in FFA_VERSION_GET
+ buf[4..8].copy_from_slice(&0x0001_0001_u32.to_le_bytes());
+
+ // Offset 12, length 4: Size of each boot information descriptor in the array
+ buf[12..16].copy_from_slice(&(DESC_SIZE as u32).to_le_bytes());
+
+ // Offset 16, length 4: Count of boot information descriptors in the array
+ buf[16..20].copy_from_slice(&(desc_cnt as u32).to_le_bytes());
+
+ // Offset 20, length 4: Offset to array of boot information descriptors
+ buf[20..24].copy_from_slice(&(DESC_ARRAY_OFFSET as u32).to_le_bytes());
+
+ // Offset 24, length 8: Reserved (MBZ)
+ buf[24..32].fill(0);
+
+ // Fill the boot info descriptor array, all offset based from DESC_ARRAY_OFFSET
+ let mut offset = DESC_ARRAY_OFFSET;
+ for desc in descriptors {
+ // Offset 0, length 16: Name of boot information passed to the consumer
+ match &desc.name {
+ BootInfoName::NullTermString(name) => {
+ assert!(name.is_ascii());
+ let name_len = name.len().min(15);
+ buf[offset..offset + name_len].copy_from_slice(&name.as_bytes()[..name_len]);
+ buf[offset + name_len..offset + 16].fill(0); // Make sure it's null terminated
+ }
+ BootInfoName::Uuid(uuid) => {
+ buf[offset..offset + 16].copy_from_slice(&uuid.to_bytes_le());
+ }
+ }
+
+ // Offset 16, length 1: Type of boot information passed to the consumer
+ let info_type = match desc.typ {
+ BootInfoType::Std(std_type) => (std_type as u8) & 0b0111_1111,
+ BootInfoType::Impdef(typ) => (0b1 << 7) | typ,
+ };
+ buf[offset + 16] = info_type;
+
+ // Offset 17, length 1: Reserved (MBZ)
+ buf[offset + 17] = 0;
+
+ // Offset 18, length 2: Flags to describe properties of boot information associated with this descriptor
+ let mut flags = 0u16;
+ if let BootInfoName::Uuid(_) = &desc.name {
+ flags |= 0b1;
+ }
+ if let BootInfoContents::Value(_) = desc.contents {
+ flags |= 0b1 << 2;
+ }
+ buf[offset + 18..offset + 20].copy_from_slice(&flags.to_le_bytes());
+
+ // Offset 20, length 4: Size (in bytes) of boot information identified by the Name and Type fields.
+ match desc.contents {
+ BootInfoContents::Address(_) => {
+ total_size = total_size.checked_add(desc.size as usize).unwrap();
+ }
+ BootInfoContents::Value(_) => {
+ assert!((1..=8).contains(&desc.size));
+ }
+ }
+ buf[offset + 20..offset + 24].copy_from_slice(&desc.size.to_le_bytes());
+
+ // Offset 24, length 8: Value or address of boot information identified by the Name and Type fields.
+ // Value or address of boot information identified by the Name and Type fields.
+ //
+ // If in the Flags field, bit\[3:2\] = b'0,
+ // * The address has the same attributes as the boot information blob address described in
+ // 5.4.3 Boot information address.
+ // * Size field contains the length (in bytes) of boot information at the specified address.
+ //
+ // If in the Flags field, bit\[3:2\] = b’1,
+ // * Size field contains the exact size of the value specified in this field.
+ // * Size is >=1 bytes and <= 8 bytes.
+ let content = match desc.contents {
+ BootInfoContents::Address(addr) => addr as u64,
+ BootInfoContents::Value(val) => val,
+ };
+ buf[offset + 24..offset + 32].copy_from_slice(&content.to_le_bytes());
+
+ offset += DESC_SIZE;
+ }
+
+ // TODO: add padding size between boot information referenced by the descriptors
+
+ // Offset 8, length 4: Size of boot information blob spanning contiguous memory
+ assert!(buf.len() <= u32::MAX as usize);
+ assert!(total_size <= buf.len());
+ buf[8..12].copy_from_slice(&(total_size as u32).to_le_bytes());
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ use uuid::uuid;
+
+ #[test]
+ fn boot_info() {
+ let desc1 = BootInfoDescriptor {
+ name: BootInfoName::NullTermString(&"test1234test1234"),
+ typ: BootInfoType::Impdef(0xab),
+ size: 4,
+ contents: BootInfoContents::Value(0xbeef),
+ };
+
+ let fdt = [0u8; 0xff];
+ let desc2 = BootInfoDescriptor {
+ name: BootInfoName::Uuid(uuid!("12345678-1234-1234-1234-123456789abc")),
+ typ: BootInfoType::Std(BootInfoStdType::Fdt),
+ size: 0xff,
+ contents: BootInfoContents::Address(&fdt as *const u8 as usize),
+ };
+
+ let mut buf = [0u8; 0x1ff];
+ BootInfoDescriptor::create(&[desc1, desc2], &mut buf);
+
+ println!("{:#x?}", &buf[0..0x0f]);
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..ca1190e
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,863 @@
+// SPDX-FileCopyrightText: Copyright 2023 Arm Limited and/or its affiliates <open-source-office@arm.com>
+// SPDX-License-Identifier: MIT OR Apache-2.0
+
+#![cfg_attr(not(test), no_std)]
+
+extern crate alloc;
+
+use alloc::string::String;
+use num_enum::TryFromPrimitive;
+use uuid::Uuid;
+
+pub mod boot_info;
+pub mod memory_management;
+pub mod partition_info;
+
+pub const FFA_PAGE_SIZE: usize = 4096;
+
+#[derive(PartialEq, Clone, Copy)]
+pub enum Instance {
+ SecurePhysical,
+ SecureVirtual(u16),
+}
+
+/// FF-A v1.1, Table 12.2: Error status codes
+#[derive(Debug, Eq, PartialEq, Clone, Copy, TryFromPrimitive)]
+#[repr(i32)]
+pub enum Error {
+ NotSupported = -1,
+ InvalidParameters = -2,
+ NoMemory = -3,
+ Busy = -4,
+ Interrupted = -5,
+ Denied = -6,
+ Retry = -7,
+ Aborted = -8,
+ NoData = -9,
+}
+
+/// FF-A v1.1: Function IDs
+#[derive(Debug, Eq, PartialEq, TryFromPrimitive)]
+#[repr(u32)]
+pub enum FuncId {
+ Error = 0x84000060,
+ Success32 = 0x84000061,
+ Success64 = 0xc4000061,
+ Interrupt = 0x84000062,
+ Version = 0x84000063,
+ Features = 0x84000064,
+ RxAcquire = 0x84000084,
+ RxRelease = 0x84000065,
+ RxTxMap32 = 0x84000066,
+ RxTxMap64 = 0xc4000066,
+ RxTxUnmap = 0x84000067,
+ PartitionInfoGet = 0x84000068,
+ IdGet = 0x84000069,
+ SpmIdGet = 0x84000085,
+ MsgWait = 0x8400006b,
+ Yield = 0x8400006c,
+ Run = 0x8400006d,
+ NormalWorldResume = 0x8400007c,
+ MsgSend2 = 0x84000086,
+ MsgSendDirectReq32 = 0x8400006f,
+ MsgSendDirectReq64 = 0xc400006f,
+ MsgSendDirectResp32 = 0x84000070,
+ MsgSendDirectResp64 = 0xc4000070,
+ MemDonate32 = 0x84000071,
+ MemDonate64 = 0xc4000071,
+ MemLend32 = 0x84000072,
+ MemLend64 = 0xc4000072,
+ MemShare32 = 0x84000073,
+ MemShare64 = 0xc4000073,
+ MemRetrieveReq32 = 0x84000074,
+ MemRetrieveReq64 = 0xc4000074,
+ MemRetrieveResp = 0x84000075,
+ MemRelinquish = 0x84000076,
+ MemReclaim = 0x84000077,
+ MemPermGet32 = 0x84000088,
+ MemPermGet64 = 0xc4000088,
+ MemPermSet32 = 0x84000089,
+ MemPermSet64 = 0xc4000089,
+ ConsoleLog32 = 0x8400008a,
+ ConsoleLog64 = 0xc400008a,
+}
+
+#[derive(Debug, Eq, PartialEq, Clone, Copy)]
+pub enum Interface {
+ Error {
+ target_info: u32,
+ error_code: Error,
+ },
+ Success {
+ target_info: u32,
+ result_regs: [u64; 6],
+ is_32bit: bool,
+ },
+ Interrupt {
+ endpoint_id: u32,
+ interrupt_id: u32,
+ },
+ Version {
+ input_version: u32,
+ },
+ VersionOut {
+ output_version: u32,
+ },
+ Features {
+ feat_id: u32,
+ input_properties: u32,
+ },
+ RxAcquire {
+ vm_id: u32,
+ },
+ RxRelease {
+ vm_id: u32,
+ },
+ RxTxMap {
+ tx_addr: u64,
+ rx_addr: u64,
+ page_cnt: u32,
+ is_32bit: bool,
+ },
+ RxTxUnmap {
+ id: u32,
+ },
+ PartitionInfoGet {
+ uuid: Uuid,
+ flags: u32,
+ },
+ IdGet,
+ SpmIdGet,
+ MsgWait,
+ Yield,
+ Run {
+ target_info: u32,
+ },
+ NormalWorldResume,
+ MsgSend2 {
+ sender_vm_id: u32,
+ flags: u32,
+ },
+ MsgSendDirectReq {
+ src_id: u16,
+ dst_id: u16,
+ flags: u32,
+ args: [u64; 5],
+ is_32bit: bool,
+ },
+ MsgSendDirectResp {
+ src_id: u16,
+ dst_id: u16,
+ flags: u32,
+ args: [u64; 5],
+ is_32bit: bool,
+ },
+ MemDonate {
+ total_len: u32,
+ frag_len: u32,
+ address: u64,
+ page_cnt: u32,
+ is_32bit: bool,
+ },
+ MemLend {
+ total_len: u32,
+ frag_len: u32,
+ address: u64,
+ page_cnt: u32,
+ is_32bit: bool,
+ },
+ MemShare {
+ total_len: u32,
+ frag_len: u32,
+ address: u64,
+ page_cnt: u32,
+ is_32bit: bool,
+ },
+ MemRetrieveReq {
+ total_len: u32,
+ frag_len: u32,
+ address: u64,
+ page_cnt: u32,
+ is_32bit: bool,
+ },
+ MemRetrieveResp {
+ total_len: u32,
+ frag_len: u32,
+ },
+ MemRelinquish,
+ MemReclaim {
+ handle: memory_management::Handle,
+ flags: u32,
+ },
+ MemPermGet {
+ base_addr: u64,
+ is_32bit: bool,
+ },
+ MemPermSet {
+ base_addr: u64,
+ page_cnt: u32,
+ mem_perm: u32,
+ is_32bit: bool,
+ },
+ ConsoleLog {
+ char_cnt: u32,
+ char_lists: [u64; 6],
+ is_32bit: bool,
+ },
+}
+
+impl TryFrom<[u64; 8]> for Interface {
+ type Error = ();
+
+ fn try_from(regs: [u64; 8]) -> Result<Self, ()> {
+ let fid = FuncId::try_from(regs[0] as u32).unwrap();
+
+ let msg = match fid {
+ FuncId::Error => Self::Error {
+ target_info: regs[1] as u32,
+ error_code: Error::try_from(regs[2] as i32).unwrap(),
+ },
+ FuncId::Success32 | FuncId::Success64 => {
+ let target_info = regs[1] as u32;
+ let mut result_regs = [regs[2], regs[3], regs[4], regs[5], regs[6], regs[7]];
+ let mut is_32bit = false;
+
+ if fid == FuncId::Success32 {
+ result_regs[0] &= u32::MAX as u64;
+ result_regs[1] &= u32::MAX as u64;
+ result_regs[2] &= u32::MAX as u64;
+ result_regs[3] &= u32::MAX as u64;
+ result_regs[4] &= u32::MAX as u64;
+ result_regs[5] &= u32::MAX as u64;
+ is_32bit = true;
+ }
+
+ Self::Success {
+ target_info,
+ result_regs,
+ is_32bit,
+ }
+ }
+ FuncId::Interrupt => Self::Interrupt {
+ endpoint_id: regs[1] as u32,
+ interrupt_id: regs[2] as u32,
+ },
+ FuncId::Version => Self::Version {
+ input_version: regs[1] as u32,
+ },
+ FuncId::Features => Self::Features {
+ feat_id: regs[1] as u32,
+ input_properties: regs[2] as u32,
+ },
+ FuncId::RxAcquire => Self::RxAcquire {
+ vm_id: regs[1] as u32,
+ },
+ FuncId::RxRelease => Self::RxRelease {
+ vm_id: regs[1] as u32,
+ },
+ FuncId::RxTxMap32 | FuncId::RxTxMap64 => {
+ let mut tx_addr = regs[1];
+ let mut rx_addr = regs[2];
+ let page_cnt = regs[3] as u32;
+ let mut is_32bit = false;
+
+ if fid == FuncId::RxTxMap32 {
+ tx_addr &= u32::MAX as u64;
+ rx_addr &= u32::MAX as u64;
+ is_32bit = true;
+ }
+
+ Self::RxTxMap {
+ tx_addr,
+ rx_addr,
+ page_cnt,
+ is_32bit,
+ }
+ }
+ FuncId::RxTxUnmap => Self::RxTxUnmap { id: regs[1] as u32 },
+ FuncId::PartitionInfoGet => {
+ let uuid_words = [
+ regs[1] as u32,
+ regs[2] as u32,
+ regs[3] as u32,
+ regs[4] as u32,
+ ];
+ let mut bytes: [u8; 16] = [0; 16];
+ for (i, b) in uuid_words.iter().flat_map(|w| w.to_le_bytes()).enumerate() {
+ bytes[i] = b;
+ }
+ Self::PartitionInfoGet {
+ uuid: Uuid::from_bytes(bytes),
+ flags: regs[5] as u32,
+ }
+ }
+ FuncId::IdGet => Self::IdGet,
+ FuncId::SpmIdGet => Self::SpmIdGet,
+ FuncId::MsgWait => Self::MsgWait,
+ FuncId::Yield => Self::Yield,
+ FuncId::Run => Self::Run {
+ target_info: regs[1] as u32,
+ },
+ FuncId::NormalWorldResume => Self::NormalWorldResume,
+ FuncId::MsgSend2 => Self::MsgSend2 {
+ sender_vm_id: regs[1] as u32,
+ flags: regs[2] as u32,
+ },
+ FuncId::MsgSendDirectReq32 | FuncId::MsgSendDirectReq64 => {
+ let src_id = (regs[1] >> 16) as u16;
+ let dst_id = regs[1] as u16;
+ let flags = regs[2] as u32;
+ let mut args = [regs[3], regs[4], regs[5], regs[6], regs[7]];
+ let mut is_32bit = false;
+
+ if fid == FuncId::MsgSendDirectReq32 {
+ args[0] &= u32::MAX as u64;
+ args[1] &= u32::MAX as u64;
+ args[2] &= u32::MAX as u64;
+ args[3] &= u32::MAX as u64;
+ args[4] &= u32::MAX as u64;
+ is_32bit = true;
+ }
+
+ Self::MsgSendDirectReq {
+ src_id,
+ dst_id,
+ flags,
+ args,
+ is_32bit,
+ }
+ }
+ FuncId::MsgSendDirectResp32 | FuncId::MsgSendDirectResp64 => {
+ let src_id = (regs[1] >> 16) as u16;
+ let dst_id = regs[1] as u16;
+ let flags = regs[2] as u32;
+ let mut args = [regs[3], regs[4], regs[5], regs[6], regs[7]];
+ let mut is_32bit = false;
+
+ if fid == FuncId::MsgSendDirectResp32 {
+ args[0] &= u32::MAX as u64;
+ args[1] &= u32::MAX as u64;
+ args[2] &= u32::MAX as u64;
+ args[3] &= u32::MAX as u64;
+ args[4] &= u32::MAX as u64;
+ is_32bit = true;
+ }
+
+ Self::MsgSendDirectResp {
+ src_id,
+ dst_id,
+ flags,
+ args,
+ is_32bit,
+ }
+ }
+ FuncId::MemDonate32 | FuncId::MemDonate64 => {
+ let total_len = regs[1] as u32;
+ let frag_len = regs[2] as u32;
+ let mut address = regs[3];
+ let page_cnt = regs[4] as u32;
+ let mut is_32bit = false;
+
+ if fid == FuncId::MemDonate32 {
+ address &= u32::MAX as u64;
+ is_32bit = true;
+ }
+
+ Self::MemDonate {
+ total_len,
+ frag_len,
+ address,
+ page_cnt,
+ is_32bit,
+ }
+ }
+ FuncId::MemLend32 | FuncId::MemLend64 => {
+ let total_len = regs[1] as u32;
+ let frag_len = regs[2] as u32;
+ let mut address = regs[3];
+ let page_cnt = regs[4] as u32;
+ let mut is_32bit = false;
+
+ if fid == FuncId::MemLend32 {
+ address &= u32::MAX as u64;
+ is_32bit = true;
+ }
+
+ Self::MemLend {
+ total_len,
+ frag_len,
+ address,
+ page_cnt,
+ is_32bit,
+ }
+ }
+ FuncId::MemShare32 | FuncId::MemShare64 => {
+ let total_len = regs[1] as u32;
+ let frag_len = regs[2] as u32;
+ let mut address = regs[3];
+ let page_cnt = regs[4] as u32;
+ let mut is_32bit = false;
+
+ if fid == FuncId::MemShare32 {
+ address &= u32::MAX as u64;
+ is_32bit = true;
+ }
+
+ Self::MemShare {
+ total_len,
+ frag_len,
+ address,
+ page_cnt,
+ is_32bit,
+ }
+ }
+ FuncId::MemRetrieveReq32 | FuncId::MemRetrieveReq64 => {
+ let total_len = regs[1] as u32;
+ let frag_len = regs[2] as u32;
+ let mut address = regs[3];
+ let page_cnt = regs[4] as u32;
+ let mut is_32bit = false;
+
+ if fid == FuncId::MemRetrieveReq32 {
+ address &= u32::MAX as u64;
+ is_32bit = true;
+ }
+
+ Self::MemRetrieveReq {
+ total_len,
+ frag_len,
+ address,
+ page_cnt,
+ is_32bit,
+ }
+ }
+ FuncId::MemRetrieveResp => Self::MemRetrieveResp {
+ total_len: regs[1] as u32,
+ frag_len: regs[2] as u32,
+ },
+ FuncId::MemRelinquish => Self::MemRelinquish,
+ FuncId::MemReclaim => Self::MemReclaim {
+ handle: memory_management::Handle::from([regs[1] as u32, regs[2] as u32]),
+ flags: regs[3] as u32,
+ },
+ FuncId::MemPermGet32 | FuncId::MemPermGet64 => {
+ let mut base_addr = regs[1];
+ let mut is_32bit = false;
+
+ if fid == FuncId::MemPermGet32 {
+ base_addr &= u32::MAX as u64;
+ is_32bit = true;
+ }
+
+ Self::MemPermGet {
+ base_addr,
+ is_32bit,
+ }
+ }
+ FuncId::MemPermSet32 | FuncId::MemPermSet64 => {
+ let mut base_addr = regs[1];
+ let page_cnt = regs[2] as u32;
+ let mem_perm = regs[3] as u32;
+ let mut is_32bit = false;
+
+ if fid == FuncId::MemPermSet32 {
+ base_addr &= u32::MAX as u64;
+ is_32bit = true;
+ }
+
+ Self::MemPermSet {
+ base_addr,
+ page_cnt,
+ mem_perm,
+ is_32bit,
+ }
+ }
+ FuncId::ConsoleLog32 | FuncId::ConsoleLog64 => {
+ let char_cnt = regs[1] as u32;
+ let mut char_lists = [regs[2], regs[3], regs[4], regs[5], regs[6], regs[7]];
+ let mut is_32bit = false;
+
+ if fid == FuncId::ConsoleLog32 {
+ char_lists[0] &= u32::MAX as u64;
+ char_lists[1] &= u32::MAX as u64;
+ char_lists[2] &= u32::MAX as u64;
+ char_lists[3] &= u32::MAX as u64;
+ char_lists[4] &= u32::MAX as u64;
+ char_lists[5] &= u32::MAX as u64;
+ is_32bit = true;
+ }
+
+ Self::ConsoleLog {
+ char_cnt,
+ char_lists,
+ is_32bit,
+ }
+ }
+ };
+
+ Ok(msg)
+ }
+}
+
+impl Interface {
+ pub fn copy_to_array(&self, a: &mut [u64; 8]) {
+ a.fill(0);
+
+ match *self {
+ Interface::Error {
+ target_info,
+ error_code,
+ } => {
+ a[0] = FuncId::Error as u64;
+ a[1] = target_info as u64;
+ a[2] = error_code as u32 as u64;
+ }
+ Interface::Success {
+ target_info,
+ result_regs,
+ is_32bit,
+ } => {
+ a[1] = target_info as u64;
+ if is_32bit {
+ a[0] = FuncId::Success32 as u64;
+ a[2] = result_regs[0] & u32::MAX as u64;
+ a[3] = result_regs[1] & u32::MAX as u64;
+ a[4] = result_regs[2] & u32::MAX as u64;
+ a[5] = result_regs[3] & u32::MAX as u64;
+ a[6] = result_regs[4] & u32::MAX as u64;
+ a[7] = result_regs[5] & u32::MAX as u64;
+ } else {
+ a[0] = FuncId::Success64 as u64;
+ a[2] = result_regs[0];
+ a[3] = result_regs[1];
+ a[4] = result_regs[2];
+ a[5] = result_regs[3];
+ a[6] = result_regs[4];
+ a[7] = result_regs[5];
+ }
+ }
+ Interface::Interrupt {
+ endpoint_id,
+ interrupt_id,
+ } => {
+ a[0] = FuncId::Interrupt as u64;
+ a[1] = endpoint_id as u64;
+ a[2] = interrupt_id as u64;
+ }
+ Interface::Version { input_version } => {
+ a[0] = FuncId::Version as u64;
+ a[1] = input_version as u64;
+ }
+ Interface::VersionOut { output_version } => {
+ a[0] = output_version as u64;
+ }
+ Interface::Features {
+ feat_id,
+ input_properties,
+ } => {
+ a[0] = FuncId::Features as u64;
+ a[1] = feat_id as u64;
+ a[2] = input_properties as u64;
+ }
+ Interface::RxAcquire { vm_id } => {
+ a[0] = FuncId::RxAcquire as u64;
+ a[1] = vm_id as u64;
+ }
+ Interface::RxRelease { vm_id } => {
+ a[0] = FuncId::RxRelease as u64;
+ a[1] = vm_id as u64;
+ }
+ Interface::RxTxMap {
+ tx_addr,
+ rx_addr,
+ page_cnt,
+ is_32bit,
+ } => {
+ a[3] = page_cnt as u64;
+ if is_32bit {
+ a[0] = FuncId::RxTxMap32 as u64;
+ a[1] = tx_addr & u32::MAX as u64;
+ a[2] = rx_addr & u32::MAX as u64;
+ } else {
+ a[0] = FuncId::RxTxMap64 as u64;
+ a[1] = tx_addr;
+ a[2] = rx_addr;
+ }
+ }
+ Interface::RxTxUnmap { id } => {
+ a[0] = FuncId::RxTxUnmap as u64;
+ a[1] = id as u64;
+ }
+ Interface::PartitionInfoGet { uuid, flags } => {
+ let bytes = uuid.to_bytes_le();
+ a[0] = FuncId::PartitionInfoGet as u64;
+ a[1] = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) as u64;
+ a[2] = u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]) as u64;
+ a[3] = u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]) as u64;
+ a[4] = u32::from_le_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]) as u64;
+ a[5] = flags as u64;
+ }
+ Interface::IdGet => a[0] = FuncId::IdGet as u64,
+ Interface::SpmIdGet => a[0] = FuncId::SpmIdGet as u64,
+ Interface::MsgWait => a[0] = FuncId::MsgWait as u64,
+ Interface::Yield => a[0] = FuncId::Yield as u64,
+ Interface::Run { target_info } => {
+ a[0] = FuncId::Run as u64;
+ a[1] = target_info as u64;
+ }
+ Interface::NormalWorldResume => a[0] = FuncId::NormalWorldResume as u64,
+ Interface::MsgSend2 {
+ sender_vm_id,
+ flags,
+ } => {
+ a[0] = FuncId::MsgSend2 as u64;
+ a[1] = sender_vm_id as u64;
+ a[2] = flags as u64;
+ }
+ Interface::MsgSendDirectReq {
+ src_id,
+ dst_id,
+ flags,
+ args,
+ is_32bit,
+ } => {
+ a[1] = (src_id as u64) << 16 | dst_id as u64;
+ a[2] = flags as u64;
+ if is_32bit {
+ a[0] = FuncId::MsgSendDirectReq32 as u64;
+ a[3] = args[0] & u32::MAX as u64;
+ a[4] = args[1] & u32::MAX as u64;
+ a[5] = args[2] & u32::MAX as u64;
+ a[6] = args[3] & u32::MAX as u64;
+ a[7] = args[4] & u32::MAX as u64;
+ } else {
+ a[0] = FuncId::MsgSendDirectReq64 as u64;
+ a[3] = args[0];
+ a[4] = args[1];
+ a[5] = args[2];
+ a[6] = args[3];
+ a[7] = args[4];
+ }
+ }
+ Interface::MsgSendDirectResp {
+ src_id,
+ dst_id,
+ flags,
+ args,
+ is_32bit,
+ } => {
+ a[1] = (src_id as u64) << 16 | dst_id as u64;
+ a[2] = flags as u64;
+ if is_32bit {
+ a[0] = FuncId::MsgSendDirectResp32 as u64;
+ a[3] = args[0] & u32::MAX as u64;
+ a[4] = args[1] & u32::MAX as u64;
+ a[5] = args[2] & u32::MAX as u64;
+ a[6] = args[3] & u32::MAX as u64;
+ a[7] = args[4] & u32::MAX as u64;
+ } else {
+ a[0] = FuncId::MsgSendDirectResp64 as u64;
+ a[3] = args[0];
+ a[4] = args[1];
+ a[5] = args[2];
+ a[6] = args[3];
+ a[7] = args[4];
+ }
+ }
+ Interface::MemDonate {
+ total_len,
+ frag_len,
+ address,
+ page_cnt,
+ is_32bit,
+ } => {
+ a[1] = total_len as u64;
+ a[2] = frag_len as u64;
+ a[4] = page_cnt as u64;
+ if is_32bit {
+ a[0] = FuncId::MemDonate32 as u64;
+ a[3] = address & u32::MAX as u64;
+ } else {
+ a[0] = FuncId::MemDonate64 as u64;
+ a[3] = address;
+ }
+ }
+ Interface::MemLend {
+ total_len,
+ frag_len,
+ address,
+ page_cnt,
+ is_32bit,
+ } => {
+ a[1] = total_len as u64;
+ a[2] = frag_len as u64;
+ a[4] = page_cnt as u64;
+ if is_32bit {
+ a[0] = FuncId::MemLend32 as u64;
+ a[3] = address & u32::MAX as u64;
+ } else {
+ a[0] = FuncId::MemLend64 as u64;
+ a[3] = address;
+ }
+ }
+ Interface::MemShare {
+ total_len,
+ frag_len,
+ address,
+ page_cnt,
+ is_32bit,
+ } => {
+ a[1] = total_len as u64;
+ a[2] = frag_len as u64;
+ a[4] = page_cnt as u64;
+ if is_32bit {
+ a[0] = FuncId::MemShare32 as u64;
+ a[3] = address & u32::MAX as u64;
+ } else {
+ a[0] = FuncId::MemShare64 as u64;
+ a[3] = address;
+ }
+ }
+ Interface::MemRetrieveReq {
+ total_len,
+ frag_len,
+ address,
+ page_cnt,
+ is_32bit,
+ } => {
+ a[1] = total_len as u64;
+ a[2] = frag_len as u64;
+ a[4] = page_cnt as u64;
+ if is_32bit {
+ a[0] = FuncId::MemRetrieveReq32 as u64;
+ a[3] = address & u32::MAX as u64;
+ } else {
+ a[0] = FuncId::MemRetrieveReq64 as u64;
+ a[3] = address;
+ }
+ }
+ Interface::MemRetrieveResp {
+ total_len,
+ frag_len,
+ } => {
+ a[0] = FuncId::MemRetrieveResp as u64;
+ a[1] = total_len as u64;
+ a[2] = frag_len as u64;
+ }
+ Interface::MemRelinquish => a[0] = FuncId::MemRelinquish as u64,
+ Interface::MemReclaim { handle, flags } => {
+ let handle_regs: [u32; 2] = handle.into();
+ a[0] = FuncId::MemReclaim as u64;
+ a[1] = handle_regs[0] as u64;
+ a[2] = handle_regs[1] as u64;
+ a[3] = flags as u64;
+ }
+ Interface::MemPermGet {
+ base_addr,
+ is_32bit,
+ } => {
+ if is_32bit {
+ a[0] = FuncId::MemPermGet32 as u64;
+ a[1] = base_addr & u32::MAX as u64;
+ } else {
+ a[0] = FuncId::MemPermGet64 as u64;
+ a[1] = base_addr;
+ }
+ }
+ Interface::MemPermSet {
+ base_addr,
+ page_cnt,
+ mem_perm,
+ is_32bit,
+ } => {
+ a[2] = page_cnt as u64;
+ a[3] = mem_perm as u64;
+
+ if is_32bit {
+ a[0] = FuncId::MemPermSet32 as u64;
+ a[1] = base_addr & u32::MAX as u64;
+ } else {
+ a[0] = FuncId::MemPermSet64 as u64;
+ a[1] = base_addr;
+ }
+ }
+ Interface::ConsoleLog {
+ char_cnt,
+ char_lists,
+ is_32bit,
+ } => {
+ a[1] = char_cnt as u64;
+ if is_32bit {
+ a[0] = FuncId::ConsoleLog32 as u64;
+ a[2] = char_lists[0] & u32::MAX as u64;
+ a[3] = char_lists[1] & u32::MAX as u64;
+ a[4] = char_lists[2] & u32::MAX as u64;
+ a[5] = char_lists[3] & u32::MAX as u64;
+ a[6] = char_lists[4] & u32::MAX as u64;
+ a[7] = char_lists[5] & u32::MAX as u64;
+ } else {
+ a[0] = FuncId::ConsoleLog64 as u64;
+ a[2] = char_lists[0];
+ a[3] = char_lists[1];
+ a[4] = char_lists[2];
+ a[5] = char_lists[3];
+ a[6] = char_lists[4];
+ a[7] = char_lists[5];
+ }
+ }
+ }
+ }
+
+ /// Helper function to create an FFA_SUCCESS interface without any arguments
+ pub fn success32_noargs() -> Self {
+ Self::Success {
+ target_info: 0,
+ result_regs: [0, 0, 0, 0, 0, 0],
+ is_32bit: true,
+ }
+ }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub struct Version(pub u16, pub u16);
+
+impl From<u32> for Version {
+ fn from(val: u32) -> Self {
+ Self((val >> 16) as u16, val as u16)
+ }
+}
+
+impl From<Version> for u32 {
+ fn from(v: Version) -> Self {
+ (v.0 as u32) << 16 | v.1 as u32
+ }
+}
+
+pub fn parse_console_log(
+ char_cnt: u32,
+ char_lists: &[u64; 6],
+ is_32bit: bool,
+) -> Result<String, Error> {
+ const CHAR_COUNT_MASK: u32 = 0xff;
+ const LOG_32_MAX_MSG_LEN: usize = 24;
+ const LOG_64_MAX_MSG_LEN: usize = 48;
+
+ let mut msg_bytes = [0u8; LOG_64_MAX_MSG_LEN + 1];
+ let char_count = (char_cnt & CHAR_COUNT_MASK) as usize;
+ let (max_length, reg_size) = if is_32bit {
+ (LOG_32_MAX_MSG_LEN, 4)
+ } else {
+ (LOG_64_MAX_MSG_LEN, 8)
+ };
+
+ if char_count < 1 || char_count > max_length {
+ return Err(Error::InvalidParameters);
+ }
+
+ for i in 0..=5 {
+ msg_bytes[reg_size * i..reg_size * (i + 1)]
+ .copy_from_slice(&char_lists[i].to_le_bytes()[0..reg_size]);
+ }
+
+ String::from_utf8(msg_bytes.to_vec()).map_err(|_| Error::InvalidParameters)
+}
diff --git a/src/memory_management.rs b/src/memory_management.rs
new file mode 100644
index 0000000..f0da51f
--- /dev/null
+++ b/src/memory_management.rs
@@ -0,0 +1,635 @@
+// SPDX-FileCopyrightText: Copyright 2023 Arm Limited and/or its affiliates <open-source-office@arm.com>
+// SPDX-License-Identifier: MIT OR Apache-2.0
+
+use alloc::vec::Vec;
+
+#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Handle(pub u64);
+
+impl From<[u32; 2]> for Handle {
+ fn from(value: [u32; 2]) -> Self {
+ Self((value[1] as u64) << 32 | value[0] as u64)
+ }
+}
+
+impl From<Handle> for [u32; 2] {
+ fn from(value: Handle) -> Self {
+ [value.0 as u32, (value.0 >> 32) as u32]
+ }
+}
+
+impl Handle {
+ pub const INVALID: u64 = 0xffff_ffff_ffff_ffff;
+}
+
+#[derive(Debug, Default, Clone, Copy, PartialEq)]
+#[repr(u16)]
+pub enum Cacheability {
+ #[default]
+ NonCacheable = Self::NON_CACHEABLE << Self::SHIFT,
+ WriteBack = Self::WRITE_BACK << Self::SHIFT,
+}
+
+impl TryFrom<u16> for Cacheability {
+ type Error = ();
+
+ fn try_from(value: u16) -> Result<Self, Self::Error> {
+ match (value >> Self::SHIFT) & Self::MASK {
+ Self::NON_CACHEABLE => Ok(Cacheability::NonCacheable),
+ Self::WRITE_BACK => Ok(Cacheability::WriteBack),
+ _ => Err(()),
+ }
+ }
+}
+
+impl Cacheability {
+ const SHIFT: usize = 2;
+ const MASK: u16 = 0b11;
+ const NON_CACHEABLE: u16 = 0b01;
+ const WRITE_BACK: u16 = 0b11;
+}
+
+#[derive(Debug, Default, Clone, Copy, PartialEq)]
+#[repr(u16)]
+pub enum Shareability {
+ #[default]
+ NonShareable = Self::NON_SHAREABLE << Self::SHIFT,
+ Outer = Self::OUTER << Self::SHIFT,
+ Inner = Self::INNER << Self::SHIFT,
+}
+
+impl TryFrom<u16> for Shareability {
+ type Error = ();
+
+ fn try_from(value: u16) -> Result<Self, Self::Error> {
+ match (value >> Self::SHIFT) & Self::MASK {
+ Self::NON_SHAREABLE => Ok(Self::NonShareable),
+ Self::OUTER => Ok(Self::Outer),
+ Self::INNER => Ok(Self::Inner),
+ _ => Err(()),
+ }
+ }
+}
+
+impl Shareability {
+ const SHIFT: usize = 0;
+ const MASK: u16 = 0b11;
+ const NON_SHAREABLE: u16 = 0b00;
+ const OUTER: u16 = 0b10;
+ const INNER: u16 = 0b11;
+}
+
+#[derive(Debug, Default, Clone, Copy)]
+#[repr(u16)]
+pub enum DeviceMemAttributes {
+ #[default]
+ DevnGnRnE = Self::DEV_NGNRNE << Self::SHIFT,
+ DevnGnRE = Self::DEV_NGNRE << Self::SHIFT,
+ DevnGRE = Self::DEV_NGRE << Self::SHIFT,
+ DevGRE = Self::DEV_GRE << Self::SHIFT,
+}
+
+impl TryFrom<u16> for DeviceMemAttributes {
+ type Error = ();
+
+ fn try_from(value: u16) -> Result<Self, Self::Error> {
+ // TODO: sanity check if it's device memory
+ match (value >> Self::SHIFT) & Self::MASK {
+ Self::DEV_NGNRNE => Ok(Self::DevnGnRnE),
+ Self::DEV_NGNRE => Ok(Self::DevnGnRE),
+ Self::DEV_NGRE => Ok(Self::DevnGRE),
+ Self::DEV_GRE => Ok(Self::DevGRE),
+ _ => Err(()),
+ }
+ }
+}
+
+impl DeviceMemAttributes {
+ const SHIFT: usize = 2;
+ const MASK: u16 = 0b11;
+ const DEV_NGNRNE: u16 = 0b00;
+ const DEV_NGNRE: u16 = 0b01;
+ const DEV_NGRE: u16 = 0b10;
+ const DEV_GRE: u16 = 0b11;
+}
+
+#[derive(Debug, Default, Clone, Copy)]
+pub enum MemType {
+ #[default]
+ NotSpecified,
+ Device(DeviceMemAttributes),
+ Normal {
+ cacheability: Cacheability,
+ shareability: Shareability,
+ },
+}
+
+impl TryFrom<u16> for MemType {
+ type Error = ();
+
+ fn try_from(value: u16) -> Result<Self, Self::Error> {
+ match (value >> Self::SHIFT) & Self::MASK {
+ Self::NOT_SPECIFIED => Ok(Self::NotSpecified),
+ Self::DEVICE => Ok(Self::Device(DeviceMemAttributes::try_from(value)?)),
+ Self::NORMAL => Ok(Self::Normal {
+ cacheability: Cacheability::try_from(value)?,
+ shareability: Shareability::try_from(value)?,
+ }),
+ _ => Err(()),
+ }
+ }
+}
+
+impl From<MemType> for u16 {
+ fn from(value: MemType) -> Self {
+ match value {
+ MemType::NotSpecified => MemType::NOT_SPECIFIED << MemType::SHIFT,
+ MemType::Device(attr) => attr as u16 | MemType::DEVICE << MemType::SHIFT,
+ MemType::Normal {
+ cacheability,
+ shareability,
+ } => cacheability as u16 | shareability as u16 | MemType::NORMAL << MemType::SHIFT,
+ }
+ }
+}
+
+impl MemType {
+ const SHIFT: usize = 4;
+ const MASK: u16 = 0b11;
+ const NOT_SPECIFIED: u16 = 0b00;
+ const DEVICE: u16 = 0b01;
+ const NORMAL: u16 = 0b10;
+}
+
+#[derive(Debug, Default, Clone, Copy, PartialEq)]
+#[repr(u16)]
+pub enum MemRegionSecurity {
+ #[default]
+ Secure = Self::SECURE << Self::SHIFT,
+ NonSecure = Self::NON_SECURE << Self::SHIFT,
+}
+
+impl TryFrom<u16> for MemRegionSecurity {
+ type Error = ();
+
+ fn try_from(value: u16) -> Result<Self, Self::Error> {
+ match (value >> Self::SHIFT) & Self::MASK {
+ Self::SECURE => Ok(Self::Secure),
+ Self::NON_SECURE => Ok(Self::NonSecure),
+ _ => Err(()),
+ }
+ }
+}
+
+impl MemRegionSecurity {
+ const SHIFT: usize = 6;
+ const MASK: u16 = 0b1;
+ const SECURE: u16 = 0b0;
+ const NON_SECURE: u16 = 0b1;
+}
+
+/// FF-A v1.1: Table 10.18: Memory region attributes descriptor
+#[derive(Debug, Default, Clone, Copy)]
+pub struct MemRegionAttributes {
+ pub security: MemRegionSecurity,
+ pub mem_type: MemType,
+}
+
+impl TryFrom<u16> for MemRegionAttributes {
+ type Error = ();
+
+ fn try_from(value: u16) -> Result<Self, Self::Error> {
+ // bits[15:7]: Reserved (MBZ)
+ assert_eq!(value >> 7, 0);
+ Ok(Self {
+ security: MemRegionSecurity::try_from(value)?,
+ mem_type: MemType::try_from(value)?,
+ })
+ }
+}
+
+impl From<MemRegionAttributes> for u16 {
+ fn from(value: MemRegionAttributes) -> Self {
+ value.security as u16 | u16::from(value.mem_type)
+ }
+}
+
+#[derive(Debug, Default, Clone, Copy)]
+#[repr(u8)]
+pub enum InstuctionAccessPerm {
+ #[default]
+ NotSpecified = Self::NOT_SPECIFIED << Self::SHIFT,
+ NotExecutable = Self::NOT_EXECUTABLE << Self::SHIFT,
+ Executable = Self::EXECUTABLE << Self::SHIFT,
+}
+
+impl TryFrom<u8> for InstuctionAccessPerm {
+ type Error = ();
+
+ fn try_from(value: u8) -> Result<Self, Self::Error> {
+ match (value >> Self::SHIFT) & Self::MASK {
+ Self::NOT_SPECIFIED => Ok(Self::NotSpecified),
+ Self::NOT_EXECUTABLE => Ok(Self::NotExecutable),
+ Self::EXECUTABLE => Ok(Self::Executable),
+ _ => Err(()),
+ }
+ }
+}
+
+impl InstuctionAccessPerm {
+ const SHIFT: usize = 2;
+ const MASK: u8 = 0b11;
+ const NOT_SPECIFIED: u8 = 0b00;
+ const NOT_EXECUTABLE: u8 = 0b01;
+ const EXECUTABLE: u8 = 0b10;
+}
+
+#[derive(Debug, Default, Clone, Copy)]
+#[repr(u8)]
+pub enum DataAccessPerm {
+ #[default]
+ NotSpecified = Self::NOT_SPECIFIED << Self::SHIFT,
+ ReadOnly = Self::READ_ONLY << Self::SHIFT,
+ ReadWrite = Self::READ_WRITE << Self::SHIFT,
+}
+
+impl TryFrom<u8> for DataAccessPerm {
+ type Error = ();
+
+ fn try_from(value: u8) -> Result<Self, Self::Error> {
+ match (value >> Self::SHIFT) & Self::MASK {
+ Self::NOT_SPECIFIED => Ok(Self::NotSpecified),
+ Self::READ_ONLY => Ok(Self::ReadOnly),
+ Self::READ_WRITE => Ok(Self::ReadWrite),
+ _ => Err(()),
+ }
+ }
+}
+
+impl DataAccessPerm {
+ const SHIFT: usize = 0;
+ const MASK: u8 = 0b11;
+ const NOT_SPECIFIED: u8 = 0b00;
+ const READ_ONLY: u8 = 0b01;
+ const READ_WRITE: u8 = 0b10;
+}
+
+/// FF-A v1.1: Table 10.15: Memory access permissions descriptor
+#[derive(Debug, Default, Clone, Copy)]
+pub struct MemAccessPermDesc {
+ pub endpoint_id: u16,
+ pub instr_access: InstuctionAccessPerm,
+ pub data_access: DataAccessPerm,
+ pub flags: u8, // TODO
+}
+
+/// FF-A v1.1 Table 10.16: Endpoint memory access descriptor
+#[derive(Debug, Default, Clone, Copy)]
+pub struct EndpointMemAccessDesc {
+ pub mem_access_perm: MemAccessPermDesc,
+ pub composite_offset: u32,
+}
+
+impl EndpointMemAccessDesc {
+ const SIZE: usize = 16;
+}
+
+/// FF-A v1.1 Table 10.21: Flags usage in FFA_MEM_DONATE, FFA_MEM_LEND and FFA_MEM_SHARE ABIs
+/// FF-A v1.1 Table 10.22: Flags usage in FFA_MEM_RETRIEVE_REQ ABI
+/// FF-A v1.1 Table 10.23: Flags usage in FFA_MEM_RETRIEVE_RESP ABI
+#[derive(Debug, Default, Clone, Copy)]
+pub struct MemTransactionFlags(pub u32); // TODO: use bitflags?
+
+#[allow(dead_code)]
+impl MemTransactionFlags {
+ const MEM_SHARE_MASK: u32 = 0b11;
+ const MEM_RETRIEVE_REQ_MASK: u32 = 0b11_1111_1111;
+ const MEM_RETRIEVE_RESP_MASK: u32 = 0b1_1111;
+ const ZERO_MEMORY: u32 = 0b1;
+ const TIME_SLICING: u32 = 0b1 << 1;
+ const ZERO_AFTER_RELINQ: u32 = 0b1 << 2;
+ pub const TYPE_SHARE: u32 = 0b01 << 3;
+ const TYPE_LEND: u32 = 0b10 << 3;
+ const TYPE_DONATE: u32 = 0b11 << 3;
+ const ALIGN_HINT_MASK: u32 = 0b1111 << 5;
+ const HINT_VALID: u32 = 0b1 << 9;
+}
+
+/// FF-A v1.1: Table 10.20: Memory transaction descriptor
+#[derive(Debug, Default)]
+pub struct MemTransactionDesc {
+ pub sender_id: u16,
+ pub mem_region_attr: MemRegionAttributes,
+ pub flags: MemTransactionFlags,
+ pub handle: Handle,
+ pub tag: u64, // TODO
+ pub ep_access_descs: Vec<EndpointMemAccessDesc>,
+}
+
+/// FF-A v1.1 Table 10.13: Composite memory region descriptor
+#[derive(Debug, Default)]
+pub struct CompositeMemRegionDesc {
+ pub total_page_cnt: u32,
+ pub constituents: Vec<ConstituentMemRegionDesc>,
+}
+
+impl CompositeMemRegionDesc {
+ const CONSTITUENT_ARRAY_OFFSET: usize = 16;
+}
+
+/// FF-A v1.1 Table 10.14: Constituent memory region descriptor
+#[derive(Debug, Default, Clone, Copy)]
+pub struct ConstituentMemRegionDesc {
+ pub address: u64,
+ pub page_cnt: u32,
+}
+
+impl ConstituentMemRegionDesc {
+ const SIZE: usize = 16;
+}
+
+impl MemTransactionDesc {
+ // Must be 16 byte aligned
+ const ENDPOINT_MEM_ACCESS_DESC_OFFSET: usize = 48;
+
+ pub fn create(&self, composite_desc: &CompositeMemRegionDesc, buf: &mut [u8]) -> usize {
+ let mem_access_desc_cnt = self.ep_access_descs.len();
+ let composite_offset = (Self::ENDPOINT_MEM_ACCESS_DESC_OFFSET
+ + mem_access_desc_cnt * EndpointMemAccessDesc::SIZE)
+ .next_multiple_of(8);
+
+ // Offset 0, length 2: ID of the Owner endpoint.
+ buf[0..2].copy_from_slice(&self.sender_id.to_le_bytes());
+
+ // Offset 2, length 2: Memory region attributes
+ let mem_reg_attr = u16::from(self.mem_region_attr);
+ buf[2..4].copy_from_slice(&mem_reg_attr.to_le_bytes());
+
+ // Offset 4, length 4: Flags
+ buf[4..8].copy_from_slice(&self.flags.0.to_le_bytes());
+
+ // Offset 8, length 8: Handle
+ buf[8..16].copy_from_slice(&self.handle.0.to_le_bytes());
+
+ // Offset 16, length 8: Tag
+ buf[16..24].copy_from_slice(&self.tag.to_le_bytes());
+
+ // Offset 24, length 4: Size of each endpoint memory access descriptor in the array.
+ buf[24..28].copy_from_slice(&(EndpointMemAccessDesc::SIZE as u32).to_le_bytes());
+
+ // Offset 28, length 4: Count of endpoint memory access descriptors.
+ buf[28..32].copy_from_slice(&(mem_access_desc_cnt as u32).to_le_bytes());
+
+ // Offset 32, length 4: 16-byte aligned offset from the base address of this descriptor to the first element of the Endpoint memory access descriptor array.
+ buf[32..36].copy_from_slice(&(Self::ENDPOINT_MEM_ACCESS_DESC_OFFSET as u32).to_le_bytes());
+
+ let mut offset = Self::ENDPOINT_MEM_ACCESS_DESC_OFFSET;
+ for desc in &self.ep_access_descs {
+ // Offset 0, length 4: Memory access permissions descriptor
+ // Offset 0, length 2: 16-bit ID of endpoint to which the memory access permissions apply
+ buf[offset..offset + 2]
+ .copy_from_slice(&desc.mem_access_perm.endpoint_id.to_le_bytes());
+
+ // Offset 2, length 1: Permissions used to access a memory region.
+ buf[offset + 2] =
+ desc.mem_access_perm.data_access as u8 | desc.mem_access_perm.instr_access as u8;
+
+ // Offset 3, length 1: ABI specific flags
+ buf[offset + 2] = desc.mem_access_perm.flags;
+
+ // Offset 4, length 4: Offset to the composite memory region descriptor to which the endpoint access permissions apply
+ buf[offset + 4..offset + 8].copy_from_slice(&(composite_offset as u32).to_le_bytes());
+
+ // Offset 8, length 8: Reserved (MBZ)
+ buf[offset + 8..offset + 16].fill(0);
+
+ offset += EndpointMemAccessDesc::SIZE;
+ }
+
+ offset = composite_offset;
+ // Offset 0, length 4: Size of the memory region described as the count of 4K pages
+ buf[offset..offset + 4].copy_from_slice(&composite_desc.total_page_cnt.to_le_bytes());
+
+ // Offset 4, length 4: Count of address ranges specified using constituent memory region descriptors
+ let addr_range_cnt = composite_desc.constituents.len() as u32;
+ buf[offset + 4..offset + 8].copy_from_slice(&addr_range_cnt.to_le_bytes());
+
+ // Offset 8, length 8: Reserved (MBZ)
+ buf[offset + 8..offset + 16].fill(0);
+
+ offset = composite_offset + CompositeMemRegionDesc::CONSTITUENT_ARRAY_OFFSET;
+ for constituent in &composite_desc.constituents {
+ // Offset 0, length 8: Base VA, PA or IPA of constituent memory region aligned to the page size (4K) granularity.
+ buf[offset..offset + 8].copy_from_slice(&constituent.address.to_le_bytes());
+
+ // Offset 8, length 4: Number of 4K pages in constituent memory region
+ buf[offset + 8..offset + 12].copy_from_slice(&constituent.page_cnt.to_le_bytes());
+
+ // Offset 12, length 4: Reserved (MBZ)
+ buf[offset + 12..offset + 16].fill(0);
+
+ offset += ConstituentMemRegionDesc::SIZE;
+ }
+
+ offset
+ }
+
+ pub fn parse(
+ &mut self,
+ composite_desc: &mut CompositeMemRegionDesc,
+ buf: &[u8],
+ ) -> Result<(), ()> {
+ // Offset 0, length 2: ID of the Owner endpoint.
+ self.sender_id = u16::from_le_bytes(buf[0..2].try_into().unwrap());
+
+ // Offset 2, length 2: Memory region attributes
+ let mem_attr = u16::from_le_bytes(buf[2..4].try_into().unwrap());
+ self.mem_region_attr = MemRegionAttributes::try_from(mem_attr)?;
+
+ // Offset 4, length 4: Flags
+ self.flags.0 = u32::from_le_bytes(buf[4..8].try_into().unwrap()); // TODO: validate
+
+ // Offset 8, length 8: Handle
+ self.handle.0 = u64::from_le_bytes(buf[8..16].try_into().unwrap());
+
+ // Offset 16, length 8: Tag
+ self.tag = u64::from_le_bytes(buf[16..24].try_into().unwrap());
+
+ // Offset 24, length 4: Size of each endpoint memory access descriptor in the array.
+ let endpoint_mem_access_desc_size = u32::from_le_bytes(buf[24..28].try_into().unwrap());
+ assert_eq!(
+ EndpointMemAccessDesc::SIZE,
+ endpoint_mem_access_desc_size as usize
+ );
+
+ // Offset 28, length 4: Count of endpoint memory access descriptors.
+ let endpoint_mem_access_desc_cnt = u32::from_le_bytes(buf[28..32].try_into().unwrap());
+
+ // Offset 32, length 4: 16-byte aligned offset from the base address of this descriptor to
+ // the first element of the Endpoint memory access descriptor array.
+ let endpoint_mem_access_desc_offset = u32::from_le_bytes(buf[32..36].try_into().unwrap());
+
+ assert!(
+ endpoint_mem_access_desc_offset
+ + endpoint_mem_access_desc_cnt * endpoint_mem_access_desc_size
+ <= buf.len() as u32
+ );
+
+ let mut composite_offset = 0;
+ let mut offset = endpoint_mem_access_desc_offset as usize;
+ for _ in 0..endpoint_mem_access_desc_cnt {
+ let mut desc = EndpointMemAccessDesc::default();
+ desc.mem_access_perm.endpoint_id =
+ u16::from_le_bytes(buf[offset..offset + 2].try_into().unwrap());
+
+ desc.mem_access_perm.instr_access = InstuctionAccessPerm::try_from(buf[offset + 2])?;
+ desc.mem_access_perm.data_access = DataAccessPerm::try_from(buf[offset + 2])?;
+ desc.mem_access_perm.flags = buf[offset + 3];
+ desc.composite_offset =
+ u32::from_le_bytes(buf[offset + 4..offset + 8].try_into().unwrap());
+ // TODO: different composite offsets?
+ composite_offset = desc.composite_offset as usize;
+
+ self.ep_access_descs.push(desc);
+
+ offset += endpoint_mem_access_desc_size as usize;
+ }
+
+ if self.handle != Handle(0) || composite_offset == 0 {
+ return Ok(());
+ }
+
+ composite_desc.total_page_cnt = u32::from_le_bytes(
+ buf[composite_offset..composite_offset + 4]
+ .try_into()
+ .unwrap(),
+ );
+
+ let addr_range_cnt = u32::from_le_bytes(
+ buf[composite_offset + 4..composite_offset + 8]
+ .try_into()
+ .unwrap(),
+ );
+
+ offset = composite_offset + CompositeMemRegionDesc::CONSTITUENT_ARRAY_OFFSET;
+ let mut total_page_cnt = 0;
+ for _ in 0..addr_range_cnt {
+ let desc = ConstituentMemRegionDesc {
+ address: u64::from_le_bytes(buf[offset..offset + 8].try_into().unwrap()),
+ page_cnt: u32::from_le_bytes(buf[offset + 8..offset + 12].try_into().unwrap()),
+ };
+ total_page_cnt += desc.page_cnt;
+
+ composite_desc.constituents.push(desc);
+
+ offset += ConstituentMemRegionDesc::SIZE;
+ }
+
+ assert_eq!(total_page_cnt, composite_desc.total_page_cnt);
+
+ Ok(())
+ }
+}
+
+/// FF-A v1.1 Table 16.25: Descriptor to relinquish a memory region
+#[derive(Debug, Default)]
+pub struct MemRelinquishDesc {
+ pub handle: Handle,
+ pub flags: u32,
+ pub endpoints: Vec<u16>,
+}
+
+impl MemRelinquishDesc {
+ const ENDPOINT_ARRAY_OFFSET: usize = 16;
+
+ pub fn parse(&mut self, buf: &[u8]) -> Result<(), ()> {
+ // Offset 0, length 8: Handle
+ self.handle.0 = u64::from_le_bytes(buf[0..8].try_into().unwrap());
+
+ // Offset 8, length 4: Flags
+ self.flags = u32::from_le_bytes(buf[8..12].try_into().unwrap()); // TODO: validate
+
+ // Offset 12, length 4: Count of endpoint ID entries in the Endpoint array
+ let endpoint_cnt = u32::from_le_bytes(buf[12..16].try_into().unwrap());
+
+ let mut offset = MemRelinquishDesc::ENDPOINT_ARRAY_OFFSET;
+ for _ in 0..endpoint_cnt as usize {
+ let endpoint = u16::from_le_bytes(buf[offset..offset + 2].try_into().unwrap());
+ self.endpoints.push(endpoint);
+ offset += 2;
+ }
+
+ Ok(())
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[allow(dead_code)]
+ const MEM_SHARE_FROM_SP1: &[u8] = &[
+ 0x05, 0x80, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x03, 0x80, 0x02, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x10, 0x40, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ ];
+
+ #[allow(dead_code)]
+ const MEM_SHARE_FROM_SP2: &[u8] = &[
+ 0x06, 0x80, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x05, 0x80, 0x02, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x07, 0x40, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ ];
+
+ #[allow(dead_code)]
+ const MEM_RETRIEVE_REQ_FROM_SP1: &[u8] = &[
+ 0x05, 0x80, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x03, 0x80, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ ];
+
+ #[allow(dead_code)]
+ const MEM_RETRIEVE_REQ_FROM_SP2: &[u8] = &[
+ 0x06, 0x80, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x05, 0x80, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ ];
+
+ #[allow(dead_code)]
+ const MEM_SHARE_FROM_NWD: &[u8] = &[
+ 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x03, 0x80, 0x02, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x22, 0x80, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ ];
+
+ #[test]
+ fn mem_share() {
+ let mut transaction_desc = MemTransactionDesc::default();
+ let mut composite_desc = CompositeMemRegionDesc::default();
+
+ transaction_desc
+ .parse(&mut composite_desc, MEM_RETRIEVE_REQ_FROM_SP1)
+ .unwrap();
+
+ println!("transaction desc: {:#x?}", transaction_desc);
+ println!("endpont desc: {:#x?}", transaction_desc.ep_access_descs);
+ println!("composite desc: {:#x?}", composite_desc);
+ println!("constituent desc: {:#x?}", composite_desc.constituents);
+ }
+}
diff --git a/src/partition_info.rs b/src/partition_info.rs
new file mode 100644
index 0000000..5301cb7
--- /dev/null
+++ b/src/partition_info.rs
@@ -0,0 +1,121 @@
+// SPDX-FileCopyrightText: Copyright 2023 Arm Limited and/or its affiliates <open-source-office@arm.com>
+// SPDX-License-Identifier: MIT OR Apache-2.0
+
+use uuid::Uuid;
+
+pub enum PartitionIdType {
+ PeEndpoint(u16),
+ SepidIndep,
+ SepidDep(u16),
+ Aux,
+}
+
+pub struct PartInfoDesc {
+ pub partition_id: u16,
+ pub uuid: Uuid,
+ pub id_type: PartitionIdType,
+ pub support_direct_req_rec: bool,
+ pub support_direct_req_send: bool,
+ pub support_indirect_msg: bool,
+ pub support_notif_rec: bool,
+ pub subscribe_vm_created: bool,
+ pub subscribe_vm_destroyed: bool,
+ pub is_aarch64: bool,
+}
+
+impl PartInfoDesc {
+ pub const SIZE: usize = 24;
+}
+
+pub fn create_partition_info(buf: &mut [u8], descriptors: &[PartInfoDesc], fill_uuid: bool) {
+ let mut offset = 0;
+
+ for desc in descriptors {
+ // Offset 0, length 2: 16-bit ID of the partition, stream or auxiliary endpoint.
+ buf[offset..offset + 2].copy_from_slice(&desc.partition_id.to_le_bytes());
+
+ // Offset 2, length 2: Execution context count or Proxy partition ID
+ match desc.id_type {
+ PartitionIdType::PeEndpoint(exec_ctx_cnt) => {
+ buf[offset + 2..offset + 4].copy_from_slice(&exec_ctx_cnt.to_le_bytes())
+ }
+ PartitionIdType::SepidDep(id) => {
+ buf[offset + 2..offset + 4].copy_from_slice(&id.to_le_bytes())
+ }
+ _ => buf[offset + 2..offset + 4].fill(0),
+ }
+
+ // Offset 4, length 4: Flags to determine partition properties.
+ let mut props = 0u32;
+ match desc.id_type {
+ PartitionIdType::PeEndpoint(_) => {
+ if desc.support_direct_req_rec {
+ props |= 0b1;
+ if desc.subscribe_vm_created {
+ // TODO: check NS phys instance
+ props |= 0b1 << 6
+ }
+ if desc.subscribe_vm_destroyed {
+ // TODO: check NS phys instance
+ props |= 0b1 << 7
+ }
+ }
+ if desc.support_direct_req_send {
+ props |= 0b1 << 1
+ }
+ if desc.support_indirect_msg {
+ props |= 0b1 << 2
+ }
+ if desc.support_notif_rec {
+ props |= 0b1 << 3
+ }
+ }
+ PartitionIdType::SepidIndep => props |= 0b01 << 4,
+ PartitionIdType::SepidDep(_) => props |= 0b10 << 4,
+ PartitionIdType::Aux => props |= 0b11 << 4,
+ }
+ if desc.is_aarch64 {
+ props |= 0b1 << 8
+ }
+ buf[offset + 4..offset + 8].copy_from_slice(&props.to_le_bytes());
+
+ // Offset 8, length 16: Partition UUID if the Nil UUID was specified. Reserved (MBZ) otherwise
+ if fill_uuid {
+ buf[offset + 8..offset + 24].copy_from_slice(desc.uuid.as_bytes());
+ } else {
+ buf[offset + 8..offset + 24].fill(0);
+ }
+
+ offset += PartInfoDesc::SIZE;
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ use uuid::uuid;
+
+ #[test]
+ fn part_info() {
+ let desc = PartInfoDesc {
+ partition_id: 0x8001,
+ uuid: uuid!("12345678-1234-1234-1234-123456789abc"),
+ id_type: PartitionIdType::PeEndpoint(1),
+ support_direct_req_rec: true,
+ support_direct_req_send: true,
+ support_indirect_msg: false,
+ support_notif_rec: false,
+ subscribe_vm_created: true,
+ subscribe_vm_destroyed: true,
+ is_aarch64: true,
+ };
+
+ let mut buf = [0u8; 0xff];
+ create_partition_info(&mut buf, &[desc], true);
+
+ println!("{:#x?}", &buf[0..0x0f]);
+
+ assert_eq!(0x8001_u16, u16::from_le_bytes([buf[0], buf[1]]));
+ }
+}