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::{
16    ffa_v1_1::{
17        composite_memory_region_descriptor, constituent_memory_region_descriptor,
18        endpoint_memory_access_descriptor, memory_access_permission_descriptor,
19        memory_relinquish_descriptor, memory_transaction_descriptor,
20    },
21    SuccessArgs,
22};
23use core::mem::size_of;
24use thiserror::Error;
25use zerocopy::{FromBytes, IntoBytes};
26
27/// Rich error types returned by this module. Should be converted to [`crate::FfaError`] when used
28/// with the `FFA_ERROR` interface.
29#[derive(Debug, Error, PartialEq, Eq)]
30pub enum Error {
31    #[error("Invalid cacheability attribute {0}")]
32    InvalidCacheability(u16),
33    #[error("Invalid shareability attribute {0}")]
34    InvalidShareability(u16),
35    #[error("Invalid device memory attributes {0}")]
36    InvalidDevMemAttributes(u16),
37    #[error("Invalid instruction access permission {0}")]
38    InvalidInstrAccessPerm(u8),
39    #[error("Invalid instruction data permission {0}")]
40    InvalidDataAccessPerm(u8),
41    #[error("Invalid memory type {0}")]
42    InvalidMemType(u16),
43    #[error("Invalid memory attributes {0}")]
44    InvalidMemAttributes(u16),
45    #[error("Composite offset mismatch")]
46    CompositeOffsetMismatch,
47    #[error("Invalid endpoint count {0}")]
48    UnsupportedEndpointCount(u32),
49    #[error("Invalid buffer size")]
50    InvalidBufferSize,
51    #[error("Malformed descriptor")]
52    MalformedDescriptor,
53    #[error("Invalid get/set instruction access permission {0}")]
54    InvalidInstrAccessPermGetSet(u32),
55    #[error("Invalid get/set instruction data permission {0}")]
56    InvalidDataAccessPermGetSet(u32),
57    #[error("Invalid page count")]
58    InvalidPageCount,
59}
60
61impl From<Error> for crate::FfaError {
62    fn from(_value: Error) -> Self {
63        Self::InvalidParameters
64    }
65}
66
67/// Memory region handle, used to identify a composite memory region description.
68#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
69pub struct Handle(pub u64);
70
71impl From<[u32; 2]> for Handle {
72    fn from(value: [u32; 2]) -> Self {
73        Self(((value[1] as u64) << 32) | value[0] as u64)
74    }
75}
76
77impl From<Handle> for [u32; 2] {
78    fn from(value: Handle) -> Self {
79        [value.0 as u32, (value.0 >> 32) as u32]
80    }
81}
82
83impl Handle {
84    pub const INVALID: u64 = 0xffff_ffff_ffff_ffff;
85}
86
87/// Cacheability attribute of a memory region. Only valid for normal memory.
88#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
89#[repr(u16)]
90pub enum Cacheability {
91    #[default]
92    NonCacheable = Self::NON_CACHEABLE << Self::SHIFT,
93    WriteBack = Self::WRITE_BACK << Self::SHIFT,
94}
95
96impl TryFrom<u16> for Cacheability {
97    type Error = Error;
98
99    fn try_from(value: u16) -> Result<Self, Self::Error> {
100        match (value >> Self::SHIFT) & Self::MASK {
101            Self::NON_CACHEABLE => Ok(Cacheability::NonCacheable),
102            Self::WRITE_BACK => Ok(Cacheability::WriteBack),
103            _ => Err(Error::InvalidCacheability(value)),
104        }
105    }
106}
107
108impl Cacheability {
109    const SHIFT: usize = 2;
110    const MASK: u16 = 0b11;
111    const NON_CACHEABLE: u16 = 0b01;
112    const WRITE_BACK: u16 = 0b11;
113}
114
115/// Shareability attribute of a memory region. Only valid for normal memory.
116#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
117#[repr(u16)]
118pub enum Shareability {
119    #[default]
120    NonShareable = Self::NON_SHAREABLE << Self::SHIFT,
121    Outer = Self::OUTER << Self::SHIFT,
122    Inner = Self::INNER << Self::SHIFT,
123}
124
125impl TryFrom<u16> for Shareability {
126    type Error = Error;
127
128    fn try_from(value: u16) -> Result<Self, Self::Error> {
129        match (value >> Self::SHIFT) & Self::MASK {
130            Self::NON_SHAREABLE => Ok(Self::NonShareable),
131            Self::OUTER => Ok(Self::Outer),
132            Self::INNER => Ok(Self::Inner),
133            _ => Err(Error::InvalidShareability(value)),
134        }
135    }
136}
137
138impl Shareability {
139    const SHIFT: usize = 0;
140    const MASK: u16 = 0b11;
141    const NON_SHAREABLE: u16 = 0b00;
142    const OUTER: u16 = 0b10;
143    const INNER: u16 = 0b11;
144}
145
146/// Device memory attributes.
147#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
148#[repr(u16)]
149pub enum DeviceMemAttributes {
150    #[default]
151    DevnGnRnE = Self::DEV_NGNRNE << Self::SHIFT,
152    DevnGnRE = Self::DEV_NGNRE << Self::SHIFT,
153    DevnGRE = Self::DEV_NGRE << Self::SHIFT,
154    DevGRE = Self::DEV_GRE << Self::SHIFT,
155}
156
157impl TryFrom<u16> for DeviceMemAttributes {
158    type Error = Error;
159
160    fn try_from(value: u16) -> Result<Self, Self::Error> {
161        match (value >> Self::SHIFT) & Self::MASK {
162            Self::DEV_NGNRNE => Ok(Self::DevnGnRnE),
163            Self::DEV_NGNRE => Ok(Self::DevnGnRE),
164            Self::DEV_NGRE => Ok(Self::DevnGRE),
165            Self::DEV_GRE => Ok(Self::DevGRE),
166            _ => Err(Error::InvalidDevMemAttributes(value)),
167        }
168    }
169}
170
171impl DeviceMemAttributes {
172    const SHIFT: usize = 2;
173    const MASK: u16 = 0b11;
174    const DEV_NGNRNE: u16 = 0b00;
175    const DEV_NGNRE: u16 = 0b01;
176    const DEV_NGRE: u16 = 0b10;
177    const DEV_GRE: u16 = 0b11;
178}
179
180/// Memory region type.
181#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
182pub enum MemType {
183    #[default]
184    NotSpecified,
185    Device(DeviceMemAttributes),
186    Normal {
187        cacheability: Cacheability,
188        shareability: Shareability,
189    },
190}
191
192impl TryFrom<u16> for MemType {
193    type Error = Error;
194
195    fn try_from(value: u16) -> Result<Self, Self::Error> {
196        match (value >> Self::SHIFT) & Self::MASK {
197            Self::NOT_SPECIFIED => Ok(Self::NotSpecified),
198            Self::DEVICE => Ok(Self::Device(value.try_into()?)),
199            Self::NORMAL => Ok(Self::Normal {
200                cacheability: value.try_into()?,
201                shareability: value.try_into()?,
202            }),
203            _ => Err(Error::InvalidMemType(value)),
204        }
205    }
206}
207
208impl From<MemType> for u16 {
209    fn from(value: MemType) -> Self {
210        match value {
211            MemType::NotSpecified => MemType::NOT_SPECIFIED << MemType::SHIFT,
212            MemType::Device(attr) => attr as u16 | (MemType::DEVICE << MemType::SHIFT),
213            MemType::Normal {
214                cacheability,
215                shareability,
216            } => cacheability as u16 | shareability as u16 | (MemType::NORMAL << MemType::SHIFT),
217        }
218    }
219}
220
221impl MemType {
222    const SHIFT: usize = 4;
223    const MASK: u16 = 0b11;
224    const NOT_SPECIFIED: u16 = 0b00;
225    const DEVICE: u16 = 0b01;
226    const NORMAL: u16 = 0b10;
227}
228
229/// Memory region security attribute.
230#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
231#[repr(u16)]
232pub enum MemRegionSecurity {
233    #[default]
234    Secure = Self::SECURE << Self::SHIFT,
235    NonSecure = Self::NON_SECURE << Self::SHIFT,
236}
237
238impl From<u16> for MemRegionSecurity {
239    fn from(value: u16) -> Self {
240        match (value >> Self::SHIFT) & Self::MASK {
241            Self::SECURE => Self::Secure,
242            Self::NON_SECURE => Self::NonSecure,
243            _ => panic!(), // The match is exhaustive for a 1-bit value
244        }
245    }
246}
247
248impl MemRegionSecurity {
249    const SHIFT: usize = 6;
250    const MASK: u16 = 0b1;
251    const SECURE: u16 = 0b0;
252    const NON_SECURE: u16 = 0b1;
253}
254
255/// Memory region attributes descriptor.
256#[derive(Debug, Default, Clone, Copy)]
257pub struct MemRegionAttributes {
258    pub security: MemRegionSecurity,
259    pub mem_type: MemType,
260}
261
262impl TryFrom<u16> for MemRegionAttributes {
263    type Error = Error;
264
265    fn try_from(value: u16) -> Result<Self, Self::Error> {
266        // bits[15:7]: Reserved (MBZ)
267        if value >> 7 == 0 {
268            Ok(Self {
269                security: value.into(),
270                mem_type: value.try_into()?,
271            })
272        } else {
273            Err(Error::InvalidMemAttributes(value))
274        }
275    }
276}
277
278impl From<MemRegionAttributes> for u16 {
279    fn from(value: MemRegionAttributes) -> Self {
280        value.security as u16 | u16::from(value.mem_type)
281    }
282}
283
284/// Instruction access permissions of a memory region.
285#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
286#[repr(u8)]
287pub enum InstuctionAccessPerm {
288    #[default]
289    NotSpecified = Self::NOT_SPECIFIED << Self::SHIFT,
290    NotExecutable = Self::NOT_EXECUTABLE << Self::SHIFT,
291    Executable = Self::EXECUTABLE << Self::SHIFT,
292}
293
294impl TryFrom<u8> for InstuctionAccessPerm {
295    type Error = Error;
296
297    fn try_from(value: u8) -> Result<Self, Self::Error> {
298        match (value >> Self::SHIFT) & Self::MASK {
299            Self::NOT_SPECIFIED => Ok(Self::NotSpecified),
300            Self::NOT_EXECUTABLE => Ok(Self::NotExecutable),
301            Self::EXECUTABLE => Ok(Self::Executable),
302            _ => Err(Error::InvalidInstrAccessPerm(value)),
303        }
304    }
305}
306
307impl InstuctionAccessPerm {
308    const SHIFT: usize = 2;
309    const MASK: u8 = 0b11;
310    const NOT_SPECIFIED: u8 = 0b00;
311    const NOT_EXECUTABLE: u8 = 0b01;
312    const EXECUTABLE: u8 = 0b10;
313}
314
315/// Data access permissions of a memory region.
316#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
317#[repr(u8)]
318pub enum DataAccessPerm {
319    #[default]
320    NotSpecified = Self::NOT_SPECIFIED << Self::SHIFT,
321    ReadOnly = Self::READ_ONLY << Self::SHIFT,
322    ReadWrite = Self::READ_WRITE << Self::SHIFT,
323}
324
325impl TryFrom<u8> for DataAccessPerm {
326    type Error = Error;
327
328    fn try_from(value: u8) -> Result<Self, Self::Error> {
329        match (value >> Self::SHIFT) & Self::MASK {
330            Self::NOT_SPECIFIED => Ok(Self::NotSpecified),
331            Self::READ_ONLY => Ok(Self::ReadOnly),
332            Self::READ_WRITE => Ok(Self::ReadWrite),
333            _ => Err(Error::InvalidDataAccessPerm(value)),
334        }
335    }
336}
337
338impl DataAccessPerm {
339    const SHIFT: usize = 0;
340    const MASK: u8 = 0b11;
341    const NOT_SPECIFIED: u8 = 0b00;
342    const READ_ONLY: u8 = 0b01;
343    const READ_WRITE: u8 = 0b10;
344}
345
346/// Endpoint memory access permissions descriptor.
347#[derive(Debug, Default, Clone, Copy)]
348pub struct MemAccessPerm {
349    pub endpoint_id: u16,
350    pub instr_access: InstuctionAccessPerm,
351    pub data_access: DataAccessPerm,
352    pub flags: u8, // TODO
353}
354
355/// Iterator of endpoint memory access permission descriptors.
356pub struct MemAccessPermIterator<'a> {
357    buf: &'a [u8],
358    offset: usize,
359    count: usize,
360}
361
362impl<'a> MemAccessPermIterator<'a> {
363    /// Create an iterator of endpoint memory access permission descriptors from a buffer.
364    fn new(buf: &'a [u8], count: usize, offset: usize) -> Result<Self, Error> {
365        let Some(total_size) = count
366            .checked_mul(size_of::<endpoint_memory_access_descriptor>())
367            .and_then(|x| x.checked_add(offset))
368        else {
369            return Err(Error::InvalidBufferSize);
370        };
371
372        if buf.len() < total_size {
373            return Err(Error::InvalidBufferSize);
374        }
375
376        Ok(Self { buf, offset, count })
377    }
378}
379
380impl Iterator for MemAccessPermIterator<'_> {
381    type Item = Result<MemAccessPerm, Error>;
382
383    fn next(&mut self) -> Option<Self::Item> {
384        if self.count > 0 {
385            let offset = self.offset;
386            self.offset += size_of::<endpoint_memory_access_descriptor>();
387            self.count -= 1;
388
389            let Ok(desc_raw) = endpoint_memory_access_descriptor::ref_from_bytes(
390                &self.buf[offset..offset + size_of::<endpoint_memory_access_descriptor>()],
391            ) else {
392                return Some(Err(Error::MalformedDescriptor));
393            };
394
395            let instr_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 data_access = match desc_raw
405                .access_perm_desc
406                .memory_access_permissions
407                .try_into()
408            {
409                Ok(v) => v,
410                Err(e) => return Some(Err(e)),
411            };
412
413            let desc = MemAccessPerm {
414                endpoint_id: desc_raw.access_perm_desc.endpoint_id,
415                instr_access,
416                data_access,
417                flags: desc_raw.access_perm_desc.flags,
418            };
419
420            return Some(Ok(desc));
421        }
422
423        None
424    }
425}
426
427/// Constituent memory region descriptor.
428#[derive(Debug, Default, Clone, Copy)]
429pub struct ConstituentMemRegion {
430    pub address: u64,
431    pub page_cnt: u32,
432}
433
434/// Iterator of constituent memory region descriptors.
435pub struct ConstituentMemRegionIterator<'a> {
436    buf: &'a [u8],
437    offset: usize,
438    count: usize,
439}
440
441impl<'a> ConstituentMemRegionIterator<'a> {
442    /// Create an iterator of constituent memory region descriptors from a buffer.
443    fn new(
444        buf: &'a [u8],
445        region_count: usize,
446        total_page_count: u32,
447        offset: usize,
448    ) -> Result<Self, Error> {
449        let descriptor_size = size_of::<constituent_memory_region_descriptor>();
450
451        let Some(total_size) = region_count
452            .checked_mul(descriptor_size)
453            .and_then(|x| x.checked_add(offset))
454        else {
455            return Err(Error::InvalidBufferSize);
456        };
457
458        if buf.len() < total_size {
459            return Err(Error::InvalidBufferSize);
460        }
461
462        // Check if the sum of of page counts in the constituent_memory_region_descriptors matches
463        // the total_page_count field of the composite_memory_region_descriptor.
464        let mut page_count_sum: u32 = 0;
465        for desc_offset in
466            (offset..offset + descriptor_size * region_count).step_by(descriptor_size)
467        {
468            let Ok(desc_raw) = constituent_memory_region_descriptor::ref_from_bytes(
469                &buf[desc_offset..desc_offset + descriptor_size],
470            ) else {
471                return Err(Error::MalformedDescriptor);
472            };
473
474            page_count_sum = page_count_sum
475                .checked_add(desc_raw.page_count)
476                .ok_or(Error::MalformedDescriptor)?;
477        }
478
479        if page_count_sum != total_page_count {
480            return Err(Error::MalformedDescriptor);
481        }
482
483        Ok(Self {
484            buf,
485            offset,
486            count: region_count,
487        })
488    }
489}
490
491impl Iterator for ConstituentMemRegionIterator<'_> {
492    type Item = Result<ConstituentMemRegion, Error>;
493
494    fn next(&mut self) -> Option<Self::Item> {
495        if self.count > 0 {
496            let offset = self.offset;
497            self.offset += size_of::<constituent_memory_region_descriptor>();
498            self.count -= 1;
499
500            let Ok(desc_raw) = constituent_memory_region_descriptor::ref_from_bytes(
501                &self.buf[offset..offset + size_of::<constituent_memory_region_descriptor>()],
502            ) else {
503                return Some(Err(Error::MalformedDescriptor));
504            };
505
506            let desc = ConstituentMemRegion {
507                address: desc_raw.address,
508                page_cnt: desc_raw.page_count,
509            };
510
511            return Some(Ok(desc));
512        }
513
514        None
515    }
516}
517
518/// Flags of a memory management transaction.
519#[derive(Debug, Default, Clone, Copy)]
520pub struct MemTransactionFlags(pub u32);
521
522impl MemTransactionFlags {
523    pub const MEM_SHARE_MASK: u32 = 0b11;
524    pub const MEM_RETRIEVE_REQ_MASK: u32 = 0b11_1111_1111;
525    pub const MEM_RETRIEVE_RESP_MASK: u32 = 0b1_1111;
526    pub const ZERO_MEMORY: u32 = 0b1;
527    pub const TIME_SLICING: u32 = 0b1 << 1;
528    pub const ZERO_AFTER_RELINQ: u32 = 0b1 << 2;
529    pub const TYPE_SHARE: u32 = 0b01 << 3;
530    pub const TYPE_LEND: u32 = 0b10 << 3;
531    pub const TYPE_DONATE: u32 = 0b11 << 3;
532    pub const ALIGN_HINT_MASK: u32 = 0b1111 << 5;
533    pub const HINT_VALID: u32 = 0b1 << 9;
534}
535
536/// Memory transaction decriptor. Used by an Owner/Lender and a Borrower/Receiver in a transaction
537/// to donate, lend or share a memory region.
538#[derive(Debug, Default)]
539pub struct MemTransactionDesc {
540    pub sender_id: u16,
541    pub mem_region_attr: MemRegionAttributes,
542    pub flags: MemTransactionFlags,
543    pub handle: Handle,
544    pub tag: u64, // TODO
545}
546
547impl MemTransactionDesc {
548    // Offset from the base of the memory transaction descriptor to the first element in the
549    // endpoint memory access descriptor array. Must be 16 byte aligned, but otherwise we're free to
550    // choose any value here. Let's just pack it right after the memory transaction descriptor.
551    const ENDPOINT_MEM_ACCESS_DESC_OFFSET: usize =
552        size_of::<memory_transaction_descriptor>().next_multiple_of(16);
553
554    // The array of constituent memory region descriptors starts right after the composite memory
555    // region descriptor
556    const CONSTITUENT_ARRAY_OFFSET: usize = size_of::<composite_memory_region_descriptor>();
557
558    /// Serialize a memory transaction descriptor and the related constituent memory region
559    /// descriptors and endpoint memory access permission descriptors into a buffer.
560    pub fn pack(
561        &self,
562        constituents: &[ConstituentMemRegion],
563        access_descriptors: &[MemAccessPerm],
564        buf: &mut [u8],
565    ) -> usize {
566        let mem_access_desc_size = size_of::<endpoint_memory_access_descriptor>();
567        let mem_access_desc_cnt = access_descriptors.len();
568
569        let transaction_desc_raw = memory_transaction_descriptor {
570            sender_endpoint_id: self.sender_id,
571            memory_region_attributes: self.mem_region_attr.into(),
572            flags: self.flags.0,
573            handle: self.handle.0,
574            tag: self.tag,
575            endpoint_mem_access_desc_size: mem_access_desc_size as u32,
576            endpoint_mem_access_desc_count: mem_access_desc_cnt as u32,
577            endpoint_mem_access_desc_array_offset: Self::ENDPOINT_MEM_ACCESS_DESC_OFFSET as u32,
578            reserved1: 0,
579            reserved2: 0,
580        };
581
582        transaction_desc_raw.write_to_prefix(buf).unwrap();
583
584        // Offset from the base of the memory transaction descriptor to the composite memory region
585        // descriptor to which the endpoint access permissions apply.
586        let composite_offset = mem_access_desc_cnt
587            .checked_mul(mem_access_desc_size)
588            .unwrap()
589            .checked_add(Self::ENDPOINT_MEM_ACCESS_DESC_OFFSET)
590            .unwrap()
591            .next_multiple_of(8);
592
593        let mut offset = Self::ENDPOINT_MEM_ACCESS_DESC_OFFSET;
594
595        for desc in access_descriptors {
596            let desc_raw = endpoint_memory_access_descriptor {
597                access_perm_desc: memory_access_permission_descriptor {
598                    endpoint_id: desc.endpoint_id,
599                    memory_access_permissions: desc.data_access as u8 | desc.instr_access as u8,
600                    flags: desc.flags,
601                },
602                composite_offset: composite_offset as u32,
603                reserved: 0,
604            };
605
606            desc_raw.write_to_prefix(&mut buf[offset..]).unwrap();
607            offset += mem_access_desc_size;
608        }
609
610        let mut total_page_count: u32 = 0;
611
612        offset = composite_offset + Self::CONSTITUENT_ARRAY_OFFSET;
613        for constituent in constituents {
614            let constituent_raw = constituent_memory_region_descriptor {
615                address: constituent.address,
616                page_count: constituent.page_cnt,
617                reserved: 0,
618            };
619
620            constituent_raw.write_to_prefix(&mut buf[offset..]).unwrap();
621            offset += size_of::<constituent_memory_region_descriptor>();
622
623            total_page_count = total_page_count
624                .checked_add(constituent_raw.page_count)
625                .expect("total_page_count overflow");
626        }
627
628        let composite_desc_raw = composite_memory_region_descriptor {
629            total_page_count,
630            address_range_count: constituents.len() as u32,
631            reserved: 0,
632        };
633
634        composite_desc_raw
635            .write_to_prefix(&mut buf[composite_offset..])
636            .unwrap();
637
638        offset
639    }
640
641    /// Deserialize a memory transaction descriptor from a buffer and return an interator of the
642    /// related endpoint memory access permission descriptors and constituent memory region
643    /// descriptors, if any.
644    pub fn unpack(
645        buf: &[u8],
646    ) -> Result<
647        (
648            MemTransactionDesc,
649            MemAccessPermIterator,
650            Option<ConstituentMemRegionIterator>,
651        ),
652        Error,
653    > {
654        let Some(transaction_desc_bytes) = buf.get(0..size_of::<memory_transaction_descriptor>())
655        else {
656            return Err(Error::InvalidBufferSize);
657        };
658
659        let Ok(transaction_desc_raw) =
660            memory_transaction_descriptor::ref_from_bytes(transaction_desc_bytes)
661        else {
662            return Err(Error::MalformedDescriptor);
663        };
664
665        if size_of::<endpoint_memory_access_descriptor>()
666            != transaction_desc_raw.endpoint_mem_access_desc_size as usize
667        {
668            return Err(Error::MalformedDescriptor);
669        }
670
671        if transaction_desc_raw.endpoint_mem_access_desc_count == 0 {
672            return Err(Error::MalformedDescriptor);
673        }
674
675        let Some(total_desc_size) = transaction_desc_raw
676            .endpoint_mem_access_desc_size
677            .checked_mul(transaction_desc_raw.endpoint_mem_access_desc_count)
678            .and_then(|x| {
679                x.checked_add(transaction_desc_raw.endpoint_mem_access_desc_array_offset)
680            })
681        else {
682            return Err(Error::InvalidBufferSize);
683        };
684
685        if buf.len() < total_desc_size as usize {
686            return Err(Error::InvalidBufferSize);
687        }
688
689        let transaction_desc = MemTransactionDesc {
690            sender_id: transaction_desc_raw.sender_endpoint_id,
691            mem_region_attr: transaction_desc_raw.memory_region_attributes.try_into()?,
692            flags: MemTransactionFlags(transaction_desc_raw.flags),
693            handle: Handle(transaction_desc_raw.handle),
694            tag: transaction_desc_raw.tag,
695        };
696
697        let mut offset = transaction_desc_raw.endpoint_mem_access_desc_array_offset as usize;
698
699        let access_desc_iter = MemAccessPermIterator::new(
700            buf,
701            transaction_desc_raw.endpoint_mem_access_desc_count as usize,
702            offset,
703        )?;
704
705        // We have to check the first endpoint memory access descriptor to get the composite offset
706        let Ok(desc_raw) = endpoint_memory_access_descriptor::ref_from_bytes(
707            &buf[offset..offset + size_of::<endpoint_memory_access_descriptor>()],
708        ) else {
709            return Err(Error::MalformedDescriptor);
710        };
711
712        offset = desc_raw.composite_offset as usize;
713
714        // An offset value of 0 indicates that the endpoint access permissions apply to a memory
715        // region description identified by the Handle (i.e. there is no composite descriptor)
716        if offset == 0 {
717            return Ok((transaction_desc, access_desc_iter, None));
718        }
719
720        let Some(composite_desc_bytes) =
721            buf.get(offset..offset + size_of::<composite_memory_region_descriptor>())
722        else {
723            return Err(Error::InvalidBufferSize);
724        };
725
726        let Ok(composite_desc_raw) =
727            composite_memory_region_descriptor::ref_from_bytes(composite_desc_bytes)
728        else {
729            return Err(Error::MalformedDescriptor);
730        };
731
732        let constituent_iter = ConstituentMemRegionIterator::new(
733            buf,
734            composite_desc_raw.address_range_count as usize,
735            composite_desc_raw.total_page_count,
736            offset + Self::CONSTITUENT_ARRAY_OFFSET,
737        )?;
738
739        Ok((transaction_desc, access_desc_iter, Some(constituent_iter)))
740    }
741}
742
743/// Iterator of endpoint IDs.
744pub struct EndpointIterator<'a> {
745    buf: &'a [u8],
746    offset: usize,
747    count: usize,
748}
749
750impl<'a> EndpointIterator<'a> {
751    /// Create an iterator of endpoint IDs from a buffer.
752    fn new(buf: &'a [u8], count: usize, offset: usize) -> Result<Self, Error> {
753        let Some(total_size) = count.checked_mul(size_of::<u16>()) else {
754            return Err(Error::InvalidBufferSize);
755        };
756
757        if buf.len() < total_size {
758            return Err(Error::InvalidBufferSize);
759        }
760
761        Ok(Self { buf, offset, count })
762    }
763}
764
765impl Iterator for EndpointIterator<'_> {
766    type Item = u16;
767
768    fn next(&mut self) -> Option<Self::Item> {
769        if self.count > 0 {
770            let offset = self.offset;
771            self.offset += size_of::<Self::Item>();
772            self.count -= 1;
773
774            let endpoint = u16::from_le_bytes([self.buf[offset], self.buf[offset + 1]]);
775            return Some(endpoint);
776        }
777
778        None
779    }
780}
781
782/// Descriptor to relinquish a memory region. Currently only supports specifying a single endpoint.
783#[derive(Debug, Default)]
784pub struct MemRelinquishDesc {
785    pub handle: Handle,
786    pub flags: u32,
787}
788
789impl MemRelinquishDesc {
790    const ENDPOINT_ARRAY_OFFSET: usize = size_of::<memory_relinquish_descriptor>();
791
792    /// Serialize memory relinquish descriptor and the endpoint IDs into a buffer.
793    pub fn pack(&self, endpoints: &[u16], buf: &mut [u8]) -> usize {
794        if let Ok(desc_raw) = memory_relinquish_descriptor::mut_from_bytes(buf) {
795            desc_raw.handle = self.handle.0;
796            desc_raw.flags = self.flags;
797            desc_raw.endpoint_count = endpoints.len().try_into().unwrap();
798
799            let endpoint_area = &mut buf[Self::ENDPOINT_ARRAY_OFFSET..];
800
801            for (endpoint, dest) in endpoints
802                .iter()
803                .zip(endpoint_area[..endpoints.len() * 2].chunks_exact_mut(2))
804            {
805                [dest[0], dest[1]] = u16::to_le_bytes(*endpoint);
806            }
807        }
808
809        Self::ENDPOINT_ARRAY_OFFSET + endpoints.len() * 2
810    }
811
812    /// Deserialize a memory relinquish descriptor from a buffer and return an iterator to the
813    /// endpoint IDs.
814    pub fn unpack(buf: &[u8]) -> Result<(MemRelinquishDesc, EndpointIterator), Error> {
815        let Some(desc_bytes) = buf.get(0..size_of::<memory_relinquish_descriptor>()) else {
816            return Err(Error::InvalidBufferSize);
817        };
818
819        let Ok(desc_raw) = memory_relinquish_descriptor::ref_from_bytes(desc_bytes) else {
820            return Err(Error::MalformedDescriptor);
821        };
822
823        let Some(total_desc_size) = (desc_raw.endpoint_count as usize)
824            .checked_mul(size_of::<u16>())
825            .and_then(|x| x.checked_add(Self::ENDPOINT_ARRAY_OFFSET))
826        else {
827            return Err(Error::InvalidBufferSize);
828        };
829
830        if buf.len() < total_desc_size {
831            return Err(Error::InvalidBufferSize);
832        }
833
834        let iterator = EndpointIterator::new(
835            buf,
836            desc_raw.endpoint_count as usize,
837            Self::ENDPOINT_ARRAY_OFFSET,
838        )?;
839
840        Ok((
841            Self {
842                handle: Handle(desc_raw.handle),
843                flags: desc_raw.flags, // TODO: validate
844            },
845            iterator,
846        ))
847    }
848}
849
850/// Flags field of the FFA_MEM_RECLAIM interface.
851#[derive(Debug, Default, Eq, PartialEq, Clone, Copy)]
852pub struct MemReclaimFlags {
853    pub zero_memory: bool,
854    pub time_slicing: bool,
855}
856
857impl MemReclaimFlags {
858    pub const ZERO_MEMORY: u32 = 0b1 << 0;
859    pub const TIME_SLICING: u32 = 0b1 << 1;
860    const MBZ_BITS: u32 = 0xffff_fffc;
861}
862
863impl TryFrom<u32> for MemReclaimFlags {
864    type Error = crate::Error;
865
866    fn try_from(val: u32) -> Result<Self, Self::Error> {
867        if (val & Self::MBZ_BITS) != 0 {
868            Err(crate::Error::InvalidMemReclaimFlags(val))
869        } else {
870            Ok(MemReclaimFlags {
871                zero_memory: val & Self::ZERO_MEMORY != 0,
872                time_slicing: val & Self::TIME_SLICING != 0,
873            })
874        }
875    }
876}
877
878impl From<MemReclaimFlags> for u32 {
879    fn from(flags: MemReclaimFlags) -> Self {
880        let mut bits: u32 = 0;
881        if flags.zero_memory {
882            bits |= MemReclaimFlags::ZERO_MEMORY;
883        }
884        if flags.time_slicing {
885            bits |= MemReclaimFlags::TIME_SLICING;
886        }
887        bits
888    }
889}
890
891/// Success argument structure for `FFA_MEM_DONATE`, `FFA_MEM_LEND` and `FFA_MEM_SHARE`.
892#[derive(Debug, Eq, PartialEq, Clone, Copy)]
893pub struct SuccessArgsMemOp {
894    pub handle: Handle,
895}
896
897impl From<SuccessArgsMemOp> for SuccessArgs {
898    fn from(value: SuccessArgsMemOp) -> Self {
899        let [handle_lo, handle_hi]: [u32; 2] = value.handle.into();
900        SuccessArgs::Args32([handle_lo, handle_hi, 0, 0, 0, 0])
901    }
902}
903
904impl TryFrom<SuccessArgs> for SuccessArgsMemOp {
905    type Error = crate::Error;
906
907    fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
908        let [handle_lo, handle_hi, ..] = value.try_get_args32()?;
909        Ok(Self {
910            handle: [handle_lo, handle_hi].into(),
911        })
912    }
913}
914
915/// Data access permission enum for `FFA_MEM_PERM_GET` and `FFA_MEM_PERM_SET` calls.
916#[derive(Debug, Clone, Copy, PartialEq, Eq)]
917#[repr(u32)]
918pub enum DataAccessPermGetSet {
919    NoAccess = Self::NO_ACCESS << Self::SHIFT,
920    ReadWrite = Self::READ_WRITE << Self::SHIFT,
921    ReadOnly = Self::READ_ONLY << Self::SHIFT,
922}
923
924impl DataAccessPermGetSet {
925    const SHIFT: usize = 0;
926    const MASK: u32 = 0b11;
927    const NO_ACCESS: u32 = 0b00;
928    const READ_WRITE: u32 = 0b01;
929    const READ_ONLY: u32 = 0b11;
930}
931
932impl TryFrom<u32> for DataAccessPermGetSet {
933    type Error = Error;
934
935    fn try_from(value: u32) -> Result<Self, Self::Error> {
936        match (value >> Self::SHIFT) & Self::MASK {
937            Self::NO_ACCESS => Ok(Self::NoAccess),
938            Self::READ_WRITE => Ok(Self::ReadWrite),
939            Self::READ_ONLY => Ok(Self::ReadOnly),
940            _ => Err(Error::InvalidDataAccessPermGetSet(value)),
941        }
942    }
943}
944
945/// Instructions access permission enum for `FFA_MEM_PERM_GET` and `FFA_MEM_PERM_SET` calls.
946#[derive(Debug, Clone, Copy, PartialEq, Eq)]
947#[repr(u32)]
948pub enum InstructionAccessPermGetSet {
949    Executable = Self::EXECUTABLE << Self::SHIFT,
950    NonExecutable = Self::NON_EXECUTABLE << Self::SHIFT,
951}
952
953impl InstructionAccessPermGetSet {
954    const SHIFT: usize = 2;
955    const MASK: u32 = 0b1;
956    const EXECUTABLE: u32 = 0b0;
957    const NON_EXECUTABLE: u32 = 0b1;
958}
959
960impl TryFrom<u32> for InstructionAccessPermGetSet {
961    type Error = Error;
962
963    fn try_from(value: u32) -> Result<Self, Self::Error> {
964        match (value >> Self::SHIFT) & Self::MASK {
965            Self::EXECUTABLE => Ok(Self::Executable),
966            Self::NON_EXECUTABLE => Ok(Self::NonExecutable),
967            _ => Err(Error::InvalidInstrAccessPermGetSet(value)),
968        }
969    }
970}
971
972/// Memory permission structure for `FFA_MEM_PERM_GET` and `FFA_MEM_PERM_SET` calls.
973#[derive(Debug, Clone, Copy, PartialEq, Eq)]
974pub struct MemPermissionsGetSet {
975    pub data_access: DataAccessPermGetSet,
976    pub instr_access: InstructionAccessPermGetSet,
977}
978
979impl TryFrom<u32> for MemPermissionsGetSet {
980    type Error = Error;
981
982    fn try_from(value: u32) -> Result<Self, Self::Error> {
983        Ok(Self {
984            data_access: value.try_into()?,
985            instr_access: value.try_into()?,
986        })
987    }
988}
989
990impl From<MemPermissionsGetSet> for u32 {
991    fn from(value: MemPermissionsGetSet) -> Self {
992        value.data_access as u32 | value.instr_access as u32
993    }
994}
995
996/// Success argument structure for `FFA_MEM_PERM_GET`.
997#[derive(Debug, Eq, PartialEq, Clone, Copy)]
998pub struct SuccessArgsMemPermGet {
999    pub perm: MemPermissionsGetSet,
1000    pub page_cnt: u32,
1001}
1002
1003impl From<SuccessArgsMemPermGet> for SuccessArgs {
1004    fn from(value: SuccessArgsMemPermGet) -> Self {
1005        assert_ne!(value.page_cnt, 0);
1006        SuccessArgs::Args32([value.perm.into(), value.page_cnt - 1, 0, 0, 0, 0])
1007    }
1008}
1009
1010impl TryFrom<SuccessArgs> for SuccessArgsMemPermGet {
1011    type Error = crate::Error;
1012
1013    fn try_from(value: SuccessArgs) -> Result<Self, Self::Error> {
1014        let [perm, page_cnt, ..] = value.try_get_args32()?;
1015        Ok(Self {
1016            perm: perm.try_into()?,
1017            page_cnt: page_cnt.checked_add(1).ok_or(Error::InvalidPageCount)?,
1018        })
1019    }
1020}
1021
1022#[cfg(test)]
1023mod tests {
1024    use super::*;
1025
1026    #[allow(dead_code)]
1027    const MEM_SHARE_FROM_SP1: &[u8] = &[
1028        0x05, 0x80, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1029        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
1030        0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1031        0x00, 0x00, 0x00, 0x03, 0x80, 0x02, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1032        0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1033        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x10, 0x40, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
1034        0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1035    ];
1036
1037    #[allow(dead_code)]
1038    const MEM_SHARE_FROM_SP2: &[u8] = &[
1039        0x06, 0x80, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1040        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
1041        0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1042        0x00, 0x00, 0x00, 0x05, 0x80, 0x02, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1043        0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1044        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x07, 0x40, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
1045        0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1046    ];
1047
1048    #[allow(dead_code)]
1049    const MEM_RETRIEVE_REQ_FROM_SP1: &[u8] = &[
1050        0x05, 0x80, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00,
1051        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
1052        0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1053        0x00, 0x00, 0x00, 0x03, 0x80, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1054        0x00, 0x00, 0x00, 0x00,
1055    ];
1056
1057    #[allow(dead_code)]
1058    const MEM_RETRIEVE_REQ_FROM_SP2: &[u8] = &[
1059        0x06, 0x80, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00,
1060        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
1061        0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1062        0x00, 0x00, 0x00, 0x05, 0x80, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1063        0x00, 0x00, 0x00, 0x00,
1064    ];
1065
1066    #[allow(dead_code)]
1067    const MEM_SHARE_FROM_NWD: &[u8] = &[
1068        0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1069        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
1070        0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1071        0x00, 0x00, 0x00, 0x03, 0x80, 0x02, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1072        0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1073        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x22, 0x80, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00,
1074        0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1075    ];
1076
1077    #[test]
1078    fn mem_share() {
1079        let (transaction_desc, access_desc, constituents) =
1080            MemTransactionDesc::unpack(MEM_SHARE_FROM_SP1).unwrap();
1081
1082        println!("transaction desc: {:#x?}", transaction_desc);
1083        access_desc.for_each(|d| println!("endpont desc: {d:#x?}"));
1084        constituents
1085            .unwrap()
1086            .for_each(|c| println!("constituent desc: {c:#x?}"));
1087    }
1088}