blob: 777a39910a9acaaa57c0318b207fbc3eaa126368 [file] [log] [blame]
// SPDX-FileCopyrightText: Copyright 2023 Arm Limited and/or its affiliates <open-source-office@arm.com>
// SPDX-License-Identifier: MIT OR Apache-2.0
use crate::ffa_v1_1::{
composite_memory_region_descriptor, constituent_memory_region_descriptor,
endpoint_memory_access_descriptor, memory_access_permission_descriptor,
memory_relinquish_descriptor, memory_transaction_descriptor,
};
use core::mem::size_of;
use thiserror::Error;
use zerocopy::{FromBytes, IntoBytes};
#[derive(Debug, Error)]
pub enum Error {
#[error("Invalid cacheability attribute {0}")]
InvalidCacheability(u16),
#[error("Invalid shareability attribute {0}")]
InvalidShareability(u16),
#[error("Invalid device memory attributes {0}")]
InvalidDevMemAttributes(u16),
#[error("Invalid instruction access permission {0}")]
InvalidInstrAccessPerm(u8),
#[error("Invalid instruction data permission {0}")]
InvalidDataAccessPerm(u8),
#[error("Invalid memory type {0}")]
InvalidMemType(u16),
#[error("Invalid memory attributes {0}")]
InvalidMemAttributes(u16),
#[error("Composite offset mismatch")]
CompositeOffsetMismatch,
#[error("Invalid endpoint count {0}")]
UnsupportedEndpointCount(u32),
#[error("Invalid buffer size")]
InvalidBufferSize,
#[error("Malformed descriptor")]
MalformedDescriptor,
}
impl From<Error> for crate::FfaError {
fn from(_value: Error) -> Self {
Self::InvalidParameters
}
}
#[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 = 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(Error::InvalidCacheability(value)),
}
}
}
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 = 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(Error::InvalidShareability(value)),
}
}
}
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 = Error;
fn try_from(value: u16) -> Result<Self, Self::Error> {
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(Error::InvalidDevMemAttributes(value)),
}
}
}
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 = 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(value.try_into()?)),
Self::NORMAL => Ok(Self::Normal {
cacheability: value.try_into()?,
shareability: value.try_into()?,
}),
_ => Err(Error::InvalidMemType(value)),
}
}
}
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 From<u16> for MemRegionSecurity {
fn from(value: u16) -> Self {
match (value >> Self::SHIFT) & Self::MASK {
Self::SECURE => Self::Secure,
Self::NON_SECURE => Self::NonSecure,
_ => panic!(), // The match is exhaustive for a 1-bit value
}
}
}
impl MemRegionSecurity {
const SHIFT: usize = 6;
const MASK: u16 = 0b1;
const SECURE: u16 = 0b0;
const NON_SECURE: u16 = 0b1;
}
#[derive(Debug, Default, Clone, Copy)]
pub struct MemRegionAttributes {
pub security: MemRegionSecurity,
pub mem_type: MemType,
}
impl TryFrom<u16> for MemRegionAttributes {
type Error = Error;
fn try_from(value: u16) -> Result<Self, Self::Error> {
// bits[15:7]: Reserved (MBZ)
if value >> 7 == 0 {
Ok(Self {
security: value.into(),
mem_type: value.try_into()?,
})
} else {
Err(Error::InvalidMemAttributes(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 = 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(Error::InvalidInstrAccessPerm(value)),
}
}
}
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 = 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(Error::InvalidDataAccessPerm(value)),
}
}
}
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;
}
#[derive(Debug, Default, Clone, Copy)]
pub struct MemAccessPerm {
pub endpoint_id: u16,
pub instr_access: InstuctionAccessPerm,
pub data_access: DataAccessPerm,
pub flags: u8, // TODO
}
pub struct MemAccessPermIterator<'a> {
buf: &'a [u8],
offset: usize,
count: usize,
}
impl<'a> MemAccessPermIterator<'a> {
fn new(buf: &'a [u8], count: usize, offset: usize) -> Result<Self, Error> {
let Some(total_size) = count
.checked_mul(size_of::<endpoint_memory_access_descriptor>())
.and_then(|x| x.checked_add(offset))
else {
return Err(Error::InvalidBufferSize);
};
if buf.len() < total_size {
return Err(Error::InvalidBufferSize);
}
Ok(Self { buf, offset, count })
}
}
impl Iterator for MemAccessPermIterator<'_> {
type Item = Result<MemAccessPerm, Error>;
fn next(&mut self) -> Option<Self::Item> {
if self.count > 0 {
let offset = self.offset;
self.offset += size_of::<endpoint_memory_access_descriptor>();
self.count -= 1;
let Ok(desc_raw) = endpoint_memory_access_descriptor::ref_from_bytes(
&self.buf[offset..offset + size_of::<endpoint_memory_access_descriptor>()],
) else {
return Some(Err(Error::MalformedDescriptor));
};
let instr_access = match desc_raw
.access_perm_desc
.memory_access_permissions
.try_into()
{
Ok(v) => v,
Err(e) => return Some(Err(e)),
};
let data_access = match desc_raw
.access_perm_desc
.memory_access_permissions
.try_into()
{
Ok(v) => v,
Err(e) => return Some(Err(e)),
};
let desc = MemAccessPerm {
endpoint_id: desc_raw.access_perm_desc.endpoint_id,
instr_access,
data_access,
flags: desc_raw.access_perm_desc.flags,
};
return Some(Ok(desc));
}
None
}
}
#[derive(Debug, Default, Clone, Copy)]
pub struct ConstituentMemRegion {
pub address: u64,
pub page_cnt: u32,
}
pub struct ConstituentMemRegionIterator<'a> {
buf: &'a [u8],
offset: usize,
count: usize,
}
impl<'a> ConstituentMemRegionIterator<'a> {
fn new(buf: &'a [u8], count: usize, offset: usize) -> Result<Self, Error> {
let Some(total_size) = count
.checked_mul(size_of::<constituent_memory_region_descriptor>())
.and_then(|x| x.checked_add(offset))
else {
return Err(Error::InvalidBufferSize);
};
if buf.len() < total_size {
return Err(Error::InvalidBufferSize);
}
Ok(Self { buf, offset, count })
}
}
impl Iterator for ConstituentMemRegionIterator<'_> {
type Item = Result<ConstituentMemRegion, Error>;
fn next(&mut self) -> Option<Self::Item> {
if self.count > 0 {
let offset = self.offset;
self.offset += size_of::<constituent_memory_region_descriptor>();
self.count -= 1;
let Ok(desc_raw) = constituent_memory_region_descriptor::ref_from_bytes(
&self.buf[offset..offset + size_of::<constituent_memory_region_descriptor>()],
) else {
return Some(Err(Error::MalformedDescriptor));
};
let desc = ConstituentMemRegion {
address: desc_raw.address,
page_cnt: desc_raw.page_count,
};
return Some(Ok(desc));
}
None
}
}
#[derive(Debug, Default, Clone, Copy)]
pub struct MemTransactionFlags(pub u32);
impl MemTransactionFlags {
pub const MEM_SHARE_MASK: u32 = 0b11;
pub const MEM_RETRIEVE_REQ_MASK: u32 = 0b11_1111_1111;
pub const MEM_RETRIEVE_RESP_MASK: u32 = 0b1_1111;
pub const ZERO_MEMORY: u32 = 0b1;
pub const TIME_SLICING: u32 = 0b1 << 1;
pub const ZERO_AFTER_RELINQ: u32 = 0b1 << 2;
pub const TYPE_SHARE: u32 = 0b01 << 3;
pub const TYPE_LEND: u32 = 0b10 << 3;
pub const TYPE_DONATE: u32 = 0b11 << 3;
pub const ALIGN_HINT_MASK: u32 = 0b1111 << 5;
pub const HINT_VALID: u32 = 0b1 << 9;
}
#[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
}
impl MemTransactionDesc {
// Offset from the base of the memory transaction descriptor to the first element in the
// endpoint memory access descriptor array. Must be 16 byte aligned, but otherwise we're free to
// choose any value here. Let's just pack it right after the memory transaction descriptor.
const ENDPOINT_MEM_ACCESS_DESC_OFFSET: usize =
size_of::<memory_transaction_descriptor>().next_multiple_of(16);
// The array of constituent memory region descriptors starts right after the composite memory
// region descriptor
const CONSTITUENT_ARRAY_OFFSET: usize = size_of::<composite_memory_region_descriptor>();
pub fn pack(
&self,
constituents: &[ConstituentMemRegion],
access_descriptors: &[MemAccessPerm],
buf: &mut [u8],
) -> usize {
let mem_access_desc_size = size_of::<endpoint_memory_access_descriptor>();
let mem_access_desc_cnt = access_descriptors.len();
let transaction_desc_raw = memory_transaction_descriptor {
sender_endpoint_id: self.sender_id,
memory_region_attributes: self.mem_region_attr.into(),
flags: self.flags.0,
handle: self.handle.0,
tag: self.tag,
endpoint_mem_access_desc_size: mem_access_desc_size as u32,
endpoint_mem_access_desc_count: mem_access_desc_cnt as u32,
endpoint_mem_access_desc_array_offset: Self::ENDPOINT_MEM_ACCESS_DESC_OFFSET as u32,
reserved1: 0,
reserved2: 0,
};
transaction_desc_raw.write_to_prefix(buf).unwrap();
// Offset from the base of the memory transaction descriptor to the composite memory region
// descriptor to which the endpoint access permissions apply.
let composite_offset = mem_access_desc_cnt
.checked_mul(mem_access_desc_size)
.unwrap()
.checked_add(Self::ENDPOINT_MEM_ACCESS_DESC_OFFSET)
.unwrap()
.next_multiple_of(8);
let mut offset = Self::ENDPOINT_MEM_ACCESS_DESC_OFFSET;
for desc in access_descriptors {
let desc_raw = endpoint_memory_access_descriptor {
access_perm_desc: memory_access_permission_descriptor {
endpoint_id: desc.endpoint_id,
memory_access_permissions: desc.data_access as u8 | desc.instr_access as u8,
flags: desc.flags,
},
composite_offset: composite_offset as u32,
reserved: 0,
};
desc_raw.write_to_prefix(&mut buf[offset..]).unwrap();
offset += mem_access_desc_size;
}
let mut total_page_count = 0;
offset = composite_offset + Self::CONSTITUENT_ARRAY_OFFSET;
for constituent in constituents {
let constituent_raw = constituent_memory_region_descriptor {
address: constituent.address,
page_count: constituent.page_cnt,
reserved: 0,
};
constituent_raw.write_to_prefix(&mut buf[offset..]).unwrap();
offset += size_of::<constituent_memory_region_descriptor>();
total_page_count += constituent_raw.page_count;
}
let composite_desc_raw = composite_memory_region_descriptor {
total_page_count,
address_range_count: constituents.len() as u32,
reserved: 0,
};
composite_desc_raw
.write_to_prefix(&mut buf[composite_offset..])
.unwrap();
offset
}
pub fn unpack(
buf: &[u8],
) -> Result<
(
MemTransactionDesc,
MemAccessPermIterator,
Option<ConstituentMemRegionIterator>,
),
Error,
> {
let Some(transaction_desc_bytes) = buf.get(0..size_of::<memory_transaction_descriptor>())
else {
return Err(Error::InvalidBufferSize);
};
let Ok(transaction_desc_raw) =
memory_transaction_descriptor::ref_from_bytes(transaction_desc_bytes)
else {
return Err(Error::MalformedDescriptor);
};
if size_of::<endpoint_memory_access_descriptor>()
!= transaction_desc_raw.endpoint_mem_access_desc_size as usize
{
return Err(Error::MalformedDescriptor);
}
if transaction_desc_raw.endpoint_mem_access_desc_count == 0 {
return Err(Error::MalformedDescriptor);
}
let Some(total_desc_size) = transaction_desc_raw
.endpoint_mem_access_desc_size
.checked_mul(transaction_desc_raw.endpoint_mem_access_desc_count)
.and_then(|x| {
x.checked_add(transaction_desc_raw.endpoint_mem_access_desc_array_offset)
})
else {
return Err(Error::InvalidBufferSize);
};
if buf.len() < total_desc_size as usize {
return Err(Error::InvalidBufferSize);
}
let transaction_desc = MemTransactionDesc {
sender_id: transaction_desc_raw.sender_endpoint_id,
mem_region_attr: transaction_desc_raw.memory_region_attributes.try_into()?,
flags: MemTransactionFlags(transaction_desc_raw.flags),
handle: Handle(transaction_desc_raw.handle),
tag: transaction_desc_raw.tag,
};
let mut offset = transaction_desc_raw.endpoint_mem_access_desc_array_offset as usize;
let access_desc_iter = MemAccessPermIterator::new(
buf,
transaction_desc_raw.endpoint_mem_access_desc_count as usize,
offset,
)?;
// We have to check the first endpoint memory access descriptor to get the composite offset
let Ok(desc_raw) = endpoint_memory_access_descriptor::ref_from_bytes(
&buf[offset..offset + size_of::<endpoint_memory_access_descriptor>()],
) else {
return Err(Error::MalformedDescriptor);
};
offset = desc_raw.composite_offset as usize;
// An offset value of 0 indicates that the endpoint access permissions apply to a memory
// region description identified by the Handle (i.e. there is no composite descriptor)
if offset == 0 {
return Ok((transaction_desc, access_desc_iter, None));
}
let Some(composite_desc_bytes) =
buf.get(offset..offset + size_of::<composite_memory_region_descriptor>())
else {
return Err(Error::InvalidBufferSize);
};
let Ok(composite_desc_raw) =
composite_memory_region_descriptor::ref_from_bytes(composite_desc_bytes)
else {
return Err(Error::MalformedDescriptor);
};
let constituent_iter = ConstituentMemRegionIterator::new(
buf,
composite_desc_raw.address_range_count as usize,
offset + Self::CONSTITUENT_ARRAY_OFFSET,
)?;
// TODO: add a sainty check to compare the composite descriptor's total page count and the
// sum of page counts from constituent memory regions (not sure if it's really valuable)
Ok((transaction_desc, access_desc_iter, Some(constituent_iter)))
}
}
#[derive(Debug, Default)]
pub struct MemRelinquishDesc {
pub handle: Handle,
pub flags: u32,
pub endpoint: u16,
}
impl TryFrom<&[u8]> for MemRelinquishDesc {
type Error = Error;
fn try_from(buf: &[u8]) -> Result<Self, Self::Error> {
let Some(desc_bytes) = buf.get(0..size_of::<memory_relinquish_descriptor>()) else {
return Err(Error::InvalidBufferSize);
};
let Ok(desc_raw) = memory_relinquish_descriptor::ref_from_bytes(desc_bytes) else {
return Err(Error::MalformedDescriptor);
};
let Some(total_desc_size) = (desc_raw.endpoint_count as usize)
.checked_mul(size_of::<u16>())
.and_then(|x| x.checked_add(Self::ENDPOINT_ARRAY_OFFSET))
else {
return Err(Error::InvalidBufferSize);
};
if buf.len() < total_desc_size {
return Err(Error::InvalidBufferSize);
}
// If the caller is a PE endpoint Borrower, then Endpoint count must equal 1. Currently only
// this case is supported. The array of endpoint IDs contains only a single element.
if desc_raw.endpoint_count != 1 {
return Err(Error::UnsupportedEndpointCount(desc_raw.endpoint_count));
}
let endpoint = u16::from_le_bytes([
buf[Self::ENDPOINT_ARRAY_OFFSET],
buf[Self::ENDPOINT_ARRAY_OFFSET + 1],
]);
Ok(Self {
handle: Handle(desc_raw.handle),
flags: desc_raw.flags, // TODO: validate
endpoint,
})
}
}
impl MemRelinquishDesc {
const ENDPOINT_ARRAY_OFFSET: usize = size_of::<memory_relinquish_descriptor>();
}
#[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 (transaction_desc, access_desc, constituents) =
MemTransactionDesc::unpack(MEM_SHARE_FROM_SP1).unwrap();
println!("transaction desc: {:#x?}", transaction_desc);
access_desc.for_each(|d| println!("endpont desc: {d:#x?}"));
constituents
.unwrap()
.for_each(|c| println!("constituent desc: {c:#x?}"));
}
}