Newer
Older
minerva / Kernel / Arch / riscv64 / pre_init.cpp
@minerva minerva on 13 Jul 2 KB Initial commit
/*
 * Copyright (c) 2023, Sönke Holz <sholz8530@gmail.com>
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#include <Kernel/Arch/riscv64/pre_init.h>

#include <Kernel/Arch/Processor.h>
#include <Kernel/Arch/riscv64/MMU.h>
#include <Kernel/Arch/riscv64/SBI.h>
#include <Kernel/Sections.h>

#include <LibELF/Relocation.h>

namespace Kernel {

UNMAP_AFTER_INIT void dbgln_without_mmu(StringView message)
{
    auto probe_result = SBI::Base::probe_extension(SBI::EID::DebugConsole);
    if (probe_result.is_error() || probe_result.value() == 0) {
        for (auto const ch : message.bytes())
            (void)SBI::Legacy::console_putchar(ch);
        (void)SBI::Legacy::console_putchar('\n');
    } else {
        for (auto const ch : message.bytes())
            (void)SBI::DBCN::debug_console_write_byte(ch);
        (void)SBI::DBCN::debug_console_write_byte('\n');
    }
}

[[noreturn]] UNMAP_AFTER_INIT void panic_without_mmu(StringView message)
{
    dbgln_without_mmu("KERNEL PANIC in pre_init :^("sv);
    dbgln_without_mmu(message);

    // We can't use Processor::halt() here, as that would result in an absolute jump.
    RISCV64::CSR::write<RISCV64::CSR::Address::SIE>(0);
    for (;;)
        asm volatile("wfi");
}

[[gnu::aligned(4)]] [[noreturn]] UNMAP_AFTER_INIT static void early_trap_handler()
{
    panic_without_mmu("Unexpected trap"sv);
}

static UNMAP_AFTER_INIT PhysicalPtr physical_load_base()
{
    PhysicalPtr physical_load_base;

    asm volatile(
        "lla %[physical_load_base], start_of_kernel_image\n"
        : [physical_load_base] "=r"(physical_load_base));

    return physical_load_base;
}

static UNMAP_AFTER_INIT PhysicalPtr dynamic_section_addr()
{
    PhysicalPtr dynamic_section_addr;

    // Use lla explicitly to prevent a GOT load.
    asm volatile(
        "lla %[dynamic_section_addr], _DYNAMIC\n"
        : [dynamic_section_addr] "=r"(dynamic_section_addr));

    return dynamic_section_addr;
}

extern "C" [[noreturn]] UNMAP_AFTER_INIT void pre_init(FlatPtr boot_hart_id, PhysicalPtr flattened_devicetree_paddr)
{
    // Apply relative relocations as if we were running at KERNEL_MAPPING_BASE.
    // This means that all global variables must be accessed with adjust_by_mapping_base, since we are still running identity mapped.
    // Otherwise, we would have to relocate twice: once while running identity mapped, and again when we enable the MMU.
    if (!ELF::perform_relative_relocations(physical_load_base(), KERNEL_MAPPING_BASE, dynamic_section_addr()))
        panic_without_mmu("Failed to perform relative relocations"sv);

    // Catch traps in pre_init
    RISCV64::CSR::write<RISCV64::CSR::Address::STVEC>(bit_cast<FlatPtr>(&early_trap_handler));

    Memory::init_page_tables_and_jump_to_init(boot_hart_id, flattened_devicetree_paddr);
}

}