blob: 910563ebc07b5c612c29c642801cf9aad2f033e8 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include "zbi.h"
#include <lib/zbitl/view.h>
#include <lib/zbitl/vmo.h>
#include <zircon/boot/image.h>
#include <zircon/status.h>
#include "decompressor.h"
#include "util.h"
namespace {
constexpr const char kBootfsVmoName[] = "uncompressed-bootfs";
zx::vmo DecompressBootfs(const zx::debuglog& log, const zx::vmar& vmar_self, const zx::vmo& zbi_vmo,
uint64_t payload_offset, uint32_t payload_size,
uint32_t uncompressed_size) {
zx::vmo bootfs_vmo;
zx_status_t status = zx::vmo::create(uncompressed_size, 0, &bootfs_vmo);
check(log, status, "cannot create BOOTFS VMO (%u bytes)", uncompressed_size);
status = zbi_decompress(log, vmar_self, zbi_vmo, payload_offset, payload_size, bootfs_vmo, 0,
uncompressed_size);
check(log, status, "failed to decompress BOOTFS");
printl(log, "decompressed BOOTFS to VMO!\n");
return bootfs_vmo;
}
zx::vmo ExtractBootfs(const zx::debuglog& log, const zx::vmar& vmar_self, const zx::vmo& zbi_vmo,
uint64_t payload_offset, uint32_t payload_size) {
zx::vmo bootfs_vmo;
if (payload_offset % ZX_PAGE_SIZE == 0) {
zx_status_t status =
zbi_vmo.create_child(ZX_VMO_CHILD_COPY_ON_WRITE, payload_offset, payload_size, &bootfs_vmo);
check(log, status, "zx_vmo_create_child failed for BOOTFS");
printl(log, "cloned uncompressed BOOTFS to VMO!\n");
return bootfs_vmo;
}
// The data is not aligned, so it must be copied into a new VMO.
zx_status_t status = zx::vmo::create(payload_size, 0, &bootfs_vmo);
check(log, status, "cannot create BOOTFS VMO (%u bytes)", payload_size);
uintptr_t bootfs_payload;
status = vmar_self.map(0, bootfs_vmo, 0, payload_size, ZX_VM_PERM_READ | ZX_VM_PERM_WRITE,
&bootfs_payload);
check(log, status, "cannot map BOOTFS VMO (%u bytes)", payload_size);
status = zbi_vmo.read(reinterpret_cast<void*>(bootfs_payload), payload_offset, payload_size);
check(log, status, "cannot read BOOTFS into VMO (%u bytes)", payload_size);
status = vmar_self.unmap(bootfs_payload, payload_size);
check(log, status, "cannot unmap BOOTFS VMO (%u bytes)", payload_size);
printl(log, "copied uncompressed BOOTFS to VMO!\n");
return bootfs_vmo;
}
} // namespace
zx::vmo GetBootfsFromZbi(const zx::debuglog& log, const zx::vmar& vmar_self,
const zx::vmo& zbi_vmo) {
zx::vmo vmo;
check(log, zbi_vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo), "cannot duplicate ZBI VMO\n");
zbitl::PermissiveView<zx::vmo> zbi(std::move(vmo));
for (auto it = zbi.begin(); it != zbi.end(); ++it) {
auto [header, payload] = *it;
if (header->type == ZBI_TYPE_STORAGE_BOOTFS) {
zx::vmo bootfs_vmo =
(header->flags & ZBI_FLAG_STORAGE_COMPRESSED)
? DecompressBootfs(log, vmar_self, zbi_vmo, payload, header->length, header->extra)
: ExtractBootfs(log, vmar_self, zbi_vmo, payload, header->length);
bootfs_vmo.set_property(ZX_PROP_NAME, kBootfsVmoName, sizeof(kBootfsVmoName) - 1);
// Signal that we've already processed this one.
// GCC's -Wmissing-field-initializers is buggy: it should allow
// designated initializers without all fields, but doesn't (in C++?).
zbi_header_t discard{};
discard.type = ZBI_TYPE_DISCARD;
if (auto ok = it.Replace(discard); ok.is_error()) {
check(log, ok.error_value(), "zx_vmo_write failed on ZBI VMO\n");
}
// Cancel error-checking since we're ending the iteration on purpose.
zbi.ignore_error();
return bootfs_vmo;
}
}
if (auto check = zbi.take_error(); check.is_error()) {
auto error = check.error_value();
if (error.storage_error) {
fail(log, "invalid ZBI: %.*s at offset %#x\n", static_cast<int>(error.zbi_error.size()),
error.zbi_error.data(), error.item_offset);
} else {
fail(log, "invalid ZBI: %.*s at offset %#x: %s\n", static_cast<int>(error.zbi_error.size()),
error.zbi_error.data(), error.item_offset, zx_status_get_string(*error.storage_error));
}
} else {
fail(log, "no '/boot' bootfs in bootstrap message\n");
}
}