blob: d3e17a0ea8f5fd7b89a8e5cb31bdd587240b22c3 [file] [log] [blame]
// Copyright 2023 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/arch/cache.h>
#include <lib/boot-options/boot-options.h>
#include <lib/code-patching/code-patches.h>
#include <lib/fit/function.h>
#include <lib/zbitl/error-stdio.h>
#include <stdlib.h>
#include <zircon/assert.h>
#include <arch/code-patches/case-id.h>
#include <arch/kernel_aspace.h>
#include <ktl/initializer_list.h>
#include <ktl/move.h>
#include <ktl/string_view.h>
#include <phys/allocation.h>
#include <phys/boot-zbi.h>
#include <phys/elf-image.h>
#include <phys/handoff.h>
#include <phys/kernel-package.h>
#include <phys/stdio.h>
#include <phys/symbolize.h>
#include "handoff-prep.h"
#include "physboot.h"
#include "physload.h"
#include <ktl/enforce.h>
namespace {
constexpr ktl::string_view kElfPhysKernel = "physzircon";
void PatchElfKernel(ElfImage& elf_kernel, const ArchPatchInfo& patch_info) {
auto apply_patch = [&patch_info](
code_patching::Patcher& patcher, CodePatchId id,
ktl::span<ktl::byte> code_to_patch,
ElfImage::PrintPatchFunction print) -> fit::result<ElfImage::Error> {
if (ArchPatchCode(patcher, patch_info, code_to_patch, id, ktl::move(print))) {
return fit::ok();
}
print({"unrecognized patch case ID"});
ZX_PANIC("%s: code-patching: unrecognized patch case ID %" PRIu32, gSymbolize->name(),
static_cast<uint32_t>(id));
};
debugf("%s: Applying %zu patches...\n", gSymbolize->name(), elf_kernel.patch_count());
// Apply patches to the kernel image.
auto result = elf_kernel.ForEachPatch<CodePatchId>(apply_patch);
if (result.is_error()) {
zbitl::PrintBootfsError(result.error_value());
abort();
}
// There's always the self-test patch, so there should never be none.
ZX_ASSERT(elf_kernel.has_patches());
}
void RelocateElfKernel(ElfImage& elf_kernel) {
debugf("%s: Relocating ELF kernel to [%#" PRIx64 ", %#" PRIx64 ")...\n", gSymbolize->name(),
elf_kernel.load_address(), elf_kernel.load_address() + elf_kernel.vaddr_size());
elf_kernel.Relocate();
}
} // namespace
PhysBootTimes gBootTimes;
[[noreturn]] void PhysLoadModuleMain(UartDriver& uart, PhysBootTimes boot_times,
KernelStorage kernel_storage) {
gBootTimes = boot_times;
gSymbolize->set_name("physboot");
// Now we're ready for the main physboot logic.
BootZircon(uart, ktl::move(kernel_storage));
}
[[noreturn]] void BootZircon(UartDriver& uart, KernelStorage kernel_storage) {
KernelStorage::Bootfs package = kernel_storage.GetKernelPackage();
ElfImage elf_kernel;
debugf("%s: Locating ELF kernel in kernel package...\n", gSymbolize->name());
if (auto result = elf_kernel.Init(package, kElfPhysKernel, true); result.is_error()) {
printf("%s: Cannot load ELF kernel \"%.*s/%.*s\" from STORAGE_KERNEL item BOOTFS: ",
gSymbolize->name(), static_cast<int>(package.directory().size()),
package.directory().data(), static_cast<int>(kElfPhysKernel.size()),
kElfPhysKernel.data());
zbitl::PrintBootfsError(result.error_value());
abort();
}
// Make sure the kernel was built to match this physboot binary.
elf_kernel.AssertInterpMatchesBuildId(gSymbolize->name(), gSymbolize->build_id());
// Use the putative eventual virtual address to relocate the kernel.
const uint64_t kernel_vaddr = kArchHandoffVirtualAddress;
Allocation loaded_elf_kernel = elf_kernel.Load(kernel_vaddr);
const ArchPatchInfo patch_info = ArchPreparePatchInfo();
PatchElfKernel(elf_kernel, patch_info);
RelocateElfKernel(elf_kernel);
// Prepare the handoff data structures. Repurpose the storage item as a
// place to put the handoff payload. The KERNEL_STORAGE payload was already
// decompressed elsewhere, so it's no longer in use.
debugf("%s: Preparing handoff data in payload at [%p, %p)\n", gSymbolize->name(),
kernel_storage.item()->payload.data(),
kernel_storage.item()->payload.data() + kernel_storage.item()->payload.size());
HandoffPrep prep;
prep.Init(kernel_storage.item()->payload);
// For now we're loading an ELF kernel in physical address mode at an
// arbitrary load address, even though it's been relocated for its final
// virtual address. The kernel's entry point is expected to be purely
// position independent long enough to switch to virtual addressing.
//
// NOTE: For real handoff with virtual addresses, this will need some inline
// asm to switch stacks and such. For interim hack kernels doing physical
// address mode handoff, they can either use the phys stack momentarily
// or have asm entry code that sets up its own stack.
elf_kernel.set_load_address(elf_kernel.physical_load_address());
debugf("%s: Ready to hand off at physical load address %#" PRIxPTR ", entry %#" PRIx64 "...\n",
gSymbolize->name(), elf_kernel.load_address(), elf_kernel.entry());
if (gBootOptions->phys_verbose) {
Allocation::GetPool().PrintMemoryRanges(gSymbolize->name());
}
auto start_elf_kernel = [&elf_kernel](PhysHandoff* handoff) {
#ifndef __x86_64__
// This runs in an identity-mapped environment, so the MMU can be safely
// turned off. The physzircon kernel entry code expects the MMU to be off.
arch::DisableMmu();
#endif
elf_kernel.Handoff<void(PhysHandoff*)>(handoff);
};
prep.DoHandoff(uart, kernel_storage.zbi().storage(), package, patch_info, start_elf_kernel);
}