/*
 * Copyright (c) 2023, Sönke Holz <soenke.holz@serenityos.org>
 *
 * 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 {

extern "C" u8 start_of_kernel_image[];
extern "C" u8 _DYNAMIC[];

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

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

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

}
