blob: d8441c037fada7a2f5c3c2023c86027dd77e8c74 [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
Tomás González83146af2025-03-04 11:32:41 +0000398 let header_version = header_raw
399 .version
400 .try_into()
401 .map_err(|_| Error::InvalidHeader)?;
Balint Dobszayde0dc802025-02-28 14:16:52 +0100402 if header_version != version {
403 return Err(Error::InvalidVersion(header_version));
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100404 }
405
406 Ok(header_raw)
407 }
408
Balint Dobszaya5846852025-02-26 15:38:53 +0100409 /// Get the size of the boot information blob spanning contiguous memory. This enables a
410 /// consumer to map all of the boot information blob in its translation regime or copy it to
411 /// another memory location without parsing each element in the boot information descriptor
412 /// array.
Balint Dobszayde0dc802025-02-28 14:16:52 +0100413 pub fn get_blob_size(version: Version, buf: &[u8]) -> Result<usize, Error> {
414 if !(Version(1, 1)..=Version(1, 2)).contains(&version) {
415 return Err(Error::InvalidVersion(version));
416 }
417
418 let header_raw = Self::get_header(version, buf)?;
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100419
420 Ok(header_raw.boot_info_blob_size as usize)
421 }
422}
423
Balint Dobszaya5846852025-02-26 15:38:53 +0100424/// Iterator of boot information descriptors.
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100425pub struct BootInfoIterator<'a> {
426 buf: &'a [u8],
427 offset: usize,
428 desc_count: usize,
429 desc_size: usize,
430}
431
432impl<'a> BootInfoIterator<'a> {
Balint Dobszaya5846852025-02-26 15:38:53 +0100433 /// Create an iterator of boot information descriptors from a buffer.
Balint Dobszayde0dc802025-02-28 14:16:52 +0100434 pub fn new(version: Version, buf: &'a [u8]) -> Result<Self, Error> {
435 let header_raw = BootInfo::get_header(version, buf)?;
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100436
437 if buf.len() < header_raw.boot_info_blob_size as usize {
438 return Err(Error::InvalidBufferSize);
439 }
440
441 if header_raw.boot_info_desc_size as usize != size_of::<boot_info_descriptor>() {
442 return Err(Error::MalformedDescriptor);
443 }
444
445 let Some(total_desc_size) = header_raw
446 .boot_info_desc_count
447 .checked_mul(header_raw.boot_info_desc_size)
448 .and_then(|x| x.checked_add(header_raw.boot_info_array_offset))
449 else {
450 return Err(Error::InvalidBufferSize);
451 };
452
453 if buf.len() < total_desc_size as usize {
454 return Err(Error::InvalidBufferSize);
455 }
456
457 Ok(Self {
458 buf,
459 offset: header_raw.boot_info_array_offset as usize,
460 desc_count: header_raw.boot_info_desc_count as usize,
461 desc_size: header_raw.boot_info_desc_size as usize,
462 })
463 }
464}
465
466impl<'a> Iterator for BootInfoIterator<'a> {
467 type Item = Result<BootInfo<'a>, Error>;
468
469 fn next(&mut self) -> Option<Self::Item> {
470 if self.desc_count > 0 {
471 let desc_offset = self.offset;
472 self.offset += self.desc_size;
473 self.desc_count -= 1;
474
475 let Ok(desc_raw) = boot_info_descriptor::ref_from_bytes(
476 &self.buf[desc_offset..desc_offset + self.desc_size],
477 ) else {
478 return Some(Err(Error::MalformedDescriptor));
479 };
480
481 if desc_raw.reserved != 0 {
482 return Some(Err(Error::MalformedDescriptor));
483 }
484
485 let typ: BootInfoType = match desc_raw.typ.try_into() {
486 Ok(v) => v,
487 Err(e) => return Some(Err(e)),
488 };
489
490 let flags: BootInfoFlags = match desc_raw.flags.try_into() {
491 Ok(v) => v,
492 Err(e) => return Some(Err(e)),
493 };
494
495 let name = match flags.name_format {
496 BootInfoNameFormat::String => {
Balint Dobszaye7578322025-06-13 19:36:34 +0200497 let Ok(name_str) = CStr::from_bytes_until_nul(desc_raw.name.as_bytes()) else {
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100498 return Some(Err(Error::InvalidName));
499 };
500 BootInfoName::NullTermString(name_str)
501 }
502 BootInfoNameFormat::Uuid => BootInfoName::Uuid(Uuid::from_bytes_le(desc_raw.name)),
503 };
504
505 let contents = match flags.contents_format {
506 BootInfoContentsFormat::Address => {
507 let contents = desc_raw.contents as usize;
508 let contents_size = desc_raw.size as usize;
509
510 let Some(offset) = contents.checked_sub(self.buf.as_ptr() as usize) else {
511 return Some(Err(Error::InvalidBufferSize));
512 };
513
514 let Some(offset_end) = offset.checked_add(contents_size) else {
515 return Some(Err(Error::InvalidBufferSize));
516 };
517
518 if self.buf.len() < offset_end {
519 return Some(Err(Error::InvalidBufferSize));
520 }
521
522 BootInfoContents::Address {
523 content_buf: &self.buf[offset..offset_end],
524 }
525 }
526
Imre Kisd7cd91c2025-04-11 17:05:38 +0200527 BootInfoContentsFormat::Value => {
528 let len = desc_raw.size as usize;
529 if (1..=8).contains(&len) {
530 BootInfoContents::Value {
531 val: desc_raw.contents,
532 len,
533 }
534 } else {
535 return Some(Err(Error::MalformedDescriptor));
536 }
537 }
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100538 };
539
540 return Some(Ok(BootInfo {
541 name,
542 typ,
543 contents,
544 }));
545 }
546
547 None
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200548 }
549}
550
551#[cfg(test)]
552mod tests {
553 use super::*;
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200554 use uuid::uuid;
555
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100556 // TODO: add tests with a known correct boot info blob
557
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200558 #[test]
559 fn boot_info() {
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100560 let desc1 = BootInfo {
561 name: BootInfoName::NullTermString(c"test1234test123"),
562 typ: BootInfoType::Impdef(BootInfoImpdefId(0x2b)),
563 contents: BootInfoContents::Value {
564 val: 0xdeadbeef,
565 len: 4,
566 },
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200567 };
568
569 let fdt = [0u8; 0xff];
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100570 let desc2 = BootInfo {
571 name: BootInfoName::Uuid(uuid!("12345678-abcd-dcba-1234-123456789abc")),
572 typ: BootInfoType::Std(BootInfoStdId::Fdt),
573 contents: BootInfoContents::Address { content_buf: &fdt },
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200574 };
575
576 let mut buf = [0u8; 0x1ff];
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100577 let buf_addr = buf.as_ptr() as usize;
Balint Dobszayde0dc802025-02-28 14:16:52 +0100578 BootInfo::pack(
579 Version(1, 1),
580 &[desc1.clone(), desc2.clone()],
581 &mut buf,
582 Some(buf_addr),
583 );
584 let mut descriptors = BootInfoIterator::new(Version(1, 1), &buf).unwrap();
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100585 let desc1_check = descriptors.next().unwrap().unwrap();
586 let desc2_check = descriptors.next().unwrap().unwrap();
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200587
Balint Dobszayb2ff2bc2024-12-19 18:59:38 +0100588 assert_eq!(desc1, desc1_check);
589 assert_eq!(desc2, desc2_check);
Balint Dobszay5bf492f2024-07-29 17:21:32 +0200590 }
591}