blob: 553c7c553987bec6c32d3f3ef6eb27ea309ec61a [file] [log] [blame]
// 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]);
}
}