arm_ffa/
memory_management.rs

1// SPDX-FileCopyrightText: Copyright 2023 Arm Limited and/or its affiliates <open-source-office@arm.com>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4//! Implementation of the FF-A Memory Management protocol.
5//!
6//! FF-A describes mechanisms and interfaces that enable FF-A components to manage access and
7//! ownership of memory regions in the physical address space. FF-A components can use a combination
8//! of Framework and Partition messages to manage memory regions in the following ways:
9//! - The Owner of a memory region can transfer its ownership to another FF-A endpoint.
10//! - The Owner of a memory region can transfer its access to one or more FF-A endpoints.
11//! - The Owner of a memory region can share access to it with one or more FF-A endpoints.
12//! - The Owner can reclaim access to a memory region after the FF-A endpoints that were granted
13//!   access to that memory region have relinquished their access.
14
15use crate::ffa_v1_1::{
16    composite_memory_region_descriptor, constituent_memory_region_descriptor,
17    endpoint_memory_access_descriptor, memory_access_permission_descriptor,
18    memory_relinquish_descriptor, memory_transaction_descriptor,
19};
20use core::mem::size_of;
21use thiserror::Error;
22use zerocopy::{FromBytes, IntoBytes};
23
24/// Rich error types returned by this module. Should be converted to [`crate::FfaError`] when used
25/// with the `FFA_ERROR` interface.
26#[derive(Debug, Error)]
27pub enum Error {
28    #[error("Invalid cacheability attribute {0}")]
29    InvalidCacheability(u16),
30    #[error("Invalid shareability attribute {0}")]
31    InvalidShareability(u16),
32    #[error("Invalid device memory attributes {0}")]
33    InvalidDevMemAttributes(u16),
34    #[error("Invalid instruction access permission {0}")]
35    InvalidInstrAccessPerm(u8),
36    #[error("Invalid instruction data permission {0}")]
37    InvalidDataAccessPerm(u8),
38    #[error("Invalid memory type {0}")]
39    InvalidMemType(u16),
40    #[error("Invalid memory attributes {0}")]
41    InvalidMemAttributes(u16),
42    #[error("Composite offset mismatch")]
43    CompositeOffsetMismatch,
44    #[error("Invalid endpoint count {0}")]
45    UnsupportedEndpointCount(u32),
46    #[error("Invalid buffer size")]
47    InvalidBufferSize,
48    #[error("Malformed descriptor")]
49    MalformedDescriptor,
50}
51
52impl From<Error> for crate::FfaError {
53    fn from(_value: Error) -> Self {
54        Self::InvalidParameters
55    }
56}
57
58/// Memory region handle, used to identify a composite memory region description.
59#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
60pub struct Handle(pub u64);
61
62impl From<[u32; 2]> for Handle {
63    fn from(value: [u32; 2]) -> Self {
64        Self(((value[1] as u64) << 32) | value[0] as u64)
65    }
66}
67
68impl From<Handle> for [u32; 2] {
69    fn from(value: Handle) -> Self {
70        [value.0 as u32, (value.0 >> 32) as u32]
71    }
72}
73
74impl Handle {
75    pub const INVALID: u64 = 0xffff_ffff_ffff_ffff;
76}
77
78/// Cacheability attribute of a memory region. Only valid for normal memory.
79#[derive(Debug, Default, Clone, Copy, PartialEq)]
80#[repr(u16)]
81pub enum Cacheability {
82    #[default]
83    NonCacheable = Self::NON_CACHEABLE << Self::SHIFT,
84    WriteBack = Self::WRITE_BACK << Self::SHIFT,
85}
86
87impl TryFrom<u16> for Cacheability {
88    type Error = Error;
89
90    fn try_from(value: u16) -> Result<Self, Self::Error> {
91        match (value >> Self::SHIFT) & Self::MASK {
92            Self::NON_CACHEABLE => Ok(Cacheability::NonCacheable),
93            Self::WRITE_BACK => Ok(Cacheability::WriteBack),
94            _ => Err(Error::InvalidCacheability(value)),
95        }
96    }
97}
98
99impl Cacheability {
100    const SHIFT: usize = 2;
101    const MASK: u16 = 0b11;
102    const NON_CACHEABLE: u16 = 0b01;
103    const WRITE_BACK: u16 = 0b11;
104}
105
106/// Shareability attribute of a memory region. Only valid for normal memory.
107#[derive(Debug, Default, Clone, Copy, PartialEq)]
108#[repr(u16)]
109pub enum Shareability {
110    #[default]
111    NonShareable = Self::NON_SHAREABLE << Self::SHIFT,
112    Outer = Self::OUTER << Self::SHIFT,
113    Inner = Self::INNER << Self::SHIFT,
114}
115
116impl TryFrom<u16> for Shareability {
117    type Error = Error;
118
119    fn try_from(value: u16) -> Result<Self, Self::Error> {
120        match (value >> Self::SHIFT) & Self::MASK {
121            Self::NON_SHAREABLE => Ok(Self::NonShareable),
122            Self::OUTER => Ok(Self::Outer),
123            Self::INNER => Ok(Self::Inner),
124            _ => Err(Error::InvalidShareability(value)),
125        }
126    }
127}
128
129impl Shareability {
130    const SHIFT: usize = 0;
131    const MASK: u16 = 0b11;
132    const NON_SHAREABLE: u16 = 0b00;
133    const OUTER: u16 = 0b10;
134    const INNER: u16 = 0b11;
135}
136
137/// Device memory attributes.
138#[derive(Debug, Default, Clone, Copy)]
139#[repr(u16)]
140pub enum DeviceMemAttributes {
141    #[default]
142    DevnGnRnE = Self::DEV_NGNRNE << Self::SHIFT,
143    DevnGnRE = Self::DEV_NGNRE << Self::SHIFT,
144    DevnGRE = Self::DEV_NGRE << Self::SHIFT,
145    DevGRE = Self::DEV_GRE << Self::SHIFT,
146}
147
148impl TryFrom<u16> for DeviceMemAttributes {
149    type Error = Error;
150
151    fn try_from(value: u16) -> Result<Self, Self::Error> {
152        match (value >> Self::SHIFT) & Self::MASK {
153            Self::DEV_NGNRNE => Ok(Self::DevnGnRnE),
154            Self::DEV_NGNRE => Ok(Self::DevnGnRE),
155            Self::DEV_NGRE => Ok(Self::DevnGRE),
156            Self::DEV_GRE => Ok(Self::DevGRE),
157            _ => Err(Error::InvalidDevMemAttributes(value)),
158        }
159    }
160}
161
162impl DeviceMemAttributes {
163    const SHIFT: usize = 2;
164    const MASK: u16 = 0b11;
165    const DEV_NGNRNE: u16 = 0b00;
166    const DEV_NGNRE: u16 = 0b01;
167    const DEV_NGRE: u16 = 0b10;
168    const DEV_GRE: u16 = 0b11;
169}
170
171/// Memory region type.
172#[derive(Debug, Default, Clone, Copy)]
173pub enum MemType {
174    #[default]
175    NotSpecified,
176    Device(DeviceMemAttributes),
177    Normal {
178        cacheability: Cacheability,
179        shareability: Shareability,
180    },
181}
182
183impl TryFrom<u16> for MemType {
184    type Error = Error;
185
186    fn try_from(value: u16) -> Result<Self, Self::Error> {
187        match (value >> Self::SHIFT) & Self::MASK {
188            Self::NOT_SPECIFIED => Ok(Self::NotSpecified),
189            Self::DEVICE => Ok(Self::Device(value.try_into()?)),
190            Self::NORMAL => Ok(Self::Normal {
191                cacheability: value.try_into()?,
192                shareability: value.try_into()?,
193            }),
194            _ => Err(Error::InvalidMemType(value)),
195        }
196    }
197}
198
199impl From<MemType> for u16 {
200    fn from(value: MemType) -> Self {
201        match value {
202            MemType::NotSpecified => MemType::NOT_SPECIFIED << MemType::SHIFT,
203            MemType::Device(attr) => attr as u16 | (MemType::DEVICE << MemType::SHIFT),
204            MemType::Normal {
205                cacheability,
206                shareability,
207            } => cacheability as u16 | shareability as u16 | (MemType::NORMAL << MemType::SHIFT),
208        }
209    }
210}
211
212impl MemType {
213    const SHIFT: usize = 4;
214    const MASK: u16 = 0b11;
215    const NOT_SPECIFIED: u16 = 0b00;
216    const DEVICE: u16 = 0b01;
217    const NORMAL: u16 = 0b10;
218}
219
220/// Memory region security attribute.
221#[derive(Debug, Default, Clone, Copy, PartialEq)]
222#[repr(u16)]
223pub enum MemRegionSecurity {
224    #[default]
225    Secure = Self::SECURE << Self::SHIFT,
226    NonSecure = Self::NON_SECURE << Self::SHIFT,
227}
228
229impl From<u16> for MemRegionSecurity {
230    fn from(value: u16) -> Self {
231        match (value >> Self::SHIFT) & Self::MASK {
232            Self::SECURE => Self::Secure,
233            Self::NON_SECURE => Self::NonSecure,
234            _ => panic!(), // The match is exhaustive for a 1-bit value
235        }
236    }
237}
238
239impl MemRegionSecurity {
240    const SHIFT: usize = 6;
241    const MASK: u16 = 0b1;
242    const SECURE: u16 = 0b0;
243    const NON_SECURE: u16 = 0b1;
244}
245
246/// Memory region attributes descriptor.
247#[derive(Debug, Default, Clone, Copy)]
248pub struct MemRegionAttributes {
249    pub security: MemRegionSecurity,
250    pub mem_type: MemType,
251}
252
253impl TryFrom<u16> for MemRegionAttributes {
254    type Error = Error;
255
256    fn try_from(value: u16) -> Result<Self, Self::Error> {
257        // bits[15:7]: Reserved (MBZ)
258        if value >> 7 == 0 {
259            Ok(Self {
260                security: value.into(),
261                mem_type: value.try_into()?,
262            })
263        } else {
264            Err(Error::InvalidMemAttributes(value))
265        }
266    }
267}
268
269impl From<MemRegionAttributes> for u16 {
270    fn from(value: MemRegionAttributes) -> Self {
271        value.security as u16 | u16::from(value.mem_type)
272    }
273}
274
275/// Instruction access permissions of a memory region.
276#[derive(Debug, Default, Clone, Copy)]
277#[repr(u8)]
278pub enum InstuctionAccessPerm {
279    #[default]
280    NotSpecified = Self::NOT_SPECIFIED << Self::SHIFT,
281    NotExecutable = Self::NOT_EXECUTABLE << Self::SHIFT,
282    Executable = Self::EXECUTABLE << Self::SHIFT,
283}
284
285impl TryFrom<u8> for InstuctionAccessPerm {
286    type Error = Error;
287
288    fn try_from(value: u8) -> Result<Self, Self::Error> {
289        match (value >> Self::SHIFT) & Self::MASK {
290            Self::NOT_SPECIFIED => Ok(Self::NotSpecified),
291            Self::NOT_EXECUTABLE => Ok(Self::NotExecutable),
292            Self::EXECUTABLE => Ok(Self::Executable),
293            _ => Err(Error::InvalidInstrAccessPerm(value)),
294        }
295    }
296}
297
298impl InstuctionAccessPerm {
299    const SHIFT: usize = 2;
300    const MASK: u8 = 0b11;
301    const NOT_SPECIFIED: u8 = 0b00;
302    const NOT_EXECUTABLE: u8 = 0b01;
303    const EXECUTABLE: u8 = 0b10;
304}
305
306/// Data access permissions of a memory region.
307#[derive(Debug, Default, Clone, Copy)]
308#[repr(u8)]
309pub enum DataAccessPerm {
310    #[default]
311    NotSpecified = Self::NOT_SPECIFIED << Self::SHIFT,
312    ReadOnly = Self::READ_ONLY << Self::SHIFT,
313    ReadWrite = Self::READ_WRITE << Self::SHIFT,
314}
315
316impl TryFrom<u8> for DataAccessPerm {
317    type Error = Error;
318
319    fn try_from(value: u8) -> Result<Self, Self::Error> {
320        match (value >> Self::SHIFT) & Self::MASK {
321            Self::NOT_SPECIFIED => Ok(Self::NotSpecified),
322            Self::READ_ONLY => Ok(Self::ReadOnly),
323            Self::READ_WRITE => Ok(Self::ReadWrite),
324            _ => Err(Error::InvalidDataAccessPerm(value)),
325        }
326    }
327}
328
329impl DataAccessPerm {
330    const SHIFT: usize = 0;
331    const MASK: u8 = 0b11;
332    const NOT_SPECIFIED: u8 = 0b00;
333    const READ_ONLY: u8 = 0b01;
334    const READ_WRITE: u8 = 0b10;
335}
336
337/// Endpoint memory access permissions descriptor.
338#[derive(Debug, Default, Clone, Copy)]
339pub struct MemAccessPerm {
340    pub endpoint_id: u16,
341    pub instr_access: InstuctionAccessPerm,
342    pub data_access: DataAccessPerm,
343    pub flags: u8, // TODO
344}
345
346/// Iterator of endpoint memory access permission descriptors.
347pub struct MemAccessPermIterator<'a> {
348    buf: &'a [u8],
349    offset: usize,
350    count: usize,
351}
352
353impl<'a> MemAccessPermIterator<'a> {
354    /// Create an iterator of endpoint memory access permission descriptors from a buffer.
355    fn new(buf: &'a [u8], count: usize, offset: usize) -> Result<Self, Error> {
356        let Some(total_size) = count
357            .checked_mul(size_of::<endpoint_memory_access_descriptor>())
358            .and_then(|x| x.checked_add(offset))
359        else {
360            return Err(Error::InvalidBufferSize);
361        };
362
363        if buf.len() < total_size {
364            return Err(Error::InvalidBufferSize);
365        }
366
367        Ok(Self { buf, offset, count })
368    }
369}
370
371impl Iterator for MemAccessPermIterator<'_> {
372    type Item = Result<MemAccessPerm, Error>;
373
374    fn next(&mut self) -> Option<Self::Item> {
375        if self.count > 0 {
376            let offset = self.offset;
377            self.offset += size_of::<endpoint_memory_access_descriptor>();
378            self.count -= 1;
379
380            let Ok(desc_raw) = endpoint_memory_access_descriptor::ref_from_bytes(
381                &self.buf[offset..offset + size_of::<endpoint_memory_access_descriptor>()],
382            ) else {
383                return Some(Err(Error::MalformedDescriptor));
384            };
385
386            let instr_access = match desc_raw
387                .access_perm_desc
388                .memory_access_permissions
389                .try_into()
390            {
391                Ok(v) => v,
392                Err(e) => return Some(Err(e)),
393            };
394
395            let data_access = match desc_raw
396                .access_perm_desc
397                .memory_access_permissions
398                .try_into()
399            {
400                Ok(v) => v,
401                Err(e) => return Some(Err(e)),
402            };
403
404            let desc = MemAccessPerm {
405                endpoint_id: desc_raw.access_perm_desc.endpoint_id,
406                instr_access,
407                data_access,
408                flags: desc_raw.access_perm_desc.flags,
409            };
410
411            return Some(Ok(desc));
412        }
413
414        None
415    }
416}
417
418/// Constituent memory region descriptor.
419#[derive(Debug, Default, Clone, Copy)]
420pub struct ConstituentMemRegion {
421    pub address: u64,
422    pub page_cnt: u32,
423}
424
425/// Iterator of constituent memory region descriptors.
426pub struct ConstituentMemRegionIterator<'a> {
427    buf: &'a [u8],
428    offset: usize,
429    count: usize,
430}
431
432impl<'a> ConstituentMemRegionIterator<'a> {
433    /// Create an iterator of constituent memory region descriptors from a buffer.
434    fn new(
435        buf: &'a [u8],
436        region_count: usize,
437        total_page_count: u32,
438        offset: usize,
439    ) -> Result<Self, Error> {
440        let descriptor_size = size_of::<constituent_memory_region_descriptor>();
441
442        let Some(total_size) = region_count
443            .checked_mul(descriptor_size)
444            .and_then(|x| x.checked_add(offset))
445        else {
446            return Err(Error::InvalidBufferSize);
447        };
448
449        if buf.len() < total_size {
450            return Err(Error::InvalidBufferSize);
451        }
452
453        // Check if the sum of of page counts in the constituent_memory_region_descriptors matches
454        // the total_page_count field of the composite_memory_region_descriptor.
455        let mut page_count_sum: u32 = 0;
456        for desc_offset in
457            (offset..offset + descriptor_size * region_count).step_by(descriptor_size)
458        {
459            let Ok(desc_raw) = constituent_memory_region_descriptor::ref_from_bytes(
460                &buf[desc_offset..desc_offset + descriptor_size],
461            ) else {
462                return Err(Error::MalformedDescriptor);
463            };
464
465            page_count_sum = page_count_sum
466                .checked_add(desc_raw.page_count)
467                .ok_or(Error::MalformedDescriptor)?;
468        }
469
470        if page_count_sum != total_page_count {
471            return Err(Error::MalformedDescriptor);
472        }
473
474        Ok(Self {
475            buf,
476            offset,
477            count: region_count,
478        })
479    }
480}
481
482impl Iterator for ConstituentMemRegionIterator<'_> {
483    type Item = Result<ConstituentMemRegion, Error>;
484
485    fn next(&mut self) -> Option<Self::Item> {
486        if self.count > 0 {
487            let offset = self.offset;
488            self.offset += size_of::<constituent_memory_region_descriptor>();
489            self.count -= 1;
490
491            let Ok(desc_raw) = constituent_memory_region_descriptor::ref_from_bytes(
492                &self.buf[offset..offset + size_of::<constituent_memory_region_descriptor>()],
493            ) else {
494                return Some(Err(Error::MalformedDescriptor));
495            };
496
497            let desc = ConstituentMemRegion {
498                address: desc_raw.address,
499                page_cnt: desc_raw.page_count,
500            };
501
502            return Some(Ok(desc));
503        }
504
505        None
506    }
507}
508
509/// Flags of a memory management transaction.
510#[derive(Debug, Default, Clone, Copy)]
511pub struct MemTransactionFlags(pub u32);
512
513impl MemTransactionFlags {
514    pub const MEM_SHARE_MASK: u32 = 0b11;
515    pub const MEM_RETRIEVE_REQ_MASK: u32 = 0b11_1111_1111;
516    pub const MEM_RETRIEVE_RESP_MASK: u32 = 0b1_1111;
517    pub const ZERO_MEMORY: u32 = 0b1;
518    pub const TIME_SLICING: u32 = 0b1 << 1;
519    pub const ZERO_AFTER_RELINQ: u32 = 0b1 << 2;
520    pub const TYPE_SHARE: u32 = 0b01 << 3;
521    pub const TYPE_LEND: u32 = 0b10 << 3;
522    pub const TYPE_DONATE: u32 = 0b11 << 3;
523    pub const ALIGN_HINT_MASK: u32 = 0b1111 << 5;
524    pub const HINT_VALID: u32 = 0b1 << 9;
525}
526
527/// Memory transaction decriptor. Used by an Owner/Lender and a Borrower/Receiver in a transaction
528/// to donate, lend or share a memory region.
529#[derive(Debug, Default)]
530pub struct MemTransactionDesc {
531    pub sender_id: u16,
532    pub mem_region_attr: MemRegionAttributes,
533    pub flags: MemTransactionFlags,
534    pub handle: Handle,
535    pub tag: u64, // TODO
536}
537
538impl MemTransactionDesc {
539    // Offset from the base of the memory transaction descriptor to the first element in the
540    // endpoint memory access descriptor array. Must be 16 byte aligned, but otherwise we're free to
541    // choose any value here. Let's just pack it right after the memory transaction descriptor.
542    const ENDPOINT_MEM_ACCESS_DESC_OFFSET: usize =
543        size_of::<memory_transaction_descriptor>().next_multiple_of(16);
544
545    // The array of constituent memory region descriptors starts right after the composite memory
546    // region descriptor
547    const CONSTITUENT_ARRAY_OFFSET: usize = size_of::<composite_memory_region_descriptor>();
548
549    /// Serialize a memory transaction descriptor and the related constituent memory region
550    /// descriptors and endpoint memory access permission descriptors into a buffer.
551    pub fn pack(
552        &self,
553        constituents: &[ConstituentMemRegion],
554        access_descriptors: &[MemAccessPerm],
555        buf: &mut [u8],
556    ) -> usize {
557        let mem_access_desc_size = size_of::<endpoint_memory_access_descriptor>();
558        let mem_access_desc_cnt = access_descriptors.len();
559
560        let transaction_desc_raw = memory_transaction_descriptor {
561            sender_endpoint_id: self.sender_id,
562            memory_region_attributes: self.mem_region_attr.into(),
563            flags: self.flags.0,
564            handle: self.handle.0,
565            tag: self.tag,
566            endpoint_mem_access_desc_size: mem_access_desc_size as u32,
567            endpoint_mem_access_desc_count: mem_access_desc_cnt as u32,
568            endpoint_mem_access_desc_array_offset: Self::ENDPOINT_MEM_ACCESS_DESC_OFFSET as u32,
569            reserved1: 0,
570            reserved2: 0,
571        };
572
573        transaction_desc_raw.write_to_prefix(buf).unwrap();
574
575        // Offset from the base of the memory transaction descriptor to the composite memory region
576        // descriptor to which the endpoint access permissions apply.
577        let composite_offset = mem_access_desc_cnt
578            .checked_mul(mem_access_desc_size)
579            .unwrap()
580            .checked_add(Self::ENDPOINT_MEM_ACCESS_DESC_OFFSET)
581            .unwrap()
582            .next_multiple_of(8);
583
584        let mut offset = Self::ENDPOINT_MEM_ACCESS_DESC_OFFSET;
585
586        for desc in access_descriptors {
587            let desc_raw = endpoint_memory_access_descriptor {
588                access_perm_desc: memory_access_permission_descriptor {
589                    endpoint_id: desc.endpoint_id,
590                    memory_access_permissions: desc.data_access as u8 | desc.instr_access as u8,
591                    flags: desc.flags,
592                },
593                composite_offset: composite_offset as u32,
594                reserved: 0,
595            };
596
597            desc_raw.write_to_prefix(&mut buf[offset..]).unwrap();
598            offset += mem_access_desc_size;
599        }
600
601        let mut total_page_count: u32 = 0;
602
603        offset = composite_offset + Self::CONSTITUENT_ARRAY_OFFSET;
604        for constituent in constituents {
605            let constituent_raw = constituent_memory_region_descriptor {
606                address: constituent.address,
607                page_count: constituent.page_cnt,
608                reserved: 0,
609            };
610
611            constituent_raw.write_to_prefix(&mut buf[offset..]).unwrap();
612            offset += size_of::<constituent_memory_region_descriptor>();
613
614            total_page_count = total_page_count
615                .checked_add(constituent_raw.page_count)
616                .expect("total_page_count overflow");
617        }
618
619        let composite_desc_raw = composite_memory_region_descriptor {
620            total_page_count,
621            address_range_count: constituents.len() as u32,
622            reserved: 0,
623        };
624
625        composite_desc_raw
626            .write_to_prefix(&mut buf[composite_offset..])
627            .unwrap();
628
629        offset
630    }
631
632    /// Deserialize a memory transaction descriptor from a buffer and return an interator of the
633    /// related endpoint memory access permission descriptors and constituent memory region
634    /// descriptors, if any.
635    pub fn unpack(
636        buf: &[u8],
637    ) -> Result<
638        (
639            MemTransactionDesc,
640            MemAccessPermIterator,
641            Option<ConstituentMemRegionIterator>,
642        ),
643        Error,
644    > {
645        let Some(transaction_desc_bytes) = buf.get(0..size_of::<memory_transaction_descriptor>())
646        else {
647            return Err(Error::InvalidBufferSize);
648        };
649
650        let Ok(transaction_desc_raw) =
651            memory_transaction_descriptor::ref_from_bytes(transaction_desc_bytes)
652        else {
653            return Err(Error::MalformedDescriptor);
654        };
655
656        if size_of::<endpoint_memory_access_descriptor>()
657            != transaction_desc_raw.endpoint_mem_access_desc_size as usize
658        {
659            return Err(Error::MalformedDescriptor);
660        }
661
662        if transaction_desc_raw.endpoint_mem_access_desc_count == 0 {
663            return Err(Error::MalformedDescriptor);
664        }
665
666        let Some(total_desc_size) = transaction_desc_raw
667            .endpoint_mem_access_desc_size
668            .checked_mul(transaction_desc_raw.endpoint_mem_access_desc_count)
669            .and_then(|x| {
670                x.checked_add(transaction_desc_raw.endpoint_mem_access_desc_array_offset)
671            })
672        else {
673            return Err(Error::InvalidBufferSize);
674        };
675
676        if buf.len() < total_desc_size as usize {
677            return Err(Error::InvalidBufferSize);
678        }
679
680        let transaction_desc = MemTransactionDesc {
681            sender_id: transaction_desc_raw.sender_endpoint_id,
682            mem_region_attr: transaction_desc_raw.memory_region_attributes.try_into()?,
683            flags: MemTransactionFlags(transaction_desc_raw.flags),
684            handle: Handle(transaction_desc_raw.handle),
685            tag: transaction_desc_raw.tag,
686        };
687
688        let mut offset = transaction_desc_raw.endpoint_mem_access_desc_array_offset as usize;
689
690        let access_desc_iter = MemAccessPermIterator::new(
691            buf,
692            transaction_desc_raw.endpoint_mem_access_desc_count as usize,
693            offset,
694        )?;
695
696        // We have to check the first endpoint memory access descriptor to get the composite offset
697        let Ok(desc_raw) = endpoint_memory_access_descriptor::ref_from_bytes(
698            &buf[offset..offset + size_of::<endpoint_memory_access_descriptor>()],
699        ) else {
700            return Err(Error::MalformedDescriptor);
701        };
702
703        offset = desc_raw.composite_offset as usize;
704
705        // An offset value of 0 indicates that the endpoint access permissions apply to a memory
706        // region description identified by the Handle (i.e. there is no composite descriptor)
707        if offset == 0 {
708            return Ok((transaction_desc, access_desc_iter, None));
709        }
710
711        let Some(composite_desc_bytes) =
712            buf.get(offset..offset + size_of::<composite_memory_region_descriptor>())
713        else {
714            return Err(Error::InvalidBufferSize);
715        };
716
717        let Ok(composite_desc_raw) =
718            composite_memory_region_descriptor::ref_from_bytes(composite_desc_bytes)
719        else {
720            return Err(Error::MalformedDescriptor);
721        };
722
723        let constituent_iter = ConstituentMemRegionIterator::new(
724            buf,
725            composite_desc_raw.address_range_count as usize,
726            composite_desc_raw.total_page_count,
727            offset + Self::CONSTITUENT_ARRAY_OFFSET,
728        )?;
729
730        Ok((transaction_desc, access_desc_iter, Some(constituent_iter)))
731    }
732}
733
734/// Descriptor to relinquish a memory region. Currently only supports specifying a single endpoint.
735#[derive(Debug, Default)]
736pub struct MemRelinquishDesc {
737    pub handle: Handle,
738    pub flags: u32,
739    pub endpoint: u16,
740}
741
742impl TryFrom<&[u8]> for MemRelinquishDesc {
743    type Error = Error;
744
745    fn try_from(buf: &[u8]) -> Result<Self, Self::Error> {
746        let Some(desc_bytes) = buf.get(0..size_of::<memory_relinquish_descriptor>()) else {
747            return Err(Error::InvalidBufferSize);
748        };
749
750        let Ok(desc_raw) = memory_relinquish_descriptor::ref_from_bytes(desc_bytes) else {
751            return Err(Error::MalformedDescriptor);
752        };
753
754        let Some(total_desc_size) = (desc_raw.endpoint_count as usize)
755            .checked_mul(size_of::<u16>())
756            .and_then(|x| x.checked_add(Self::ENDPOINT_ARRAY_OFFSET))
757        else {
758            return Err(Error::InvalidBufferSize);
759        };
760
761        if buf.len() < total_desc_size {
762            return Err(Error::InvalidBufferSize);
763        }
764
765        // If the caller is a PE endpoint Borrower, then Endpoint count must equal 1. Currently only
766        // this case is supported. The array of endpoint IDs contains only a single element.
767        if desc_raw.endpoint_count != 1 {
768            return Err(Error::UnsupportedEndpointCount(desc_raw.endpoint_count));
769        }
770
771        let endpoint = u16::from_le_bytes([
772            buf[Self::ENDPOINT_ARRAY_OFFSET],
773            buf[Self::ENDPOINT_ARRAY_OFFSET + 1],
774        ]);
775
776        Ok(Self {
777            handle: Handle(desc_raw.handle),
778            flags: desc_raw.flags, // TODO: validate
779            endpoint,
780        })
781    }
782}
783
784impl MemRelinquishDesc {
785    const ENDPOINT_ARRAY_OFFSET: usize = size_of::<memory_relinquish_descriptor>();
786}
787
788#[cfg(test)]
789mod tests {
790    use super::*;
791
792    #[allow(dead_code)]
793    const MEM_SHARE_FROM_SP1: &[u8] = &[
794        0x05, 0x80, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
795        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
796        0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
797        0x00, 0x00, 0x00, 0x03, 0x80, 0x02, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
798        0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
799        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x10, 0x40, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
800        0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
801    ];
802
803    #[allow(dead_code)]
804    const MEM_SHARE_FROM_SP2: &[u8] = &[
805        0x06, 0x80, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
806        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
807        0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
808        0x00, 0x00, 0x00, 0x05, 0x80, 0x02, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
809        0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
810        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x07, 0x40, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
811        0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
812    ];
813
814    #[allow(dead_code)]
815    const MEM_RETRIEVE_REQ_FROM_SP1: &[u8] = &[
816        0x05, 0x80, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00,
817        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
818        0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
819        0x00, 0x00, 0x00, 0x03, 0x80, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
820        0x00, 0x00, 0x00, 0x00,
821    ];
822
823    #[allow(dead_code)]
824    const MEM_RETRIEVE_REQ_FROM_SP2: &[u8] = &[
825        0x06, 0x80, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00,
826        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
827        0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
828        0x00, 0x00, 0x00, 0x05, 0x80, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
829        0x00, 0x00, 0x00, 0x00,
830    ];
831
832    #[allow(dead_code)]
833    const MEM_SHARE_FROM_NWD: &[u8] = &[
834        0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
835        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
836        0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
837        0x00, 0x00, 0x00, 0x03, 0x80, 0x02, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
838        0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
839        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x22, 0x80, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00,
840        0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
841    ];
842
843    #[test]
844    fn mem_share() {
845        let (transaction_desc, access_desc, constituents) =
846            MemTransactionDesc::unpack(MEM_SHARE_FROM_SP1).unwrap();
847
848        println!("transaction desc: {:#x?}", transaction_desc);
849        access_desc.for_each(|d| println!("endpont desc: {d:#x?}"));
850        constituents
851            .unwrap()
852            .for_each(|c| println!("constituent desc: {c:#x?}"));
853    }
854}