blob: 0af182e70d8beae8d4575fbd35e3be3ef8975a06 [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 "bootdata.h"
#include <lib/hermetic-decompressor/hermetic-decompressor.h>
#include <lib/zx/vmar.h>
#include <lib/zx/vmo.h>
#include <string.h>
#include <zircon/boot/image.h>
#include <zircon/syscalls.h>
#include "util.h"
namespace {
constexpr const char kBootfsVmoName[] = "uncompressed-bootfs";
#ifdef ZBI_COMPRESSION_MAGIC
class EngineService {
public:
using Magic = HermeticDecompressorEngineService::Magic;
static constexpr Magic kMagic = HermeticDecompressorEngineService::ZBI_COMPRESSION_MAGIC;
EngineService(zx_handle_t job, zx_handle_t engine, zx_handle_t vdso)
: job_(job), engine_(engine), vdso_(vdso) {}
auto job() const { return zx::unowned_job{*job_}; }
zx_status_t GetEngine(Magic magic, zx::unowned_vmo* vmo) {
if (magic == kMagic) {
*vmo = zx::unowned_vmo{engine_};
return ZX_OK;
}
return ZX_ERR_NOT_FOUND;
}
zx_status_t GetVdso(zx::unowned_vmo* vmo) {
*vmo = zx::unowned_vmo{vdso_->get()};
return ZX_OK;
}
private:
zx::unowned_job job_;
zx::unowned_vmo engine_;
zx::unowned_vmo vdso_;
};
using Decompressor = HermeticDecompressorWithEngineService<EngineService>;
#endif // ZBI_COMPRESSION_MAGIC
} // namespace
zx_handle_t bootdata_get_bootfs(zx_handle_t log, zx_handle_t vmar_self, zx_handle_t job,
zx_handle_t engine_vmo, zx_handle_t vdso_vmo,
zx_handle_t bootdata_vmo) {
size_t off = 0;
for (;;) {
zbi_header_t bootdata;
zx_status_t status = zx_vmo_read(bootdata_vmo, &bootdata, off, sizeof(bootdata));
check(log, status, "zx_vmo_read failed on bootdata VMO");
if (!(bootdata.flags & ZBI_FLAG_VERSION)) {
fail(log, "bootdata v1 no longer supported");
}
switch (bootdata.type) {
case ZBI_TYPE_CONTAINER:
if (off == 0) {
// Quietly skip container header.
bootdata.length = 0;
} else {
fail(log, "container in the middle of bootdata");
}
break;
case ZBI_TYPE_STORAGE_BOOTFS: {
zx::vmo bootfs_vmo;
if (bootdata.flags & ZBI_FLAG_STORAGE_COMPRESSED) {
#ifdef ZBI_COMPRESSION_MAGIC
status = zx::vmo::create(bootdata.extra, 0, &bootfs_vmo);
check(log, status, "cannot create BOOTFS VMO (%u bytes)", bootdata.extra);
bootfs_vmo.set_property(ZX_PROP_NAME, kBootfsVmoName, sizeof(kBootfsVmoName) - 1);
status = Decompressor(job, engine_vmo, vdso_vmo)(*zx::unowned_vmo{bootdata_vmo},
off + sizeof(bootdata), bootdata.length,
bootfs_vmo, 0, bootdata.extra);
check(log, status, "failed to decompress BOOTFS");
#else
fail(log, "userboot built without BOOTFS decompressor!");
#endif
} else {
off += sizeof(bootdata);
if (off % ZX_PAGE_SIZE == 0) {
status = zx_vmo_create_child(bootdata_vmo, ZX_VMO_CHILD_COPY_ON_WRITE, off,
bootdata.length, bootfs_vmo.reset_and_get_address());
check(log, status, "zx_vmo_create_child failed for BOOTFS");
} else {
// The data is not aligned, so it must be copied into a new VMO.
status = zx::vmo::create(bootdata.length, 0, &bootfs_vmo);
check(log, status, "cannot create BOOTFS VMO (%u bytes)", bootdata.length);
zx::unowned_vmar vmar{vmar_self};
uintptr_t bootfs_payload;
status = vmar->map(0, bootfs_vmo, 0, bootdata.length,
ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, &bootfs_payload);
check(log, status, "cannot map BOOTFS VMO (%u bytes)", bootdata.length);
status = zx_vmo_read(bootdata_vmo, reinterpret_cast<void*>(bootfs_payload), off,
bootdata.length);
check(log, status, "cannot read BOOTFS into VMO (%u bytes)", bootdata.length);
status = vmar->unmap(bootfs_payload, bootdata.length);
check(log, status, "cannot unmap BOOTFS VMO (%u bytes)", bootdata.length);
}
}
// Signal that we've already processed this one.
bootdata.type = ZBI_TYPE_DISCARD;
check(log,
zx_vmo_write(bootdata_vmo, &bootdata.type, off + offsetof(zbi_header_t, type),
sizeof(bootdata.type)),
"zx_vmo_write failed on bootdata VMO\n");
printl(log,
#ifdef ZBI_COMPRESSION_MAGIC
"decompressed"
#else
"copied uncompressed"
#endif
" BOOTFS to VMO!\n");
return bootfs_vmo.release();
}
}
off += ZBI_ALIGN(static_cast<uint32_t>(sizeof(bootdata)) + bootdata.length);
}
fail(log, "no '/boot' bootfs in bootstrap message\n");
}