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/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);
+    }
+}