1use core::ffi::CStr;
20use thiserror::Error;
21use uuid::Uuid;
22use zerocopy::{FromBytes, IntoBytes};
23
24use crate::{
27 ffa_v1_1::{boot_info_descriptor, boot_info_header},
28 UuidHelper, Version,
29};
30
31#[derive(Debug, Error, PartialEq, Eq, Clone, Copy)]
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
65#[derive(Clone, Debug, PartialEq, Eq)]
67pub enum BootInfoName<'a> {
68 NullTermString(&'a CStr),
69 Uuid(Uuid),
70}
71
72#[derive(Clone, Copy, Debug, PartialEq, Eq)]
74#[repr(u8)]
75pub enum BootInfoStdId {
76 Fdt = Self::FDT,
77 Hob = Self::HOB,
78}
79
80impl 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
97#[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
107#[derive(Clone, Copy, Debug, PartialEq, Eq)]
109pub enum BootInfoType {
110 Std(BootInfoStdId),
111 Impdef(BootInfoImpdefId),
112}
113
114impl 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 }
124}
125
126impl From<BootInfoType> for u8 {
127 fn from(value: BootInfoType) -> Self {
128 match value {
129 BootInfoType::Std(std_type) => {
130 std_type as u8 | (BootInfoType::TYPE_STANDARD << BootInfoType::TYPE_SHIFT)
131 }
132 BootInfoType::Impdef(impdef_type) => {
133 impdef_type.0 | (BootInfoType::TYPE_IMPDEF << BootInfoType::TYPE_SHIFT)
134 }
135 }
136 }
137}
138
139impl BootInfoType {
140 const TYPE_SHIFT: usize = 7;
142 const TYPE_MASK: u8 = 0b1;
143 const TYPE_STANDARD: u8 = 0b0;
144 const TYPE_IMPDEF: u8 = 0b1;
145 const ID_MASK: u8 = 0b0111_1111;
147}
148
149#[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 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
236#[derive(Clone, Debug, PartialEq, Eq)]
238pub struct BootInfo<'a> {
239 pub name: BootInfoName<'a>,
240 pub typ: BootInfoType,
241 pub contents: BootInfoContents<'a>,
242}
243
244impl BootInfo<'_> {
245 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
258 const DESC_ARRAY_OFFSET: usize = size_of::<boot_info_header>().next_multiple_of(8);
262 const DESC_SIZE: usize = size_of::<boot_info_descriptor>();
263
264 assert!(buf.len() <= u32::MAX as usize);
265
266 let desc_cnt = descriptors.len();
267
268 let mut total_offset = 0usize;
284
285 total_offset = total_offset.checked_add(DESC_ARRAY_OFFSET).unwrap();
287
288 total_offset = total_offset
290 .checked_add(desc_cnt.checked_mul(DESC_SIZE).unwrap())
291 .unwrap();
292
293 assert!(total_offset <= buf.len());
295
296 let mut desc_array_offset = DESC_ARRAY_OFFSET;
298
299 for desc in descriptors {
300 let mut desc_raw = boot_info_descriptor::default();
301
302 let name_format = match &desc.name {
303 BootInfoName::NullTermString(name) => {
304 let name_len = name.count_bytes().min(15);
306 desc_raw.name[..name_len].copy_from_slice(&name.to_bytes()[..name_len]);
307 desc_raw.name[name_len..].fill(0);
309
310 BootInfoNameFormat::String
311 }
312 BootInfoName::Uuid(uuid) => {
313 desc_raw.name.copy_from_slice(&UuidHelper::to_bytes(*uuid));
314 BootInfoNameFormat::Uuid
315 }
316 };
317
318 let contents_format = match desc.contents {
319 BootInfoContents::Address { content_buf } => {
320 total_offset = total_offset.next_multiple_of(8);
328
329 let buf_addr = mapped_addr.unwrap_or(buf.as_ptr() as usize);
333
334 let content_addr = buf_addr.checked_add(total_offset).unwrap();
338
339 let content_len = content_buf.len();
341 total_offset.checked_add(content_len).unwrap();
342
343 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
352 }
353 BootInfoContents::Value { val, len } => {
354 assert!((1..=8).contains(&len));
355 desc_raw.contents = val;
356 desc_raw.size = len as u32;
357
358 BootInfoContentsFormat::Value
359 }
360 };
361
362 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;
374 }
375
376 let header_raw = boot_info_header {
377 signature: 0x0ffa,
378 version: version.into(),
379 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 };
385
386 header_raw.write_to_prefix(buf).unwrap();
387 }
388
389 fn get_header(version: Version, buf: &[u8]) -> Result<&boot_info_header, Error> {
391 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
398 let header_version = header_raw
399 .version
400 .try_into()
401 .map_err(|_| Error::InvalidHeader)?;
402 if header_version != version {
403 return Err(Error::InvalidVersion(header_version));
404 }
405
406 Ok(header_raw)
407 }
408
409 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)?;
419
420 Ok(header_raw.boot_info_blob_size as usize)
421 }
422}
423
424pub struct BootInfoIterator<'a> {
426 buf: &'a [u8],
427 offset: usize,
428 desc_count: usize,
429 desc_size: usize,
430}
431
432impl<'a> BootInfoIterator<'a> {
433 pub fn new(version: Version, buf: &'a [u8]) -> Result<Self, Error> {
435 let header_raw = BootInfo::get_header(version, buf)?;
436
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 => {
497 let Ok(name_str) = CStr::from_bytes_until_nul(desc_raw.name.as_bytes()) else {
498 return Some(Err(Error::InvalidName));
499 };
500 BootInfoName::NullTermString(name_str)
501 }
502 BootInfoNameFormat::Uuid => {
503 BootInfoName::Uuid(UuidHelper::from_bytes(desc_raw.name))
504 }
505 };
506
507 let contents = match flags.contents_format {
508 BootInfoContentsFormat::Address => {
509 let contents = desc_raw.contents as usize;
510 let contents_size = desc_raw.size as usize;
511
512 let Some(offset) = contents.checked_sub(self.buf.as_ptr() as usize) else {
513 return Some(Err(Error::InvalidBufferSize));
514 };
515
516 let Some(offset_end) = offset.checked_add(contents_size) else {
517 return Some(Err(Error::InvalidBufferSize));
518 };
519
520 if self.buf.len() < offset_end {
521 return Some(Err(Error::InvalidBufferSize));
522 }
523
524 BootInfoContents::Address {
525 content_buf: &self.buf[offset..offset_end],
526 }
527 }
528
529 BootInfoContentsFormat::Value => {
530 let len = desc_raw.size as usize;
531 if (1..=8).contains(&len) {
532 BootInfoContents::Value {
533 val: desc_raw.contents,
534 len,
535 }
536 } else {
537 return Some(Err(Error::MalformedDescriptor));
538 }
539 }
540 };
541
542 return Some(Ok(BootInfo {
543 name,
544 typ,
545 contents,
546 }));
547 }
548
549 None
550 }
551}
552
553#[cfg(test)]
554mod tests {
555 use super::*;
556 use uuid::uuid;
557
558 #[test]
559 fn boot_info() {
560 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 },
567 };
568
569 let fdt = [0u8; 0xff];
570 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 },
574 };
575
576 let mut buf = [0u8; 0x1ff];
577 let buf_addr = buf.as_ptr() as usize;
578 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();
585 let desc1_check = descriptors.next().unwrap().unwrap();
586 let desc2_check = descriptors.next().unwrap().unwrap();
587
588 assert_eq!(desc1, desc1_check);
589 assert_eq!(desc2, desc2_check);
590
591 assert_eq!(BootInfo::get_blob_size(Version(1, 1), &buf), Ok(351));
592
593 let fa = (buf.as_ptr() as u64 + 96).to_le_bytes();
594
595 let expected = [
596 0xfa, 0x0f, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x20, 0x00,
598 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
599 0x00, 0x00, 0x00, 0x00, 0x74, 0x65, 0x73, 0x74, 0x31, 0x32, 0x33, 0x34, 0x74, 0x65, 0x73, 0x74, 0x31, 0x32,
602 0x33, 0x00, 0xab, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde,
603 0x00, 0x00, 0x00, 0x00, 0x12, 0x34, 0x56, 0x78, 0xab, 0xcd, 0xdc, 0xba, 0x12, 0x34, 0x12, 0x34, 0x56, 0x78,
606 0x9a, 0xbc, 0x00, 0x00, 0x01, 0x00, 0xff, 0x00, 0x00, 0x00, fa[0], fa[1], fa[2], fa[3],
607 fa[4], fa[5], fa[6], fa[7], ];
609
610 assert_eq!(expected, buf[0..expected.len()]);
611 }
612
613 #[test]
614 pub fn get_blob_size_invalid_version() {
615 let buf = [0; 0x100];
616
617 assert_eq!(
618 BootInfo::get_blob_size(Version(0, 1), &buf),
619 Err(Error::InvalidVersion(Version(0, 1)))
620 );
621 assert_eq!(
622 BootInfo::get_blob_size(Version(1, 0), &buf),
623 Err(Error::InvalidVersion(Version(1, 0)))
624 );
625 assert_eq!(
626 BootInfo::get_blob_size(Version(2, 1), &buf),
627 Err(Error::InvalidVersion(Version(2, 1)))
628 );
629 assert_eq!(
630 BootInfo::get_blob_size(Version(2, 2), &buf),
631 Err(Error::InvalidVersion(Version(2, 2)))
632 );
633 }
634
635 #[test]
636 pub fn boot_info_iter_new_err1() {
637 assert!(BootInfoIterator::new(Version(1, 1), &[0; 4]).is_err());
638 }
639
640 #[test]
641 pub fn boot_info_iter_new_err2() {
642 assert!(BootInfoIterator::new(
644 Version(1, 1),
645 &[
646 0xfa, 0x0f, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x20, 0x00,
647 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
648 0x00, 0x00, 0x00, 0x00
649 ]
650 )
651 .is_err());
652 }
653
654 #[test]
655 pub fn boot_info_iter_new_err3() {
656 assert!(BootInfoIterator::new(
658 Version(1, 1),
659 &[
660 0xfa, 0x0f, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x20, 0x00,
661 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
662 0x00, 0x00, 0x00, 0x00, 0x74, 0x65, 0x73, 0x74, 0x31, 0x32, 0x33, 0x34, 0x74, 0x65,
663 0x73, 0x74, 0x31, 0x32, 0x33, 0x00, 0xab, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00,
664 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, 0x00, 0x00,
665 ]
666 )
667 .is_err());
668 }
669
670 #[test]
671 pub fn boot_info_iter_new_err4() {
672 assert!(BootInfoIterator::new(
674 Version(1, 1),
675 &[
676 0xfa, 0x0f, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x10, 0x00,
677 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
678 0x00, 0x00, 0x00, 0x00, 0x74, 0x65, 0x73, 0x74, 0x31, 0x32, 0x33, 0x34, 0x74, 0x65,
679 0x73, 0x74, 0x31, 0x32, 0x33, 0x00, 0xab, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00,
680 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, 0x00, 0x00,
681 ]
682 )
683 .is_err());
684 }
685
686 #[test]
687 pub fn boot_info_iter_new_err5() {
688 assert!(BootInfoIterator::new(
690 Version(1, 1),
691 &[
692 0xfa, 0x0f, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x20, 0x00,
693 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
694 0x00, 0x00, 0x00, 0x00, 0x74, 0x65, 0x73, 0x74, 0x31, 0x32, 0x33, 0x34, 0x74, 0x65,
695 0x73, 0x74, 0x31, 0x32, 0x33, 0x00, 0xab, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00,
696 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, 0x00, 0x00,
697 ]
698 )
699 .is_err());
700 }
701}