Move to zerocopy: boot info

Signed-off-by: Balint Dobszay <balint.dobszay@arm.com>
Change-Id: Ifebe431448c38b7a829cf018f4fa8badbfb74d58
diff --git a/src/boot_info.rs b/src/boot_info.rs
index 553c7c5..30850e5 100644
--- a/src/boot_info.rs
+++ b/src/boot_info.rs
@@ -1,182 +1,533 @@
 // SPDX-FileCopyrightText: Copyright 2023 Arm Limited and/or its affiliates <open-source-office@arm.com>
 // SPDX-License-Identifier: MIT OR Apache-2.0
 
+use crate::{
+    ffa_v1_1::{boot_info_descriptor, boot_info_header},
+    Version,
+};
+use core::ffi::CStr;
+use thiserror::Error;
 use uuid::Uuid;
+use zerocopy::{FromBytes, IntoBytes};
 
-pub enum BootInfoName {
-    NullTermString(&'static str),
+#[derive(Debug, Error)]
+pub enum Error {
+    #[error("Invalid standard type {0}")]
+    InvalidStdType(u8),
+    #[error("Invalid type {0}")]
+    InvalidType(u8),
+    #[error("Invalid contents format {0}")]
+    InvalidContentsFormat(u16),
+    #[error("Invalid name format {0}")]
+    InvalidNameFormat(u16),
+    #[error("Invalid name")]
+    InvalidName,
+    #[error("Invalid flags {0}")]
+    InvalidFlags(u16),
+    #[error("Invalid header size or alignment")]
+    InvalidHeader,
+    #[error("Invalid buffer size")]
+    InvalidBufferSize,
+    #[error("Invalid signature")]
+    InvalidSignature,
+    #[error("Invalid version {0}")]
+    InvalidVersion(Version),
+    #[error("Malformed descriptor")]
+    MalformedDescriptor,
+}
+
+impl From<Error> for crate::FfaError {
+    fn from(_value: Error) -> Self {
+        Self::InvalidParameters
+    }
+}
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum BootInfoName<'a> {
+    NullTermString(&'a CStr),
     Uuid(Uuid),
 }
 
-#[derive(Clone, Copy)]
-pub enum BootInfoStdType {
-    Fdt = 0,
-    Hob = 1,
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+#[repr(u8)]
+pub enum BootInfoStdId {
+    Fdt = Self::FDT,
+    Hob = Self::HOB,
 }
 
+impl TryFrom<u8> for BootInfoStdId {
+    type Error = Error;
+
+    fn try_from(value: u8) -> Result<Self, Self::Error> {
+        match value {
+            Self::FDT => Ok(BootInfoStdId::Fdt),
+            Self::HOB => Ok(BootInfoStdId::Hob),
+            _ => Err(Error::InvalidStdType(value)),
+        }
+    }
+}
+
+impl BootInfoStdId {
+    const FDT: u8 = 0;
+    const HOB: u8 = 1;
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub struct BootInfoImpdefId(pub u8);
+
+impl From<u8> for BootInfoImpdefId {
+    fn from(value: u8) -> Self {
+        Self(value)
+    }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 pub enum BootInfoType {
-    Std(BootInfoStdType),
-    Impdef(u8),
+    Std(BootInfoStdId),
+    Impdef(BootInfoImpdefId),
 }
 
-pub enum BootInfoContents {
-    Address(usize),
-    Value(u64),
+impl TryFrom<u8> for BootInfoType {
+    type Error = Error;
+
+    fn try_from(value: u8) -> Result<Self, Self::Error> {
+        match (value >> Self::TYPE_SHIFT) & Self::TYPE_MASK {
+            Self::TYPE_STANDARD => Ok(BootInfoType::Std((value & Self::ID_MASK).try_into()?)),
+            Self::TYPE_IMPDEF => Ok(BootInfoType::Impdef((value & Self::ID_MASK).into())),
+            _ => Err(Error::InvalidType(value)),
+        }
+    }
 }
 
-pub struct BootInfoDescriptor {
-    /// Name of boot information passed to the consumer
-    pub name: BootInfoName,
+impl From<BootInfoType> for u8 {
+    fn from(value: BootInfoType) -> Self {
+        match value {
+            BootInfoType::Std(std_type) => {
+                std_type as u8 | BootInfoType::TYPE_STANDARD << BootInfoType::TYPE_SHIFT
+            }
+            BootInfoType::Impdef(impdef_type) => {
+                impdef_type.0 | BootInfoType::TYPE_IMPDEF << BootInfoType::TYPE_SHIFT
+            }
+        }
+    }
+}
 
-    /// Type of boot information passed to the consumer
+impl BootInfoType {
+    // This field contains the boot info type at bit[7] and the boot info identifier in bits[6:0]
+    const TYPE_SHIFT: usize = 7;
+    const TYPE_MASK: u8 = 0b1;
+    const TYPE_STANDARD: u8 = 0b0;
+    const TYPE_IMPDEF: u8 = 0b1;
+    // Mask for boot info identifier in bits[6:0]
+    const ID_MASK: u8 = 0b0111_1111;
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum BootInfoContents<'a> {
+    Address { content_buf: &'a [u8] },
+    Value { val: u64, len: usize },
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+#[repr(u16)]
+enum BootInfoContentsFormat {
+    Address = Self::ADDRESS << Self::SHIFT,
+    Value = Self::VALUE << Self::SHIFT,
+}
+
+impl TryFrom<u16> for BootInfoContentsFormat {
+    type Error = Error;
+
+    fn try_from(value: u16) -> Result<Self, Self::Error> {
+        match (value >> Self::SHIFT) & Self::MASK {
+            Self::ADDRESS => Ok(BootInfoContentsFormat::Address),
+            Self::VALUE => Ok(BootInfoContentsFormat::Value),
+            _ => Err(Error::InvalidContentsFormat(value)),
+        }
+    }
+}
+
+impl BootInfoContentsFormat {
+    const SHIFT: usize = 2;
+    const MASK: u16 = 0b11;
+    const ADDRESS: u16 = 0b00;
+    const VALUE: u16 = 0b01;
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+#[repr(u16)]
+enum BootInfoNameFormat {
+    String = Self::STRING << Self::SHIFT,
+    Uuid = Self::UUID << Self::SHIFT,
+}
+
+impl TryFrom<u16> for BootInfoNameFormat {
+    type Error = Error;
+
+    fn try_from(value: u16) -> Result<Self, Self::Error> {
+        match (value >> Self::SHIFT) & Self::MASK {
+            Self::STRING => Ok(BootInfoNameFormat::String),
+            Self::UUID => Ok(BootInfoNameFormat::Uuid),
+            _ => Err(Error::InvalidNameFormat(value)),
+        }
+    }
+}
+
+impl BootInfoNameFormat {
+    const SHIFT: usize = 0;
+    const MASK: u16 = 0b11;
+    const STRING: u16 = 0b00;
+    const UUID: u16 = 0b01;
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+struct BootInfoFlags {
+    contents_format: BootInfoContentsFormat,
+    name_format: BootInfoNameFormat,
+}
+
+impl TryFrom<u16> for BootInfoFlags {
+    type Error = Error;
+
+    fn try_from(value: u16) -> Result<Self, Self::Error> {
+        // bits[15:4]: Reserved (MBZ)
+        if value >> 4 == 0 {
+            Ok(Self {
+                contents_format: BootInfoContentsFormat::try_from(value)?,
+                name_format: BootInfoNameFormat::try_from(value)?,
+            })
+        } else {
+            Err(Error::InvalidFlags(value))
+        }
+    }
+}
+
+impl From<BootInfoFlags> for u16 {
+    fn from(value: BootInfoFlags) -> Self {
+        value.contents_format as u16 | value.name_format as u16
+    }
+}
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct BootInfo<'a> {
+    pub name: BootInfoName<'a>,
     pub typ: BootInfoType,
-
-    /// Size (in bytes) of boot information identified by the Name and Type fields
-    pub size: u32,
-
-    pub contents: BootInfoContents,
+    pub contents: BootInfoContents<'a>,
 }
 
-impl BootInfoDescriptor {
-    pub fn create(descriptors: &[BootInfoDescriptor], buf: &mut [u8]) {
+impl BootInfo<'_> {
+    pub fn pack(descriptors: &[BootInfo], buf: &mut [u8], mapped_addr: Option<usize>) {
         // Offset from the base of the header to the first element in the boot info descriptor array
-        // Must be 8 byte aligned
-        const DESC_ARRAY_OFFSET: usize = 32;
+        // Must be 8 byte aligned, but otherwise we're free to choose any value here.
+        // Let's just  pack the array right after the header.
+        const DESC_ARRAY_OFFSET: usize = size_of::<boot_info_header>().next_multiple_of(8);
+        const DESC_SIZE: usize = size_of::<boot_info_descriptor>();
 
-        /// In FF-A v1.1, Table 5.8: Boot information descriptor is 32 bytes long
-        const DESC_SIZE: usize = 32;
+        assert!(buf.len() <= u32::MAX as usize);
 
-        // assert!(descriptors.len() <= u32::MAX as usize);
         let desc_cnt = descriptors.len();
 
-        // Add the already known fields, later we have to add the sizes referenced by the individual descriptors
-        let mut total_size = 0usize;
-        total_size = total_size.checked_add(DESC_ARRAY_OFFSET).unwrap();
-        total_size = total_size
+        // Add the already known fields, later we have to add the sizes referenced by the individual
+        // descriptors
+
+        // The Size of boot information blob field specifies the size of the blob that spans one or
+        // more contiguous 4K pages used by the producer to populate it. It is calculated by adding
+        // the following values:
+        // 1. Boot information descriptor array offset
+        // 2. Product of Boot information descriptor count and Boot information descriptor size.
+        // 3. Total size of all boot information referenced by boot information descriptors.
+        //    This is determined by adding the values in the Size field of each boot information
+        //    descriptor whose Contents field contains an address.
+        // 4. Any padding between,
+        //    1. The boot information descriptor array and the boot information referenced from it.
+        //    2. Distinct instances of boot information referenced from the boot information
+        //       descriptor array.
+        let mut total_offset = 0usize;
+
+        // No. 1 from the "Size of boot information blob" list
+        total_offset = total_offset.checked_add(DESC_ARRAY_OFFSET).unwrap();
+
+        // No. 2 from the "Size of boot information blob" list
+        total_offset = total_offset
             .checked_add(desc_cnt.checked_mul(DESC_SIZE).unwrap())
             .unwrap();
 
-        // Create the boot info header starting at offset 0 in the buffer
-        // Offset 0, length 4: Hexadecimal value 0x0FFA to identify the header
-        buf[0..4].copy_from_slice(&0x0ffa_u32.to_le_bytes());
-
-        // Offset 4, length 4: Version of the boot information blob encoded as in FFA_VERSION_GET
-        buf[4..8].copy_from_slice(&0x0001_0001_u32.to_le_bytes());
-
-        // Offset 12, length 4: Size of each boot information descriptor in the array
-        buf[12..16].copy_from_slice(&(DESC_SIZE as u32).to_le_bytes());
-
-        // Offset 16, length 4: Count of boot information descriptors in the array
-        buf[16..20].copy_from_slice(&(desc_cnt as u32).to_le_bytes());
-
-        // Offset 20, length 4: Offset to array of boot information descriptors
-        buf[20..24].copy_from_slice(&(DESC_ARRAY_OFFSET as u32).to_le_bytes());
-
-        // Offset 24, length 8: Reserved (MBZ)
-        buf[24..32].fill(0);
+        // Fail early if the buffer is too small
+        assert!(total_offset <= buf.len());
 
         // Fill the boot info descriptor array, all offset based from DESC_ARRAY_OFFSET
-        let mut offset = DESC_ARRAY_OFFSET;
+        let mut desc_array_offset = DESC_ARRAY_OFFSET;
+
         for desc in descriptors {
-            // Offset 0, length 16: Name of boot information passed to the consumer
-            match &desc.name {
+            let mut desc_raw = boot_info_descriptor::default();
+
+            let name_format = match &desc.name {
                 BootInfoName::NullTermString(name) => {
-                    assert!(name.is_ascii());
-                    let name_len = name.len().min(15);
-                    buf[offset..offset + name_len].copy_from_slice(&name.as_bytes()[..name_len]);
-                    buf[offset + name_len..offset + 16].fill(0); // Make sure it's null terminated
+                    // count_bytes() doesn't include nul terminator
+                    let name_len = name.count_bytes().min(15);
+                    desc_raw.name[..name_len].copy_from_slice(&name.to_bytes()[..name_len]);
+                    // Add nul terminator and zero fill the rest
+                    desc_raw.name[name_len..].fill(0);
+
+                    BootInfoNameFormat::String
                 }
                 BootInfoName::Uuid(uuid) => {
-                    buf[offset..offset + 16].copy_from_slice(&uuid.to_bytes_le());
+                    desc_raw.name.copy_from_slice(&uuid.to_bytes_le());
+                    BootInfoNameFormat::Uuid
                 }
-            }
-
-            // Offset 16, length 1: Type of boot information passed to the consumer
-            let info_type = match desc.typ {
-                BootInfoType::Std(std_type) => (std_type as u8) & 0b0111_1111,
-                BootInfoType::Impdef(typ) => (0b1 << 7) | typ,
             };
-            buf[offset + 16] = info_type;
 
-            // Offset 17, length 1: Reserved (MBZ)
-            buf[offset + 17] = 0;
+            let contents_format = match desc.contents {
+                BootInfoContents::Address { content_buf } => {
+                    // We have to copy the contents referenced by the boot info descriptor into the
+                    // boot info blob. At this offset we're after the boot info header and all of
+                    // the boot info descriptors. The contents referenced from the individual boot
+                    // info descriptors will get copied to this starting address. The 8 byte
+                    // alignment is not explicitly mentioned by the spec, but it's better to have it
+                    // anyway.
+                    // No. 4 from the "Size of boot information blob" list
+                    total_offset = total_offset.next_multiple_of(8);
 
-            // Offset 18, length 2: Flags to describe properties of boot information associated with this descriptor
-            let mut flags = 0u16;
-            if let BootInfoName::Uuid(_) = &desc.name {
-                flags |= 0b1;
-            }
-            if let BootInfoContents::Value(_) = desc.contents {
-                flags |= 0b1 << 2;
-            }
-            buf[offset + 18..offset + 20].copy_from_slice(&flags.to_le_bytes());
+                    // The mapped_addr argument contains the address where buf is mapped to in the
+                    // consumer's translation regime. If it's None, we assume identity mapping is
+                    // used, so the buffer's address stays the same.
+                    let buf_addr = mapped_addr.unwrap_or(buf.as_ptr() as usize);
 
-            // Offset 20, length 4: Size (in bytes) of boot information identified by the Name and Type fields.
-            match desc.contents {
-                BootInfoContents::Address(_) => {
-                    total_size = total_size.checked_add(desc.size as usize).unwrap();
+                    // The content's address in the consumer's translation regime will be the
+                    // buffer's address in the consumer's translation regime plus the offset of the
+                    // content within the boot info blob.
+                    let content_addr = buf_addr.checked_add(total_offset).unwrap();
+
+                    // Check if the content fits before copying
+                    let content_len = content_buf.len();
+                    total_offset.checked_add(content_len).unwrap();
+
+                    // Do the copy and increase the total size
+                    // No. 3 from the "Size of boot information blob" list
+                    buf[total_offset..total_offset + content_len].copy_from_slice(content_buf);
+                    total_offset += content_len;
+
+                    desc_raw.contents = content_addr as u64;
+                    desc_raw.size = content_len as u32;
+
+                    BootInfoContentsFormat::Address
                 }
-                BootInfoContents::Value(_) => {
-                    assert!((1..=8).contains(&desc.size));
-                }
-            }
-            buf[offset + 20..offset + 24].copy_from_slice(&desc.size.to_le_bytes());
+                BootInfoContents::Value { val, len } => {
+                    assert!((1..=8).contains(&len));
+                    desc_raw.contents = val;
+                    desc_raw.size = len as u32;
 
-            // Offset 24, length 8: Value or address of boot information identified by the Name and Type fields.
-            // Value or address of boot information identified by the Name and Type fields.
-            //
-            // If in the Flags field, bit\[3:2\] = b'0,
-            // * The address has the same attributes as the boot information blob address described in
-            //   5.4.3 Boot information address.
-            // * Size field contains the length (in bytes) of boot information at the specified address.
-            //
-            // If in the Flags field, bit\[3:2\] = b’1,
-            // * Size field contains the exact size of the value specified in this field.
-            // * Size is >=1 bytes and <= 8 bytes.
-            let content = match desc.contents {
-                BootInfoContents::Address(addr) => addr as u64,
-                BootInfoContents::Value(val) => val,
+                    BootInfoContentsFormat::Value
+                }
             };
-            buf[offset + 24..offset + 32].copy_from_slice(&content.to_le_bytes());
 
-            offset += DESC_SIZE;
+            let flags = BootInfoFlags {
+                contents_format,
+                name_format,
+            };
+
+            desc_raw.flags = flags.into();
+            desc_raw.typ = desc.typ.into();
+
+            desc_raw
+                .write_to_prefix(&mut buf[desc_array_offset..])
+                .unwrap();
+            desc_array_offset += DESC_SIZE;
         }
 
-        // TODO: add padding size between boot information referenced by the descriptors
+        let header_raw = boot_info_header {
+            signature: 0x0ffa,
+            version: Version(1, 1).into(),
+            boot_info_blob_size: total_offset as u32,
+            boot_info_desc_size: DESC_SIZE as u32,
+            boot_info_desc_count: desc_cnt as u32,
+            boot_info_array_offset: DESC_ARRAY_OFFSET as u32,
+            reserved: 0,
+        };
 
-        // Offset 8, length 4: Size of boot information blob spanning contiguous memory
-        assert!(buf.len() <= u32::MAX as usize);
-        assert!(total_size <= buf.len());
-        buf[8..12].copy_from_slice(&(total_size as u32).to_le_bytes());
+        header_raw.write_to_prefix(buf).unwrap();
+    }
+
+    /// Validate and return the boot information header
+    fn get_header(buf: &[u8]) -> Result<&boot_info_header, Error> {
+        let (header_raw, _) =
+            boot_info_header::ref_from_prefix(buf).map_err(|_| Error::InvalidHeader)?;
+
+        if header_raw.signature != 0x0ffa {
+            return Err(Error::InvalidSignature);
+        }
+
+        let version = Version::from(header_raw.version);
+        if version != Version(1, 1) {
+            return Err(Error::InvalidVersion(version));
+        }
+
+        Ok(header_raw)
+    }
+
+    /// Get the size of the boot information blob spanning contiguous memory.
+    ///
+    /// This enables a consumer to map all of the boot information blob in its translation regime
+    /// or copy it to another memory location without parsing each element in the boot information
+    /// descriptor array.
+    pub fn get_blob_size(buf: &[u8]) -> Result<usize, Error> {
+        let header_raw = Self::get_header(buf)?;
+
+        Ok(header_raw.boot_info_blob_size as usize)
+    }
+}
+
+pub struct BootInfoIterator<'a> {
+    buf: &'a [u8],
+    offset: usize,
+    desc_count: usize,
+    desc_size: usize,
+}
+
+impl<'a> BootInfoIterator<'a> {
+    pub fn new(buf: &'a [u8]) -> Result<Self, Error> {
+        let header_raw = BootInfo::get_header(buf)?;
+
+        if buf.len() < header_raw.boot_info_blob_size as usize {
+            return Err(Error::InvalidBufferSize);
+        }
+
+        if header_raw.boot_info_desc_size as usize != size_of::<boot_info_descriptor>() {
+            return Err(Error::MalformedDescriptor);
+        }
+
+        let Some(total_desc_size) = header_raw
+            .boot_info_desc_count
+            .checked_mul(header_raw.boot_info_desc_size)
+            .and_then(|x| x.checked_add(header_raw.boot_info_array_offset))
+        else {
+            return Err(Error::InvalidBufferSize);
+        };
+
+        if buf.len() < total_desc_size as usize {
+            return Err(Error::InvalidBufferSize);
+        }
+
+        Ok(Self {
+            buf,
+            offset: header_raw.boot_info_array_offset as usize,
+            desc_count: header_raw.boot_info_desc_count as usize,
+            desc_size: header_raw.boot_info_desc_size as usize,
+        })
+    }
+}
+
+impl<'a> Iterator for BootInfoIterator<'a> {
+    type Item = Result<BootInfo<'a>, Error>;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        if self.desc_count > 0 {
+            let desc_offset = self.offset;
+            self.offset += self.desc_size;
+            self.desc_count -= 1;
+
+            let Ok(desc_raw) = boot_info_descriptor::ref_from_bytes(
+                &self.buf[desc_offset..desc_offset + self.desc_size],
+            ) else {
+                return Some(Err(Error::MalformedDescriptor));
+            };
+
+            if desc_raw.reserved != 0 {
+                return Some(Err(Error::MalformedDescriptor));
+            }
+
+            let typ: BootInfoType = match desc_raw.typ.try_into() {
+                Ok(v) => v,
+                Err(e) => return Some(Err(e)),
+            };
+
+            let flags: BootInfoFlags = match desc_raw.flags.try_into() {
+                Ok(v) => v,
+                Err(e) => return Some(Err(e)),
+            };
+
+            let name = match flags.name_format {
+                BootInfoNameFormat::String => {
+                    let Ok(name_str) = CStr::from_bytes_with_nul(desc_raw.name.as_bytes()) else {
+                        return Some(Err(Error::InvalidName));
+                    };
+                    BootInfoName::NullTermString(name_str)
+                }
+                BootInfoNameFormat::Uuid => BootInfoName::Uuid(Uuid::from_bytes_le(desc_raw.name)),
+            };
+
+            let contents = match flags.contents_format {
+                BootInfoContentsFormat::Address => {
+                    let contents = desc_raw.contents as usize;
+                    let contents_size = desc_raw.size as usize;
+
+                    let Some(offset) = contents.checked_sub(self.buf.as_ptr() as usize) else {
+                        return Some(Err(Error::InvalidBufferSize));
+                    };
+
+                    let Some(offset_end) = offset.checked_add(contents_size) else {
+                        return Some(Err(Error::InvalidBufferSize));
+                    };
+
+                    if self.buf.len() < offset_end {
+                        return Some(Err(Error::InvalidBufferSize));
+                    }
+
+                    BootInfoContents::Address {
+                        content_buf: &self.buf[offset..offset_end],
+                    }
+                }
+
+                BootInfoContentsFormat::Value => BootInfoContents::Value {
+                    val: desc_raw.contents,
+                    len: desc_raw.size as usize,
+                },
+            };
+
+            return Some(Ok(BootInfo {
+                name,
+                typ,
+                contents,
+            }));
+        }
+
+        None
     }
 }
 
 #[cfg(test)]
 mod tests {
     use super::*;
-
     use uuid::uuid;
 
+    // TODO: add tests with a known correct boot info blob
+
     #[test]
     fn boot_info() {
-        let desc1 = BootInfoDescriptor {
-            name: BootInfoName::NullTermString(&"test1234test1234"),
-            typ: BootInfoType::Impdef(0xab),
-            size: 4,
-            contents: BootInfoContents::Value(0xbeef),
+        let desc1 = BootInfo {
+            name: BootInfoName::NullTermString(c"test1234test123"),
+            typ: BootInfoType::Impdef(BootInfoImpdefId(0x2b)),
+            contents: BootInfoContents::Value {
+                val: 0xdeadbeef,
+                len: 4,
+            },
         };
 
         let fdt = [0u8; 0xff];
-        let desc2 = BootInfoDescriptor {
-            name: BootInfoName::Uuid(uuid!("12345678-1234-1234-1234-123456789abc")),
-            typ: BootInfoType::Std(BootInfoStdType::Fdt),
-            size: 0xff,
-            contents: BootInfoContents::Address(&fdt as *const u8 as usize),
+        let desc2 = BootInfo {
+            name: BootInfoName::Uuid(uuid!("12345678-abcd-dcba-1234-123456789abc")),
+            typ: BootInfoType::Std(BootInfoStdId::Fdt),
+            contents: BootInfoContents::Address { content_buf: &fdt },
         };
 
         let mut buf = [0u8; 0x1ff];
-        BootInfoDescriptor::create(&[desc1, desc2], &mut buf);
+        let buf_addr = buf.as_ptr() as usize;
+        BootInfo::pack(&[desc1.clone(), desc2.clone()], &mut buf, Some(buf_addr));
+        let mut descriptors = BootInfoIterator::new(&buf).unwrap();
+        let desc1_check = descriptors.next().unwrap().unwrap();
+        let desc2_check = descriptors.next().unwrap().unwrap();
 
-        println!("{:#x?}", &buf[0..0x0f]);
+        assert_eq!(desc1, desc1_check);
+        assert_eq!(desc2, desc2_check);
     }
 }
diff --git a/src/ffa_v1_1.rs b/src/ffa_v1_1.rs
new file mode 100644
index 0000000..e16ec06
--- /dev/null
+++ b/src/ffa_v1_1.rs
@@ -0,0 +1,75 @@
+// SPDX-FileCopyrightText: Copyright 2025 Arm Limited and/or its affiliates <open-source-office@arm.com>
+// SPDX-License-Identifier: MIT OR Apache-2.0
+
+#![allow(non_camel_case_types)]
+
+use zerocopy_derive::*;
+
+/// Table 5.8: Boot information descriptor
+#[derive(Default, FromBytes, IntoBytes, KnownLayout, Immutable)]
+#[repr(C, packed)]
+pub(crate) struct boot_info_descriptor {
+    /// Offset 0, length 16: Name of boot information passed to the consumer.
+    pub(crate) name: [u8; 16],
+    /// Offset 16, length 1: Type of boot information passed to the consumer.
+    /// - Bit\[7\]: Boot information type.
+    ///   + b’0: Standard boot information.
+    ///   + b’1: Implementation defined boot information.
+    /// - Bits\[6:0\]: Boot information identifier.
+    ///   + Standard boot information (bit\[7\] = b’0).
+    ///     * 0: Flattened device tree (FDT).
+    ///     * 1: Hand-Off Block (HOB) List.
+    ///     * All other identifiers are reserved.
+    ///   + Implementation defined identifiers (bit\[7\] = b’ 1).
+    ///     * Identifier is defined by the implementation.
+    pub(crate) typ: u8,
+    /// Offset 17, length 1: Reserved (MBZ)
+    pub(crate) reserved: u8,
+    /// Offset 18, length 2: Flags to describe properties of boot information associated with this
+    /// descriptor.
+    /// - Bits\[15:4\]: Reserved (MBZ).
+    /// - Bits\[3:2\]: Format of Contents field.
+    ///   + b’0: Address of boot information identified by the Name and Type fields.
+    ///   + b’1: Value of boot information identified by the Name and Type fields.
+    ///   + All other bit encodings are reserved for future use.
+    /// - Bits\[1:0\]: Format of Name field.
+    ///   + b’0: Null terminated string.
+    ///   + b’1: UUID encoded in little-endian byte order.
+    ///   + All other bit encodings are reserved for future use.
+    pub(crate) flags: u16,
+    /// Offset 20, length 4: Size (in bytes) of boot information identified by the Name and Type
+    /// fields
+    pub(crate) size: u32,
+    /// Offset 24, length 8: Value or address of boot information identified by the Name and Type
+    /// fields.
+    ///
+    /// If in the Flags field, bit\[3:2\] = b'0,
+    /// * The address has the same attributes as the boot information blob address described in
+    ///   5.4.3 Boot information address.
+    /// * Size field contains the length (in bytes) of boot information at the specified address.
+    ///
+    /// If in the Flags field, bit\[3:2\] = b’1,
+    /// * Size field contains the exact size of the value specified in this field.
+    /// * Size is >=1 bytes and <= 8 bytes.
+    pub(crate) contents: u64,
+}
+
+/// Table 5.9: Boot information header
+#[derive(Default, FromBytes, IntoBytes, KnownLayout, Immutable)]
+#[repr(C, packed)]
+pub(crate) struct boot_info_header {
+    /// Offset 0, length 4: Hexadecimal value 0x0FFA to identify the header
+    pub(crate) signature: u32,
+    /// Offset 4, length 4: Version of the boot information blob encoded as in FFA_VERSION_GET
+    pub(crate) version: u32,
+    /// Offset 8, length 4: Size of boot information blob spanning contiguous memory
+    pub(crate) boot_info_blob_size: u32,
+    /// Offset 12, length 4: Size of each boot information descriptor in the array
+    pub(crate) boot_info_desc_size: u32,
+    /// Offset 16, length 4: Count of boot information descriptors in the array
+    pub(crate) boot_info_desc_count: u32,
+    /// Offset 20, length 4: Offset to array of boot information descriptors
+    pub(crate) boot_info_array_offset: u32,
+    /// Offset 24, length 8: Reserved (MBZ)
+    pub(crate) reserved: u64,
+}
diff --git a/src/lib.rs b/src/lib.rs
index f9699aa..a331580 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -11,6 +11,7 @@
 use uuid::Uuid;
 
 pub mod boot_info;
+mod ffa_v1_1;
 pub mod memory_management;
 pub mod partition_info;