Files
MontaukOS/kernel/src/Arch/aarch64/Platform.cpp
T

141 lines
4.9 KiB
C++

/*
* Platform.cpp
* aarch64 bring-up hooks (implements Arch/Platform.hpp)
* Copyright (c) 2026 Daniel Hammer
*/
#include <Arch/Platform.hpp>
#include <Arch/Cpu.hpp>
#include <Arch/Smp.hpp>
#include <Arch/aarch64/Gic.hpp>
#include <Arch/aarch64/Bringup.hpp>
#include <ACPI/FADT.hpp>
#include <Terminal/Terminal.hpp>
#include <CppLib/Stream.hpp>
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);
}
}
}