blob: cafe980cfd72c4542f7f75b03468a4f6f15d8172 [file] [log] [blame]
// Copyright 2021 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 "physboot.h"
#include <inttypes.h>
#include <lib/arch/zbi-boot.h>
#include <lib/boot-options/boot-options.h>
#include <lib/code-patching/code-patches.h>
#include <lib/code-patching/code-patching.h>
#include <lib/memalloc/range.h>
#include <lib/zbitl/error-stdio.h>
#include <stdio.h>
#include <zircon/assert.h>
#include <ktl/move.h>
#include <phys/allocation.h>
#include <phys/boot-zbi.h>
#include <phys/handoff.h>
#include <phys/kernel-package.h>
#include <phys/stdio.h>
#include <phys/symbolize.h>
#include <phys/uart.h>
#include "handoff-prep.h"
#include "log.h"
#ifdef __x86_64__
#include <phys/trampoline-boot.h>
using ChainBoot = TrampolineBoot;
#else
using ChainBoot = BootZbi;
#endif
#include <ktl/enforce.h>
namespace {
// A guess about the upper bound on reserve_memory_size so we can do a single
// allocation before decoding the header and probably not need to relocate.
constexpr uint64_t kKernelBssEstimate = 1024 * 1024 * 2;
ChainBoot LoadZirconZbi(KernelStorage::Bootfs kernelfs, const ArchPatchInfo& patch_info) {
// Now we select our kernel ZBI.
debugf("%s: Locating ZBI file in kernel package...\n", gSymbolize->name());
auto it = kernelfs.find(kKernelZbiName);
if (auto result = kernelfs.take_error(); result.is_error()) {
printf("physboot: Error in looking for kernel ZBI within STORAGE_KERNEL item: ");
zbitl::PrintBootfsError(result.error_value());
abort();
}
if (it == kernelfs.end()) {
printf("physboot: Could not find kernel ZBI (%.*s/%.*s) within STORAGE_KERNEL item\n",
static_cast<int>(kernelfs.directory().size()), kernelfs.directory().data(),
static_cast<int>(kKernelZbiName.size()), kKernelZbiName.data());
abort();
}
ktl::span<ktl::byte> kernel_bytes = {const_cast<ktl::byte*>(it->data.data()), it->data.size()};
debugf("%s: Found %zu-byte kernel ZBI, locating patches...\n", gSymbolize->name(),
kernel_bytes.size());
// Patch the kernel image in the BOOTFS in place before loading it.
code_patching::Patcher patcher;
if (auto result = patcher.Init(kernelfs); result.is_error()) {
printf("physboot: Failed to initialize code patching: ");
code_patching::PrintPatcherError(result.error_value());
abort();
}
debugf("%s: Applying %zu patches...\n", gSymbolize->name(), patcher.patches().size());
// There's always the self-test patch, so there should never be none.
ZX_ASSERT(!patcher.patches().empty());
for (const code_patching::Directive& patch : patcher.patches()) {
ZX_ASSERT(patch.range_start >= KERNEL_LINK_ADDRESS);
ZX_ASSERT(patch.range_size <= kernel_bytes.size());
ZX_ASSERT(kernel_bytes.size() - patch.range_size >= patch.range_start - KERNEL_LINK_ADDRESS);
ktl::span<ktl::byte> insns =
kernel_bytes.subspan(patch.range_start - KERNEL_LINK_ADDRESS, patch.range_size);
auto print = [patch](ktl::initializer_list<ktl::string_view> strings) {
printf("%s: code-patching: ", ProgramName());
for (ktl::string_view str : strings) {
stdout->Write(str);
}
printf(": [%#" PRIx64 ", %#" PRIx64 ")\n", patch.range_start,
patch.range_start + patch.range_size);
};
if (!ArchPatchCode(patcher, patch_info, insns, static_cast<CodePatchId>(patch.id), print)) {
ZX_PANIC("%s: code-patching: unrecognized patch case ID: %" PRIu32 ": [%#" PRIx64
", %#" PRIx64 ")",
ProgramName(), patch.id, patch.range_start, patch.range_start + patch.range_size);
}
}
debugf("%s: Examining ZBI...\n", gSymbolize->name());
BootZbi::InputZbi kernel_zbi(kernel_bytes);
ChainBoot boot;
if (auto result = boot.Init(kernel_zbi); result.is_error()) {
printf("physboot: Cannot read STORAGE_KERNEL item ZBI: ");
zbitl::PrintViewCopyError(result.error_value());
abort();
}
debugf("%s: Loading ZBI kernel...\n", gSymbolize->name());
if (auto result = boot.Load(kKernelBssEstimate); result.is_error()) {
printf("physboot: Cannot load decompressed kernel: ");
zbitl::PrintViewCopyError(result.error_value());
abort();
}
return boot;
}
} // namespace
[[noreturn]] void BootZircon(UartDriver& uart, KernelStorage kernel_storage) {
KernelStorage::Bootfs package = kernel_storage.GetKernelPackage();
const ArchPatchInfo patch_info = ArchPreparePatchInfo();
ChainBoot boot = LoadZirconZbi(package, patch_info);
// Repurpose the storage item as a place to put the handoff payload.
KernelStorage::Zbi::iterator handoff_item = kernel_storage.item();
// `boot`'s data ZBI at this point is the tail of the decompressed kernel
// ZBI; overwrite that with the original data ZBI.
boot.DataZbi() = kernel_storage.zbi();
Allocation relocated_zbi;
if (boot.MustRelocateDataZbi()) {
// Actually, the original data ZBI must be moved elsewhere since it
// overlaps the space where the fixed-address kernel will be loaded.
fbl::AllocChecker ac;
relocated_zbi =
Allocation::New(ac, memalloc::Type::kDataZbi, kernel_storage.zbi().storage().size(),
arch::kZbiBootDataAlignment);
if (!ac.check()) {
printf("physboot: Cannot allocate %#zx bytes aligned to %#zx for relocated data ZBI!\n",
kernel_storage.zbi().storage().size(), arch::kZbiBootDataAlignment);
abort();
}
if (auto result = kernel_storage.zbi().Copy(relocated_zbi.data(), kernel_storage.zbi().begin(),
kernel_storage.zbi().end());
result.is_error()) {
kernel_storage.zbi().ignore_error();
printf("physboot: Failed to relocate data ZBI: ");
zbitl::PrintViewCopyError(result.error_value());
printf("\n");
abort();
}
ZX_ASSERT(kernel_storage.zbi().take_error().is_ok());
// Rediscover the handoff item's new location in memory.
ChainBoot::Zbi relocated_image(relocated_zbi.data());
auto it = relocated_image.begin();
while (it != relocated_image.end() && it.item_offset() < handoff_item.item_offset()) {
++it;
}
ZX_ASSERT(it != relocated_image.end());
ZX_ASSERT(relocated_image.take_error().is_ok());
boot.DataZbi() = ktl::move(relocated_image);
handoff_item = it;
}
ktl::span zbi = boot.DataZbi().storage();
// Prepare the handoff data structures.
HandoffPrep prep;
prep.Init(handoff_item->payload);
prep.DoHandoff(uart, zbi, package, patch_info, [&boot](PhysHandoff* handoff) {
// Even though the kernel is still a ZBI and mostly using the ZBI protocol
// for booting, the PhysHandoff pointer (physical address) is now the
// argument to the kernel, not the data ZBI address.
boot.Boot(handoff);
});
}