/* * Platform.cpp * aarch64 bring-up hooks (implements Arch/Platform.hpp) * Copyright (c) 2026 Daniel Hammer */ #include #include #include #include #include #include #include #include using namespace Kt; extern "C" char ArchExceptionVectors[]; // Vectors.S namespace Arch { // SCTLR_EL1.WXN: writable memory is execute-never static constexpr uint64_t SctlrWxn = 1ULL << 19; // SCTLR_EL1.UCT/UCI: EL0 may read CTR_EL0 and clean/invalidate caches // by address (what __builtin___clear_cache needs for generated code). static constexpr uint64_t SctlrUct = 1ULL << 15; static constexpr uint64_t SctlrUci = 1ULL << 26; // PSCI 0.2+ function IDs (SMC32 calling convention) static constexpr uint64_t PsciSystemOff = 0x84000008; static constexpr uint64_t PsciSystemReset = 0x84000009; // How the firmware takes PSCI calls, from the FADT's ARM_BOOT_ARCH. enum class PsciConduit { None, Smc, Hvc }; static PsciConduit g_psci = PsciConduit::None; static uint64_t PsciCall(uint64_t function) { register uint64_t x0 asm("x0") = function; register uint64_t x1 asm("x1") = 0; register uint64_t x2 asm("x2") = 0; register uint64_t x3 asm("x3") = 0; if (g_psci == PsciConduit::Hvc) { asm volatile("hvc #0" : "+r"(x0), "+r"(x1), "+r"(x2), "+r"(x3) :: "memory"); } else if (g_psci == PsciConduit::Smc) { asm volatile("smc #0" : "+r"(x0), "+r"(x1), "+r"(x2), "+r"(x3) :: "memory"); } return x0; } static void InitializePsci(Hal::ACPI::CommonSDTHeader* xsdt) { Hal::FADT::ParsedFADT fadt{}; if (!Hal::FADT::Parse(xsdt, fadt) || !fadt.Valid) return; if (!(fadt.ArmBootArchFlags & Hal::FADT::ArmBootPsciCompliant)) { KernelLogStream(WARNING, "PSCI") << "Firmware is not PSCI compliant: no reset or power-off"; return; } g_psci = (fadt.ArmBootArchFlags & Hal::FADT::ArmBootPsciUseHvc) ? PsciConduit::Hvc : PsciConduit::Smc; KernelLogStream(OK, "PSCI") << "PSCI through " << (g_psci == PsciConduit::Hvc ? "HVC" : "SMC"); } void InitializeCpuRegisters() { // Run on SP_EL1 so exceptions taken from the kernel arrive on the // stack it is already using (Vectors.S has no SP_EL0 handlers). asm volatile("mov x9, sp\n\tmsr spsel, #1\n\tmov sp, x9" ::: "x9", "memory"); // The vectors need no allocation, so take over from the // bootloader's right away. asm volatile("msr vbar_el1, %0\n\tisb" :: "r"(ArchExceptionVectors) : "memory"); // Kernel mappings are RWX (Paging.cpp), so writable must not // imply execute-never; user code may maintain its own caches. uint64_t sctlr; asm volatile("mrs %0, sctlr_el1" : "=r"(sctlr)); sctlr &= ~SctlrWxn; sctlr |= SctlrUct | SctlrUci; asm volatile("msr sctlr_el1, %0\n\tisb" :: "r"(sctlr)); // CPACR_EL1.FPEN = 0b11: FP/SIMD usable at EL0 and EL1 (the kernel // itself is built -mgeneral-regs-only; Context.S saves user state). uint64_t cpacr; asm volatile("mrs %0, cpacr_el1" : "=r"(cpacr)); cpacr |= 3ULL << 20; asm volatile("msr cpacr_el1, %0\n\tisb" :: "r"(cpacr)); } void EarlyInitialize() { // No per-CPU data yet: Smp::TryGetCurrentCpuData must see null. asm volatile("msr tpidr_el1, xzr"); InitializeCpuRegisters(); } // Installed by EarlyInitialize. void InitializeTraps() { KernelLogStream(OK, "Arch") << "Installed exception vectors at 0x" << base::hex << (uint64_t)ArchExceptionVectors; } // MAIR_EL1 attribute indices are chosen together with the page-table // format (Paging.cpp), so there is nothing to program separately. void InitializeMemoryAttributes() { } void InitializeInterrupts(Hal::ACPI::CommonSDTHeader* xsdt) { Smp::InitBsp(); if (!Gic::Initialize(xsdt)) { KernelLogStream(ERROR, "Arch") << "aarch64: no interrupt controller, running without IRQs"; } InitializePsci(xsdt); } void InitializePlatformDevices() { } // EL0 entry needs no global state beyond Arch::ActivateThread. void PrepareUserMode() { } void LateInitialize() { } void ListPlatformDevices(PlatformDeviceSink sink, void* context) { if (Gic::IsInitialized()) { sink(context, 1, "GICv2", "ARM Generic Interrupt Controller"); sink(context, 2, "Generic Timer", "ARM architected timer (EL1 virtual)"); } } void Reboot() { PsciCall(PsciSystemReset); HaltForever(); } void PowerOff() { if (g_psci != PsciConduit::None) { PsciCall(PsciSystemOff); } } }