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