blob: 6ac9f360a92ecc87550ab4cb0f89bae9d5edb805 [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 <inttypes.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/array.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/main.h>
#include <phys/stdio.h>
#include <phys/symbolize.h>
#include <phys/uart.h>
#include "handoff-prep.h"
#include <ktl/enforce.h>
#ifdef __x86_64__
#include <phys/trampoline-boot.h>
using ChainBoot = TrampolineBoot;
#else
using ChainBoot = BootZbi;
#endif
#include <ktl/enforce.h>
namespace {
PhysBootTimes gBootTimes;
// 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) {
// Now we select our kernel ZBI.
auto it = kernelfs.find({kDefaultKernelPackage, 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>(kDefaultKernelPackage.size()), kDefaultKernelPackage.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()};
// Patch the kernel image in the BOOTFS in place before loading it.
code_patching::Patcher patcher;
if (auto result = patcher.Init(ktl::move(kernelfs), kDefaultKernelPackage); result.is_error()) {
printf("physboot: Failed to initialize code patching: ");
code_patching::PrintPatcherError(result.error_value());
abort();
}
ArchPatchCode(ktl::move(patcher), kernel_bytes, KERNEL_LINK_ADDRESS);
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();
}
if (auto result = boot.Load(kKernelBssEstimate); result.is_error()) {
printf("physboot: Cannot load decompressed kernel: ");
zbitl::PrintViewCopyError(result.error_value());
abort();
}
return boot;
}
[[noreturn]] void BootZircon(KernelStorage kernel_storage) {
ChainBoot boot = LoadZirconZbi(kernel_storage.GetBootfs());
// 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().storage() = {
const_cast<ktl::byte*>(kernel_storage.zbi().storage().data()),
kernel_storage.zbi().storage().size(),
};
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;
}
// Prepare the handoff data structures.
HandoffPrep prep;
prep.Init(handoff_item->payload);
// Hand off the boot options first, which don't really change. But keep a
// mutable reference to update boot_options.serial later to include live
// driver state and not just configuration like other BootOptions members do.
BootOptions& handoff_options = prep.SetBootOptions(*gBootOptions);
// Use the updated copy from now on.
gBootOptions = &handoff_options;
prep.SummarizeMiscZbiItems(boot.DataZbi().storage());
prep.SetInstrumentation();
gBootTimes.SampleNow(PhysBootTimes::kZbiDone);
// Now that all time samples have been collected, copy gBootTimes into the
// hand-off.
prep.handoff()->times = gBootTimes;
// Copy any post-Init() serial state from the live driver here in physboot
// into the handoff BootOptions. There should be no more printing from here
// on. TODO(fxbug.dev/84107): Actually there is some printing in BootZbi,
// but no current drivers carry post-Init() state so it's harmless for now.
GetUartDriver().Visit(
[&handoff_options](const auto& driver) { handoff_options.serial = driver.uart(); });
// 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(prep.handoff());
}
} // namespace
void ZbiMain(void* zbi_ptr, arch::EarlyTicks ticks) {
MainSymbolize symbolize("physboot");
InitMemory(zbi_ptr);
gBootTimes.Set(PhysBootTimes::kZbiEntry, ticks);
// This marks the interval between handoff from the boot loader (kZbiEntry)
// and phys environment setup with identity-mapped memory management et al.
gBootTimes.SampleNow(PhysBootTimes::kPhysSetup);
auto zbi_header = static_cast<zbi_header_t*>(zbi_ptr);
auto zbi = zbitl::StorageFromRawHeader<ktl::span<ktl::byte>>(zbi_header);
// Unpack the compressed KERNEL_STORAGE payload.
KernelStorage kernel_storage;
kernel_storage.Init(zbitl::View{zbi});
kernel_storage.GetTimes(gBootTimes);
// TODO(mcgrathr): Bloat the binary so the total kernel.zbi size doesn't
// get too comfortably small while physboot functionality is still growing.
static const ktl::array<char, 512 * 1024> kPad{1};
__asm__ volatile("" ::"m"(kPad), "r"(kPad.data()));
BootZircon(ktl::move(kernel_storage));
}