| // 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"); |
| } |