Skip to main content

tf_firmware_handoff/
lib.rs

1// SPDX-FileCopyrightText: Copyright The tf-firmware-handoff Contributors.
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4//! [crate::TransferList]: crate::TransferList
5#![doc = include_str!("../README.md")]
6#![no_std]
7
8pub mod entries;
9
10use crate::entries::Void;
11use bitflags::bitflags;
12use core::{
13    cell::Cell,
14    cmp::Ordering,
15    ffi::c_void,
16    mem::offset_of,
17    ops::{Deref, DerefMut, Range},
18};
19use num_enum::TryFromPrimitive;
20use thiserror::Error;
21use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
22
23const BASE_ALIGNMENT: usize = 8;
24
25const TL_SIGNATURE: u32 = 0x4a0f_b10b;
26const TL_VERSION: u8 = 0x1;
27
28const TL_ASSUMED_HEADER_SIZE: usize = 0x18;
29const TE_HEADER_SIZE: usize = 0x8;
30
31const REGISTER_CONVENTION: u8 = 1;
32
33#[derive(Debug, Clone, Copy, Error, PartialEq, Eq)]
34pub enum ValidationError {
35    #[error("Invalid signature")]
36    InvalidSignature,
37    #[error("Size mismatch")]
38    SizeMismatch,
39    #[error("Checksum mismatch")]
40    ChecksumMismatch,
41    #[error("TL version is too old")]
42    VersionTooOld,
43    #[error("TL version is invalid")]
44    InvalidVersion,
45}
46
47#[derive(Debug, Clone, Copy, Error, PartialEq, Eq)]
48pub enum Error {
49    #[error("TL is read-only")]
50    ReadOnly,
51    #[error("Invalid Transfer List: {0}")]
52    Validation(#[from] ValidationError),
53    #[error("Needs reallocation")]
54    /// Returned when trying to increase the size of the [`TransferList`], for
55    /// example by adding a new entry while the size of the underlying buffer is too small to
56    /// contain it.
57    ///
58    /// Before retrying, the caller should allocate a new buffer with the restriction given by the
59    /// error's fields and call [`relocate`][crate::TransferList::relocate] on the
60    /// [`TransferList`].
61    NeedsReallocation {
62        /// Minimum size of the buffer to give [`relocate`][crate::TransferList::relocate] before
63        /// retrying.
64        required_size: u32,
65    },
66    #[error("Not enough memory")]
67    NotEnoughMemory,
68    #[error("Could not convert byte sequence to data")]
69    FromBytes,
70}
71
72#[derive(Debug, Clone, Copy, Error, PartialEq, Eq)]
73pub enum GetError<'a, T>
74where
75    T: FromTLEPayload<'a>,
76{
77    #[error("Payload could not be accessed")]
78    Payload(T::Error),
79    #[error("No entry with the requested tag found")]
80    NotFound,
81    #[error("Other: {0}")]
82    Other(#[from] Error),
83}
84
85/// Identifier of a [`TransferListEntry`].
86#[derive(Debug, Clone, Copy, PartialEq, Eq)]
87pub enum TLETag {
88    Standard(StandardTLETag),
89    NonStandard(NonStandardTLETag),
90}
91
92impl From<StandardTLETag> for TLETag {
93    fn from(value: StandardTLETag) -> Self {
94        Self::Standard(value)
95    }
96}
97
98impl From<NonStandardTLETag> for TLETag {
99    fn from(value: NonStandardTLETag) -> Self {
100        Self::NonStandard(value)
101    }
102}
103
104impl TLETag {
105    const STANDARD_RANGE: Range<u32> = 0x0..0x80_0000;
106    const NON_STANDARD_RANGE: Range<u32> = 0xff_f000..0x100_0000;
107}
108
109#[derive(Debug, Clone, Copy, PartialEq, Eq)]
110pub enum TLETagError {
111    Reserved,
112    InvalidStandardValue(u32),
113    InvalidNonStandardValue(u32),
114}
115
116impl TryFrom<[u8; 3]> for TLETag {
117    type Error = TLETagError;
118
119    fn try_from(value: [u8; 3]) -> Result<Self, Self::Error> {
120        let value = u32::from_le_bytes([value[0], value[1], value[2], 0]);
121
122        if Self::STANDARD_RANGE.contains(&value) {
123            match value.try_into() {
124                Ok(v) => Ok(Self::Standard(v)),
125                Err(_) => Err(Self::Error::InvalidStandardValue(value)),
126            }
127        } else {
128            value.try_into().map(Self::NonStandard)
129        }
130    }
131}
132
133impl From<TLETag> for [u8; 3] {
134    fn from(value: TLETag) -> Self {
135        let value = match value {
136            TLETag::Standard(t) => t as u32,
137            TLETag::NonStandard(NonStandardTLETag(t)) => {
138                assert!(TLETag::NON_STANDARD_RANGE.contains(&t));
139                t
140            }
141        };
142
143        let bytes = value.to_le_bytes();
144        assert_eq!(bytes[3], 0);
145
146        [bytes[0], bytes[1], bytes[2]]
147    }
148}
149
150/// Standardized TLE tags.
151#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive)]
152#[repr(u32)]
153pub enum StandardTLETag {
154    Void = 0x0,
155    Fdt = 0x1,
156    HobB = 0x2,
157    HobL = 0x3,
158    AcpiAggr = 0x4,
159    EventLog = 0x5,
160    TpmCrbBase = 0x6,
161    // Trusted Firmware entries
162    OpteePageablePartAddress = 0x100,
163    DtFormattedSpmcManifest = 0x101,
164    Aarch64EntrypointInfo = 0x102,
165    FfaSpBinary = 0x103,
166    MbedTlsHeapInfo = 0x105,
167    DtFormattedFfaManifest = 0x106,
168    RwMemoryLayout64 = 0x107,
169    Aarch32EntrypointInfo = 0x108,
170    GptErrorInfo = 0x109,
171}
172
173#[derive(Debug, Clone, Copy, PartialEq, Eq)]
174pub struct NonStandardTLETag(u32);
175
176impl TryFrom<u32> for NonStandardTLETag {
177    type Error = TLETagError;
178
179    fn try_from(value: u32) -> Result<Self, Self::Error> {
180        if TLETag::NON_STANDARD_RANGE.contains(&value) {
181            Ok(Self(value))
182        } else {
183            Err(Self::Error::InvalidNonStandardValue(value))
184        }
185    }
186}
187
188impl NonStandardTLETag {
189    pub fn tag(&self) -> u32 {
190        self.0
191    }
192}
193
194#[inline(always)]
195fn slice_checksum(slice: &[u8]) -> u8 {
196    slice.iter().fold(0, |a, v| a.wrapping_add(*v))
197}
198
199#[derive(Debug, PartialEq, Eq, Clone, Copy)]
200enum OperationMode {
201    RW,
202    RO,
203}
204
205#[derive(Debug, PartialEq, Eq)]
206#[repr(C)]
207/// View over a memory region to be interpreted as a TransferList (or Glob List). The memory is not
208/// owned by this struct.
209///
210/// ## Creating a TL struct
211///
212/// The [`TransferList`] structure can be instantiated via either [`new`][Self::new]
213/// or [`create`][Self::create] which, respectively, wraps a memory region already containing a TL
214/// allowing to read and modify it, or create a fresh TL from an uninitialized memory region. The
215/// [`from_raw_ptr`][Self::from_raw_ptr] function achieves the same as [`new`][Self::new] but
216/// through a pointer rather than a reference (see the safety instructions).
217///
218/// As the TL struct only consists of a wrapper
219/// around the memory region, their lifetime are bound; but the [`relocate`][Self::relocate] can be
220/// used to move the TL to another buffer, thus binding to its lifetime instead.
221///
222/// ## Entry management
223///
224/// Entries of the Transfer List can be accessed either through an iterator or directly using their
225/// tags. An iterator can be constructed using the [`entries`][Self::entries] or
226/// [`entries_mut`][Self::entries_mut], and will yield respectively [`TransferListEntry`] or
227/// [`TransferListEntryMut`] that can be used to read from/write to the entry. Alternatively, the
228/// [`get`][Self::get] and [`get_mut`][Self::get_mut] methods can be used to directly retreive an
229/// entry based on its tag and convert it to a struct through the use of the [`FromTLEPayload`]
230/// trait.
231///
232/// ## Entry allocation
233///
234/// New entries can be added using the following functions:
235/// - [`add_entry`][Self::add_entry]
236/// - [`add_uninitialized_entry`][Self::add_uninitialized_entry]
237/// - [`add_aligned_entry`][Self::add_aligned_entry]
238/// - [`add_uninitialized_aligned_entry`][Self::add_uninitialized_aligned_entry]
239///
240/// `add_uninitialized_*` functions will not modify the data present in the entry, but only its
241/// header. `add_*aligned_entry` ensure that the address of the content of the entry will be aligned
242/// to the number of bits given as arguments.
243///
244/// These functions may return an [`Error::NeedsReallocation`], indicating that the buffer
245/// containing the TL is too small to complete the allocation. In that case, the caller may allocate
246/// a new memory region of the size specified in the error's
247/// [`required_size`][Error::NeedsReallocation::required_size] field and
248/// [`relocate`][Self::relocate] the TL there. If done so, reattempting the allocation with the same
249/// parameters will succeed.
250pub struct TransferList<'a> {
251    header: &'a mut TransferListHeader,
252    payload: &'a mut [u8],
253    operation_mode: OperationMode,
254}
255
256#[derive(Debug, PartialEq, Eq, IntoBytes, FromBytes, KnownLayout)]
257#[repr(C)]
258struct TransferListHeader {
259    signature: u32,
260    checksum: Cell<u8>,
261    version: u8,
262    header_size: u8,
263    alignment: u8,
264    used_size: u32,
265    total_size: u32,
266    flags: TransferListFlags,
267    reserved: u32,
268}
269
270#[derive(Debug, PartialEq, Eq, Immutable, IntoBytes, FromBytes, KnownLayout)]
271struct TransferListFlags(u32);
272
273bitflags! {
274    impl TransferListFlags: u32 {
275        const HAS_CHECKSUM = 1 << 0;
276    }
277
278}
279
280#[derive(Debug, PartialEq, Eq)]
281#[repr(C)]
282/// Immutable view over a [`TransferList`]'s entry. The memory is not owned by this struct.
283pub struct TransferListEntry<'a> {
284    header: &'a TransferListEntryHeader,
285    payload: &'a [u8],
286}
287
288#[derive(Debug, PartialEq, Eq)]
289#[repr(C)]
290/// Mutable view over a [`TransferList`]'s entry. The memory is not owned by this struct.
291pub struct TransferListEntryMut<'a> {
292    header: &'a TransferListEntryHeader,
293    payload: &'a mut [u8],
294    tl_cs: Option<&'a Cell<u8>>,
295}
296
297#[derive(Debug, PartialEq, Eq, Immutable, IntoBytes, FromBytes, KnownLayout)]
298#[repr(C)]
299pub struct TransferListEntryHeader {
300    tag: [u8; 3],
301    pub header_size: u8,
302    pub data_size: u32,
303}
304
305impl TransferListEntryHeader {
306    pub fn tag(&self) -> Result<TLETag, TLETagError> {
307        self.tag.try_into()
308    }
309}
310
311impl<'a> TransferList<'a> {
312    /// Creates a `TransferList` backed by the given memory region. The region must already contain
313    /// a valid `TransferList`. To create a new one, see [`TransferList::create`].
314    pub fn new(data: &'a mut [u8]) -> Result<Self, Error> {
315        if data.len() < TL_ASSUMED_HEADER_SIZE {
316            return Err(Error::Validation(ValidationError::SizeMismatch));
317        }
318
319        let header_size = (data[offset_of!(TransferListHeader, header_size)] as usize)
320            .next_multiple_of(BASE_ALIGNMENT);
321
322        if data.len() < header_size {
323            return Err(Error::Validation(ValidationError::SizeMismatch));
324        }
325
326        let (header, payload) = data.split_at_mut(header_size);
327
328        let Ok(header) = TransferListHeader::mut_from_bytes(header) else {
329            return Err(Error::Validation(ValidationError::SizeMismatch));
330        };
331
332        let mut s = Self {
333            header,
334            payload,
335            operation_mode: OperationMode::RO,
336        };
337
338        s.validate()?;
339
340        Ok(s)
341    }
342
343    /// Instantiate a fresh TransferList from an uninitialized buffer. To use buffer already
344    /// containing a Transfer List, see [`TransferList::new`].
345    pub fn create(data: &'a mut [u8], use_checksum: bool) -> Result<Self, Error> {
346        let len = data.len();
347        if len < TL_ASSUMED_HEADER_SIZE {
348            return Err(Error::NotEnoughMemory);
349        }
350
351        let (header, payload) = data.split_at_mut(TL_ASSUMED_HEADER_SIZE);
352
353        // Zeroes out the payload to prevent any data leaks.
354        payload.fill(0);
355
356        let header = TransferListHeader::mut_from_bytes(header).map_err(|_| Error::FromBytes)?;
357
358        header.signature = TL_SIGNATURE;
359        header.checksum = Cell::new(0);
360        header.version = TL_VERSION;
361        header.header_size = TL_ASSUMED_HEADER_SIZE as u8;
362        header.alignment = 0x3;
363        header.used_size = TL_ASSUMED_HEADER_SIZE as u32;
364        header.total_size = len as u32;
365        header.flags = if use_checksum {
366            TransferListFlags::HAS_CHECKSUM
367        } else {
368            TransferListFlags::empty()
369        };
370
371        let mut s = Self {
372            header,
373            payload,
374            operation_mode: OperationMode::RW,
375        };
376
377        if use_checksum {
378            let cs = s.compute_checksum();
379            s.header.checksum.set(cs.wrapping_neg());
380        }
381
382        Ok(s)
383    }
384
385    /// Construct a `TransferList` from a raw pointer, pointing to an already existing
386    /// `TransferList`.
387    ///
388    /// ## SAFETY
389    ///
390    /// This function will not cause undefined behavior as long as:
391    ///
392    /// - The little-endian representation of `ptr[0x0c..0x10]` as an unsigned value is less or
393    ///   equal to the size of the memory region where `ptr` points to.
394    /// - The memory region where `ptr` points and of size `ptr[0x0c..0x10]` remains accessible for
395    ///   the lifetime `'a`.
396    /// - The memory region where `ptr` points and of size `ptr[0x0c..0x10]` will not be accessed
397    ///   through any means other than the resulting TL struct for the lifetime `'a`.
398    pub unsafe fn from_raw_ptr(ptr: *mut u8) -> Result<Self, Error> {
399        let size = unsafe { *(ptr.add(offset_of!(TransferListHeader, total_size)) as *const u32) }
400            as usize;
401
402        let slice = core::ptr::slice_from_raw_parts_mut(ptr, size);
403        let slice = unsafe { &mut *slice };
404
405        Self::new(slice)
406    }
407
408    fn requires_rw(&self) -> Result<(), Error> {
409        match self.operation_mode {
410            OperationMode::RW => Ok(()),
411            OperationMode::RO => Err(Error::ReadOnly),
412        }
413    }
414
415    /// Clones this TL to the location given, allowing to increase the TL's size.
416    ///
417    /// This function should be used after receiving a [`NeedReallocation`][enum@crate::Error],
418    /// with a buffer of at least `required_size` elements.
419    pub fn relocate<'b>(&mut self, data: &'b mut [u8]) -> Result<TransferList<'b>, Error> {
420        self.requires_rw()?;
421
422        // Computes the index where to put the new TL in `data` to preserve alignment.
423        // See https://firmwarehandoff.github.io/firmware_handoff/main/transfer_list.html#relocating-a-tl.
424        let tl_base_addr = self.header.as_mut_bytes().as_ptr() as usize;
425        let target_base = data.as_ptr() as usize;
426        let target_size = data.len();
427
428        let alignment_mask = (1 << self.header.alignment) - 1;
429        let alignment_offset = tl_base_addr & alignment_mask;
430
431        let mut new_tl_base = (target_base & !alignment_mask) + alignment_offset;
432        if new_tl_base < target_base {
433            new_tl_base += alignment_mask + 1;
434        }
435
436        let new_tl_base_index = new_tl_base - target_base;
437        if new_tl_base_index + self.header.used_size as usize > target_size {
438            return Err(Error::NotEnoughMemory);
439        }
440
441        // Re-indexes the new slice and extract header and payload references.
442        let new_slice = &mut data[new_tl_base_index..];
443        let new_size = new_slice.len();
444        let (header, payload) = new_slice.split_at_mut(self.header.header_size as usize);
445        let header = TransferListHeader::mut_from_bytes(header).map_err(|_| Error::FromBytes)?;
446
447        // Copies data from the old TL to the new one.
448        let old_payload_size = self.header.total_size as usize - self.header.header_size as usize;
449        header
450            .as_mut_bytes()
451            .copy_from_slice(self.header.as_mut_bytes());
452        payload[..old_payload_size].copy_from_slice(self.payload);
453
454        header.total_size = new_size as u32;
455        if self.is_checksum_enabled() {
456            header.checksum.update(|cs| {
457                cs.wrapping_add(slice_checksum(self.header.total_size.as_bytes()))
458                    .wrapping_sub(slice_checksum(header.total_size.as_bytes()))
459            });
460        }
461
462        Ok(TransferList {
463            header,
464            payload,
465            operation_mode: self.operation_mode,
466        })
467    }
468
469    /// Performs a full validation of the TransferList structure:
470    ///
471    /// - Checks the magic bytes of the signature.
472    /// - Checks the TL's version field to choose an operation mode.
473    ///   - Read/Write if it is `0x1`.
474    ///   - Read-only if it is less than `0x1`.
475    ///   - Failure if it is `0x0` or greater than `0x1`.
476    /// - Checks that the sizes are coherent amongst them and with the underlying buffer.
477    /// - Recomputes and checks the TL's checksum.
478    fn validate(&mut self) -> Result<(), Error> {
479        // Signature check.
480        if self.header.signature != TL_SIGNATURE {
481            return Err(Error::Validation(ValidationError::InvalidSignature));
482        }
483
484        // Version check. Updates the `operation_mode` field accordingly.
485        if self.header.version == 0 {
486            return Err(Error::Validation(ValidationError::InvalidVersion));
487        }
488        match self.header.version.cmp(&TL_VERSION) {
489            Ordering::Less => {
490                return Err(Error::Validation(ValidationError::VersionTooOld));
491            }
492            Ordering::Equal => self.operation_mode = OperationMode::RW,
493            Ordering::Greater => self.operation_mode = OperationMode::RO,
494        }
495
496        // Checks that the sizes are coherent.
497        if self.header.used_size < self.header.header_size as u32
498            || !self.header.used_size.is_multiple_of(BASE_ALIGNMENT as u32)
499            || !self.header.total_size.is_multiple_of(BASE_ALIGNMENT as u32)
500            || !self.header.header_size.is_multiple_of(BASE_ALIGNMENT as u8)
501            || self.header.used_size > self.header.total_size
502            || (self.header.total_size as usize)
503                > (self.payload.len() + self.header.header_size as usize)
504        {
505            return Err(Error::Validation(ValidationError::SizeMismatch));
506        }
507
508        // Verifies checksum.
509        if self.is_checksum_enabled() {
510            let cs = self.compute_checksum();
511            if cs != 0 {
512                return Err(Error::Validation(ValidationError::ChecksumMismatch));
513            }
514        }
515
516        Ok(())
517    }
518
519    fn compute_checksum(&mut self) -> u8 {
520        slice_checksum(self.header.as_mut_bytes()).wrapping_add(slice_checksum(
521            &self.payload[..(self.header.used_size - self.header.header_size as u32) as usize],
522        ))
523    }
524
525    fn update_checksum(&mut self, old: u8, new: u8) {
526        assert!(self.is_checksum_enabled());
527
528        self.header
529            .checksum
530            .update(|cs| cs.wrapping_add(old).wrapping_sub(new));
531    }
532
533    fn is_checksum_enabled(&self) -> bool {
534        self.header.flags.contains(TransferListFlags::HAS_CHECKSUM)
535    }
536
537    /// Adds a new entry with the given tag and size in the TransferList. The payload field of the
538    /// entry is left uninitialized.
539    pub fn add_uninitialized_entry(&mut self, tag: TLETag, len: usize) -> Result<(), Error> {
540        self.requires_rw()?;
541
542        let index = self.allocate_entry(len)?;
543        self.create_entry_header(tag, len, index)?;
544        Ok(())
545    }
546
547    /// Adds a new entry with the given tag in the TransferList. `data` is copied into the entry's
548    /// payload.
549    pub fn add_entry(&mut self, tag: TLETag, data: &[u8]) -> Result<(), Error> {
550        self.requires_rw()?;
551
552        let index = self.allocate_entry(data.len())?;
553        self.create_entry_with_data(tag, data, index)?;
554        Ok(())
555    }
556
557    /// Allocates and creates a new entry in the TransferList. Returns the index of the allocated
558    /// region in the `payload` slice.
559    fn allocate_entry(&mut self, len: usize) -> Result<usize, Error> {
560        let len = len.next_multiple_of(BASE_ALIGNMENT);
561        let base = self.payload.as_ptr() as usize;
562
563        // Searches for a Void entry to reclaim.
564        let Some(e) = self.entries_mut()?.find(|e| {
565            matches!(e.header.tag(), Ok(TLETag::Standard(StandardTLETag::Void)))
566                && len <= (e.header.data_size as usize).next_multiple_of(BASE_ALIGNMENT)
567        }) else {
568            // No entry found, allocates by creating a new one at the end of the TL.
569            return self.allocate_entry_end(len);
570        };
571
572        let data_size = (e.header.data_size as usize).next_multiple_of(BASE_ALIGNMENT);
573        let entry_index = e.payload.as_ptr() as usize - e.header.header_size as usize - base;
574
575        // Creates a new Void entry serving as padding between the entry being allocated and the
576        // next one. If the allocated entry has the exact same size as the Void entry
577        // reclaimed, no padding is necessary.
578        if e.header.data_size as usize != len {
579            let pad_index = entry_index + (TE_HEADER_SIZE + len).next_multiple_of(BASE_ALIGNMENT);
580            self.create_entry_header(
581                Void::TAG,
582                data_size - len.next_multiple_of(BASE_ALIGNMENT) - TE_HEADER_SIZE,
583                pad_index,
584            )?;
585        }
586
587        Ok(entry_index)
588    }
589
590    /// Allocates and creates a new entry at the end of the TransferList. Entries marked with
591    /// `XFERLIST_VOID` are not considered. Returns the index of the allocated region in the
592    /// `payload` slice.
593    fn allocate_entry_end(&mut self, len: usize) -> Result<usize, Error> {
594        let total_required_len = (len + TE_HEADER_SIZE).next_multiple_of(BASE_ALIGNMENT);
595        let index = self.header.used_size as usize - self.header.header_size as usize;
596
597        // Checks that the buffer is enough for the requested entry size.
598        if (self.header.total_size as usize - self.header.header_size as usize)
599            < index + total_required_len
600        {
601            let alignment_mask = (1 << self.header.alignment) - 1;
602            let base_addr = self.header.as_mut_bytes().as_ptr() as usize;
603            let alignment_offset = base_addr & alignment_mask;
604
605            return Err(Error::NeedsReallocation {
606                required_size: (alignment_offset
607                    + self.header.used_size as usize
608                    + total_required_len) as u32,
609            });
610        }
611
612        // Removes the used_size from the checksum.
613        let removed_cs = if self.is_checksum_enabled() {
614            slice_checksum(self.header.used_size.as_bytes())
615        } else {
616            0
617        };
618
619        self.header.used_size += total_required_len as u32;
620
621        // Adds back the used_size to the checksum.
622        if self.is_checksum_enabled() {
623            self.update_checksum(removed_cs, slice_checksum(self.header.used_size.as_bytes()));
624        }
625
626        Ok(index)
627    }
628
629    /// Creates an entry at the given index with the data provided.
630    fn create_entry_with_data(
631        &mut self,
632        tag: TLETag,
633        data: &[u8],
634        index: usize,
635    ) -> Result<(), Error> {
636        // Precomputes the range in which the payload of the entry will be stored (excluding its
637        // header).
638        let te_payload_range = (index + TE_HEADER_SIZE)..(index + TE_HEADER_SIZE + data.len());
639
640        // Updates the checksum if needed.
641        if self.is_checksum_enabled() {
642            let old_cs = slice_checksum(&self.payload[te_payload_range.clone()]);
643            let new_cs = slice_checksum(data);
644            self.update_checksum(old_cs, new_cs);
645        }
646
647        // Sets up the entry header.
648        self.create_entry_header(tag, data.len(), index)?;
649
650        // Copies the entry's payload.
651        self.payload[te_payload_range].copy_from_slice(data);
652
653        Ok(())
654    }
655
656    /// Creates an entry at the given index in the payload.
657    fn create_entry_header(&mut self, tag: TLETag, len: usize, index: usize) -> Result<(), Error> {
658        let is_checksum_enabled = self.is_checksum_enabled(); // Computed now to avoid borrow checker issue.
659        let location = &mut self.payload[index..(index + len + TE_HEADER_SIZE)];
660
661        let (header, _) = TransferListEntryHeader::mut_from_prefix(location).unwrap();
662
663        let old_cs = if is_checksum_enabled {
664            slice_checksum(header.as_bytes())
665        } else {
666            0
667        };
668
669        header.tag = tag.into();
670        header.header_size = TE_HEADER_SIZE as u8;
671        header.data_size = len as u32;
672
673        if is_checksum_enabled {
674            let new_cs = slice_checksum(header.as_bytes());
675            self.update_checksum(old_cs, new_cs);
676        }
677
678        Ok(())
679    }
680
681    /// Adds a new entry with the given tag and size in the TransferList. The payload field of the
682    /// entry is left uninitialized. The payload of the entry will be aligned with `1 <<
683    /// alignment`.
684    pub fn add_uninitialized_aligned_entry(
685        &mut self,
686        tag: TLETag,
687        len: usize,
688        alignment: u8,
689    ) -> Result<(), Error> {
690        self.requires_rw()?;
691
692        // Align the end of the TL.
693        self.align_end(alignment).map_err(|e| match e {
694            Error::NeedsReallocation { required_size } => Error::NeedsReallocation {
695                required_size: required_size
696                    + TE_HEADER_SIZE as u32
697                    + len.next_multiple_of(BASE_ALIGNMENT) as u32,
698            },
699            e => e,
700        })?;
701
702        // Allocate the entry.
703        let index = self.allocate_entry_end(len)?;
704        self.create_entry_header(tag, len, index)?;
705
706        // Update the TL's alignment requirement if needed.
707        self.update_alignment_header(alignment);
708
709        Ok(())
710    }
711
712    /// Adds a new entry with the given tag in the TransferList. The payload field of the entry is
713    /// initialized from the `data` slice. The payload of the entry will be aligned with `1 <<
714    /// alignment`.
715    pub fn add_aligned_entry(
716        &mut self,
717        tag: TLETag,
718        data: &[u8],
719        alignment: u8,
720    ) -> Result<(), Error> {
721        self.requires_rw()?;
722
723        // Align the end of the TL.
724        self.align_end(alignment).map_err(|e| match e {
725            Error::NeedsReallocation { required_size } => Error::NeedsReallocation {
726                required_size: required_size
727                    + TE_HEADER_SIZE as u32
728                    + data.len().next_multiple_of(BASE_ALIGNMENT) as u32,
729            },
730            e => e,
731        })?;
732
733        // Allocate the entry.
734        let index = self.allocate_entry_end(data.len())?;
735        self.create_entry_with_data(tag, data, index)?;
736
737        // Update the TL's alignment requirement if needed.
738        self.update_alignment_header(alignment);
739
740        Ok(())
741    }
742
743    /// Align the end of the TL to the given value by adding an `XFERLIST_VOID` entry. Does nothing
744    /// if the requirement is already met.
745    fn align_end(&mut self, alignment: u8) -> Result<(), Error> {
746        // TODO: should not commit if the subsequent allocation in add_*_aligned would fail ?
747
748        let base_addr = self.header.as_mut_bytes().as_ptr() as usize;
749        let mask = (1 << alignment) - 1;
750        let tl_end = base_addr + self.header.used_size as usize + TE_HEADER_SIZE;
751
752        // Checks whether the end of the TL is already aligned.
753        if tl_end & mask != 0 {
754            // If not, we allocate an `XFERLIST_VOID` entry to serve as padding.
755            let padding_len = ((1 << alignment) - (tl_end & mask)) - TE_HEADER_SIZE;
756            let padding_index = self.allocate_entry_end(padding_len)?;
757            self.create_entry_header(entries::Void::TAG, padding_len, padding_index)?;
758        }
759
760        Ok(())
761    }
762
763    /// Updates the `alignment` field of the header to the maximum of `header.alignment` and
764    /// `new_alignment`.
765    fn update_alignment_header(&mut self, new_alignment: u8) {
766        if self.header.alignment < new_alignment {
767            if self.is_checksum_enabled() {
768                self.update_checksum(self.header.alignment, new_alignment);
769            }
770
771            self.header.alignment = new_alignment
772        }
773    }
774
775    /// Creates an iterator yielding immutable references to all the [`TransferListEntry`] of this
776    /// `TransferList`.
777    pub fn entries<'b>(&'b self) -> impl Iterator<Item = TransferListEntry<'b>>
778    where
779        'a: 'b,
780    {
781        TransferListEntryIterator::<'b> {
782            slice: Cell::new(
783                &self.payload
784                    [..(self.header.used_size as usize - self.header.header_size as usize)],
785            ),
786        }
787    }
788
789    /// Creates an iterator yielding mutable references to all the [`TransferListEntry`] of this
790    /// `TransferList`.
791    pub fn entries_mut<'b>(
792        &'b mut self,
793    ) -> Result<impl Iterator<Item = TransferListEntryMut<'b>>, Error>
794    where
795        'a: 'b,
796    {
797        self.requires_rw()?;
798
799        Ok(TransferListEntryIteratorMut::<'b> {
800            tl_cs: self.is_checksum_enabled().then_some(&self.header.checksum),
801            slice: Cell::new(
802                &mut self.payload
803                    [..(self.header.used_size as usize - self.header.header_size as usize)],
804            ),
805        })
806    }
807
808    /// Locates and finds the TLE with tag indicated by [`TLETagged`] and convert it into `T`.
809    ///
810    /// If there are multiple entries with the same tag, this returns the first occurrence.
811    pub fn get<'b, T>(&'b self) -> Result<TLEWrapperRef<'b, T>, GetError<'b, T>>
812    where
813        T: TLETagged + FromTLEPayload<'b>,
814        'a: 'b,
815    {
816        match self.entries().find(|t| t.header.tag() == Ok(T::TAG)) {
817            Some(some) => some.get().map_err(GetError::Payload),
818            None => Err(GetError::NotFound),
819        }
820    }
821
822    /// Locates and finds the TLE with tag indicated by [`TLETagged`] and convert it into `T`.
823    ///
824    /// If there are multiple entries with the same tag, this returns the first occurrence.
825    ///
826    /// Similarly to [`TransferListEntryMut::get_mut`], the TLE is given through a wrapper that
827    /// automatically updates the checksum of the TL, if needed.
828    pub fn get_mut<'b, T>(&'b mut self) -> Result<TLEWrapperMut<'b, T>, GetError<'b, T>>
829    where
830        T: TLETagged + FromTLEPayload<'b>,
831        'a: 'b,
832    {
833        match self.entries_mut()?.find(|t| t.header.tag() == Ok(T::TAG)) {
834            Some(some) => some.get_mut().map_err(GetError::Payload),
835            None => Err(GetError::NotFound),
836        }
837    }
838
839    /// Returns the value for the registers `R0..R4` when handing off to an AArch32 receiver.
840    ///
841    /// `translation`: Translate the given virtual address to a physical one.
842    pub fn aarch32_regs<T>(&self, translation: T) -> [u32; 4]
843    where
844        T: FnOnce(*const c_void) -> u32,
845    {
846        const AARCH32_SIGNATURE_BITS: u32 = 24;
847
848        [
849            0,
850            ((REGISTER_CONVENTION as u32) << AARCH32_SIGNATURE_BITS)
851                | (self.header.signature & ((1 << AARCH32_SIGNATURE_BITS) - 1)),
852            // TODO: might be simplified using self.get once Fdt is fully implemented.
853            self.entries()
854                .find(|e| e.header.tag() == Ok(StandardTLETag::Fdt.into()))
855                .map(|e| e.payload.as_ptr() as u32)
856                .unwrap_or(0),
857            translation(self.header as *const TransferListHeader as *const c_void),
858        ]
859    }
860
861    /// Returns the value for the registers `R0..R4` when handing off to an AArch64 receiver.
862    ///
863    /// `translation`: Translate the given virtual address to a physical one.
864    pub fn aarch64_regs<T>(&self, translation: T) -> [u64; 4]
865    where
866        T: FnOnce(*const c_void) -> u64,
867    {
868        const AARCH64_SIGNATURE_BITS: u32 = 32;
869
870        [
871            // TODO: might be simplified using self.get once Fdt is fully implemented.
872            self.entries()
873                .find(|e| e.header.tag() == Ok(StandardTLETag::Fdt.into()))
874                .map(|e| e.payload.as_ptr() as u64)
875                .unwrap_or(0),
876            ((REGISTER_CONVENTION as u64) << AARCH64_SIGNATURE_BITS) | self.header.signature as u64,
877            0,
878            translation(self.header as *const TransferListHeader as *const c_void),
879        ]
880    }
881}
882
883struct TransferListEntryIterator<'a> {
884    slice: Cell<&'a [u8]>,
885}
886
887impl<'a> Iterator for TransferListEntryIterator<'a> {
888    type Item = TransferListEntry<'a>;
889
890    fn next(&mut self) -> Option<Self::Item> {
891        let slice = self.slice.take();
892
893        // Ensures that the reads below are in-bounds.
894        if slice.len() < TE_HEADER_SIZE {
895            return None;
896        }
897
898        let header_size = slice[0x3] as usize;
899        let data_size = *u32::ref_from_bytes(&slice[0x4..0x8]).unwrap() as usize;
900
901        let entry_size = header_size.checked_add(data_size)?;
902        let next_entry_size = entry_size.next_multiple_of(BASE_ALIGNMENT);
903
904        if next_entry_size > slice.len()
905            || next_entry_size < entry_size
906            || header_size > slice.len()
907            || data_size > slice.len()
908        {
909            return None;
910        }
911
912        // Splits the TL content's between the next entry and the rest.
913        let (payload, remaining_data) = slice.split_at(entry_size);
914
915        // Skips the inter-TE padding.
916        let remaining_data = &remaining_data[(next_entry_size - entry_size)..];
917
918        self.slice.set(remaining_data);
919
920        // Parses the next entry.
921        let (header, payload) = payload.split_at(header_size);
922        let header = TransferListEntryHeader::ref_from_bytes(header).ok()?;
923
924        Some(TransferListEntry { header, payload })
925    }
926}
927
928impl<'a> TransferListEntry<'a> {
929    pub fn get<'b, T>(self) -> Result<TLEWrapperRef<'b, T>, T::Error>
930    where
931        T: FromTLEPayload<'b>,
932        'a: 'b,
933    {
934        T::from_bytes(self.payload).map(|e| TLEWrapperRef {
935            header: self.header,
936            tle: e,
937        })
938    }
939}
940
941struct TransferListEntryIteratorMut<'a> {
942    tl_cs: Option<&'a Cell<u8>>,
943    slice: Cell<&'a mut [u8]>,
944}
945
946impl<'a> Iterator for TransferListEntryIteratorMut<'a> {
947    type Item = TransferListEntryMut<'a>;
948
949    fn next(&mut self) -> Option<Self::Item> {
950        let slice = self.slice.take();
951
952        // Ensures that the reads below are in-bounds.
953        if slice.len() < TE_HEADER_SIZE {
954            return None;
955        }
956
957        let header_size = slice[offset_of!(TransferListEntryHeader, header_size)] as usize;
958        let data_size = *u32::ref_from_bytes(&slice[0x4..0x8]).unwrap() as usize;
959
960        let entry_size = header_size.checked_add(data_size)?;
961        let next_entry_size = entry_size.next_multiple_of(BASE_ALIGNMENT);
962
963        if next_entry_size > slice.len()
964            || next_entry_size < entry_size
965            || header_size > slice.len()
966            || data_size > slice.len()
967        {
968            return None;
969        }
970
971        // Splits the TL content's between the next entry and the rest.
972        let (payload, remaining_data) = slice.split_at_mut(entry_size);
973
974        // Skips the inter-TE padding.
975        let remaining_data =
976            &mut remaining_data[(entry_size.next_multiple_of(BASE_ALIGNMENT) - entry_size)..];
977
978        self.slice.set(remaining_data);
979
980        // Parses the next entry.
981        let (header, payload) = payload.split_at_mut(header_size);
982        let header = TransferListEntryHeader::mut_from_bytes(header).ok()?;
983
984        Some(TransferListEntryMut {
985            header,
986            payload,
987            tl_cs: self.tl_cs,
988        })
989    }
990}
991
992impl<'a> TransferListEntryMut<'a> {
993    /// Convert the entry payload to an immutable reference using [`FromTLEPayload`].
994    pub fn get<'b, T>(self) -> Result<TLEWrapperRef<'b, T>, T::Error>
995    where
996        T: FromTLEPayload<'b>,
997        'a: 'b,
998    {
999        T::from_bytes(self.payload).map(|e| TLEWrapperRef {
1000            header: self.header,
1001            tle: e,
1002        })
1003    }
1004
1005    /// Convert the entry payload to a mutable reference using [`FromTLEPayload`].
1006    ///
1007    /// Similarly to [`TransferList::get_mut`], the TLE is given through a wrapper that
1008    /// automatically updates the checksum of the TL, if needed.
1009    pub fn get_mut<'b, T>(self) -> Result<TLEWrapperMut<'b, T>, T::Error>
1010    where
1011        T: FromTLEPayload<'b>,
1012        'a: 'b,
1013    {
1014        let cs = self
1015            .tl_cs
1016            .map(|tl_cs| (tl_cs, slice_checksum(self.payload)));
1017
1018        T::from_bytes_mut(self.payload).map(|e| TLEWrapperMut {
1019            header: self.header,
1020            tle: e,
1021            cs,
1022            dirty: false,
1023        })
1024    }
1025}
1026
1027/// This traits allows to easily re-interpret a [`TransferListEntry`]'s payload into another type.
1028///
1029/// ## Example
1030///
1031/// This example show how to make a simple implementation of `FromTLEPayload` into a type wrapping
1032/// the reference.
1033///
1034/// ```
1035/// # use core::convert::Infallible;
1036/// # use tf_firmware_handoff::{TLETagged, TLETag, FromTLEPayload, StandardTLETag};
1037///
1038/// pub struct Fdt<'a>(pub &'a [u8]);
1039/// pub struct FdtMut<'a>(pub &'a [u8]);
1040///
1041/// impl<'a> TLETagged for Fdt<'a> {
1042///     const TAG: TLETag = TLETag::Standard(StandardTLETag::Fdt);
1043/// }
1044///
1045/// impl<'a> FromTLEPayload<'a> for Fdt<'a> {
1046///     type OutputRef = Self;
1047///     type OutputMut = FdtMut<'a>;
1048///     type Error = Infallible;
1049///
1050///     fn from_bytes(payload: &'a [u8]) -> Result<Self::OutputRef, Self::Error> {
1051///         Ok(Self(payload))
1052///     }
1053///
1054///     fn from_bytes_mut(payload: &'a mut [u8]) -> Result<Self::OutputMut, Self::Error> {
1055///         Ok(FdtMut(payload))
1056///     }
1057///
1058///     fn as_bytes(val: &Self::OutputRef) -> impl Iterator<Item = u8> {
1059///         val.0.iter().copied()
1060///     }
1061///
1062///     fn mut_as_bytes(val: &mut Self::OutputMut) -> impl Iterator<Item = u8> {
1063///         val.0.iter().copied()
1064///     }
1065/// }
1066/// ```
1067pub trait FromTLEPayload<'a> {
1068    /// Outcome of a conversion from an immutable memory slice. Usually a `Result<&'a T, _>`.
1069    type OutputRef;
1070    /// Outcome of a conversion from an mutable memory slice. Usually a `Result<&'a mut T, _>`.
1071    ///
1072    /// Any changes made to the TLE entry through that interface should immediately be made visible
1073    /// in the TL's memory region.
1074    type OutputMut;
1075    type Error;
1076
1077    /// Reinterprets an immutable `payload` and yields the corresponding
1078    /// [`FromTLEPayload::OutputRef`].
1079    fn from_bytes(payload: &'a [u8]) -> Result<Self::OutputRef, Self::Error>;
1080    /// Reinterprets a mutable `payload` and yields the corresponding [`FromTLEPayload::OutputMut`].
1081    fn from_bytes_mut(payload: &'a mut [u8]) -> Result<Self::OutputMut, Self::Error>;
1082
1083    /// Iterator over the bytes of the immutable conversion output.
1084    fn as_bytes(val: &Self::OutputRef) -> impl Iterator<Item = u8>;
1085    /// Iterator over the bytes of the mutable conversion output.
1086    fn mut_as_bytes(val: &mut Self::OutputMut) -> impl Iterator<Item = u8>;
1087}
1088
1089impl<'a, T> FromTLEPayload<'a> for T
1090where
1091    T: FromBytes + IntoBytes + Immutable + KnownLayout + 'a,
1092{
1093    type OutputRef = &'a T;
1094    type OutputMut = &'a mut T;
1095    type Error = Error;
1096
1097    fn from_bytes(payload: &'a [u8]) -> Result<Self::OutputRef, Self::Error> {
1098        T::try_ref_from_bytes(payload).map_err(|_| Error::FromBytes)
1099    }
1100
1101    fn from_bytes_mut(payload: &'a mut [u8]) -> Result<Self::OutputMut, Self::Error> {
1102        T::try_mut_from_bytes(payload).map_err(|_| Error::FromBytes)
1103    }
1104
1105    fn as_bytes(val: &Self::OutputRef) -> impl Iterator<Item = u8> {
1106        val.as_bytes().iter().copied()
1107    }
1108
1109    fn mut_as_bytes(val: &mut Self::OutputMut) -> impl Iterator<Item = u8> {
1110        val.as_bytes().iter().copied()
1111    }
1112}
1113
1114/// This trait specifies the [`TLETag`] associated with a struct. See [`TransferList::get`] and
1115/// [`TransferList::get_mut`].
1116pub trait TLETagged {
1117    /// The tag that identifies this TLE in a Transfer List.
1118    const TAG: TLETag;
1119}
1120
1121#[derive(Debug)]
1122pub struct TLEWrapperRef<'a, T>
1123where
1124    T: FromTLEPayload<'a>,
1125{
1126    header: &'a TransferListEntryHeader,
1127    tle: T::OutputRef,
1128}
1129
1130impl<'a, T> Deref for TLEWrapperRef<'a, T>
1131where
1132    T: FromTLEPayload<'a>,
1133{
1134    type Target = T::OutputRef;
1135
1136    fn deref(&self) -> &Self::Target {
1137        &self.tle
1138    }
1139}
1140
1141impl<'a, T> TLEWrapperRef<'a, T>
1142where
1143    T: FromTLEPayload<'a>,
1144{
1145    pub fn header(&self) -> &'a TransferListEntryHeader {
1146        self.header
1147    }
1148}
1149
1150#[derive(Debug)]
1151pub struct TLEWrapperMut<'a, T>
1152where
1153    T: FromTLEPayload<'a>,
1154{
1155    header: &'a TransferListEntryHeader,
1156    tle: T::OutputMut,
1157    cs: Option<(&'a Cell<u8>, u8)>,
1158    dirty: bool,
1159}
1160
1161impl<'a, T> TLEWrapperMut<'a, T>
1162where
1163    T: FromTLEPayload<'a>,
1164{
1165    pub fn header(&self) -> &'a TransferListEntryHeader {
1166        self.header
1167    }
1168}
1169
1170impl<'a, T> Deref for TLEWrapperMut<'a, T>
1171where
1172    T: FromTLEPayload<'a>,
1173{
1174    type Target = T::OutputMut;
1175
1176    fn deref(&self) -> &Self::Target {
1177        &self.tle
1178    }
1179}
1180
1181impl<'a, T> DerefMut for TLEWrapperMut<'a, T>
1182where
1183    T: FromTLEPayload<'a>,
1184{
1185    fn deref_mut(&mut self) -> &mut Self::Target {
1186        self.dirty = true;
1187        &mut self.tle
1188    }
1189}
1190
1191impl<'a, T> Drop for TLEWrapperMut<'a, T>
1192where
1193    T: FromTLEPayload<'a>,
1194{
1195    fn drop(&mut self) {
1196        if self.dirty
1197            && let Some((tl_cs, orig_cs)) = self.cs
1198        {
1199            let cs = T::mut_as_bytes(&mut self.tle).fold(0u8, |a, v| a.wrapping_add(v));
1200            tl_cs.update(|v| v.wrapping_add(orig_cs).wrapping_sub(cs));
1201        }
1202    }
1203}
1204
1205#[cfg(test)]
1206mod tests {
1207    use core::cell::Cell;
1208    use zerocopy::IntoBytes;
1209
1210    use crate::{
1211        Error, GetError, OperationMode, StandardTLETag, TLETag, TransferList,
1212        TransferListEntryHeader, TransferListFlags, TransferListHeader, ValidationError,
1213        entries::{ExecEpInfo32, ExecEpInfo64, ParamHeader},
1214    };
1215
1216    #[test]
1217    fn standard_tag_from_u8() {
1218        assert_eq!(
1219            TLETag::try_from([0; 3]),
1220            Ok(TLETag::Standard(StandardTLETag::Void))
1221        );
1222        assert_eq!(
1223            TLETag::try_from([1, 0, 0]),
1224            Ok(TLETag::Standard(StandardTLETag::Fdt))
1225        );
1226        assert_eq!(
1227            TLETag::try_from([9, 1, 0]),
1228            Ok(TLETag::Standard(StandardTLETag::GptErrorInfo))
1229        );
1230    }
1231
1232    #[test]
1233    fn tag_to_u8() {
1234        assert_eq!(
1235            <TLETag as Into<[u8; 3]>>::into(TLETag::Standard(StandardTLETag::Void)),
1236            [0; 3]
1237        );
1238        assert_eq!(
1239            <TLETag as Into<[u8; 3]>>::into(TLETag::Standard(StandardTLETag::Fdt)),
1240            [1, 0, 0]
1241        );
1242        assert_eq!(
1243            <TLETag as Into<[u8; 3]>>::into(TLETag::Standard(StandardTLETag::GptErrorInfo)),
1244            [9, 1, 0]
1245        );
1246    }
1247
1248    macro_rules! open_tl_and_buf {
1249        ($tl:ident, $buf:ident, $path:expr, $size:expr) => {
1250            let mut $buf = [0; $size];
1251            let input = include_bytes!(concat!("../test_data/", $path));
1252            $buf[..input.len()].copy_from_slice(input);
1253            #[allow(unused_mut)]
1254            let mut $tl = crate::TransferList::new(&mut $buf).unwrap();
1255        };
1256    }
1257
1258    macro_rules! open_tl {
1259        ($tl:ident, $path:expr, $size:expr) => {
1260            open_tl_and_buf!($tl, buf, $path, $size)
1261        };
1262    }
1263
1264    #[test]
1265    fn tl_nocs_is_valid() {
1266        open_tl!(tl, "tl-nocs.bin", 4096);
1267        assert_eq!(tl.validate(), Ok(()))
1268    }
1269
1270    #[test]
1271    fn tl_nocs() {
1272        open_tl!(tl, "tl-nocs.bin", 4096);
1273
1274        assert_eq!(tl.header.signature, 0x4a0f_b10b);
1275        assert_eq!(tl.header.checksum.get(), 0);
1276        assert_eq!(tl.header.version, 1);
1277        assert_eq!(tl.header.header_size, 0x18);
1278        assert_eq!(tl.header.alignment, 3);
1279        assert_eq!(tl.header.used_size, 120);
1280        assert_eq!(tl.header.total_size, 4096);
1281        assert_eq!(tl.header.flags, TransferListFlags::empty());
1282        assert_eq!(tl.header.reserved, 0);
1283
1284        let mut iter = tl.entries();
1285
1286        let entry = iter.next().unwrap();
1287        assert_eq!(iter.next(), None);
1288
1289        let entry = entry.get::<ExecEpInfo64>().unwrap();
1290
1291        assert_eq!(
1292            **entry,
1293            ExecEpInfo64 {
1294                ep_info_hdr: ParamHeader {
1295                    ty: 1,
1296                    version: 2,
1297                    size: 0x58,
1298                    attr: 8
1299                },
1300                pc: 0x4020000,
1301                spsr: 467,
1302                pad0: 0,
1303                x: [0x4001008, 0x4001000, 0, 0, 0, 0, 0, 0]
1304            }
1305        )
1306    }
1307
1308    #[test]
1309    fn tl_cs() {
1310        open_tl!(tl, "tl-cs.bin", 4096);
1311
1312        assert_eq!(tl.header.signature, 0x4a0f_b10b);
1313        assert_eq!(tl.header.checksum.get(), 118);
1314        assert_eq!(tl.header.version, 1);
1315        assert_eq!(tl.header.header_size, 0x18);
1316        assert_eq!(tl.header.alignment, 3);
1317        assert_eq!(tl.header.used_size, 120);
1318        assert_eq!(tl.header.total_size, 4096);
1319        assert_eq!(tl.header.flags, TransferListFlags::HAS_CHECKSUM);
1320        assert_eq!(tl.header.reserved, 0);
1321
1322        let mut iter = tl.entries();
1323
1324        let entry = iter.next().unwrap();
1325        assert_eq!(iter.next(), None);
1326
1327        let entry = entry.get::<ExecEpInfo64>().unwrap();
1328
1329        assert_eq!(
1330            **entry,
1331            ExecEpInfo64 {
1332                ep_info_hdr: ParamHeader {
1333                    ty: 1,
1334                    version: 2,
1335                    size: 0x58,
1336                    attr: 8
1337                },
1338                pc: 0x4020000,
1339                spsr: 467,
1340                pad0: 0,
1341                x: [0x4001008, 0x4001000, 0, 0, 0, 0, 0, 0]
1342            }
1343        )
1344    }
1345
1346    #[test]
1347    fn tl_cs_from_ptr() {
1348        let mut buf = [0; 4096];
1349        let input = include_bytes!("../test_data/tl-cs.bin");
1350        buf[..input.len()].copy_from_slice(input);
1351        let tl = unsafe { TransferList::from_raw_ptr(buf.as_mut_ptr()).unwrap() };
1352
1353        assert_eq!(tl.header.signature, 0x4a0f_b10b);
1354        assert_eq!(tl.header.checksum.get(), 118);
1355        assert_eq!(tl.header.version, 1);
1356        assert_eq!(tl.header.header_size, 0x18);
1357        assert_eq!(tl.header.alignment, 3);
1358        assert_eq!(tl.header.used_size, 120);
1359        assert_eq!(tl.header.total_size, 4096);
1360        assert_eq!(tl.header.flags, TransferListFlags::HAS_CHECKSUM);
1361        assert_eq!(tl.header.reserved, 0);
1362
1363        let mut iter = tl.entries();
1364
1365        let entry = iter.next().unwrap();
1366        assert_eq!(iter.next(), None);
1367
1368        let entry = entry.get::<ExecEpInfo64>().unwrap();
1369
1370        assert_eq!(
1371            **entry,
1372            ExecEpInfo64 {
1373                ep_info_hdr: ParamHeader {
1374                    ty: 1,
1375                    version: 2,
1376                    size: 0x58,
1377                    attr: 8
1378                },
1379                pc: 0x4020000,
1380                spsr: 467,
1381                pad0: 0,
1382                x: [0x4001008, 0x4001000, 0, 0, 0, 0, 0, 0]
1383            }
1384        )
1385    }
1386
1387    #[test]
1388    fn create_tl_nocs() {
1389        let mut buf = [0; 4096];
1390        let mut tl = TransferList::create(&mut buf, false).unwrap();
1391
1392        assert_eq!(
1393            *tl.header,
1394            TransferListHeader {
1395                signature: 0x4a0f_b10b,
1396                checksum: Cell::new(0),
1397                version: 1,
1398                header_size: 0x18,
1399                alignment: 3,
1400                used_size: 0x18,
1401                total_size: 0x1000,
1402                flags: TransferListFlags::empty(),
1403                reserved: 0
1404            }
1405        );
1406
1407        assert_eq!(tl.entries().count(), 0);
1408        assert_eq!(tl.validate(), Ok(()));
1409    }
1410
1411    #[test]
1412    fn create_tl_cs() {
1413        let mut buf = [0; 4096];
1414        let mut tl = TransferList::create(&mut buf, true).unwrap();
1415
1416        assert_eq!(
1417            *tl.header,
1418            TransferListHeader {
1419                signature: 0x4a0f_b10b,
1420                checksum: Cell::new(0xa6),
1421                version: 1,
1422                header_size: 0x18,
1423                alignment: 3,
1424                used_size: 0x18,
1425                total_size: 0x1000,
1426                flags: TransferListFlags::HAS_CHECKSUM,
1427                reserved: 0
1428            }
1429        );
1430
1431        assert_eq!(tl.entries().count(), 0);
1432        assert_eq!(tl.validate(), Ok(()));
1433    }
1434
1435    #[test]
1436    fn create_too_small() {
1437        let mut buf = [0; 0x10];
1438        assert_eq!(
1439            TransferList::create(&mut buf, true),
1440            Err(Error::NotEnoughMemory)
1441        );
1442    }
1443
1444    #[test]
1445    fn tl_invalid_sig() {
1446        let mut buf = [0; 4096];
1447        let input = include_bytes!("../test_data/tl-nocs.bin");
1448        buf[..input.len()].copy_from_slice(input);
1449        buf[0x0] = 0;
1450        assert_eq!(
1451            TransferList::new(&mut buf),
1452            Err(Error::Validation(ValidationError::InvalidSignature)),
1453        );
1454    }
1455
1456    #[test]
1457    fn tl_invalid_version() {
1458        let mut buf = [0; 4096];
1459        let input = include_bytes!("../test_data/tl-nocs.bin");
1460        buf[..input.len()].copy_from_slice(input);
1461        buf[0x5] = 0;
1462        assert_eq!(
1463            TransferList::new(&mut buf),
1464            Err(Error::Validation(ValidationError::InvalidVersion)),
1465        );
1466    }
1467
1468    #[test]
1469    fn tl_invalid_checksum() {
1470        let mut buf = [0; 4096];
1471        let input = include_bytes!("../test_data/tl-cs.bin");
1472        buf[..input.len()].copy_from_slice(input);
1473        buf[0x4] += 5;
1474        assert_eq!(
1475            TransferList::new(&mut buf),
1476            Err(Error::Validation(ValidationError::ChecksumMismatch)),
1477        );
1478    }
1479
1480    #[test]
1481    fn tl_buffer_too_small() {
1482        let mut buf = [0; 2048];
1483        let input = include_bytes!("../test_data/tl-nocs.bin");
1484        buf[..input.len()].copy_from_slice(input);
1485        assert_eq!(
1486            TransferList::new(&mut buf),
1487            Err(Error::Validation(ValidationError::SizeMismatch)),
1488        );
1489    }
1490
1491    #[test]
1492    fn tl_too_recent_is_ro() {
1493        let mut buf = [0; 4096];
1494        let input = include_bytes!("../test_data/tl-nocs.bin");
1495        buf[..input.len()].copy_from_slice(input);
1496        buf[0x5] = 2;
1497
1498        let tl = TransferList::new(&mut buf).unwrap();
1499        assert_eq!(tl.operation_mode, OperationMode::RO);
1500    }
1501
1502    macro_rules! mut_test {
1503        ($fn:ident,$path:literal,$checksum:literal) => {
1504            let mut buf = [0; 4096];
1505            let input = include_bytes!($path);
1506            buf[..input.len()].copy_from_slice(input);
1507            $fn(&mut buf, $checksum);
1508        };
1509    }
1510
1511    #[test]
1512    fn push_back_with_data_nocs() {
1513        mut_test!(push_back_with_data, "../test_data/tl-nocs.bin", false);
1514    }
1515    #[test]
1516    fn push_back_with_data_cs() {
1517        mut_test!(push_back_with_data, "../test_data/tl-cs.bin", true);
1518    }
1519
1520    fn push_back_with_data(buf: &mut [u8], checksum: bool) {
1521        let mut tl = TransferList::new(buf).unwrap();
1522
1523        assert_eq!(
1524            tl.add_entry(StandardTLETag::DtFormattedFfaManifest.into(), &[4; 8]),
1525            Ok(())
1526        );
1527
1528        let entry = tl.entries().nth(1).unwrap();
1529        assert_eq!(
1530            *entry.header,
1531            TransferListEntryHeader {
1532                tag: [6, 1, 0],
1533                header_size: 8,
1534                data_size: 8
1535            }
1536        );
1537        assert_eq!(entry.payload, &[4; 8]);
1538        assert_eq!(tl.header.used_size, 136);
1539
1540        if checksum {
1541            assert_eq!(tl.validate(), Ok(()));
1542        }
1543
1544        assert_eq!(
1545            buf[120..136],
1546            [6, 1, 0, 8, 8, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4]
1547        );
1548    }
1549
1550    #[test]
1551    fn push_back_uninitialized_nocs() {
1552        mut_test!(push_back_uninitialized, "../test_data/tl-nocs.bin", false);
1553    }
1554    #[test]
1555    fn push_back_uninitialized_cs() {
1556        mut_test!(push_back_uninitialized, "../test_data/tl-cs.bin", true);
1557    }
1558
1559    fn push_back_uninitialized(buf: &mut [u8], checksum: bool) {
1560        let mut tl = TransferList::new(buf).unwrap();
1561
1562        assert_eq!(
1563            tl.add_uninitialized_entry(StandardTLETag::DtFormattedFfaManifest.into(), 32),
1564            Ok(())
1565        );
1566
1567        if checksum {
1568            assert_eq!(tl.validate(), Ok(()));
1569        }
1570
1571        let entry = tl.entries().nth(1).unwrap();
1572        assert_eq!(
1573            *entry.header,
1574            TransferListEntryHeader {
1575                tag: [6, 1, 0],
1576                header_size: 8,
1577                data_size: 32
1578            }
1579        );
1580        assert_eq!(tl.header.used_size, 160);
1581
1582        if checksum {
1583            assert_eq!(tl.validate(), Ok(()));
1584        }
1585
1586        assert_eq!(buf[120..128], [6, 1, 0, 8, 32, 0, 0, 0]);
1587        assert_eq!(buf[0x8..0xc], [160, 0, 0, 0]);
1588    }
1589
1590    #[test]
1591    fn push_back_uninitialized_oom() {
1592        open_tl_and_buf!(tl, buf, "tl-nocs.bin", 4096);
1593
1594        assert_eq!(
1595            tl.add_uninitialized_entry(
1596                TLETag::Standard(StandardTLETag::DtFormattedFfaManifest),
1597                4000
1598            ),
1599            Err(Error::NeedsReallocation {
1600                required_size: 4128 // used_size (120) + TE header (8) + requested_size (4000)
1601            })
1602        );
1603    }
1604
1605    #[test]
1606    fn push_back_with_data_oom() {
1607        open_tl_and_buf!(tl, buf, "tl-nocs.bin", 4096);
1608
1609        assert_eq!(
1610            tl.add_entry(StandardTLETag::DtFormattedFfaManifest.into(), &[1; 4000]),
1611            Err(Error::NeedsReallocation {
1612                required_size: 4128 // used_size (120) + TE header (8) + requested_size (4000)
1613            })
1614        );
1615    }
1616
1617    #[test]
1618    fn add_uninitialized_aligned_entry_nocs() {
1619        mut_test!(
1620            add_uninitialized_aligned_entry,
1621            "../test_data/tl-nocs.bin",
1622            false
1623        );
1624    }
1625    #[test]
1626    fn add_uninitialized_aligned_entry_cs() {
1627        mut_test!(
1628            add_uninitialized_aligned_entry,
1629            "../test_data/tl-cs.bin",
1630            true
1631        );
1632    }
1633
1634    fn add_uninitialized_aligned_entry(buf: &mut [u8], checksum: bool) {
1635        let align = 10;
1636
1637        let base = buf.as_ptr() as usize;
1638        let mut tl = TransferList::new(buf).unwrap();
1639
1640        let end = base + 128; // used_size + TE header
1641
1642        let offset = ((1 << align) - (end & ((1 << align) - 1))) % (1 << align);
1643        let pad_size = offset - 8;
1644
1645        assert_eq!(
1646            tl.add_uninitialized_aligned_entry(
1647                StandardTLETag::DtFormattedFfaManifest.into(),
1648                128,
1649                align
1650            ),
1651            Ok(())
1652        );
1653
1654        {
1655            let mut iter = tl.entries().skip(1);
1656
1657            if pad_size != 0 {
1658                let void = iter.next().unwrap();
1659                assert_eq!(
1660                    *void.header,
1661                    TransferListEntryHeader {
1662                        tag: [0, 0, 0],
1663                        header_size: 8,
1664                        data_size: pad_size as u32
1665                    }
1666                );
1667            }
1668
1669            let entry = iter.next().unwrap();
1670            assert_eq!(
1671                *entry.header,
1672                TransferListEntryHeader {
1673                    tag: [6, 1, 0],
1674                    header_size: 8,
1675                    data_size: 128
1676                }
1677            );
1678        }
1679
1680        assert_eq!(tl.header.alignment, align);
1681        assert_eq!(tl.header.used_size, 256 + offset as u32);
1682
1683        if checksum {
1684            assert_eq!(tl.validate(), Ok(()));
1685        }
1686
1687        if pad_size != 0 {
1688            assert_eq!(buf[120..124], [0, 0, 0, 8]);
1689            assert_eq!(&buf[124..128], (pad_size as u32).as_bytes());
1690        }
1691        assert_eq!(
1692            buf[(120 + offset)..(128 + offset)],
1693            [6, 1, 0, 8, 128, 0, 0, 0]
1694        );
1695        assert_eq!(&buf[0x8..0xc], (120 + offset as u32 + 8 + 128).as_bytes());
1696        assert_eq!(buf[0x7], align);
1697    }
1698
1699    #[test]
1700    fn add_small_entries_nocs() {
1701        mut_test!(add_small_entries, "../test_data/tl-nocs.bin", false);
1702    }
1703    #[test]
1704    fn add_small_entries_cs() {
1705        mut_test!(add_small_entries, "../test_data/tl-cs.bin", true);
1706    }
1707
1708    fn add_small_entries(buf: &mut [u8], checksum: bool) {
1709        let mut tl = TransferList::new(buf).unwrap();
1710        tl.add_entry(StandardTLETag::Fdt.into(), &[1; 3]).unwrap();
1711        tl.add_entry(StandardTLETag::HobB.into(), &[2; 3]).unwrap();
1712        tl.add_entry(StandardTLETag::HobL.into(), &[3; 3]).unwrap();
1713        tl.add_entry(StandardTLETag::AcpiAggr.into(), &[4; 3])
1714            .unwrap();
1715
1716        assert_eq!(tl.header.used_size, 184);
1717
1718        for (i, e) in tl.entries().skip(1).enumerate() {
1719            let i = (i as u8) + 1;
1720
1721            assert_eq!(
1722                *e.header,
1723                TransferListEntryHeader {
1724                    tag: [i, 0, 0],
1725                    header_size: 8,
1726                    data_size: 3
1727                }
1728            );
1729
1730            assert_eq!(e.payload, &[i; 3]);
1731        }
1732
1733        if checksum {
1734            assert_eq!(tl.validate(), Ok(()));
1735        }
1736    }
1737
1738    #[test]
1739    fn add_small_aligned_entry_nocs() {
1740        mut_test!(add_small_aligned_entry, "../test_data/tl-nocs.bin", false);
1741    }
1742    #[test]
1743    fn add_small_aligned_entry_cs() {
1744        mut_test!(add_small_aligned_entry, "../test_data/tl-cs.bin", true);
1745    }
1746
1747    fn add_small_aligned_entry(buf: &mut [u8], checksum: bool) {
1748        let mut tl = TransferList::new(buf).unwrap();
1749        tl.add_aligned_entry(StandardTLETag::DtFormattedFfaManifest.into(), &[1; 32], 1)
1750            .unwrap();
1751
1752        assert_eq!(tl.entries().count(), 2); // No padding should be allocated.
1753
1754        let entry = tl.entries().nth(1).unwrap();
1755        assert_eq!(
1756            *entry.header,
1757            TransferListEntryHeader {
1758                tag: [6, 1, 0],
1759                header_size: 8,
1760                data_size: 32
1761            }
1762        );
1763
1764        assert_eq!(entry.payload, &[1; 32]);
1765
1766        assert_eq!(tl.header.used_size, 160);
1767        assert_eq!(tl.header.alignment, 3);
1768
1769        if checksum {
1770            assert_eq!(tl.validate(), Ok(()));
1771        }
1772    }
1773
1774    #[test]
1775    fn add_aligned_entry_nocs() {
1776        mut_test!(add_aligned_entry, "../test_data/tl-nocs.bin", false);
1777    }
1778    #[test]
1779    fn add_aligned_entry_cs() {
1780        mut_test!(add_aligned_entry, "../test_data/tl-cs.bin", true);
1781    }
1782
1783    fn add_aligned_entry(buf: &mut [u8], checksum: bool) {
1784        let align = 10;
1785
1786        let base = buf.as_ptr() as usize;
1787
1788        let mut tl = TransferList::new(buf).unwrap();
1789
1790        let end = base + 128; // used_size + TE header
1791
1792        let offset = ((1 << align) - (end & ((1 << align) - 1))) % (1 << align);
1793        let pad_size = offset - 8;
1794
1795        assert_eq!(
1796            tl.add_aligned_entry(
1797                StandardTLETag::DtFormattedFfaManifest.into(),
1798                &[4; 128],
1799                align
1800            ),
1801            Ok(())
1802        );
1803
1804        if checksum {
1805            assert_eq!(tl.validate(), Ok(()));
1806        }
1807
1808        {
1809            let mut iter = tl.entries().skip(1);
1810
1811            if pad_size != 0 {
1812                let void = iter.next().unwrap();
1813                assert_eq!(
1814                    *void.header,
1815                    TransferListEntryHeader {
1816                        tag: [0, 0, 0],
1817                        header_size: 8,
1818                        data_size: pad_size as u32
1819                    }
1820                );
1821            }
1822
1823            let entry = iter.next().unwrap();
1824            assert_eq!(
1825                *entry.header,
1826                TransferListEntryHeader {
1827                    tag: [6, 1, 0],
1828                    header_size: 8,
1829                    data_size: 128
1830                }
1831            );
1832        }
1833
1834        assert_eq!(tl.header.alignment, align);
1835        assert_eq!(tl.header.used_size, 256 + offset as u32);
1836
1837        if checksum {
1838            assert_eq!(tl.validate(), Ok(()));
1839        }
1840
1841        if pad_size != 0 {
1842            assert_eq!(buf[120..124], [0, 0, 0, 8]);
1843            assert_eq!(&buf[124..128], (pad_size as u32).as_bytes());
1844        }
1845        assert_eq!(
1846            buf[(120 + offset)..(128 + offset)],
1847            [6, 1, 0, 8, 128, 0, 0, 0]
1848        );
1849        assert_eq!(buf[(128 + offset)..(256 + offset)], [4u8; 128]);
1850        assert_eq!(&buf[0x8..0xc], (120 + offset as u32 + 8 + 128).as_bytes());
1851        assert_eq!(buf[0x7], align);
1852    }
1853
1854    #[test]
1855    fn add_reclaim_nocs() {
1856        mut_test!(add_reclaim, "../test_data/tl-nocs.bin", false);
1857    }
1858    #[test]
1859    fn add_reclaim_cs() {
1860        mut_test!(add_reclaim, "../test_data/tl-cs.bin", true);
1861    }
1862
1863    fn add_reclaim(buf: &mut [u8], checksum: bool) {
1864        let align = 10;
1865
1866        let base = buf.as_ptr() as usize;
1867        let mut tl = TransferList::new(buf).unwrap();
1868
1869        let end = base + 128; // used_size + TE header
1870
1871        let offset = ((1 << align) - (end & ((1 << align) - 1))) % (1 << align);
1872        let pad_size = offset - 8;
1873
1874        assert_eq!(
1875            tl.add_uninitialized_aligned_entry(
1876                StandardTLETag::DtFormattedFfaManifest.into(),
1877                128,
1878                align
1879            ),
1880            Ok(())
1881        );
1882        assert_eq!(tl.header.used_size, 256 + offset as u32);
1883
1884        // If through bad luck the padding for the alignment is not large enough, restart. Since we
1885        // now know that the end of the TL at 2^10 + 128, the next padding will be large
1886        // enough for the allocation.
1887        if pad_size < 40 {
1888            add_reclaim(buf, checksum);
1889            return;
1890        }
1891
1892        assert_eq!(
1893            Ok(()),
1894            tl.add_uninitialized_entry(StandardTLETag::AcpiAggr.into(), 32)
1895        );
1896        assert_eq!(tl.header.used_size, 256 + offset as u32); // Size should not be modified.
1897
1898        assert_eq!(tl.entries().count(), 4);
1899
1900        let tags = [
1901            StandardTLETag::Aarch64EntrypointInfo.into(),
1902            StandardTLETag::AcpiAggr.into(),
1903            StandardTLETag::Void.into(),
1904            StandardTLETag::DtFormattedFfaManifest.into(),
1905        ];
1906        let sizes = [88, 32, pad_size - 40, 128];
1907
1908        for (i, e) in tl.entries().enumerate() {
1909            assert_eq!(e.header.tag(), Ok(tags[i]));
1910            assert_eq!(e.header.data_size as usize, sizes[i]);
1911        }
1912
1913        if checksum {
1914            assert_eq!(tl.validate(), Ok(()));
1915        }
1916    }
1917
1918    #[test]
1919    fn relocate() {
1920        open_tl!(tl, "tl-cs.bin", 4096);
1921
1922        let mut new_buf = [0; 5120];
1923        let mut new_tl = tl.relocate(&mut new_buf).unwrap();
1924
1925        assert_eq!(new_tl.header.signature, tl.header.signature);
1926        assert_eq!(new_tl.header.checksum.get(), 114);
1927        assert_eq!(new_tl.header.version, tl.header.version);
1928        assert_eq!(new_tl.header.header_size, tl.header.header_size);
1929        assert_eq!(new_tl.header.alignment, tl.header.alignment);
1930        assert_eq!(new_tl.header.used_size, tl.header.used_size);
1931        assert_eq!(new_tl.header.total_size, 5120);
1932        assert_eq!(new_tl.header.flags, tl.header.flags);
1933        assert_eq!(new_tl.header.reserved, tl.header.reserved);
1934
1935        assert_eq!(new_tl.validate(), Ok(()));
1936
1937        assert_eq!(tl.entries().count(), new_tl.entries().count());
1938        for (entry, new_entry) in tl.entries().zip(new_tl.entries()) {
1939            assert_eq!(entry, new_entry);
1940        }
1941
1942        assert_eq!(
1943            new_buf[0..0x18],
1944            [
1945                0x0b, 0xb1, 0x0f, 0x4a, // Signature
1946                114,  // Checksum
1947                0x1,  // Version
1948                0x18, // Header size
1949                0x3,  // Alignment
1950                0x78, 0x0, 0x0, 0x0, // Used size
1951                0x0, 0x14, 0x0, 0x0, // Total size
1952                0x1, 0x0, 0x0, 0x0, // Flags
1953                0x0, 0x0, 0x0, 0x0 // Reserved
1954            ]
1955        )
1956    }
1957
1958    #[test]
1959    fn relocate_preserves_alignment() {
1960        open_tl!(tl, "tl-cs.bin", 4096);
1961        tl.add_uninitialized_aligned_entry(StandardTLETag::DtFormattedFfaManifest.into(), 128, 9)
1962            .unwrap();
1963
1964        let mut new_buf = [0; 5120];
1965        let new_tl = tl.relocate(&mut new_buf).unwrap();
1966
1967        assert_eq!(tl.entries().count(), 3);
1968
1969        let entry = new_tl
1970            .entries()
1971            .find(|e| e.header.tag() == Ok(StandardTLETag::DtFormattedFfaManifest.into()))
1972            .unwrap();
1973
1974        assert!((entry.payload.as_ptr() as usize).is_multiple_of(1 << 9));
1975    }
1976
1977    #[test]
1978    fn relocate_then_alloc() {
1979        open_tl!(tl, "tl-cs.bin", 4096);
1980
1981        let err = tl
1982            .add_uninitialized_entry(StandardTLETag::DtFormattedFfaManifest.into(), 4096)
1983            .unwrap_err();
1984        let Error::NeedsReallocation { required_size } = err else {
1985            panic!()
1986        };
1987
1988        assert_eq!(required_size, 4224);
1989        let mut new_buf = [0; 4224];
1990
1991        let mut new_tl = tl.relocate(&mut new_buf).unwrap();
1992
1993        assert_eq!(
1994            new_tl.add_uninitialized_entry(StandardTLETag::DtFormattedFfaManifest.into(), 4096),
1995            Ok(())
1996        );
1997    }
1998
1999    #[test]
2000    fn relocate_too_small() {
2001        open_tl!(tl, "tl-cs.bin", 4096);
2002        let mut new_buf = [0; 119];
2003
2004        assert_eq!(tl.relocate(&mut new_buf), Err(Error::NotEnoughMemory));
2005    }
2006
2007    #[test]
2008    fn get_valid() {
2009        open_tl!(tl, "tl-cs.bin", 4096);
2010        assert!(tl.get::<ExecEpInfo64>().is_ok());
2011        assert!(tl.get_mut::<ExecEpInfo64>().is_ok());
2012    }
2013
2014    #[test]
2015    fn get_not_found() {
2016        open_tl!(tl, "tl-cs.bin", 4096);
2017        assert!(matches!(tl.get::<ExecEpInfo32>(), Err(GetError::NotFound)));
2018    }
2019
2020    #[test]
2021    fn get_then_mut_updates_checksum() {
2022        open_tl!(tl, "tl-cs.bin", 4096);
2023
2024        let mut entry = tl.get_mut::<ExecEpInfo64>().unwrap();
2025        entry.x.fill(0x1234_abcd);
2026        drop(entry);
2027
2028        assert_eq!(Ok(()), tl.validate());
2029    }
2030
2031    #[test]
2032    fn readonly_tl() {
2033        open_tl!(tl, "tl-cs.bin", 4096);
2034        tl.operation_mode = OperationMode::RO;
2035
2036        let mut buf = [0; 4096];
2037        assert_eq!(tl.relocate(&mut buf), Err(Error::ReadOnly));
2038
2039        assert_eq!(
2040            tl.add_aligned_entry(StandardTLETag::Aarch64EntrypointInfo.into(), &[0; 8], 1),
2041            Err(Error::ReadOnly)
2042        );
2043        assert_eq!(
2044            tl.add_uninitialized_aligned_entry(StandardTLETag::Aarch32EntrypointInfo.into(), 32, 1),
2045            Err(Error::ReadOnly)
2046        );
2047        assert_eq!(
2048            tl.add_entry(StandardTLETag::AcpiAggr.into(), &[0; 8],),
2049            Err(Error::ReadOnly)
2050        );
2051        assert_eq!(
2052            tl.add_uninitialized_entry(StandardTLETag::AcpiAggr.into(), 32),
2053            Err(Error::ReadOnly)
2054        );
2055    }
2056
2057    #[test]
2058    fn aarch32_regs_no_fdt() {
2059        let mut buf = [0; 4096];
2060        let input = include_bytes!("../test_data/tl-cs.bin");
2061        buf[..input.len()].copy_from_slice(input);
2062        let tl = TransferList::new(&mut buf).unwrap();
2063
2064        assert_eq!(
2065            tl.aarch32_regs(|addr| addr as u32),
2066            [0, 0x010f_b10b, 0, buf.as_ptr() as u32]
2067        )
2068    }
2069
2070    #[test]
2071    fn aarch64_regs_no_fdt() {
2072        let mut buf = [0; 4096];
2073        let input = include_bytes!("../test_data/tl-cs.bin");
2074        buf[..input.len()].copy_from_slice(input);
2075        let tl = TransferList::new(&mut buf).unwrap();
2076
2077        assert_eq!(
2078            tl.aarch64_regs(|addr| addr as u64),
2079            [0, 0x0000_0001_4a0f_b10b, 0, buf.as_ptr() as u64]
2080        )
2081    }
2082
2083    #[test]
2084    fn aarch32_regs_fdt() {
2085        let mut buf = [0; 4096];
2086        let input = include_bytes!("../test_data/tl-fdt.bin");
2087        buf[..input.len()].copy_from_slice(input);
2088        let base = buf.as_ptr() as u32;
2089        let tl = TransferList::new(&mut buf).unwrap();
2090
2091        assert_eq!(
2092            tl.aarch32_regs(|addr| addr as u32),
2093            [0, 0x010f_b10b, base + 0x20, base]
2094        )
2095    }
2096
2097    #[test]
2098    fn aarch64_regs_fdt() {
2099        let mut buf = [0; 4096];
2100        let input = include_bytes!("../test_data/tl-fdt.bin");
2101        buf[..input.len()].copy_from_slice(input);
2102        let base = buf.as_ptr() as u64;
2103        let tl = TransferList::new(&mut buf).unwrap();
2104
2105        assert_eq!(
2106            tl.aarch64_regs(|addr| addr as u64),
2107            [base + 0x20, 0x0000_0001_4a0f_b10b, 0, base]
2108        )
2109    }
2110
2111    macro_rules! add_or_reloc {
2112        ($buf:ident, $tl:ident, $tag:expr, $len:expr) => {
2113            match $tl.add_uninitialized_entry($tag, $len) {
2114                Ok(_) => {}
2115                Err(Error::NeedsReallocation { required_size }) => {
2116                    let buf = &mut $buf[0..required_size as usize];
2117                    $tl = $tl.relocate(buf).unwrap();
2118                    $tl.add_uninitialized_entry($tag, $len).unwrap();
2119                }
2120                Err(e) => panic!("unexpected error: {e:?}"),
2121            }
2122        };
2123        ($buf:ident, $tl:ident, $tag:expr, $len:expr, $align:expr) => {
2124            match $tl.add_uninitialized_aligned_entry($tag, $len, $align) {
2125                Ok(_) => {}
2126                Err(Error::NeedsReallocation { required_size }) => {
2127                    let buf = &mut $buf[0..required_size as usize];
2128                    $tl = $tl.relocate(buf).unwrap();
2129                    $tl.add_uninitialized_aligned_entry($tag, $len, $align)
2130                        .unwrap();
2131                }
2132                Err(e) => panic!("unexpected error: {e:?}"),
2133            }
2134        };
2135    }
2136
2137    macro_rules! assert_entry_matches {
2138        ($tl:ident, $tag:expr, $len:expr) => {
2139            assert_eq!(
2140                $tl.entries()
2141                    .find(|e| e.header.tag == $tag)
2142                    .unwrap()
2143                    .payload
2144                    .len(),
2145                $len
2146            );
2147        };
2148        ($tl:ident, $tag:expr, $len:expr, $align:expr) => {
2149            let tl = $tl.entries().find(|e| e.header.tag == $tag).unwrap();
2150            let len = tl.payload.len();
2151            let ptr = (tl.payload.as_ptr() as usize);
2152            let align = ptr.trailing_zeros();
2153
2154            assert_eq!(
2155                len, $len,
2156                "For entry with tag {:?}, expected len of {:x} but got {len:x}",
2157                $tag, $len
2158            );
2159            assert!(
2160                align >= $align,
2161                "For entry with tag {:?}, expected alignment of at least {:x} but got {align:x} (0x{ptr:x})",
2162                $tag,
2163                $align
2164            );
2165        };
2166    }
2167
2168    #[test]
2169    fn add_reloc_seq1() {
2170        let mut buf1 = [0; 4096];
2171        let mut buf2 = [0; 4096];
2172        let mut buf3 = [0; 4096];
2173        let mut buf4 = [0; 4096];
2174        let mut buf5 = [0; 4096];
2175        let mut buf6 = [0; 4096];
2176
2177        let mut tl = TransferList::create(&mut buf1[0..256], true).unwrap();
2178
2179        add_or_reloc!(buf2, tl, StandardTLETag::Fdt.into(), 128);
2180        add_or_reloc!(buf3, tl, StandardTLETag::HobB.into(), 53);
2181        add_or_reloc!(buf4, tl, StandardTLETag::HobL.into(), 200, 7);
2182        add_or_reloc!(buf5, tl, StandardTLETag::AcpiAggr.into(), 192, 6);
2183        add_or_reloc!(buf6, tl, StandardTLETag::EventLog.into(), 48);
2184
2185        assert_entry_matches!(tl, [1, 0, 0], 128);
2186        assert_entry_matches!(tl, [2, 0, 0], 53);
2187        assert_entry_matches!(tl, [3, 0, 0], 200, 7);
2188        assert_entry_matches!(tl, [4, 0, 0], 192, 6);
2189        assert_entry_matches!(tl, [5, 0, 0], 48);
2190    }
2191
2192    #[test]
2193    fn add_reloc_seq2() {
2194        let mut buf1 = [0; 4096];
2195        let mut buf2 = [0; 4096];
2196        let mut buf3 = [0; 4096];
2197        let mut buf4 = [0; 4096];
2198        let mut buf5 = [0; 4096];
2199        let mut buf6 = [0; 4096];
2200
2201        let mut tl = TransferList::create(&mut buf1[0..256], true).unwrap();
2202
2203        add_or_reloc!(buf2, tl, StandardTLETag::Fdt.into(), 36, 2);
2204        add_or_reloc!(buf3, tl, StandardTLETag::HobB.into(), 64, 8);
2205        add_or_reloc!(buf4, tl, StandardTLETag::HobL.into(), 150);
2206        add_or_reloc!(buf5, tl, StandardTLETag::AcpiAggr.into(), 133, 1);
2207        add_or_reloc!(buf6, tl, StandardTLETag::EventLog.into(), 267);
2208
2209        assert_entry_matches!(tl, [1, 0, 0], 36, 2);
2210        assert_entry_matches!(tl, [2, 0, 0], 64, 8);
2211        assert_entry_matches!(tl, [3, 0, 0], 150);
2212        assert_entry_matches!(tl, [4, 0, 0], 133, 1);
2213        assert_entry_matches!(tl, [5, 0, 0], 267);
2214    }
2215
2216    #[test]
2217    fn add_reloc_seq3() {
2218        let mut buf1 = [0; 4096];
2219        let mut buf2 = [0; 4096];
2220        let mut buf3 = [0; 4096];
2221        let mut buf4 = [0; 4096];
2222
2223        let mut tl = TransferList::create(&mut buf1[0..256], true).unwrap();
2224
2225        add_or_reloc!(buf2, tl, StandardTLETag::Fdt.into(), 48);
2226        add_or_reloc!(buf3, tl, StandardTLETag::HobB.into(), 96, 4);
2227        add_or_reloc!(buf4, tl, StandardTLETag::HobL.into(), 200, 8);
2228
2229        assert_entry_matches!(tl, [1, 0, 0], 48);
2230        assert_entry_matches!(tl, [2, 0, 0], 96, 4);
2231        assert_entry_matches!(tl, [3, 0, 0], 200, 8);
2232    }
2233
2234    #[test]
2235    fn add_reloc_seq4() {
2236        let mut buf1 = [0; 4096];
2237        let mut buf2 = [0; 4096];
2238        let mut buf3 = [0; 4096];
2239        let mut buf4 = [0; 4096];
2240        let mut buf5 = [0; 4096];
2241        let mut buf6 = [0; 4096];
2242        let mut buf7 = [0; 4096];
2243
2244        let mut tl = TransferList::create(&mut buf1[0..256], true).unwrap();
2245
2246        add_or_reloc!(buf3, tl, StandardTLETag::Fdt.into(), 32, 2);
2247        add_or_reloc!(buf4, tl, StandardTLETag::HobB.into(), 24, 4);
2248        add_or_reloc!(buf5, tl, StandardTLETag::HobL.into(), 20, 8);
2249        add_or_reloc!(buf6, tl, StandardTLETag::AcpiAggr.into(), 12);
2250        add_or_reloc!(buf7, tl, StandardTLETag::EventLog.into(), 10, 2);
2251        add_or_reloc!(buf2, tl, StandardTLETag::TpmCrbBase.into(), 16, 1);
2252
2253        assert_entry_matches!(tl, [1, 0, 0], 32, 2);
2254        assert_entry_matches!(tl, [2, 0, 0], 24, 4);
2255        assert_entry_matches!(tl, [3, 0, 0], 20, 8);
2256        assert_entry_matches!(tl, [4, 0, 0], 12);
2257        assert_entry_matches!(tl, [5, 0, 0], 10, 2);
2258        assert_entry_matches!(tl, [6, 0, 0], 16, 1);
2259    }
2260
2261    #[test]
2262    fn add_reloc_seq5() {
2263        let mut buf1 = [0; 4096];
2264        let mut buf2 = [0; 4096];
2265        let mut buf3 = [0; 4096];
2266        let mut buf4 = [0; 4096];
2267        let mut buf5 = [0; 4096];
2268
2269        let mut tl = TransferList::create(&mut buf1[0..256], true).unwrap();
2270
2271        add_or_reloc!(buf2, tl, StandardTLETag::Fdt.into(), 100);
2272        add_or_reloc!(buf3, tl, StandardTLETag::HobB.into(), 200);
2273        add_or_reloc!(buf4, tl, StandardTLETag::HobL.into(), 300);
2274        add_or_reloc!(buf5, tl, StandardTLETag::AcpiAggr.into(), 400);
2275
2276        assert_entry_matches!(tl, [1, 0, 0], 100);
2277        assert_entry_matches!(tl, [2, 0, 0], 200);
2278        assert_entry_matches!(tl, [3, 0, 0], 300);
2279        assert_entry_matches!(tl, [4, 0, 0], 400);
2280    }
2281
2282    #[test]
2283    fn add_reloc_seq6() {
2284        let mut buf1 = [0; 4096];
2285        let mut buf2 = [0; 4096];
2286        let mut buf3 = [0; 4096];
2287        let mut buf4 = [0; 4096];
2288        let mut buf5 = [0; 4096];
2289        let mut buf6 = [0; 4096];
2290
2291        let mut tl = TransferList::create(&mut buf1[0..256], true).unwrap();
2292
2293        add_or_reloc!(buf2, tl, StandardTLETag::Fdt.into(), 60, 2);
2294        add_or_reloc!(buf3, tl, StandardTLETag::HobB.into(), 128, 4);
2295        add_or_reloc!(buf4, tl, StandardTLETag::HobL.into(), 256, 1);
2296        add_or_reloc!(buf5, tl, StandardTLETag::AcpiAggr.into(), 512, 8);
2297        add_or_reloc!(buf6, tl, StandardTLETag::EventLog.into(), 1024, 2);
2298
2299        assert_entry_matches!(tl, [1, 0, 0], 60, 2);
2300        assert_entry_matches!(tl, [2, 0, 0], 128, 4);
2301        assert_entry_matches!(tl, [3, 0, 0], 256, 1);
2302        assert_entry_matches!(tl, [4, 0, 0], 512, 8);
2303        assert_entry_matches!(tl, [5, 0, 0], 1024, 2);
2304    }
2305}