Skip to main content

bootfs/
bootfs.rs

1// Copyright 2026 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use anyhow::{Error, anyhow};
6use std::fmt::Write as _;
7use std::slice;
8use zbi::zbi_format::{ZBI_FLAGS_CRC32, ZBI_FLAGS_STORAGE_COMPRESSED};
9use zbi::{ZbiContainer, ZbiType};
10use zerocopy::IntoBytes as _;
11use zx::{Name, VmarFlags, Vmo};
12use zx_libc::sanitizer::Log;
13
14/// Maps the ZBI VMO into a VMAR and parses the ZBI container.
15pub fn get_zbi_container(
16    zbi_vmo: &Vmo,
17    vmar: &zx::Vmar,
18) -> Result<ZbiContainer<&'static [u8]>, Error> {
19    let zbi_size = zbi_vmo.get_size()? as usize;
20    let zbi_addr = vmar.map(0, zbi_vmo, 0, zbi_size, VmarFlags::PERM_READ)?;
21    // SAFETY: zbi_addr points to a valid memory mapping in vmar of zbi_size bytes with read
22    // permissions.
23    let zbi_slice = unsafe {
24        slice::from_raw_parts(core::ptr::with_exposed_provenance::<u8>(zbi_addr), zbi_size)
25    };
26    ZbiContainer::parse(zbi_slice).map_err(|e| anyhow!("Failed to parse ZBI: {e:?}"))
27}
28
29/// Extracts the BOOTFS VMO from the ZBI container, decompressing it if needed.
30pub fn get_bootfs_vmo(
31    container: &ZbiContainer<&[u8]>,
32    vmar: &zx::Vmar,
33    check_crc: bool,
34    log: &mut Log,
35) -> Result<Vmo, Error> {
36    let bootfs_item = container
37        .iter()
38        .find(|item| item.header.type_ == ZbiType::StorageBootFs as u32)
39        .ok_or_else(|| anyhow!("StorageBootFs item not found in ZBI"))?;
40
41    if check_crc && (bootfs_item.header.flags & ZBI_FLAGS_CRC32) != 0 {
42        writeln!(log, "Checking BOOTFS item CRC32 {:#x}...", bootfs_item.header.crc32)?;
43        let mut header_without_crc = *bootfs_item.header;
44        header_without_crc.crc32 = 0;
45        let mut hasher = crc32fast::Hasher::new();
46        hasher.update(header_without_crc.as_bytes());
47        hasher.update(bootfs_item.payload.as_bytes());
48        if hasher.finalize() == bootfs_item.header.crc32 {
49            writeln!(log, "BOOTFS payload matches item CRC32.")?;
50        } else {
51            writeln!(log, "*** BOOTFS payload DOES NOT MATCH item CRC32! ***")?;
52        }
53    }
54
55    let is_compressed = (bootfs_item.header.flags & ZBI_FLAGS_STORAGE_COMPRESSED) != 0;
56    let bootfs_vmo = if is_compressed {
57        let uncompressed_size = bootfs_item.header.extra as usize;
58        let vmo = Vmo::create(uncompressed_size as u64)?;
59        let bootfs_addr =
60            vmar.map(0, &vmo, 0, uncompressed_size, VmarFlags::PERM_READ | VmarFlags::PERM_WRITE)?;
61
62        let payload = bootfs_item.payload.as_bytes();
63        // SAFETY: bootfs_addr points to a valid writable memory mapping in vmar of
64        // uncompressed_size bytes.
65        let dst_slice = unsafe {
66            slice::from_raw_parts_mut(
67                core::ptr::with_exposed_provenance_mut::<u8>(bootfs_addr),
68                uncompressed_size,
69            )
70        };
71        zstd_safe::decompress(dst_slice, payload)
72            .map_err(|e| anyhow!("zstd decompression failed with error code: {}", e))?;
73
74        // SAFETY: bootfs_addr was successfully mapped above with length uncompressed_size in vmar.
75        unsafe {
76            vmar.unmap(bootfs_addr, uncompressed_size)?;
77        }
78        vmo
79    } else {
80        let payload_bytes = bootfs_item.payload.as_bytes();
81        let vmo = Vmo::create(payload_bytes.len() as u64)?;
82        vmo.write(payload_bytes, 0)?;
83        vmo
84    };
85
86    bootfs_vmo.set_name(&Name::new("uncompressed-bootfs")?)?;
87    Ok(bootfs_vmo)
88}