blob: c5d3a62cf7d7852cd6d3082ff49f422141b5705c [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <lib/zbitl/error-stdio.h>
#include <lib/zircon_boot/zircon_boot.h>
#include <phys/boot-zbi.h>
#include <phys/stdio.h>
#include "backends.h"
#include "boot_zbi_items.h"
#include "gpt.h"
#include "utils.h"
namespace gigaboot {
namespace {
// Reasonable guess for the size of additional zbi items on this platform.
constexpr size_t kKernelBufferZbiSizeEstimate = 0x8000;
bool ReadFromPartition(ZirconBootOps* ops, const char* part, size_t offset, size_t size, void* dst,
size_t* read_size) {
auto gpt_device = FindEfiGptDevice();
if (gpt_device.is_error()) {
return false;
}
auto load_res = gpt_device.value().Load();
if (load_res.is_error()) {
return false;
}
*read_size = size;
part = MaybeMapPartitionName(gpt_device.value(), part).data();
return gpt_device.value().ReadPartition(part, offset, size, dst).is_ok();
}
bool WriteToPartition(ZirconBootOps* ops, const char* part, size_t offset, size_t size,
const void* src, size_t* write_size) {
auto gpt_device = FindEfiGptDevice();
if (gpt_device.is_error()) {
return false;
}
auto load_res = gpt_device.value().Load();
if (load_res.is_error()) {
return false;
}
*write_size = size;
part = MaybeMapPartitionName(gpt_device.value(), part).data();
return gpt_device.value().WritePartition(part, src, offset, size).is_ok();
}
void Boot(ZirconBootOps* ops, zbi_header_t* zbi, size_t capacity) {
// TODO(https://fxbug.dev/42159148): Implement the same relocation logic in zircon_boot
// library and use it here to validate.
printf("Booting zircon\n");
BootZbi::InputZbi input_zbi_view(
zbitl::StorageFromRawHeader(static_cast<const zbi_header_t*>(zbi)));
BootZbi boot;
if (auto result = boot.Init(input_zbi_view); result.is_error()) {
printf("boot: Not a bootable ZBI: ");
zbitl::PrintViewCopyError(result.error_value());
abort();
}
// Reserve extra capacity for memory map items to be added next.
// Once we add memory map related items, we must not do anything that can change the memory map,
// i.e. dynamic allocation. Because BootZbi::Load() do memory allocation, we can only add these
// items after.
uint32_t extra_data_capacity = 64 * 1024;
if (auto result = boot.Load(extra_data_capacity); result.is_error()) {
printf("boot: Failed to load ZBI: ");
zbitl::PrintViewCopyError(result.error_value());
abort();
}
void* data_zbi = reinterpret_cast<void*>(boot.DataLoadAddress());
size_t data_zbi_capacity = boot.DataZbi().storage().size();
auto memory_attr =
AddMemoryItems(data_zbi, data_zbi_capacity, reinterpret_cast<ZbiContext*>(ops->context));
if (memory_attr.is_error()) {
printf("Failed to add additional memory ranges\n");
abort();
}
size_t mkey = memory_attr.value();
efi_status exit_res = gEfiSystemTable->BootServices->ExitBootServices(gEfiImageHandle, mkey);
if (exit_res != EFI_SUCCESS) {
printf("Failed to exit boot service %s\n", gigaboot::EfiStatusToString(exit_res));
abort();
}
// Once we exit boot service, console output will be set to NULL. Thus we must not print from now
// on otherwise it crashes.
PhysConsole& console = PhysConsole::Get();
console.set_serial(*console.null());
console.set_graphics(*console.null());
boot.Boot();
}
bool AddZbiItems(ZirconBootOps* ops, zbi_header_t* image, size_t capacity,
const AbrSlotIndex* slot) {
// TODO(b/235489025): To implement. Append necessary ZBI items for booting the ZBI image. Refers
// to the C Gigaboot implementation in function boot_zircon() in
// `src/firmware/gigaboot/src/zircon.c` for what items are needed.
return AddGigabootZbiItems(image, capacity, slot, reinterpret_cast<ZbiContext*>(ops->context));
}
bool ReadPermanentAttributes(ZirconBootOps* ops, AvbAtxPermanentAttributes* attribute) {
ZX_ASSERT(attribute);
const cpp20::span<const uint8_t> perm_attr = GetPermanentAttributes();
if (perm_attr.size() != sizeof(AvbAtxPermanentAttributes)) {
return false;
}
memcpy(attribute, perm_attr.data(), perm_attr.size());
return true;
}
bool ReadPermanentAttributesHash(ZirconBootOps* ops, uint8_t* hash) {
ZX_ASSERT(hash);
const cpp20::span<const uint8_t> perm_attr_hash = GetPermanentAttributesHash();
memcpy(hash, perm_attr_hash.data(), perm_attr_hash.size());
return true;
}
uint8_t* GetLoadBuffer(ZirconBootOps* ops, size_t* size) {
ZX_ASSERT(size);
efi_physical_addr addr;
*size += kKernelBufferZbiSizeEstimate;
efi_status status = gEfiSystemTable->BootServices->AllocatePages(
AllocateAnyPages, EfiLoaderData, DivideRoundUp(*size, kUefiPageSize), &addr);
return status == EFI_SUCCESS ? reinterpret_cast<uint8_t*>(addr) : nullptr;
}
bool VerifiedBootGetPartitionSize(ZirconBootOps* ops, const char* part, size_t* out) {
auto gpt_device = FindEfiGptDevice();
if (gpt_device.is_error()) {
return false;
}
auto load_res = gpt_device.value().Load();
if (load_res.is_error()) {
return false;
}
part = MaybeMapPartitionName(gpt_device.value(), part).data();
const gpt_entry_t* gpt_entry = gpt_device.value().FindPartition(part);
if (!gpt_entry) {
return false;
}
*out = (gpt_entry->last - gpt_entry->first + 1) * gpt_device.value().BlockSize();
return true;
}
// TODO(b/235489025): Implement these callbacks correctly.
bool VerifiedBootReadIsDeviceLocked(ZirconBootOps* ops, bool* out_is_locked) {
*out_is_locked = true;
return true;
}
bool VerifiedBootWriteRollbackIndex(ZirconBootOps* ops, size_t rollback_index_location,
uint64_t rollback_index) {
return true;
}
bool VerifiedBootReadRollbackIndex(ZirconBootOps* ops, size_t rollback_index_location,
uint64_t* out_rollback_index) {
*out_rollback_index = 0;
return true;
}
} // namespace
// TODO(b/269178761): write unit tests for "default" implementation of zircon boot ops code.
ZirconBootOps GetZirconBootOps() {
static ZbiContext zbi_context = {};
ZirconBootOps zircon_boot_ops;
zircon_boot_ops.context = &zbi_context;
zircon_boot_ops.read_from_partition = ReadFromPartition;
zircon_boot_ops.write_to_partition = WriteToPartition;
zircon_boot_ops.boot = Boot;
zircon_boot_ops.add_zbi_items = AddZbiItems;
zircon_boot_ops.get_kernel_load_buffer = GetLoadBuffer;
zircon_boot_ops.firmware_can_boot_kernel_slot = nullptr;
zircon_boot_ops.verified_boot_get_partition_size = VerifiedBootGetPartitionSize;
zircon_boot_ops.verified_boot_read_rollback_index = VerifiedBootReadRollbackIndex;
zircon_boot_ops.verified_boot_write_rollback_index = VerifiedBootWriteRollbackIndex;
zircon_boot_ops.verified_boot_read_is_device_locked = VerifiedBootReadIsDeviceLocked;
zircon_boot_ops.verified_boot_read_permanent_attributes = ReadPermanentAttributes;
zircon_boot_ops.verified_boot_read_permanent_attributes_hash = ReadPermanentAttributesHash;
return zircon_boot_ops;
}
} // namespace gigaboot