1use fuchsia_zbi_abi::{ZBI_ALIGNMENT_BYTES, ZBI_FLAGS_CRC32};
6
7use log::info;
8use std::collections::{HashMap, HashSet};
9use std::mem::size_of;
10use std::sync::LazyLock;
11use thiserror::Error;
12use zerocopy::Ref;
13
14pub use fuchsia_zbi_abi::{
15 ZBI_CONTAINER_MAGIC, ZBI_FLAGS_VERSION, ZBI_ITEM_MAGIC, ZBI_ITEM_NO_CRC32, ZbiType,
16 zbi_container_header, zbi_header_t,
17};
18
19const ZBI_HEADER_SIZE: usize = size_of::<zbi_header_t>();
20
21static PAGE_SIZE: LazyLock<u32> = LazyLock::new(|| zx::system_get_page_size());
22
23#[derive(Debug, Error, Eq, PartialEq)]
24pub enum ZbiParserError {
25 #[error("Failed to read {} bytes at offset {}: {}", size, offset, status)]
26 FailedToReadPayload { size: usize, offset: u32, status: zx::Status },
27
28 #[error("Failed to zero {} bytes at offset {}: {}", size, offset, status)]
29 FailedToZeroMemory { size: usize, offset: u32, status: zx::Status },
30
31 #[error("Failed to parse bytes as an unaligned zbi_header_t")]
32 FailedToParseHeader,
33
34 #[error("Failed to validate header, magic was {} but expected {}", actual, ZBI_ITEM_MAGIC)]
35 InvalidHeaderMagic { actual: u32 },
36
37 #[error(
38 "Failed to validate container header, type was {:#?} but expected {:#?}",
39 zbi_type,
40 ZbiType::Container
41 )]
42 InvalidContainerHeaderType { zbi_type: ZbiType },
43
44 #[error(
45 "Failed to validate container header, extra magic was {} but expected {}",
46 actual,
47 ZBI_CONTAINER_MAGIC
48 )]
49 InvalidContainerHeaderExtraMagic { actual: u32 },
50
51 #[error("Header flags {:#b} missing flag version {:#b}", flags, ZBI_FLAGS_VERSION)]
52 MissingZbiVersionFlag { flags: u32 },
53
54 #[error("ZBI header contains a bad CRC32")]
55 BadCRC32,
56
57 #[error("{:?} was not found in the ZBI", zbi_type)]
58 ItemNotFound { zbi_type: ZbiType },
59
60 #[error(
61 "{:?} is not being stored by this parser configuration, and will never be present",
62 zbi_type
63 )]
64 ItemNotStored { zbi_type: ZbiType },
65
66 #[error("{:?} with an extra value of {} was not found in the ZBI", zbi_type, extra)]
67 ItemWithExtraNotFound { zbi_type: ZbiType, extra: u32 },
68
69 #[error("Failed to decommit pages: {}", status)]
70 FailedToDecommitPages { status: zx::Status },
71
72 #[error("ZBI parser bailed to prevent a u32 overflow.")]
73 Overflow,
74
75 #[error("Unknown error in the ZBI parser!")]
76 Unknown,
77}
78
79#[derive(Debug, PartialEq)]
80pub struct DecommitRange {
81 start: u32,
82 end: u32,
83}
84
85#[derive(Debug, Clone, Copy)]
86pub struct ZbiItem {
87 header_offset: u32,
88 item_offset: u32,
89 item_length: u32,
90 extra: u32,
91 raw_type: u32,
92}
93
94#[derive(Debug, PartialEq)]
95pub struct ZbiResult {
96 pub bytes: Vec<u8>,
97 pub extra: u32, }
99
100#[derive(Debug)]
101pub struct ZbiParser {
102 vmo: zx::Vmo,
103 parsed: bool,
104 items: HashMap<ZbiType, Vec<ZbiItem>>,
105 items_to_store: HashSet<ZbiType>,
106 decommit_ranges: Vec<DecommitRange>,
107}
108
109impl ZbiParser {
110 pub fn align_zbi_item(length: u32) -> Result<u32, ZbiParserError> {
112 let rem = length % ZBI_ALIGNMENT_BYTES;
113 if rem > 0 {
114 length.checked_add(ZBI_ALIGNMENT_BYTES - rem).ok_or(ZbiParserError::Overflow)
115 } else {
116 Ok(length)
117 }
118 }
119
120 fn round_up_to_page(address: u32) -> Result<u32, ZbiParserError> {
123 Ok(ZbiParser::round_down_to_page(
124 address.checked_add(*PAGE_SIZE - 1).ok_or(ZbiParserError::Overflow)?,
125 ))
126 }
127
128 fn round_down_to_page(address: u32) -> u32 {
131 address - (address % *PAGE_SIZE)
132 }
133
134 fn get_header<'a>(
135 &self,
136 bytes: &'a [u8],
137 ) -> Result<(ZbiType, Ref<&'a [u8], zbi_header_t>), ZbiParserError> {
138 let header = Ref::<&[u8], zbi_header_t>::from_bytes(&bytes[..])
139 .map_err(Into::into)
140 .map_err(|_: zerocopy::SizeError<_, _>| ZbiParserError::FailedToParseHeader)?;
141
142 if header.magic.get() != ZBI_ITEM_MAGIC {
143 return Err(ZbiParserError::InvalidHeaderMagic { actual: header.magic.get() });
144 }
145
146 if header.flags.get() & ZBI_FLAGS_VERSION == 0 {
147 return Err(ZbiParserError::MissingZbiVersionFlag { flags: header.flags.get() });
148 }
149
150 if (header.flags.get() & ZBI_FLAGS_CRC32 == 0) && (header.crc32.get() != ZBI_ITEM_NO_CRC32)
151 {
152 return Err(ZbiParserError::BadCRC32);
153 }
154
155 Ok((ZbiType::from_raw(header.zbi_type.get()), header))
156 }
157
158 fn should_store_item(&self, zbi_type: ZbiType) -> bool {
159 if self.items_to_store.is_empty() {
160 zbi_type != ZbiType::Unknown
163 } else {
164 self.items_to_store.contains(&zbi_type)
165 }
166 }
167
168 fn decommit_range(&mut self, start: u32, end: u32) -> Result<(), ZbiParserError> {
169 let start = ZbiParser::round_up_to_page(start)?;
170 let end = ZbiParser::round_down_to_page(end);
171
172 if start < end {
173 self.vmo
174 .op_range(zx::VmoOp::DECOMMIT, start.into(), (end - start).into())
175 .map_err(|status| ZbiParserError::FailedToDecommitPages { status })?;
176 self.decommit_ranges.push(DecommitRange { start, end });
177 info!("[ZBI Parser] Decommitted BOOTDATA VMO from {:x} to {:x}", start, end);
178 }
179
180 Ok(())
181 }
182
183 fn get_zbi_result(
184 &self,
185 zbi_type: ZbiType,
186 zbi_item: &ZbiItem,
187 ) -> Result<ZbiResult, ZbiParserError> {
188 let (length, offset) = if zbi_type == ZbiType::StorageRamdisk {
192 (ZBI_HEADER_SIZE + zbi_item.item_length as usize, zbi_item.header_offset)
193 } else {
194 (zbi_item.item_length as usize, zbi_item.item_offset)
195 };
196
197 let mut bytes = vec![0; length];
198 self.vmo.read(&mut bytes, offset.into()).map_err(|status| {
199 ZbiParserError::FailedToReadPayload { size: bytes.len(), offset, status }
200 })?;
201
202 Ok(ZbiResult { bytes, extra: zbi_item.extra })
203 }
204
205 pub fn new(vmo: zx::Vmo) -> ZbiParser {
206 Self {
207 vmo,
208 parsed: false,
209 items: HashMap::new(),
210 items_to_store: HashSet::new(),
211 decommit_ranges: Vec::new(),
212 }
213 }
214
215 pub fn set_store_item(mut self, zbi_type: ZbiType) -> Self {
218 assert!(
219 ZbiType::from_raw(zbi_type as u32) != ZbiType::Unknown,
220 "You must add a u32 -> ZbiType mapping for any new item you wish to store"
221 );
222 self.items_to_store.insert(zbi_type);
223 self
224 }
225
226 pub fn try_get_item(
229 &self,
230 zbi_type_raw: u32,
231 extra: Option<u32>,
232 ) -> Result<Vec<ZbiResult>, ZbiParserError> {
233 let zbi_type = ZbiType::from_raw(zbi_type_raw);
234 if !self.items_to_store.is_empty() && !self.items_to_store.contains(&zbi_type) {
235 return Err(ZbiParserError::ItemNotStored { zbi_type });
238 }
239
240 if !self.items.contains_key(&zbi_type) {
241 return Err(ZbiParserError::ItemNotFound { zbi_type });
242 }
243
244 let want_item = |item: &ZbiItem| -> bool {
245 if zbi_type.into_raw() != zbi_type_raw && item.raw_type != zbi_type_raw {
248 return false;
249 }
250
251 if let Some(extra) = extra {
253 if extra != item.extra {
254 return false;
255 }
256 }
257
258 true
259 };
260
261 let mut result: Vec<ZbiResult> = Vec::new();
262 for item in &self.items[&zbi_type] {
263 if want_item(item) {
264 result.push(self.get_zbi_result(zbi_type, &item)?);
265 }
266 }
267
268 Ok(result)
269 }
270
271 pub fn try_get_last_matching_item(
275 &self,
276 zbi_type_raw: u32,
277 extra: u32,
278 ) -> Result<ZbiResult, ZbiParserError> {
279 let zbi_type = ZbiType::from_raw(zbi_type_raw);
280 if !self.items.contains_key(&zbi_type) {
281 return Err(ZbiParserError::ItemNotFound { zbi_type });
282 }
283
284 for item in self.items[&zbi_type].iter().rev() {
287 if item.extra == extra && item.raw_type == zbi_type_raw {
288 return Ok(self.get_zbi_result(zbi_type, &item)?);
289 }
290 }
291
292 Err(ZbiParserError::ItemWithExtraNotFound { zbi_type, extra })
293 }
294
295 pub fn get_items(&self) -> Result<HashMap<ZbiType, Vec<ZbiResult>>, ZbiParserError> {
297 let mut result = HashMap::new();
298 for key in self.items.keys() {
299 result.insert(key.clone(), self.try_get_item(key.into_raw(), None)?);
300 }
301 Ok(result)
302 }
303
304 pub fn release_item(&mut self, zbi_type: ZbiType) -> Result<(), ZbiParserError> {
306 if !self.items.contains_key(&zbi_type) {
307 return Err(ZbiParserError::ItemNotFound { zbi_type });
308 }
309
310 let mut possible_decommit_range = Vec::new();
311 for item in &self.items[&zbi_type] {
312 let length = u32::try_from(ZBI_HEADER_SIZE).map_err(|_| ZbiParserError::Unknown)?
313 + item.item_length;
314 self.vmo.op_range(zx::VmoOp::ZERO, item.header_offset.into(), length.into()).map_err(
315 |status| ZbiParserError::FailedToZeroMemory {
316 size: length as usize,
317 offset: item.header_offset,
318 status,
319 },
320 )?;
321
322 possible_decommit_range.push(DecommitRange {
323 start: item.header_offset,
324 end: item.item_offset + item.item_length,
325 });
326 }
327
328 self.items.remove(&zbi_type);
329 let on_same_page = |start: u32, end: u32| -> Result<bool, ZbiParserError> {
330 let end = end.checked_sub(1).ok_or(ZbiParserError::Overflow)?;
335 Ok((start / *PAGE_SIZE) == (end / *PAGE_SIZE))
336 };
337
338 for mut decommit_range in possible_decommit_range {
339 let mut adjusted_end = false;
340 let mut adjusted_start = false;
341 'zbi_key_loop: for items in self.items.values() {
342 for item in items {
343 let item_start = item.header_offset;
347 let item_end = item.item_offset + item.item_length;
348
349 if !adjusted_start && on_same_page(decommit_range.start, item_end)? {
350 adjusted_start = true;
352 decommit_range.start = decommit_range
353 .start
354 .checked_add(*PAGE_SIZE)
355 .ok_or(ZbiParserError::Overflow)?;
356 }
357
358 if !adjusted_end && on_same_page(item_start, decommit_range.end)? {
359 adjusted_end = true;
361 decommit_range.end =
362 decommit_range.end.checked_sub(*PAGE_SIZE).unwrap_or(0);
363 }
364
365 if adjusted_start && adjusted_end {
366 break 'zbi_key_loop;
367 }
368 }
369 }
370
371 decommit_range.end = ZbiParser::round_up_to_page(decommit_range.end)?;
375 decommit_range.start = ZbiParser::round_down_to_page(decommit_range.start);
376
377 self.decommit_range(decommit_range.start, decommit_range.end)?;
378 }
379
380 Ok(())
381 }
382
383 pub fn parse(mut self) -> Result<Self, ZbiParserError> {
386 assert!(!self.parsed, "Parse should only be invoked once!");
389 self.parsed = true;
390
391 let mut vmo_offset: u32 = 0;
392 let mut header_bytes = [0; ZBI_HEADER_SIZE];
393
394 self.vmo.read(&mut header_bytes, vmo_offset.into()).map_err(|status| {
395 ZbiParserError::FailedToReadPayload {
396 size: header_bytes.len(),
397 offset: vmo_offset,
398 status,
399 }
400 })?;
401
402 let (zbi_type, header) = self.get_header(&header_bytes)?;
403 if zbi_type != ZbiType::Container {
404 return Err(ZbiParserError::InvalidContainerHeaderType { zbi_type });
405 }
406 if header.extra.get() != ZBI_CONTAINER_MAGIC {
407 return Err(ZbiParserError::InvalidContainerHeaderExtraMagic {
408 actual: header.extra.get(),
409 });
410 }
411
412 let header_offset = u32::try_from(ZBI_HEADER_SIZE).map_err(|_| ZbiParserError::Unknown)?;
414
415 vmo_offset = header_offset;
416 let mut remaining_length = header.length.get();
417
418 let mut decommit_start = 0;
419 let mut decommit_end = 0;
420
421 while remaining_length > header_offset {
422 let mut header_bytes = [0; ZBI_HEADER_SIZE];
423 let current_offset = vmo_offset;
424 self.vmo.read(&mut header_bytes, current_offset.into()).map_err(|status| {
425 ZbiParserError::FailedToReadPayload {
426 size: header_bytes.len(),
427 offset: vmo_offset,
428 status,
429 }
430 })?;
431
432 let (zbi_type, header) = self.get_header(&header_bytes)?;
433 let next_item = ZbiParser::align_zbi_item(
434 header_offset.checked_add(header.length.get()).ok_or(ZbiParserError::Overflow)?,
435 )?;
436
437 vmo_offset = vmo_offset.checked_add(next_item).ok_or(ZbiParserError::Overflow)?;
438 remaining_length =
439 remaining_length.checked_sub(next_item).ok_or(ZbiParserError::Overflow)?;
440
441 if self.should_store_item(zbi_type) {
442 let entry = self.items.entry(zbi_type).or_insert(Vec::new());
443 entry.push(ZbiItem {
445 header_offset: current_offset,
446 item_offset: current_offset + header_offset,
447 item_length: header.length.get(),
448 extra: if zbi_type == ZbiType::StorageRamdisk { 0 } else { header.extra.get() },
449 raw_type: header.zbi_type.get(),
450 });
451
452 self.decommit_range(decommit_start, decommit_end)?;
456 decommit_start = vmo_offset;
457 } else {
458 decommit_end = vmo_offset;
460 }
461 }
462
463 if decommit_end > decommit_start {
464 decommit_end = ZbiParser::round_up_to_page(decommit_end)?;
467 self.decommit_range(decommit_start, decommit_end)?;
468 }
469
470 Ok(self)
471 }
472}
473#[cfg(test)]
474mod tests {
475 use super::*;
476 use anyhow::Error;
477 use zerocopy::IntoBytes;
478 use zerocopy::byteorder::little_endian::U32;
479
480 fn check_item_bytes(builder: &ZbiBuilder, parser: &ZbiParser) {
481 for (zbi_type, items) in &parser.items {
482 let expected = builder.get_bytes(items);
483 let actual = match parser.try_get_item(zbi_type.into_raw(), None) {
484 Ok(val) => val.iter().map(|result| result.bytes.clone()).collect(),
485 Err(_) => {
486 assert!(false);
487 Vec::new()
488 }
489 };
490 assert_eq!(expected, actual);
491 }
492 }
493
494 fn check_extracted_items(parser: &ZbiParser, expected: &[ZbiType]) {
495 let actual = parser.get_items().unwrap();
496 assert_eq!(actual.keys().len(), expected.len());
497 assert!(expected.iter().all(|k| { actual.contains_key(k) }));
498 }
499
500 struct ZbiBuilder {
501 zbi_bytes: Vec<u8>,
502 }
503
504 impl ZbiBuilder {
505 fn get_bytes(&self, zbi_items: &[ZbiItem]) -> Vec<&[u8]> {
506 zbi_items
507 .iter()
508 .map(|item| {
509 let start = item.item_offset as usize;
510 let end = (item.item_offset + item.item_length) as usize;
511
512 &self.zbi_bytes[start..end]
513 })
514 .collect()
515 }
516
517 fn simple_header(zbi_type: ZbiType, length: u32) -> zbi_header_t {
518 zbi_header_t {
519 zbi_type: U32::new(zbi_type.into_raw()),
520 length: U32::new(length),
521 extra: U32::new(if zbi_type == ZbiType::Container {
522 ZBI_CONTAINER_MAGIC
523 } else {
524 0
525 }),
526 flags: U32::new(ZBI_FLAGS_VERSION),
527 reserved_0: U32::new(0),
528 reserved_1: U32::new(0),
529 magic: U32::new(ZBI_ITEM_MAGIC),
530 crc32: U32::new(ZBI_ITEM_NO_CRC32),
531 }
532 }
533
534 fn new() -> Self {
535 Self { zbi_bytes: Vec::new() }
536 }
537
538 fn add_header(mut self, header: zbi_header_t) -> Self {
539 let bytes = header.as_bytes();
540 self.zbi_bytes.extend(bytes);
541 self
542 }
543
544 fn add_item(mut self, length: u32) -> Self {
545 for i in 0..ZbiParser::align_zbi_item(length).unwrap() {
546 self.zbi_bytes.push((i % u8::MAX as u32).try_into().unwrap());
548 }
549 self
550 }
551
552 fn calculate_item_length(mut self) -> Self {
553 let item_length =
554 U32::new(u32::try_from(self.zbi_bytes.len() - ZBI_HEADER_SIZE).unwrap());
555 let item_length_bytes = item_length.as_bytes();
556
557 let mut i = 4usize;
558 for x in item_length_bytes {
559 self.zbi_bytes[i] = *x;
560 i += 1;
561 }
562
563 self
564 }
565
566 fn generate(self) -> Result<(zx::Vmo, Self), Error> {
567 let vmo = zx::Vmo::create(self.zbi_bytes.len().try_into()?)?;
568 vmo.write(&self.zbi_bytes, 0)?;
569 Ok((vmo, self))
570 }
571 }
572
573 #[fuchsia::test]
574 async fn empty_zbi() {
575 let (zbi, _builder) = ZbiBuilder::new().generate().expect("Failed to create ZBI");
576 let parser = ZbiParser::new(zbi).parse();
577
578 assert!(parser.is_err());
579 assert_eq!(
580 parser.unwrap_err(),
581 ZbiParserError::FailedToReadPayload {
582 size: ZBI_HEADER_SIZE,
583 offset: 0,
584 status: zx::Status::OUT_OF_RANGE
585 }
586 );
587 }
588
589 #[fuchsia::test]
590 async fn zero_content_zbi() {
591 let (zbi, _builder) = ZbiBuilder::new()
592 .add_header(zbi_container_header(0))
593 .generate()
594 .expect("Failed to create ZBI");
595 let parser = ZbiParser::new(zbi).parse().expect("Failed to parse ZBI");
596
597 assert_eq!(parser.items.len(), 0);
598 }
599
600 #[fuchsia::test]
601 async fn zbi_smaller_than_header() {
602 let mut builder =
603 ZbiBuilder::new().add_header(ZbiBuilder::simple_header(ZbiType::Container, 0));
604
605 builder.zbi_bytes.resize(builder.zbi_bytes.len() / 2, 0);
607
608 let (zbi, _builder) = builder.generate().expect("Failed to create ZBI");
609 let parser = ZbiParser::new(zbi).parse();
610
611 assert!(parser.is_err());
614 assert_eq!(parser.unwrap_err(), ZbiParserError::InvalidHeaderMagic { actual: 0 });
615 }
616
617 #[fuchsia::test]
618 async fn invalid_container_header_fields() {
619 let mut header = ZbiBuilder::simple_header(ZbiType::Container, 0);
620
621 header.extra = U32::new(0);
623
624 let (zbi, _builder) =
625 ZbiBuilder::new().add_header(header).generate().expect("Failed to create ZBI");
626 let parser = ZbiParser::new(zbi).parse();
627
628 assert!(parser.is_err());
629 assert_eq!(
630 parser.unwrap_err(),
631 ZbiParserError::InvalidContainerHeaderExtraMagic { actual: 0 }
632 );
633
634 let (zbi, _builder) = ZbiBuilder::new()
637 .add_header(ZbiBuilder::simple_header(ZbiType::Crashlog, 0))
638 .generate()
639 .expect("failed to create zbi");
640 let parser = ZbiParser::new(zbi).parse();
641
642 assert!(parser.is_err());
643 assert_eq!(
644 parser.unwrap_err(),
645 ZbiParserError::InvalidContainerHeaderType { zbi_type: ZbiType::Crashlog }
646 );
647 }
648
649 #[fuchsia::test]
650 async fn invalid_header_flags() {
651 let mut header = ZbiBuilder::simple_header(ZbiType::Container, 0);
652
653 header.flags = U32::new(0);
655
656 let (zbi, _builder) =
657 ZbiBuilder::new().add_header(header).generate().expect("Failed to create ZBI");
658 let parser = ZbiParser::new(zbi).parse();
659
660 assert!(parser.is_err());
661 assert_eq!(parser.unwrap_err(), ZbiParserError::MissingZbiVersionFlag { flags: 0 });
662
663 let mut header = ZbiBuilder::simple_header(ZbiType::Container, 0);
664
665 header.crc32 = U32::new(0);
667
668 let (zbi, _builder) =
669 ZbiBuilder::new().add_header(header).generate().expect("failed to create zbi");
670 let parser = ZbiParser::new(zbi).parse();
671
672 assert!(parser.is_err());
673 assert_eq!(parser.unwrap_err(), ZbiParserError::BadCRC32);
674 }
675
676 #[fuchsia::test]
677 async fn zbi_item_overflows_u32() {
678 let (zbi, _builder) = ZbiBuilder::new()
679 .add_header(ZbiBuilder::simple_header(ZbiType::Container, 12345))
680 .add_header(ZbiBuilder::simple_header(ZbiType::Crashlog, u32::MAX))
681 .generate()
682 .expect("failed to create zbi");
683 let parser = ZbiParser::new(zbi).parse();
684
685 assert!(parser.is_err());
686 assert_eq!(parser.unwrap_err(), ZbiParserError::Overflow);
687 }
688
689 #[fuchsia::test]
690 async fn zbi_item_has_correct_extra_field() {
691 let mut crash_header = ZbiBuilder::simple_header(ZbiType::Crashlog, 0x80);
692 crash_header.extra = U32::new(0xABCD);
693
694 let (zbi, _builder) = ZbiBuilder::new()
695 .add_header(ZbiBuilder::simple_header(ZbiType::Container, 0))
696 .add_header(crash_header)
697 .add_item(0x80)
698 .calculate_item_length()
699 .generate()
700 .expect("failed to create zbi");
701 let parser = ZbiParser::new(zbi).parse().expect("Failed to parse ZBI");
702
703 let item = parser
704 .try_get_last_matching_item(ZbiType::Crashlog.into_raw(), 0xABCD)
705 .expect("Failed to get item");
706 assert_eq!(item.extra, 0xABCD);
707
708 assert_eq!(
709 parser.try_get_last_matching_item(ZbiType::Crashlog.into_raw(), 0xDEF).unwrap_err(),
710 ZbiParserError::ItemWithExtraNotFound { zbi_type: ZbiType::Crashlog, extra: 0xDEF }
711 );
712 }
713
714 #[fuchsia::test]
715 async fn zbi_items_have_eight_byte_alignment() {
716 let (zbi, builder) = ZbiBuilder::new()
717 .add_header(ZbiBuilder::simple_header(ZbiType::Container, 0))
718 .add_header(ZbiBuilder::simple_header(ZbiType::Crashlog, 25)) .add_item(25)
720 .add_header(ZbiBuilder::simple_header(ZbiType::Cmdline, 32))
721 .add_item(32)
722 .calculate_item_length()
723 .generate()
724 .expect("failed to create zbi");
725 let parser = ZbiParser::new(zbi).parse().expect("Failed to parse ZBI");
726
727 let crash_items = &parser.items[&ZbiType::Crashlog];
728 assert!(crash_items.len() == 1);
729 let crash_item = &crash_items[0];
730
731 let cmdline_items = &parser.items[&ZbiType::Cmdline];
732 assert!(cmdline_items.len() == 1);
733 let cmdline_item = &cmdline_items[0];
734
735 let crash_item_length = crash_item.item_offset + crash_item.item_length;
738 assert!(crash_item_length != cmdline_item.header_offset + 1);
739 assert!(cmdline_item.header_offset % 8 == 0);
740 assert!((8 - crash_item_length % 8) + crash_item_length == cmdline_item.header_offset);
741
742 check_item_bytes(&builder, &parser);
743 }
744
745 #[fuchsia::test]
746 async fn ramdisk_item_contains_header() {
747 let size = 0x40;
750 let mut ramdisk_header = ZbiBuilder::simple_header(ZbiType::StorageRamdisk, size);
751 ramdisk_header.extra = U32::new(0xABCD);
752
753 let (zbi, builder) = ZbiBuilder::new()
754 .add_header(ZbiBuilder::simple_header(ZbiType::Container, 0))
755 .add_header(ramdisk_header)
756 .add_item(size)
757 .calculate_item_length()
758 .generate()
759 .expect("failed to create zbi");
760
761 let parser = ZbiParser::new(zbi).parse().expect("Failed to parse ZBI");
762
763 let item = parser
765 .try_get_last_matching_item(ZbiType::StorageRamdisk.into_raw(), 0x0)
766 .expect("Failed to get item");
767
768 assert_eq!(item.bytes.len(), ZBI_HEADER_SIZE + size as usize);
770 let offset = parser.items[&ZbiType::StorageRamdisk][0].header_offset as usize;
771 assert_eq!(builder.zbi_bytes[offset..(offset + item.bytes.len())], item.bytes);
772 }
773
774 #[fuchsia::test]
775 async fn pages_with_only_filtered_items_decommitted() {
776 let (zbi, builder) = ZbiBuilder::new()
791 .add_header(ZbiBuilder::simple_header(ZbiType::Container, 0))
792 .add_header(ZbiBuilder::simple_header(ZbiType::Crashlog, 2048))
793 .add_item(2048)
794 .add_header(ZbiBuilder::simple_header(ZbiType::Crashlog, 4096))
795 .add_item(4096)
796 .add_header(ZbiBuilder::simple_header(ZbiType::Cmdline, 16384))
797 .add_item(16384)
798 .add_header(ZbiBuilder::simple_header(ZbiType::StorageBootfsFactory, 250))
799 .add_item(250)
800 .add_header(ZbiBuilder::simple_header(ZbiType::ImageArgs, 2048))
801 .add_item(2048)
802 .calculate_item_length()
803 .generate()
804 .expect("failed to create zbi");
805 let parser = ZbiParser::new(zbi)
806 .set_store_item(ZbiType::Crashlog)
807 .set_store_item(ZbiType::StorageBootfsFactory)
808 .parse()
809 .expect("Failed to parse ZBI");
810
811 check_extracted_items(&parser, &[ZbiType::Crashlog, ZbiType::StorageBootfsFactory]);
812 check_item_bytes(&builder, &parser);
813
814 assert_eq!(
815 parser.decommit_ranges,
816 vec![
817 DecommitRange { start: 0x2000, end: 0x5000 },
821 DecommitRange { start: 0x6000, end: 0x7000 }
824 ]
825 );
826 }
827
828 #[fuchsia::test]
829 async fn get_then_decommit_driver_metadata() {
830 let mut driver_metadata_header1 = ZbiBuilder::simple_header(ZbiType::Unknown, 0xFC0);
838 let mut driver_metadata_header2 = ZbiBuilder::simple_header(ZbiType::Unknown, 0x40);
839 let mut driver_metadata_header3 = ZbiBuilder::simple_header(ZbiType::Unknown, 0x40);
840
841 driver_metadata_header1.zbi_type =
842 U32::new((0xABCD << 8) | ZbiType::DriverMetadata.into_raw());
843 assert_eq!(
844 ZbiType::from_raw(driver_metadata_header1.zbi_type.get()),
845 ZbiType::DriverMetadata
846 );
847
848 driver_metadata_header2.zbi_type =
849 U32::new((0xDCBA << 8) | ZbiType::DriverMetadata.into_raw());
850 assert_eq!(
851 ZbiType::from_raw(driver_metadata_header2.zbi_type.get()),
852 ZbiType::DriverMetadata
853 );
854
855 driver_metadata_header3.zbi_type =
856 U32::new((0xEEEE << 8) | ZbiType::DriverMetadata.into_raw());
857 assert_eq!(
858 ZbiType::from_raw(driver_metadata_header3.zbi_type.get()),
859 ZbiType::DriverMetadata
860 );
861
862 let (zbi, builder) = ZbiBuilder::new()
863 .add_header(ZbiBuilder::simple_header(ZbiType::Container, 0))
864 .add_header(driver_metadata_header1.clone())
865 .add_item(0xFC0)
866 .add_header(ZbiBuilder::simple_header(ZbiType::Crashlog, 0xF80))
867 .add_item(0xF80)
868 .add_header(driver_metadata_header2.clone())
869 .add_item(0x40)
870 .add_header(driver_metadata_header3.clone())
871 .add_item(0x40)
872 .calculate_item_length()
873 .generate()
874 .expect("failed to create zbi");
875 let mut parser = ZbiParser::new(zbi)
876 .set_store_item(ZbiType::DriverMetadata)
877 .set_store_item(ZbiType::Crashlog)
878 .parse()
879 .expect("Failed to parse ZBI");
880
881 assert_eq!(parser.items.get(&ZbiType::Crashlog).unwrap()[0].header_offset, 0x1000);
885 assert_eq!(parser.items.get(&ZbiType::DriverMetadata).unwrap()[2].header_offset, 0x2000);
886
887 check_item_bytes(&builder, &parser);
888
889 assert_eq!(
892 parser
893 .try_get_last_matching_item(driver_metadata_header1.zbi_type.get(), 0)
894 .unwrap()
895 .bytes
896 .len(),
897 driver_metadata_header1.length.get() as usize
898 );
899
900 assert_eq!(
901 parser
902 .try_get_last_matching_item(driver_metadata_header2.zbi_type.get(), 0)
903 .unwrap()
904 .bytes
905 .len(),
906 driver_metadata_header2.length.get() as usize
907 );
908
909 assert_eq!(
910 parser
911 .try_get_last_matching_item(driver_metadata_header3.zbi_type.get(), 0)
912 .unwrap()
913 .bytes
914 .len(),
915 driver_metadata_header3.length.get() as usize
916 );
917
918 assert!(parser.release_item(ZbiType::DriverMetadata).is_ok());
919 assert!(parser.try_get_item(ZbiType::DriverMetadata.into_raw(), None).is_err());
920
921 assert_eq!(
922 parser.decommit_ranges,
923 vec![
924 DecommitRange { start: 0x0, end: 0x1000 },
927 DecommitRange { start: 0x2000, end: 0x3000 },
929 ]
930 );
931
932 check_item_bytes(&builder, &parser);
933 }
934
935 #[fuchsia::test]
936 async fn unknown_items_are_decommitted() {
937 let (zbi, builder) = ZbiBuilder::new()
938 .add_header(ZbiBuilder::simple_header(ZbiType::Container, 0))
939 .add_header(ZbiBuilder::simple_header(ZbiType::Unknown, 0x1500))
940 .add_item(0x1500)
941 .add_header(ZbiBuilder::simple_header(ZbiType::Crashlog, 0x100))
942 .add_item(0x100)
943 .calculate_item_length()
944 .generate()
945 .expect("failed to create zbi");
946 let parser = ZbiParser::new(zbi).parse().expect("Failed to parse ZBI");
947
948 check_extracted_items(&parser, &[ZbiType::Crashlog]);
949 check_item_bytes(&builder, &parser);
950
951 assert_eq!(
952 parser.decommit_ranges,
953 vec![
954 DecommitRange { start: 0x0, end: 0x1000 },
956 ]
957 );
958 }
959
960 #[fuchsia::test]
961 async fn get_once_item_zeroes_parent_memory() {
962 let (zbi, builder) = ZbiBuilder::new()
963 .add_header(ZbiBuilder::simple_header(ZbiType::Container, 0))
964 .add_header(ZbiBuilder::simple_header(ZbiType::Crashlog, 2048))
965 .add_item(2048)
966 .add_header(ZbiBuilder::simple_header(ZbiType::ImageArgs, 2048))
967 .add_item(2048)
968 .add_header(ZbiBuilder::simple_header(ZbiType::ImageArgs, 8192))
969 .add_item(8192)
970 .add_header(ZbiBuilder::simple_header(ZbiType::StorageBootfsFactory, 32))
971 .add_item(32)
972 .calculate_item_length()
973 .generate()
974 .expect("failed to create zbi");
975
976 let mut parser = ZbiParser::new(zbi).parse().expect("Failed to parse ZBI");
977
978 check_extracted_items(
979 &parser,
980 &[ZbiType::Crashlog, ZbiType::ImageArgs, ZbiType::StorageBootfsFactory],
981 );
982 check_item_bytes(&builder, &parser);
983
984 assert!(parser.try_get_item(ZbiType::ImageArgs.into_raw(), None).is_ok());
985
986 let first_image_item = parser.items[&ZbiType::ImageArgs][0];
987 let second_image_item = parser.items[&ZbiType::ImageArgs][0];
988
989 assert!(parser.release_item(ZbiType::ImageArgs).is_ok());
990
991 let mut bytes = vec![0; ZBI_HEADER_SIZE + first_image_item.item_length as usize];
993 let expected_bytes = vec![0; ZBI_HEADER_SIZE + first_image_item.item_length as usize];
994 parser.vmo.read(&mut bytes, first_image_item.header_offset.into()).unwrap();
995 assert_eq!(bytes, expected_bytes);
996
997 let mut bytes = vec![0; ZBI_HEADER_SIZE + second_image_item.item_length as usize];
998 let expected_bytes = vec![0; ZBI_HEADER_SIZE + second_image_item.item_length as usize];
999 parser.vmo.read(&mut bytes, second_image_item.header_offset.into()).unwrap();
1000 assert_eq!(bytes, expected_bytes);
1001
1002 assert!(parser.try_get_item(ZbiType::ImageArgs.into_raw(), None).is_err());
1004
1005 assert_eq!(
1006 parser.decommit_ranges,
1007 vec![
1008 DecommitRange { start: 0x1000, end: 0x2000 },
1012 DecommitRange { start: 0x1000, end: 0x3000 }
1013 ]
1014 );
1015
1016 check_item_bytes(&builder, &parser);
1018 }
1019
1020 #[fuchsia::test]
1021 async fn get_items_of_unstored_type() {
1022 let (zbi, _builder) = ZbiBuilder::new()
1023 .add_header(ZbiBuilder::simple_header(ZbiType::Container, 0))
1024 .add_header(ZbiBuilder::simple_header(ZbiType::Crashlog, 2048))
1025 .add_item(2048)
1026 .add_header(ZbiBuilder::simple_header(ZbiType::Cmdline, 16384))
1027 .add_item(16384)
1028 .calculate_item_length()
1029 .generate()
1030 .expect("failed to create zbi");
1031
1032 let parser = ZbiParser::new(zbi)
1033 .set_store_item(ZbiType::Crashlog)
1034 .set_store_item(ZbiType::StorageBootfsFactory)
1035 .parse()
1036 .expect("Failed to parse ZBI");
1037
1038 let result = parser.try_get_item(ZbiType::Cmdline.into_raw(), None);
1039 assert_eq!(
1040 result.unwrap_err(),
1041 ZbiParserError::ItemNotStored { zbi_type: ZbiType::Cmdline }
1042 );
1043 }
1044
1045 #[fuchsia::test]
1046 async fn get_items_of_type_and_optional_extra() {
1047 let dm1: u32 = (0xABCD << 8) | ZbiType::DriverMetadata.into_raw();
1048 let mut dm_header1 = ZbiBuilder::simple_header(ZbiType::Unknown, 0x40);
1049 dm_header1.zbi_type = U32::new(dm1);
1050
1051 let dm2: u32 = (0xDCBA << 8) | ZbiType::DriverMetadata.into_raw();
1052 let mut dm2_header1 = ZbiBuilder::simple_header(ZbiType::Unknown, 0x40);
1053 dm2_header1.zbi_type = U32::new(dm2);
1054 dm2_header1.extra = U32::new(1);
1055
1056 let mut dm2_header2 = ZbiBuilder::simple_header(ZbiType::Unknown, 0x40);
1057 dm2_header2.zbi_type = U32::new(dm2);
1058 dm2_header2.extra = U32::new(2);
1059
1060 let (zbi, _builder) = ZbiBuilder::new()
1063 .add_header(ZbiBuilder::simple_header(ZbiType::Container, 0))
1064 .add_header(ZbiBuilder::simple_header(ZbiType::Crashlog, 0x200))
1065 .add_item(0x200)
1066 .add_header(dm_header1.clone())
1067 .add_item(0x40)
1068 .add_header(dm2_header1.clone())
1069 .add_item(0x40)
1070 .add_header(dm2_header2.clone())
1071 .add_item(0x40)
1072 .calculate_item_length()
1073 .generate()
1074 .expect("failed to create zbi");
1075 let parser = ZbiParser::new(zbi)
1076 .set_store_item(ZbiType::Crashlog)
1077 .set_store_item(ZbiType::DriverMetadata)
1078 .parse()
1079 .expect("Failed to parse ZBI");
1080
1081 let not_present_dm = (0xAAA << 8) | ZbiType::DriverMetadata.into_raw();
1083 let result = parser.try_get_item(not_present_dm, None).expect("failed to get item");
1084 assert!(result.is_empty());
1085
1086 let result = parser
1088 .try_get_item(ZbiType::DriverMetadata.into_raw(), None)
1089 .expect("failed to get item");
1090 assert_eq!(result.len(), 3);
1091
1092 let result = parser.try_get_item(dm2, None).expect("failed to get item");
1094 assert_eq!(result.len(), 2);
1095
1096 let result = parser.try_get_item(dm2, Some(2)).expect("failed to get item");
1098 assert_eq!(result.len(), 1);
1099 assert_eq!(result[0].extra, 2);
1100 }
1101}