blob: 553c7c553987bec6c32d3f3ef6eb27ea309ec61a [file] [log] [blame]
Balint Dobszay5bf492f2024-07-29 17:21:32 +02001// SPDX-FileCopyrightText: Copyright 2023 Arm Limited and/or its affiliates <open-source-office@arm.com>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4use uuid::Uuid;
5
6pub enum BootInfoName {
7 NullTermString(&'static str),
8 Uuid(Uuid),
9}
10
11#[derive(Clone, Copy)]
12pub enum BootInfoStdType {
13 Fdt = 0,
14 Hob = 1,
15}
16
17pub enum BootInfoType {
18 Std(BootInfoStdType),
19 Impdef(u8),
20}
21
22pub enum BootInfoContents {
23 Address(usize),
24 Value(u64),
25}
26
27pub struct BootInfoDescriptor {
28 /// Name of boot information passed to the consumer
29 pub name: BootInfoName,
30
31 /// Type of boot information passed to the consumer
32 pub typ: BootInfoType,
33
34 /// Size (in bytes) of boot information identified by the Name and Type fields
35 pub size: u32,
36
37 pub contents: BootInfoContents,
38}
39
40impl BootInfoDescriptor {
41 pub fn create(descriptors: &[BootInfoDescriptor], buf: &mut [u8]) {
42 // Offset from the base of the header to the first element in the boot info descriptor array
43 // Must be 8 byte aligned
44 const DESC_ARRAY_OFFSET: usize = 32;
45
46 /// In FF-A v1.1, Table 5.8: Boot information descriptor is 32 bytes long
47 const DESC_SIZE: usize = 32;
48
49 // assert!(descriptors.len() <= u32::MAX as usize);
50 let desc_cnt = descriptors.len();
51
52 // Add the already known fields, later we have to add the sizes referenced by the individual descriptors
53 let mut total_size = 0usize;
54 total_size = total_size.checked_add(DESC_ARRAY_OFFSET).unwrap();
55 total_size = total_size
56 .checked_add(desc_cnt.checked_mul(DESC_SIZE).unwrap())
57 .unwrap();
58
59 // Create the boot info header starting at offset 0 in the buffer
60 // Offset 0, length 4: Hexadecimal value 0x0FFA to identify the header
61 buf[0..4].copy_from_slice(&0x0ffa_u32.to_le_bytes());
62
63 // Offset 4, length 4: Version of the boot information blob encoded as in FFA_VERSION_GET
64 buf[4..8].copy_from_slice(&0x0001_0001_u32.to_le_bytes());
65
66 // Offset 12, length 4: Size of each boot information descriptor in the array
67 buf[12..16].copy_from_slice(&(DESC_SIZE as u32).to_le_bytes());
68
69 // Offset 16, length 4: Count of boot information descriptors in the array
70 buf[16..20].copy_from_slice(&(desc_cnt as u32).to_le_bytes());
71
72 // Offset 20, length 4: Offset to array of boot information descriptors
73 buf[20..24].copy_from_slice(&(DESC_ARRAY_OFFSET as u32).to_le_bytes());
74
75 // Offset 24, length 8: Reserved (MBZ)
76 buf[24..32].fill(0);
77
78 // Fill the boot info descriptor array, all offset based from DESC_ARRAY_OFFSET
79 let mut offset = DESC_ARRAY_OFFSET;
80 for desc in descriptors {
81 // Offset 0, length 16: Name of boot information passed to the consumer
82 match &desc.name {
83 BootInfoName::NullTermString(name) => {
84 assert!(name.is_ascii());
85 let name_len = name.len().min(15);
86 buf[offset..offset + name_len].copy_from_slice(&name.as_bytes()[..name_len]);
87 buf[offset + name_len..offset + 16].fill(0); // Make sure it's null terminated
88 }
89 BootInfoName::Uuid(uuid) => {
90 buf[offset..offset + 16].copy_from_slice(&uuid.to_bytes_le());
91 }
92 }
93
94 // Offset 16, length 1: Type of boot information passed to the consumer
95 let info_type = match desc.typ {
96 BootInfoType::Std(std_type) => (std_type as u8) & 0b0111_1111,
97 BootInfoType::Impdef(typ) => (0b1 << 7) | typ,
98 };
99 buf[offset + 16] = info_type;
100
101 // Offset 17, length 1: Reserved (MBZ)
102 buf[offset + 17] = 0;
103
104 // Offset 18, length 2: Flags to describe properties of boot information associated with this descriptor
105 let mut flags = 0u16;
106 if let BootInfoName::Uuid(_) = &desc.name {
107 flags |= 0b1;
108 }
109 if let BootInfoContents::Value(_) = desc.contents {
110 flags |= 0b1 << 2;
111 }
112 buf[offset + 18..offset + 20].copy_from_slice(&flags.to_le_bytes());
113
114 // Offset 20, length 4: Size (in bytes) of boot information identified by the Name and Type fields.
115 match desc.contents {
116 BootInfoContents::Address(_) => {
117 total_size = total_size.checked_add(desc.size as usize).unwrap();
118 }
119 BootInfoContents::Value(_) => {
120 assert!((1..=8).contains(&desc.size));
121 }
122 }
123 buf[offset + 20..offset + 24].copy_from_slice(&desc.size.to_le_bytes());
124
125 // Offset 24, length 8: Value or address of boot information identified by the Name and Type fields.
126 // Value or address of boot information identified by the Name and Type fields.
127 //
128 // If in the Flags field, bit\[3:2\] = b'0,
129 // * The address has the same attributes as the boot information blob address described in
130 // 5.4.3 Boot information address.
131 // * Size field contains the length (in bytes) of boot information at the specified address.
132 //
133 // If in the Flags field, bit\[3:2\] = b’1,
134 // * Size field contains the exact size of the value specified in this field.
135 // * Size is >=1 bytes and <= 8 bytes.
136 let content = match desc.contents {
137 BootInfoContents::Address(addr) => addr as u64,
138 BootInfoContents::Value(val) => val,
139 };
140 buf[offset + 24..offset + 32].copy_from_slice(&content.to_le_bytes());
141
142 offset += DESC_SIZE;
143 }
144
145 // TODO: add padding size between boot information referenced by the descriptors
146
147 // Offset 8, length 4: Size of boot information blob spanning contiguous memory
148 assert!(buf.len() <= u32::MAX as usize);
149 assert!(total_size <= buf.len());
150 buf[8..12].copy_from_slice(&(total_size as u32).to_le_bytes());
151 }
152}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157
158 use uuid::uuid;
159
160 #[test]
161 fn boot_info() {
162 let desc1 = BootInfoDescriptor {
163 name: BootInfoName::NullTermString(&"test1234test1234"),
164 typ: BootInfoType::Impdef(0xab),
165 size: 4,
166 contents: BootInfoContents::Value(0xbeef),
167 };
168
169 let fdt = [0u8; 0xff];
170 let desc2 = BootInfoDescriptor {
171 name: BootInfoName::Uuid(uuid!("12345678-1234-1234-1234-123456789abc")),
172 typ: BootInfoType::Std(BootInfoStdType::Fdt),
173 size: 0xff,
174 contents: BootInfoContents::Address(&fdt as *const u8 as usize),
175 };
176
177 let mut buf = [0u8; 0x1ff];
178 BootInfoDescriptor::create(&[desc1, desc2], &mut buf);
179
180 println!("{:#x?}", &buf[0..0x0f]);
181 }
182}