Initial version

Add some common FF-A functionality. Supported features (roughly):
  - define FF-A function IDs and error codes
  - create boot info descriptor
  - create partition info descriptor
  - create and parse memory transaction descriptor
  - parse memory relinquish descriptor
  - parse console log message

Limitations apply, code quality is not production ready.

Signed-off-by: Balint Dobszay <balint.dobszay@arm.com>
Change-Id: Ibb3fde9d4c0d0d9b8823bb84cdcf23eb3f9d087a
diff --git a/src/boot_info.rs b/src/boot_info.rs
new file mode 100644
index 0000000..553c7c5
--- /dev/null
+++ b/src/boot_info.rs
@@ -0,0 +1,182 @@
+// SPDX-FileCopyrightText: Copyright 2023 Arm Limited and/or its affiliates <open-source-office@arm.com>
+// SPDX-License-Identifier: MIT OR Apache-2.0
+
+use uuid::Uuid;
+
+pub enum BootInfoName {
+    NullTermString(&'static str),
+    Uuid(Uuid),
+}
+
+#[derive(Clone, Copy)]
+pub enum BootInfoStdType {
+    Fdt = 0,
+    Hob = 1,
+}
+
+pub enum BootInfoType {
+    Std(BootInfoStdType),
+    Impdef(u8),
+}
+
+pub enum BootInfoContents {
+    Address(usize),
+    Value(u64),
+}
+
+pub struct BootInfoDescriptor {
+    /// Name of boot information passed to the consumer
+    pub name: BootInfoName,
+
+    /// Type of boot information passed to the consumer
+    pub typ: BootInfoType,
+
+    /// Size (in bytes) of boot information identified by the Name and Type fields
+    pub size: u32,
+
+    pub contents: BootInfoContents,
+}
+
+impl BootInfoDescriptor {
+    pub fn create(descriptors: &[BootInfoDescriptor], buf: &mut [u8]) {
+        // 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;
+
+        /// In FF-A v1.1, Table 5.8: Boot information descriptor is 32 bytes long
+        const DESC_SIZE: usize = 32;
+
+        // 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
+            .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);
+
+        // Fill the boot info descriptor array, all offset based from DESC_ARRAY_OFFSET
+        let mut offset = DESC_ARRAY_OFFSET;
+        for desc in descriptors {
+            // Offset 0, length 16: Name of boot information passed to the consumer
+            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
+                }
+                BootInfoName::Uuid(uuid) => {
+                    buf[offset..offset + 16].copy_from_slice(&uuid.to_bytes_le());
+                }
+            }
+
+            // 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;
+
+            // 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());
+
+            // 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();
+                }
+                BootInfoContents::Value(_) => {
+                    assert!((1..=8).contains(&desc.size));
+                }
+            }
+            buf[offset + 20..offset + 24].copy_from_slice(&desc.size.to_le_bytes());
+
+            // 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,
+            };
+            buf[offset + 24..offset + 32].copy_from_slice(&content.to_le_bytes());
+
+            offset += DESC_SIZE;
+        }
+
+        // TODO: add padding size between boot information referenced by the descriptors
+
+        // 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());
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    use uuid::uuid;
+
+    #[test]
+    fn boot_info() {
+        let desc1 = BootInfoDescriptor {
+            name: BootInfoName::NullTermString(&"test1234test1234"),
+            typ: BootInfoType::Impdef(0xab),
+            size: 4,
+            contents: BootInfoContents::Value(0xbeef),
+        };
+
+        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 mut buf = [0u8; 0x1ff];
+        BootInfoDescriptor::create(&[desc1, desc2], &mut buf);
+
+        println!("{:#x?}", &buf[0..0x0f]);
+    }
+}