Files
serenity/Kernel/Arch/aarch64/pre_init.cpp
T
Sönke Holz e6ae1993ea Kernel: Simplify pre_init self-relocation a bit
Now that all declarations have hidden visibility, we don't need to use
inline asm to avoid GOT references here anymore.
2026-08-05 15:39:22 +02:00

81 lines
3.1 KiB
C++

/*
* Copyright (c) 2023, Timon Kruiper <[email protected]>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/StringView.h>
#include <AK/Types.h>
#include <Kernel/Arch/aarch64/CPU.h>
#include <Kernel/Sections.h>
#include <LibELF/Relocation.h>
// We arrive here from boot.S with the MMU disabled and in an unknown exception level (EL).
// The kernel is linked at the virtual address, so we have to be really carefull when accessing
// global variables, as the MMU is not yet enabled.
// FIXME: This should probably be shared with the Prekernel.
namespace Kernel {
extern "C" u8 start_of_kernel_image[];
extern "C" u8 _DYNAMIC[];
extern "C" [[noreturn]] void init();
extern "C" [[noreturn]] void pre_init(PhysicalPtr flattened_devicetree_paddr);
extern "C" [[noreturn]] void pre_init(PhysicalPtr flattened_devicetree_paddr)
{
// Apply relative relocations as if we were running at KERNEL_MAPPING_BASE.
// This means that we shouldn't access anything during pre_init that relies on relocations (e.g. vtables).
// Otherwise, we would have to relocate twice: once while running identity mapped, and again when we enable the MMU.
auto physical_load_base = bit_cast<PhysicalPtr>(+start_of_kernel_image);
auto dynamic_section_addr = bit_cast<PhysicalPtr>(+_DYNAMIC);
if (!ELF::perform_relative_relocations(physical_load_base, KERNEL_MAPPING_BASE, dynamic_section_addr))
panic_without_mmu("Failed to perform relative relocations"sv);
// We want to drop to EL1 as soon as possible, because that is the
// exception level the kernel should run at.
initialize_exceptions();
// Next step is to set up page tables and enable the MMU.
Memory::init_page_tables(flattened_devicetree_paddr);
// At this point the MMU is enabled, physical memory is identity mapped,
// and the kernel is also mapped into higher virtual memory. However we are still executing
// from the physical memory address, so we have to jump to the kernel in high memory. We also need to
// switch the stack pointer to high memory, such that we can unmap the identity mapping.
// Continue execution at high virtual address.
asm volatile(
"adrp x0, 1f \n"
"add x0, x0, :lo12:1f \n"
"add x0, x0, %[base] \n"
"br x0 \n"
"1: \n" ::[base] "r"(g_boot_info.physical_to_virtual_offset)
: "x0");
// Add kernel_mapping_base to the stack pointer, such that it is also using the mapping
// in high virtual memory.
asm volatile(
"mov x0, %[base] \n"
"add sp, sp, x0 \n" ::[base] "r"(g_boot_info.physical_to_virtual_offset)
: "x0");
// We can now unmap the identity map as everything is running in high virtual memory at this point.
Memory::unmap_identity_map();
// Clear the frame pointer (x29) and link register (x30) to make sure the kernel cannot backtrace
// into this code, and jump to actual init function in the kernel.
register FlatPtr const x0 asm("x0") = bit_cast<FlatPtr>(&g_boot_info);
asm volatile(
"mov x29, xzr \n"
"mov x30, xzr \n"
"b init \n" ::"r"(x0));
VERIFY_NOT_REACHED();
}
}