blob: 6ee37790fd1f27eab6b423dec9cab9f1b1623285 [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 "physload.h"
#include <inttypes.h>
#include <lib/boot-options/boot-options.h>
#include <lib/elfldltl/diagnostics.h>
#include <lib/elfldltl/dynamic.h>
#include <lib/elfldltl/link.h>
#include <lib/elfldltl/memory.h>
#include <lib/elfldltl/relocation.h>
#include <lib/zbitl/error-stdio.h>
#include <lib/zbitl/view.h>
#include <ktl/array.h>
#include <ktl/byte.h>
#include <ktl/move.h>
#include <ktl/span.h>
#include <ktl/string_view.h>
#include <phys/address-space.h>
#include <phys/elf-image.h>
#include <phys/handoff.h>
#include <phys/kernel-package.h>
#include <phys/main.h>
#include <phys/stdio.h>
#include <phys/symbolize.h>
#include "log.h"
#include <ktl/enforce.h>
namespace {
// physload, physboot, EL2, kernel
constexpr size_t kMaxPhysloadModules = 4;
void LogSerial(FILE* out = stdout) {
fprintf(out, "%s: Console configured as ", ProgramName());
gBootOptions->Show("kernel.serial"sv, false, out);
}
} // namespace
[[noreturn]] void ZbiMain(void* zbi_ptr, arch::EarlyTicks ticks) {
PhysBootTimes times;
times.Set(PhysBootTimes::kZbiEntry, ticks);
MainSymbolize symbolize("physload");
if (gBootOptions->phys_verbose) {
symbolize.Context();
LogSerial();
}
AddressSpace aspace;
InitMemory(zbi_ptr, &aspace);
// This marks the interval between handoff from the boot loader (kZbiEntry)
// and phys environment setup with identity-mapped memory management et al.
times.SampleNow(PhysBootTimes::kPhysSetup);
// Start collecting the log in memory as well as logging to the console.
Log log;
{
// Prime the log with what would already have been written to the console
// under kernel.phys.verbose=true (even if it wasn't), but don't send that
// to the console.
FILE log_file{&log};
symbolize.ContextAlways(&log_file);
LogSerial(&log_file);
Allocation::GetPool().PrintMemoryRanges(symbolize.name(), &log_file);
}
// Now mirror all stdout to the log, and write debugf there even if verbose
// logging to stdout is disabled.
gLog = &log;
log.SetStdout();
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(times);
// TODO: add some time samples around bootfs decoding, loading, etc.
KernelStorage::Bootfs bootfs = kernel_storage.root();
// Provide space for loading modules.
ktl::array<const ElfImage*, kMaxPhysloadModules> modules_storage;
symbolize.ReplaceModulesStorage(Symbolize::ModuleList(modules_storage));
ktl::string_view next_file_name = gBootOptions->phys_next.data();
// Load up the next module.
ElfImage next_elf;
if (auto result = next_elf.Init(bootfs, next_file_name, true); result.is_error()) {
zbitl::PrintBootfsError(result.error_value());
abort();
}
// We don't support any code-patching for physboot - or any physload module
// at the moment - though we could if there were any worth doing.
ZX_ASSERT_MSG(!next_elf.has_patches(),
"kernel.phys.next ELF image with code-patches not supported");
// Load the image, in place if space or copied elsewhere if not.
Allocation loaded = next_elf.Load();
// Relocate the image.
next_elf.Relocate();
next_elf.AssertInterpMatchesBuildId(symbolize.name(), symbolize.build_id());
if (gBootOptions->phys_verbose) {
symbolize.LogHandoff(next_elf.name(), next_elf.entry());
}
// Call into the entry point. It must not return, but it will keep using the
// same stack so it can safely take references to our stack objects.
next_elf.Handoff<PhysLoadHandoffFunction>(next_elf, &log, gArchPhysInfo, GetUartDriver(),
&symbolize, gBootOptions, Allocation::GetPool(),
gAddressSpace, times, ktl::move(kernel_storage));
}