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