blob: 9d902cf523cd8e297c4283d577968289c05e5ab8 [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
Balint Dobszaya5846852025-02-26 15:38:53 +01004//! Implementation of the FF-A Boot information protocol.
5//!
6//! An SP or SPMC could rely on boot information for their initialization e.g. a flattened device
7//! tree with nodes to describe the devices and memory regions assigned to the SP or SPMC. FF-A
8//! specifies a protocol that can be used by a producer to pass boot information to a consumer at a
9//! Secure FF-A instance. The Framework assumes that the boot information protocol is used by a
10//! producer and consumer pair that reside at adjacent exception levels as listed below.
11//! - SPMD (producer) and an SPMC (consumer) in either S-EL1 or S-EL2.
12//! - An SPMC (producer) and SP (consumer) pair listed below.
13//! - EL3 SPMC and a Logical S-EL1 SP.
14//! - S-EL2 SPMC and Physical S-EL1 SP.
15//! - EL3 SPMC and a S-EL0 SP.
16//! - S-EL2 SPMC and a S-EL0 SP.
17//! - S-EL1 SPMC and a S-EL0 SP.
18
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +010019use core::ffi::CStr;
20use thiserror::Error;
Balint Dobszay5bf492f2024-07-29 17:21:32 +020021use uuid::Uuid;
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +010022use zerocopy::{FromBytes, IntoBytes};
Balint Dobszay5bf492f2024-07-29 17:21:32 +020023
Balint Dobszayde0dc802025-02-28 14:16:52 +010024// This module uses FF-A v1.1 types by default.
25// FF-A v1.2 didn't introduce any changes to the data stuctures used by this module.
26use crate::{
27 ffa_v1_1::{boot_info_descriptor, boot_info_header},
28 Version,
29};
30
Balint Dobszaya5846852025-02-26 15:38:53 +010031/// Rich error types returned by this module. Should be converted to [`crate::FfaError`] when used
32/// with the `FFA_ERROR` interface.
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +010033#[derive(Debug, Error)]
34pub enum Error {
35 #[error("Invalid standard type {0}")]
36 InvalidStdType(u8),
37 #[error("Invalid type {0}")]
38 InvalidType(u8),
39 #[error("Invalid contents format {0}")]
40 InvalidContentsFormat(u16),
41 #[error("Invalid name format {0}")]
42 InvalidNameFormat(u16),
43 #[error("Invalid name")]
44 InvalidName,
45 #[error("Invalid flags {0}")]
46 InvalidFlags(u16),
47 #[error("Invalid header size or alignment")]
48 InvalidHeader,
49 #[error("Invalid buffer size")]
50 InvalidBufferSize,
51 #[error("Invalid signature")]
52 InvalidSignature,
53 #[error("Invalid version {0}")]
54 InvalidVersion(Version),
55 #[error("Malformed descriptor")]
56 MalformedDescriptor,
57}
58
59impl From<Error> for crate::FfaError {
60 fn from(_value: Error) -> Self {
61 Self::InvalidParameters
62 }
63}
64
Balint Dobszaya5846852025-02-26 15:38:53 +010065/// Name of boot information descriptor.
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +010066#[derive(Clone, Debug, PartialEq, Eq)]
67pub enum BootInfoName<'a> {
68 NullTermString(&'a CStr),
Balint Dobszay5bf492f2024-07-29 17:21:32 +020069 Uuid(Uuid),
70}
71
Balint Dobszaya5846852025-02-26 15:38:53 +010072/// ID for supported standard boot information types.
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +010073#[derive(Clone, Copy, Debug, PartialEq, Eq)]
74#[repr(u8)]
75pub enum BootInfoStdId {
76 Fdt = Self::FDT,
77 Hob = Self::HOB,
Balint Dobszay5bf492f2024-07-29 17:21:32 +020078}
79
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +010080impl TryFrom<u8> for BootInfoStdId {
81 type Error = Error;
82
83 fn try_from(value: u8) -> Result<Self, Self::Error> {
84 match value {
85 Self::FDT => Ok(BootInfoStdId::Fdt),
86 Self::HOB => Ok(BootInfoStdId::Hob),
87 _ => Err(Error::InvalidStdType(value)),
88 }
89 }
90}
91
92impl BootInfoStdId {
93 const FDT: u8 = 0;
94 const HOB: u8 = 1;
95}
96
Balint Dobszaya5846852025-02-26 15:38:53 +010097/// ID for implementation defined boot information type.
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +010098#[derive(Clone, Copy, Debug, PartialEq, Eq)]
99pub struct BootInfoImpdefId(pub u8);
100
101impl From<u8> for BootInfoImpdefId {
102 fn from(value: u8) -> Self {
103 Self(value)
104 }
105}
106
Balint Dobszaya5846852025-02-26 15:38:53 +0100107/// Boot information type.
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100108#[derive(Clone, Copy, Debug, PartialEq, Eq)]
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200109pub enum BootInfoType {
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100110 Std(BootInfoStdId),
111 Impdef(BootInfoImpdefId),
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200112}
113
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100114impl TryFrom<u8> for BootInfoType {
115 type Error = Error;
116
117 fn try_from(value: u8) -> Result<Self, Self::Error> {
118 match (value >> Self::TYPE_SHIFT) & Self::TYPE_MASK {
119 Self::TYPE_STANDARD => Ok(BootInfoType::Std((value & Self::ID_MASK).try_into()?)),
120 Self::TYPE_IMPDEF => Ok(BootInfoType::Impdef((value & Self::ID_MASK).into())),
121 _ => Err(Error::InvalidType(value)),
122 }
123 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200124}
125
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100126impl From<BootInfoType> for u8 {
127 fn from(value: BootInfoType) -> Self {
128 match value {
129 BootInfoType::Std(std_type) => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +0100130 std_type as u8 | (BootInfoType::TYPE_STANDARD << BootInfoType::TYPE_SHIFT)
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100131 }
132 BootInfoType::Impdef(impdef_type) => {
Balint Dobszaye9a3e762025-02-26 17:29:57 +0100133 impdef_type.0 | (BootInfoType::TYPE_IMPDEF << BootInfoType::TYPE_SHIFT)
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100134 }
135 }
136 }
137}
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200138
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100139impl BootInfoType {
140 // This field contains the boot info type at bit[7] and the boot info identifier in bits[6:0]
141 const TYPE_SHIFT: usize = 7;
142 const TYPE_MASK: u8 = 0b1;
143 const TYPE_STANDARD: u8 = 0b0;
144 const TYPE_IMPDEF: u8 = 0b1;
145 // Mask for boot info identifier in bits[6:0]
146 const ID_MASK: u8 = 0b0111_1111;
147}
148
Balint Dobszaya5846852025-02-26 15:38:53 +0100149/// Boot information contents.
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100150#[derive(Clone, Copy, Debug, PartialEq, Eq)]
151pub enum BootInfoContents<'a> {
152 Address { content_buf: &'a [u8] },
153 Value { val: u64, len: usize },
154}
155
156#[derive(Clone, Copy, Debug, PartialEq, Eq)]
157#[repr(u16)]
158enum BootInfoContentsFormat {
159 Address = Self::ADDRESS << Self::SHIFT,
160 Value = Self::VALUE << Self::SHIFT,
161}
162
163impl TryFrom<u16> for BootInfoContentsFormat {
164 type Error = Error;
165
166 fn try_from(value: u16) -> Result<Self, Self::Error> {
167 match (value >> Self::SHIFT) & Self::MASK {
168 Self::ADDRESS => Ok(BootInfoContentsFormat::Address),
169 Self::VALUE => Ok(BootInfoContentsFormat::Value),
170 _ => Err(Error::InvalidContentsFormat(value)),
171 }
172 }
173}
174
175impl BootInfoContentsFormat {
176 const SHIFT: usize = 2;
177 const MASK: u16 = 0b11;
178 const ADDRESS: u16 = 0b00;
179 const VALUE: u16 = 0b01;
180}
181
182#[derive(Clone, Copy, Debug, PartialEq, Eq)]
183#[repr(u16)]
184enum BootInfoNameFormat {
185 String = Self::STRING << Self::SHIFT,
186 Uuid = Self::UUID << Self::SHIFT,
187}
188
189impl TryFrom<u16> for BootInfoNameFormat {
190 type Error = Error;
191
192 fn try_from(value: u16) -> Result<Self, Self::Error> {
193 match (value >> Self::SHIFT) & Self::MASK {
194 Self::STRING => Ok(BootInfoNameFormat::String),
195 Self::UUID => Ok(BootInfoNameFormat::Uuid),
196 _ => Err(Error::InvalidNameFormat(value)),
197 }
198 }
199}
200
201impl BootInfoNameFormat {
202 const SHIFT: usize = 0;
203 const MASK: u16 = 0b11;
204 const STRING: u16 = 0b00;
205 const UUID: u16 = 0b01;
206}
207
208#[derive(Clone, Copy, Debug, PartialEq, Eq)]
209struct BootInfoFlags {
210 contents_format: BootInfoContentsFormat,
211 name_format: BootInfoNameFormat,
212}
213
214impl TryFrom<u16> for BootInfoFlags {
215 type Error = Error;
216
217 fn try_from(value: u16) -> Result<Self, Self::Error> {
218 // bits[15:4]: Reserved (MBZ)
219 if value >> 4 == 0 {
220 Ok(Self {
221 contents_format: BootInfoContentsFormat::try_from(value)?,
222 name_format: BootInfoNameFormat::try_from(value)?,
223 })
224 } else {
225 Err(Error::InvalidFlags(value))
226 }
227 }
228}
229
230impl From<BootInfoFlags> for u16 {
231 fn from(value: BootInfoFlags) -> Self {
232 value.contents_format as u16 | value.name_format as u16
233 }
234}
235
Balint Dobszaya5846852025-02-26 15:38:53 +0100236/// Boot information descriptor.
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100237#[derive(Clone, Debug, PartialEq, Eq)]
238pub struct BootInfo<'a> {
239 pub name: BootInfoName<'a>,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200240 pub typ: BootInfoType,
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100241 pub contents: BootInfoContents<'a>,
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200242}
243
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100244impl BootInfo<'_> {
Balint Dobszaya5846852025-02-26 15:38:53 +0100245 /// Serialize a list of boot information descriptors into a buffer. The `mapped_addr` parameter
246 /// should contain the address of the buffer in the consumers translation regime (typically a
247 /// virtual address where the buffer is mapped to). This is necessary since there are
248 /// self-references within the serialized data structure which must be described with an
249 /// absolute address according to the FF-A spec.
Balint Dobszayde0dc802025-02-28 14:16:52 +0100250 pub fn pack(
251 version: Version,
252 descriptors: &[BootInfo],
253 buf: &mut [u8],
254 mapped_addr: Option<usize>,
255 ) {
256 assert!((Version(1, 1)..=Version(1, 2)).contains(&version));
257
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200258 // Offset from the base of the header to the first element in the boot info descriptor array
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100259 // Must be 8 byte aligned, but otherwise we're free to choose any value here.
Balint Dobszaya5846852025-02-26 15:38:53 +0100260 // Let's just pack the array right after the header.
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100261 const DESC_ARRAY_OFFSET: usize = size_of::<boot_info_header>().next_multiple_of(8);
262 const DESC_SIZE: usize = size_of::<boot_info_descriptor>();
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200263
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100264 assert!(buf.len() <= u32::MAX as usize);
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200265
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200266 let desc_cnt = descriptors.len();
267
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100268 // Add the already known fields, later we have to add the sizes referenced by the individual
269 // descriptors
270
271 // The Size of boot information blob field specifies the size of the blob that spans one or
272 // more contiguous 4K pages used by the producer to populate it. It is calculated by adding
273 // the following values:
274 // 1. Boot information descriptor array offset
275 // 2. Product of Boot information descriptor count and Boot information descriptor size.
276 // 3. Total size of all boot information referenced by boot information descriptors.
277 // This is determined by adding the values in the Size field of each boot information
278 // descriptor whose Contents field contains an address.
279 // 4. Any padding between,
280 // 1. The boot information descriptor array and the boot information referenced from it.
281 // 2. Distinct instances of boot information referenced from the boot information
282 // descriptor array.
283 let mut total_offset = 0usize;
284
285 // No. 1 from the "Size of boot information blob" list
286 total_offset = total_offset.checked_add(DESC_ARRAY_OFFSET).unwrap();
287
288 // No. 2 from the "Size of boot information blob" list
289 total_offset = total_offset
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200290 .checked_add(desc_cnt.checked_mul(DESC_SIZE).unwrap())
291 .unwrap();
292
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100293 // Fail early if the buffer is too small
294 assert!(total_offset <= buf.len());
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200295
296 // Fill the boot info descriptor array, all offset based from DESC_ARRAY_OFFSET
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100297 let mut desc_array_offset = DESC_ARRAY_OFFSET;
298
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200299 for desc in descriptors {
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100300 let mut desc_raw = boot_info_descriptor::default();
301
302 let name_format = match &desc.name {
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200303 BootInfoName::NullTermString(name) => {
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100304 // count_bytes() doesn't include nul terminator
305 let name_len = name.count_bytes().min(15);
306 desc_raw.name[..name_len].copy_from_slice(&name.to_bytes()[..name_len]);
307 // Add nul terminator and zero fill the rest
308 desc_raw.name[name_len..].fill(0);
309
310 BootInfoNameFormat::String
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200311 }
312 BootInfoName::Uuid(uuid) => {
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100313 desc_raw.name.copy_from_slice(&uuid.to_bytes_le());
314 BootInfoNameFormat::Uuid
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200315 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200316 };
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200317
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100318 let contents_format = match desc.contents {
319 BootInfoContents::Address { content_buf } => {
320 // We have to copy the contents referenced by the boot info descriptor into the
321 // boot info blob. At this offset we're after the boot info header and all of
322 // the boot info descriptors. The contents referenced from the individual boot
323 // info descriptors will get copied to this starting address. The 8 byte
324 // alignment is not explicitly mentioned by the spec, but it's better to have it
325 // anyway.
326 // No. 4 from the "Size of boot information blob" list
327 total_offset = total_offset.next_multiple_of(8);
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200328
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100329 // The mapped_addr argument contains the address where buf is mapped to in the
330 // consumer's translation regime. If it's None, we assume identity mapping is
331 // used, so the buffer's address stays the same.
332 let buf_addr = mapped_addr.unwrap_or(buf.as_ptr() as usize);
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200333
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100334 // The content's address in the consumer's translation regime will be the
335 // buffer's address in the consumer's translation regime plus the offset of the
336 // content within the boot info blob.
337 let content_addr = buf_addr.checked_add(total_offset).unwrap();
338
339 // Check if the content fits before copying
340 let content_len = content_buf.len();
341 total_offset.checked_add(content_len).unwrap();
342
343 // Do the copy and increase the total size
344 // No. 3 from the "Size of boot information blob" list
345 buf[total_offset..total_offset + content_len].copy_from_slice(content_buf);
346 total_offset += content_len;
347
348 desc_raw.contents = content_addr as u64;
349 desc_raw.size = content_len as u32;
350
351 BootInfoContentsFormat::Address
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200352 }
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100353 BootInfoContents::Value { val, len } => {
354 assert!((1..=8).contains(&len));
355 desc_raw.contents = val;
356 desc_raw.size = len as u32;
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200357
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100358 BootInfoContentsFormat::Value
359 }
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200360 };
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200361
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100362 let flags = BootInfoFlags {
363 contents_format,
364 name_format,
365 };
366
367 desc_raw.flags = flags.into();
368 desc_raw.typ = desc.typ.into();
369
370 desc_raw
371 .write_to_prefix(&mut buf[desc_array_offset..])
372 .unwrap();
373 desc_array_offset += DESC_SIZE;
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200374 }
375
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100376 let header_raw = boot_info_header {
377 signature: 0x0ffa,
Balint Dobszayde0dc802025-02-28 14:16:52 +0100378 version: version.into(),
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100379 boot_info_blob_size: total_offset as u32,
380 boot_info_desc_size: DESC_SIZE as u32,
381 boot_info_desc_count: desc_cnt as u32,
382 boot_info_array_offset: DESC_ARRAY_OFFSET as u32,
383 reserved: 0,
384 };
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200385
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100386 header_raw.write_to_prefix(buf).unwrap();
387 }
388
389 /// Validate and return the boot information header
Balint Dobszayde0dc802025-02-28 14:16:52 +0100390 fn get_header(version: Version, buf: &[u8]) -> Result<&boot_info_header, Error> {
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100391 let (header_raw, _) =
392 boot_info_header::ref_from_prefix(buf).map_err(|_| Error::InvalidHeader)?;
393
394 if header_raw.signature != 0x0ffa {
395 return Err(Error::InvalidSignature);
396 }
397
Balint Dobszayde0dc802025-02-28 14:16:52 +0100398 let header_version = header_raw.version.into();
399 if header_version != version {
400 return Err(Error::InvalidVersion(header_version));
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100401 }
402
403 Ok(header_raw)
404 }
405
Balint Dobszaya5846852025-02-26 15:38:53 +0100406 /// Get the size of the boot information blob spanning contiguous memory. This enables a
407 /// consumer to map all of the boot information blob in its translation regime or copy it to
408 /// another memory location without parsing each element in the boot information descriptor
409 /// array.
Balint Dobszayde0dc802025-02-28 14:16:52 +0100410 pub fn get_blob_size(version: Version, buf: &[u8]) -> Result<usize, Error> {
411 if !(Version(1, 1)..=Version(1, 2)).contains(&version) {
412 return Err(Error::InvalidVersion(version));
413 }
414
415 let header_raw = Self::get_header(version, buf)?;
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100416
417 Ok(header_raw.boot_info_blob_size as usize)
418 }
419}
420
Balint Dobszaya5846852025-02-26 15:38:53 +0100421/// Iterator of boot information descriptors.
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100422pub struct BootInfoIterator<'a> {
423 buf: &'a [u8],
424 offset: usize,
425 desc_count: usize,
426 desc_size: usize,
427}
428
429impl<'a> BootInfoIterator<'a> {
Balint Dobszaya5846852025-02-26 15:38:53 +0100430 /// Create an iterator of boot information descriptors from a buffer.
Balint Dobszayde0dc802025-02-28 14:16:52 +0100431 pub fn new(version: Version, buf: &'a [u8]) -> Result<Self, Error> {
432 let header_raw = BootInfo::get_header(version, buf)?;
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100433
434 if buf.len() < header_raw.boot_info_blob_size as usize {
435 return Err(Error::InvalidBufferSize);
436 }
437
438 if header_raw.boot_info_desc_size as usize != size_of::<boot_info_descriptor>() {
439 return Err(Error::MalformedDescriptor);
440 }
441
442 let Some(total_desc_size) = header_raw
443 .boot_info_desc_count
444 .checked_mul(header_raw.boot_info_desc_size)
445 .and_then(|x| x.checked_add(header_raw.boot_info_array_offset))
446 else {
447 return Err(Error::InvalidBufferSize);
448 };
449
450 if buf.len() < total_desc_size as usize {
451 return Err(Error::InvalidBufferSize);
452 }
453
454 Ok(Self {
455 buf,
456 offset: header_raw.boot_info_array_offset as usize,
457 desc_count: header_raw.boot_info_desc_count as usize,
458 desc_size: header_raw.boot_info_desc_size as usize,
459 })
460 }
461}
462
463impl<'a> Iterator for BootInfoIterator<'a> {
464 type Item = Result<BootInfo<'a>, Error>;
465
466 fn next(&mut self) -> Option<Self::Item> {
467 if self.desc_count > 0 {
468 let desc_offset = self.offset;
469 self.offset += self.desc_size;
470 self.desc_count -= 1;
471
472 let Ok(desc_raw) = boot_info_descriptor::ref_from_bytes(
473 &self.buf[desc_offset..desc_offset + self.desc_size],
474 ) else {
475 return Some(Err(Error::MalformedDescriptor));
476 };
477
478 if desc_raw.reserved != 0 {
479 return Some(Err(Error::MalformedDescriptor));
480 }
481
482 let typ: BootInfoType = match desc_raw.typ.try_into() {
483 Ok(v) => v,
484 Err(e) => return Some(Err(e)),
485 };
486
487 let flags: BootInfoFlags = match desc_raw.flags.try_into() {
488 Ok(v) => v,
489 Err(e) => return Some(Err(e)),
490 };
491
492 let name = match flags.name_format {
493 BootInfoNameFormat::String => {
494 let Ok(name_str) = CStr::from_bytes_with_nul(desc_raw.name.as_bytes()) else {
495 return Some(Err(Error::InvalidName));
496 };
497 BootInfoName::NullTermString(name_str)
498 }
499 BootInfoNameFormat::Uuid => BootInfoName::Uuid(Uuid::from_bytes_le(desc_raw.name)),
500 };
501
502 let contents = match flags.contents_format {
503 BootInfoContentsFormat::Address => {
504 let contents = desc_raw.contents as usize;
505 let contents_size = desc_raw.size as usize;
506
507 let Some(offset) = contents.checked_sub(self.buf.as_ptr() as usize) else {
508 return Some(Err(Error::InvalidBufferSize));
509 };
510
511 let Some(offset_end) = offset.checked_add(contents_size) else {
512 return Some(Err(Error::InvalidBufferSize));
513 };
514
515 if self.buf.len() < offset_end {
516 return Some(Err(Error::InvalidBufferSize));
517 }
518
519 BootInfoContents::Address {
520 content_buf: &self.buf[offset..offset_end],
521 }
522 }
523
524 BootInfoContentsFormat::Value => BootInfoContents::Value {
525 val: desc_raw.contents,
526 len: desc_raw.size as usize,
527 },
528 };
529
530 return Some(Ok(BootInfo {
531 name,
532 typ,
533 contents,
534 }));
535 }
536
537 None
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200538 }
539}
540
541#[cfg(test)]
542mod tests {
543 use super::*;
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200544 use uuid::uuid;
545
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100546 // TODO: add tests with a known correct boot info blob
547
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200548 #[test]
549 fn boot_info() {
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100550 let desc1 = BootInfo {
551 name: BootInfoName::NullTermString(c"test1234test123"),
552 typ: BootInfoType::Impdef(BootInfoImpdefId(0x2b)),
553 contents: BootInfoContents::Value {
554 val: 0xdeadbeef,
555 len: 4,
556 },
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200557 };
558
559 let fdt = [0u8; 0xff];
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100560 let desc2 = BootInfo {
561 name: BootInfoName::Uuid(uuid!("12345678-abcd-dcba-1234-123456789abc")),
562 typ: BootInfoType::Std(BootInfoStdId::Fdt),
563 contents: BootInfoContents::Address { content_buf: &fdt },
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200564 };
565
566 let mut buf = [0u8; 0x1ff];
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100567 let buf_addr = buf.as_ptr() as usize;
Balint Dobszayde0dc802025-02-28 14:16:52 +0100568 BootInfo::pack(
569 Version(1, 1),
570 &[desc1.clone(), desc2.clone()],
571 &mut buf,
572 Some(buf_addr),
573 );
574 let mut descriptors = BootInfoIterator::new(Version(1, 1), &buf).unwrap();
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100575 let desc1_check = descriptors.next().unwrap().unwrap();
576 let desc2_check = descriptors.next().unwrap().unwrap();
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200577
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100578 assert_eq!(desc1, desc1_check);
579 assert_eq!(desc2, desc2_check);
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200580 }
581}