feat: make kernel architecture-agnostic; primitive work on arm port

This commit is contained in:
daniel committed 2026-10-24 21:25:18 +02:00
1 parent dab7d5427b
commit 1bae77ec84
164 files changed
+3664 -1641

No files matched your search

+120
View File
@@ -0,0 +1,120 @@
/*
* Cpu.hpp
* aarch64 implementation of the Arch/Cpu.hpp primitives
* Copyright (c) 2026 Daniel Hammer
*/
#pragma once
#include <cstdint>
namespace Arch {
// PSTATE.I in the DAIF view
static constexpr uint64_t DaifIrqMask = 1ULL << 7;
inline void Pause() {
asm volatile("yield" ::: "memory");
}
// Device-nGnRE / Normal-NC (write-combining) stores complete in order
// once a DSB has been issued.
inline void StoreFence() {
asm volatile("dsb st" ::: "memory");
}
inline void MemoryFence() {
asm volatile("dsb sy" ::: "memory");
}
inline void EnableInterrupts() {
asm volatile("msr daifclr, #2" ::: "memory");
}
inline void DisableInterrupts() {
asm volatile("msr daifset, #2" ::: "memory");
}
inline bool InterruptsEnabled() {
uint64_t daif;
asm volatile("mrs %0, daif" : "=r"(daif));
return (daif & DaifIrqMask) == 0;
}
inline IrqFlags SaveAndDisableInterrupts() {
uint64_t daif;
asm volatile("mrs %0, daif\n\tmsr daifset, #2" : "=r"(daif) :: "memory");
return daif;
}
inline void RestoreInterrupts(IrqFlags flags) {
asm volatile("msr daif, %0" :: "r"(flags) : "memory");
}
inline void Halt() {
asm volatile("wfi" ::: "memory");
}
// WFI wakes on a pending interrupt even while PSTATE.I masks it, so
// checking for work and then waiting with IRQs masked cannot lose a
// wakeup. Unmask briefly afterwards so the pending IRQ is taken.
inline void HaltWithInterruptsDisabled() {
asm volatile("wfi\n\tmsr daifclr, #2\n\tisb\n\tmsr daifset, #2" ::: "memory");
}
[[noreturn]] inline void HaltForever() {
for (;;) {
asm volatile("msr daifset, #0xf\n\twfi" ::: "memory");
}
}
inline uint64_t ReadTimestamp() {
uint64_t value;
asm volatile("isb\n\tmrs %0, cntvct_el0" : "=r"(value) :: "memory");
return value;
}
// The generic timer runs from boot at CNTFRQ_EL0 Hz, so it times short
// delays without calibration.
inline void IoDelay() {
uint64_t frequency;
asm volatile("mrs %0, cntfrq_el0" : "=r"(frequency));
uint64_t ticks = frequency / 1000000 + 1;
uint64_t start = ReadTimestamp();
while (ReadTimestamp() - start < ticks) {
Pause();
}
}
// ARMv8 has no "clean the whole cache" instruction: set/way maintenance
// is only safe from firmware, which PSCI performs on power transitions.
// Order outstanding accesses so the firmware call sees them.
inline void WritebackInvalidateCaches() {
asm volatile("dsb sy\n\tisb" ::: "memory");
}
inline void FlushTlbPage(uint64_t virtualAddress) {
asm volatile("dsb ishst\n\ttlbi vaae1is, %0\n\tdsb ish\n\tisb"
:: "r"(virtualAddress >> 12) : "memory");
}
inline void FlushTlbAll() {
asm volatile("dsb ishst\n\ttlbi vmalle1is\n\tdsb ish\n\tisb" ::: "memory");
}
// The process (lower) half of the address space is translated through
// TTBR0_EL1; the kernel half through TTBR1_EL1, which never changes.
static constexpr uint64_t TtbrAddressMask = 0x0000FFFFFFFFFFFEULL;
inline uint64_t ReadPageTableRoot() {
uint64_t ttbr;
asm volatile("mrs %0, ttbr0_el1" : "=r"(ttbr));
return ttbr & TtbrAddressMask;
}
// No ASIDs yet: every switch invalidates the old process's entries.
inline void WritePageTableRoot(uint64_t rootPhys) {
asm volatile("dsb ishst\n\tmsr ttbr0_el1, %0\n\tisb\n\ttlbi vmalle1\n\tdsb nsh\n\tisb"
:: "r"(rootPhys) : "memory");
}
}