feat: add support for aarch64 timer/interrupts, port MontaukOS userspace
This commit is contained in:
1 parent
8b6379fb7e
commit
8c09ccef66
101 files changed
+1720
-629
No files matched your search
+3
-1
@@ -153,8 +153,10 @@ ifeq ($(ARCH),aarch64)
|
||||
override CXX += \
|
||||
-target aarch64-unknown-none
|
||||
endif
|
||||
# Plain char is signed on x86; the generic kernel is written for that.
|
||||
override SHARED_FLAGS += \
|
||||
-mgeneral-regs-only
|
||||
-mgeneral-regs-only \
|
||||
-fsigned-char
|
||||
override LDFLAGS += \
|
||||
-Wl,-m,aarch64elf
|
||||
endif
|
||||
|
||||
@@ -38,8 +38,24 @@ namespace Hal {
|
||||
return;
|
||||
}
|
||||
|
||||
// Map and parse the DSDT to find \_S5_
|
||||
auto* dsdt = (void*)Memory::HHDM(fadt.DsdtAddress);
|
||||
|
||||
AML::InitializeInterpreter(dsdt, xsdt);
|
||||
|
||||
if (AML::GetInterpreter().IsInitialized()) {
|
||||
AcpiDevices::DeviceList devices{};
|
||||
AcpiDevices::EnumerateAll(devices);
|
||||
}
|
||||
|
||||
AcpiSleep::Initialize(xsdt);
|
||||
|
||||
// Hardware-reduced ACPI (ARM) has no PM1 registers: the
|
||||
// architecture powers off instead (Arch::PowerOff).
|
||||
if ((fadt.Flags & FADT::FlagHardwareReduced) || fadt.PM1aControlBlock == 0) {
|
||||
KernelLogStream(INFO, "ACPI") << "No PM1 control block - ACPI S5 shutdown unavailable";
|
||||
return;
|
||||
}
|
||||
|
||||
AML::S5Object s5 = AML::FindS5(dsdt);
|
||||
|
||||
if (!s5.Valid) {
|
||||
@@ -55,23 +71,6 @@ namespace Hal {
|
||||
|
||||
KernelLogStream(OK, "ACPI") << "ACPI shutdown initialized (PM1a=" << base::hex
|
||||
<< (uint64_t)g_pm1aControlBlock << " SLP_TYPa=" << (uint64_t)g_slpTypA << ")";
|
||||
|
||||
// Initialize the full AML interpreter with the DSDT.
|
||||
// This loads the entire DSDT namespace, enabling device enumeration,
|
||||
// method evaluation, and field I/O for the rest of the kernel.
|
||||
AML::InitializeInterpreter(dsdt, xsdt);
|
||||
|
||||
// Enumerate ACPI devices now that the namespace is loaded
|
||||
if (AML::GetInterpreter().IsInitialized()) {
|
||||
AcpiDevices::DeviceList devices{};
|
||||
AcpiDevices::EnumerateAll(devices);
|
||||
}
|
||||
|
||||
// Initialize S3 suspend support (reads FACS and \_S3_ from namespace)
|
||||
AcpiSleep::Initialize(xsdt);
|
||||
|
||||
// Note: ACPI event handling (SCI, power button) is initialized
|
||||
// from Main.cpp after the APIC subsystem is ready.
|
||||
}
|
||||
|
||||
bool IsAvailable() {
|
||||
|
||||
@@ -107,6 +107,8 @@ namespace Hal {
|
||||
result.AcpiEnable = fadt->AcpiEnable;
|
||||
result.AcpiDisable = fadt->AcpiDisable;
|
||||
result.Flags = fadt->Flags;
|
||||
result.ArmBootArchFlags = fadt->Header.Length >= offsetof(Table, ArmBootArchFlags) + 2
|
||||
? fadt->ArmBootArchFlags : 0;
|
||||
result.Valid = true;
|
||||
|
||||
KernelLogStream(INFO, "FADT") << "DSDT at " << base::hex << result.DsdtAddress;
|
||||
|
||||
@@ -69,6 +69,13 @@ namespace Hal {
|
||||
uint8_t X_GPE1Block[12];
|
||||
} __attribute__((packed));
|
||||
|
||||
// FADT Flags
|
||||
constexpr uint32_t FlagHardwareReduced = 1u << 20;
|
||||
|
||||
// ARM_BOOT_ARCH flags (ACPI 5.1+)
|
||||
constexpr uint16_t ArmBootPsciCompliant = 1u << 0;
|
||||
constexpr uint16_t ArmBootPsciUseHvc = 1u << 1;
|
||||
|
||||
struct ParsedFADT {
|
||||
uint64_t DsdtAddress;
|
||||
uint64_t FacsAddress;
|
||||
@@ -91,6 +98,7 @@ namespace Hal {
|
||||
uint8_t AcpiEnable;
|
||||
uint8_t AcpiDisable;
|
||||
uint32_t Flags;
|
||||
uint16_t ArmBootArchFlags; // 0 when the table predates the field
|
||||
bool Valid;
|
||||
};
|
||||
|
||||
|
||||
@@ -12,4 +12,4 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#define MONTAUK_BUILD_NUMBER 29
|
||||
#define MONTAUK_BUILD_NUMBER 37
|
||||
@@ -73,6 +73,9 @@ namespace montauk::abi {
|
||||
Hal::AcpiShutdown::Shutdown();
|
||||
}
|
||||
|
||||
/* Hardware-reduced platforms (PSCI on ARM) */
|
||||
Arch::PowerOff();
|
||||
|
||||
/* Fallback: EFI ResetSystem */
|
||||
if (Efi::g_ResetSystem) {
|
||||
Memory::VMM::ActivateKernelAddressSpace();
|
||||
|
||||
@@ -79,6 +79,11 @@ namespace Arch {
|
||||
// Write back and invalidate all CPU caches (before power state changes).
|
||||
inline void WritebackInvalidateCaches();
|
||||
|
||||
// Make instructions just written to [address, address+size) (any
|
||||
// mapping of them) visible to instruction fetch on every CPU. A no-op
|
||||
// where instruction fetch snoops data writes (x86).
|
||||
inline void SyncInstructionCache(const void* address, uint64_t size);
|
||||
|
||||
// Invalidate this CPU's translation for one virtual page.
|
||||
inline void FlushTlbPage(uint64_t virtualAddress);
|
||||
|
||||
|
||||
@@ -51,4 +51,8 @@ namespace Arch {
|
||||
// ResetSystem). Never returns.
|
||||
[[noreturn]] void Reboot();
|
||||
|
||||
// Switch the machine off where ACPI S5 does not apply (hardware-reduced
|
||||
// ACPI: ARM's PSCI SYSTEM_OFF). Returns if the platform cannot.
|
||||
void PowerOff();
|
||||
|
||||
}
|
||||
@@ -93,6 +93,21 @@ namespace Arch {
|
||||
}
|
||||
|
||||
// The TLBI operand is VA[55:12] in bits [43:0]; the bits above are RES0.
|
||||
// Clean the data cache to the point of unification, then invalidate
|
||||
// the whole instruction cache: invalidating by address would only
|
||||
// reach the alias it names on an aliasing (VIPT) instruction cache.
|
||||
inline void SyncInstructionCache(const void* address, uint64_t size) {
|
||||
uint64_t ctr;
|
||||
asm volatile("mrs %0, ctr_el0" : "=r"(ctr));
|
||||
uint64_t line = 4ULL << ((ctr >> 16) & 0xF); // DminLine
|
||||
uint64_t start = (uint64_t)address & ~(line - 1);
|
||||
uint64_t end = (uint64_t)address + size;
|
||||
for (uint64_t p = start; p < end; p += line) {
|
||||
asm volatile("dc cvau, %0" :: "r"(p) : "memory");
|
||||
}
|
||||
asm volatile("dsb ish\n\tic ialluis\n\tdsb ish\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) & ((1ULL << 44) - 1)) : "memory");
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Gic.hpp
|
||||
* aarch64 GICv2 interrupt controller (private to the port)
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <ACPI/ACPI.hpp>
|
||||
|
||||
namespace Arch::Gic {
|
||||
|
||||
// Bring up the distributor and the calling CPU's interface from the
|
||||
// ACPI MADT. Returns false when no usable GIC is described.
|
||||
bool Initialize(Hal::ACPI::CommonSDTHeader* xsdt);
|
||||
|
||||
bool IsInitialized();
|
||||
|
||||
// The XSDT Initialize was given (for the other ACPI tables the port reads).
|
||||
Hal::ACPI::CommonSDTHeader* CurrentXsdt();
|
||||
|
||||
// Per-CPU part of Initialize, for secondary CPUs.
|
||||
void InitializeCpuInterface();
|
||||
|
||||
// Map a private peripheral interrupt (16-31) onto an IRQ slot and enable
|
||||
// it on the calling CPU.
|
||||
void RoutePpi(uint32_t intid, uint8_t irq, bool edgeTriggered);
|
||||
|
||||
// Called for an IRQ slot after the interrupt is acknowledged and before
|
||||
// it is completed (EOI): level-triggered sources deassert here.
|
||||
using AckHook = void (*)();
|
||||
void SetAckHook(uint8_t irq, AckHook hook);
|
||||
|
||||
// The calling CPU's target bit, as the distributor sees it.
|
||||
uint8_t CurrentTargetMask();
|
||||
|
||||
// Find an ACPI table in the XSDT by signature.
|
||||
Hal::ACPI::CommonSDTHeader* FindTable(Hal::ACPI::CommonSDTHeader* xsdt, const char* signature);
|
||||
|
||||
}
|
||||
@@ -1,63 +1,400 @@
|
||||
/*
|
||||
* Interrupts.cpp
|
||||
* aarch64 IRQ slot dispatch (implements Arch/Interrupts.hpp)
|
||||
* aarch64 GICv2 driver and IRQ slot dispatch (implements Arch/Interrupts.hpp)
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#include <Arch/Interrupts.hpp>
|
||||
#include <Arch/Smp.hpp>
|
||||
#include <Arch/aarch64/Gic.hpp>
|
||||
#include <Memory/HHDM.hpp>
|
||||
#include <Memory/Paging.hpp>
|
||||
#include <Terminal/Terminal.hpp>
|
||||
#include <CppLib/Stream.hpp>
|
||||
|
||||
// IRQ slots are delivered by the interrupt controller driver (GICv2/v3,
|
||||
// not written yet), which maps INTIDs onto slots and calls ArchIrqDispatch
|
||||
// from the IRQ exception vector after acknowledging the interrupt.
|
||||
// IRQ slots map onto GIC interrupt IDs (INTIDs):
|
||||
// IRQ_TIMER the generic timer's PPI (Timer.cpp routes it)
|
||||
// 24-45 (MSI slots) SPIs of the GICv2m MSI frame, one per slot
|
||||
// IRQ_TLB_SHOOTDOWN,
|
||||
// IRQ_RESCHEDULE SGIs 0 and 1
|
||||
// The PC platform lines (1-23) have no ARM equivalent; RouteLegacyIrq
|
||||
// refuses them and drivers fall back to MSI or polling.
|
||||
|
||||
using namespace Kt;
|
||||
|
||||
namespace Arch {
|
||||
|
||||
// Distributor registers
|
||||
static constexpr uint32_t GICD_CTLR = 0x000;
|
||||
static constexpr uint32_t GICD_TYPER = 0x004;
|
||||
static constexpr uint32_t GICD_ISENABLER = 0x100;
|
||||
static constexpr uint32_t GICD_ICENABLER = 0x180;
|
||||
static constexpr uint32_t GICD_ICPENDR = 0x280;
|
||||
static constexpr uint32_t GICD_ICACTIVER = 0x380;
|
||||
static constexpr uint32_t GICD_IPRIORITYR = 0x400;
|
||||
static constexpr uint32_t GICD_ITARGETSR = 0x800;
|
||||
static constexpr uint32_t GICD_ICFGR = 0xC00;
|
||||
static constexpr uint32_t GICD_SGIR = 0xF00;
|
||||
|
||||
// CPU interface registers
|
||||
static constexpr uint32_t GICC_CTLR = 0x00;
|
||||
static constexpr uint32_t GICC_PMR = 0x04;
|
||||
static constexpr uint32_t GICC_BPR = 0x08;
|
||||
static constexpr uint32_t GICC_IAR = 0x0C;
|
||||
static constexpr uint32_t GICC_EOIR = 0x10;
|
||||
|
||||
// GICv2m MSI frame registers
|
||||
static constexpr uint32_t V2M_MSI_TYPER = 0x008;
|
||||
static constexpr uint32_t V2M_MSI_SETSPI_NS = 0x040;
|
||||
|
||||
static constexpr uint32_t SpuriousIntid = 1020;
|
||||
static constexpr uint32_t MaxIntids = 1020;
|
||||
static constexpr uint8_t DefaultPriority = 0xA0;
|
||||
static constexpr uint8_t NoSlot = 0xFF;
|
||||
|
||||
static constexpr uint8_t FirstMsiSlot = 24;
|
||||
static constexpr uint8_t LastMsiSlot = 45;
|
||||
|
||||
// MADT interrupt-controller structures (ACPI 6.x)
|
||||
static constexpr uint8_t MadtGicc = 0x0B;
|
||||
static constexpr uint8_t MadtGicd = 0x0C;
|
||||
static constexpr uint8_t MadtGicMsiFrame = 0x0D;
|
||||
|
||||
struct MadtEntry {
|
||||
uint8_t Type;
|
||||
uint8_t Length;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct MadtGiccEntry {
|
||||
MadtEntry Header;
|
||||
uint16_t Reserved;
|
||||
uint32_t CpuInterfaceNumber;
|
||||
uint32_t AcpiProcessorUid;
|
||||
uint32_t Flags;
|
||||
uint32_t ParkingProtocolVersion;
|
||||
uint32_t PerformanceInterruptGsiv;
|
||||
uint64_t ParkedAddress;
|
||||
uint64_t PhysicalBaseAddress; // GICC
|
||||
uint64_t Gicv;
|
||||
uint64_t Gich;
|
||||
uint32_t VgicMaintenanceInterrupt;
|
||||
uint64_t GicrBaseAddress;
|
||||
uint64_t Mpidr;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct MadtGicdEntry {
|
||||
MadtEntry Header;
|
||||
uint16_t Reserved;
|
||||
uint32_t GicId;
|
||||
uint64_t PhysicalBaseAddress;
|
||||
uint32_t SystemVectorBase;
|
||||
uint8_t GicVersion;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct MadtMsiFrameEntry {
|
||||
MadtEntry Header;
|
||||
uint16_t Reserved;
|
||||
uint32_t FrameId;
|
||||
uint64_t PhysicalBaseAddress;
|
||||
uint32_t Flags; // bit 0: SpiCount/SpiBase override TYPER
|
||||
uint16_t SpiCount;
|
||||
uint16_t SpiBase;
|
||||
} __attribute__((packed));
|
||||
|
||||
static constexpr uint32_t MadtHeaderSize = 44; // SDT header + LAPIC address + flags
|
||||
|
||||
static IrqHandler g_irqHandlers[IRQ_COUNT] = {};
|
||||
static Gic::AckHook g_ackHooks[IRQ_COUNT] = {};
|
||||
static uint8_t g_intidToSlot[MaxIntids];
|
||||
static uint32_t g_slotToIntid[IRQ_COUNT];
|
||||
|
||||
static volatile uint8_t* g_gicd = nullptr;
|
||||
static volatile uint8_t* g_gicc = nullptr;
|
||||
static uint32_t g_intidCount = 0;
|
||||
|
||||
static Hal::ACPI::CommonSDTHeader* g_xsdt = nullptr;
|
||||
|
||||
static uint64_t g_msiFramePhys = 0;
|
||||
static uint32_t g_msiSpiBase = 0;
|
||||
static uint32_t g_msiSpiCount = 0;
|
||||
|
||||
static inline uint32_t GicdRead(uint32_t reg) {
|
||||
return *(volatile uint32_t*)(g_gicd + reg);
|
||||
}
|
||||
|
||||
static inline void GicdWrite(uint32_t reg, uint32_t value) {
|
||||
*(volatile uint32_t*)(g_gicd + reg) = value;
|
||||
}
|
||||
|
||||
static inline void GicdWrite8(uint32_t reg, uint8_t value) {
|
||||
*(volatile uint8_t*)(g_gicd + reg) = value;
|
||||
}
|
||||
|
||||
static inline uint8_t GicdRead8(uint32_t reg) {
|
||||
return *(volatile uint8_t*)(g_gicd + reg);
|
||||
}
|
||||
|
||||
static inline uint32_t GiccRead(uint32_t reg) {
|
||||
return *(volatile uint32_t*)(g_gicc + reg);
|
||||
}
|
||||
|
||||
static inline void GiccWrite(uint32_t reg, uint32_t value) {
|
||||
*(volatile uint32_t*)(g_gicc + reg) = value;
|
||||
}
|
||||
|
||||
static volatile uint8_t* MapRegisters(uint64_t phys, uint64_t size) {
|
||||
for (uint64_t offset = 0; offset < size; offset += 0x1000) {
|
||||
Memory::VMM::g_paging->MapMMIO(phys + offset, Memory::HHDM(phys + offset));
|
||||
}
|
||||
return (volatile uint8_t*)Memory::HHDM(phys);
|
||||
}
|
||||
|
||||
static void SetConfig(uint32_t intid, bool edgeTriggered) {
|
||||
uint32_t reg = GICD_ICFGR + (intid / 16) * 4;
|
||||
uint32_t shift = (intid % 16) * 2 + 1;
|
||||
uint32_t value = GicdRead(reg);
|
||||
if (edgeTriggered) value |= 1u << shift;
|
||||
else value &= ~(1u << shift);
|
||||
GicdWrite(reg, value);
|
||||
}
|
||||
|
||||
static void EnableIntid(uint32_t intid) {
|
||||
GicdWrite(GICD_ISENABLER + (intid / 32) * 4, 1u << (intid % 32));
|
||||
}
|
||||
|
||||
static void Bind(uint32_t intid, uint8_t irq) {
|
||||
if (intid >= MaxIntids || irq >= IRQ_COUNT) return;
|
||||
g_intidToSlot[intid] = irq;
|
||||
g_slotToIntid[irq] = intid;
|
||||
}
|
||||
|
||||
// Route a shared peripheral interrupt to the boot CPU and unmask it.
|
||||
static void RouteSpi(uint32_t intid, uint8_t irq, bool edgeTriggered) {
|
||||
if (intid < 32 || intid >= g_intidCount) return;
|
||||
Bind(intid, irq);
|
||||
GicdWrite8(GICD_IPRIORITYR + intid, DefaultPriority);
|
||||
GicdWrite8(GICD_ITARGETSR + intid, Smp::GetCpuData(0)->arch.gicTargetMask);
|
||||
SetConfig(intid, edgeTriggered);
|
||||
EnableIntid(intid);
|
||||
}
|
||||
|
||||
namespace Gic {
|
||||
|
||||
Hal::ACPI::CommonSDTHeader* FindTable(Hal::ACPI::CommonSDTHeader* xsdt, const char* signature) {
|
||||
if (xsdt == nullptr) return nullptr;
|
||||
uint32_t count = (xsdt->Length - sizeof(Hal::ACPI::CommonSDTHeader)) / 8;
|
||||
auto* entries = (uint64_t*)((uint64_t)xsdt + sizeof(Hal::ACPI::CommonSDTHeader));
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
auto* header = (Hal::ACPI::CommonSDTHeader*)Memory::HHDM(entries[i]);
|
||||
if (header->Signature[0] == signature[0] && header->Signature[1] == signature[1]
|
||||
&& header->Signature[2] == signature[2] && header->Signature[3] == signature[3]) {
|
||||
return header;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// ITARGETSR0-7 are banked: each byte reads back the reading CPU's
|
||||
// own interface bit.
|
||||
uint8_t CurrentTargetMask() {
|
||||
uint32_t value = GicdRead(GICD_ITARGETSR);
|
||||
return (uint8_t)(value | value >> 8 | value >> 16 | value >> 24);
|
||||
}
|
||||
|
||||
void InitializeCpuInterface() {
|
||||
// SGIs and PPIs are banked per CPU: disable the PPIs until a
|
||||
// driver routes one, keep the SGIs (IPIs) enabled.
|
||||
GicdWrite(GICD_ICENABLER, 0xFFFF0000u);
|
||||
GicdWrite(GICD_ISENABLER, 0x0000FFFFu);
|
||||
GicdWrite(GICD_ICPENDR, 0xFFFFFFFFu);
|
||||
for (uint32_t intid = 0; intid < 32; intid++) {
|
||||
GicdWrite8(GICD_IPRIORITYR + intid, DefaultPriority);
|
||||
}
|
||||
|
||||
GiccWrite(GICC_PMR, 0xF0);
|
||||
GiccWrite(GICC_BPR, 0);
|
||||
GiccWrite(GICC_CTLR, 1);
|
||||
|
||||
auto* cpu = Smp::GetCurrentCpuData();
|
||||
cpu->arch.gicTargetMask = CurrentTargetMask();
|
||||
}
|
||||
|
||||
void RoutePpi(uint32_t intid, uint8_t irq, bool edgeTriggered) {
|
||||
if (intid < 16 || intid >= 32) return;
|
||||
Bind(intid, irq);
|
||||
SetConfig(intid, edgeTriggered);
|
||||
EnableIntid(intid);
|
||||
}
|
||||
|
||||
void SetAckHook(uint8_t irq, AckHook hook) {
|
||||
if (irq < IRQ_COUNT) g_ackHooks[irq] = hook;
|
||||
}
|
||||
|
||||
bool IsInitialized() {
|
||||
return g_gicc != nullptr;
|
||||
}
|
||||
|
||||
Hal::ACPI::CommonSDTHeader* CurrentXsdt() {
|
||||
return g_xsdt;
|
||||
}
|
||||
|
||||
bool Initialize(Hal::ACPI::CommonSDTHeader* xsdt) {
|
||||
g_xsdt = xsdt;
|
||||
auto* madt = FindTable(xsdt, "APIC");
|
||||
if (madt == nullptr) {
|
||||
KernelLogStream(ERROR, "GIC") << "No MADT";
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t gicdPhys = 0, giccPhys = 0;
|
||||
uint8_t version = 0;
|
||||
auto* cursor = (uint8_t*)madt + MadtHeaderSize;
|
||||
auto* end = (uint8_t*)madt + madt->Length;
|
||||
while (cursor + sizeof(MadtEntry) <= end) {
|
||||
auto* entry = (MadtEntry*)cursor;
|
||||
if (entry->Length < sizeof(MadtEntry)) break;
|
||||
|
||||
if (entry->Type == MadtGicd) {
|
||||
auto* gicd = (MadtGicdEntry*)entry;
|
||||
gicdPhys = gicd->PhysicalBaseAddress;
|
||||
version = gicd->GicVersion;
|
||||
} else if (entry->Type == MadtGicc && giccPhys == 0) {
|
||||
giccPhys = ((MadtGiccEntry*)entry)->PhysicalBaseAddress;
|
||||
} else if (entry->Type == MadtGicMsiFrame && g_msiFramePhys == 0) {
|
||||
auto* frame = (MadtMsiFrameEntry*)entry;
|
||||
g_msiFramePhys = frame->PhysicalBaseAddress;
|
||||
if (frame->Flags & 1) {
|
||||
g_msiSpiBase = frame->SpiBase;
|
||||
g_msiSpiCount = frame->SpiCount;
|
||||
}
|
||||
}
|
||||
cursor += entry->Length;
|
||||
}
|
||||
|
||||
// Version 0 means "infer from the hardware"; 1 and 2 share a
|
||||
// programming model.
|
||||
if (gicdPhys == 0 || giccPhys == 0 || version > 2) {
|
||||
KernelLogStream(ERROR, "GIC") << "No GICv2 in the MADT (version "
|
||||
<< base::dec << (uint64_t)version << ")";
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < MaxIntids; i++) g_intidToSlot[i] = NoSlot;
|
||||
for (int i = 0; i < IRQ_COUNT; i++) g_slotToIntid[i] = SpuriousIntid;
|
||||
Bind(0, IRQ_TLB_SHOOTDOWN);
|
||||
Bind(1, IRQ_RESCHEDULE);
|
||||
|
||||
g_gicd = MapRegisters(gicdPhys, 0x1000);
|
||||
g_gicc = MapRegisters(giccPhys, 0x2000);
|
||||
|
||||
g_intidCount = ((GicdRead(GICD_TYPER) & 0x1F) + 1) * 32;
|
||||
if (g_intidCount > MaxIntids) g_intidCount = MaxIntids;
|
||||
|
||||
// Distributor: everything masked, idle, level-triggered and
|
||||
// aimed at no CPU until RouteSpi claims it.
|
||||
GicdWrite(GICD_CTLR, 0);
|
||||
for (uint32_t intid = 32; intid < g_intidCount; intid += 32) {
|
||||
GicdWrite(GICD_ICENABLER + (intid / 32) * 4, 0xFFFFFFFFu);
|
||||
GicdWrite(GICD_ICPENDR + (intid / 32) * 4, 0xFFFFFFFFu);
|
||||
GicdWrite(GICD_ICACTIVER + (intid / 32) * 4, 0xFFFFFFFFu);
|
||||
}
|
||||
for (uint32_t intid = 32; intid < g_intidCount; intid++) {
|
||||
GicdWrite8(GICD_IPRIORITYR + intid, DefaultPriority);
|
||||
GicdWrite8(GICD_ITARGETSR + intid, 0);
|
||||
}
|
||||
for (uint32_t intid = 32; intid < g_intidCount; intid += 16) {
|
||||
GicdWrite(GICD_ICFGR + (intid / 16) * 4, 0);
|
||||
}
|
||||
GicdWrite(GICD_CTLR, 1);
|
||||
|
||||
InitializeCpuInterface();
|
||||
|
||||
if (g_msiFramePhys != 0) {
|
||||
volatile uint8_t* frame = MapRegisters(g_msiFramePhys, 0x1000);
|
||||
if (g_msiSpiCount == 0) {
|
||||
uint32_t typer = *(volatile uint32_t*)(frame + V2M_MSI_TYPER);
|
||||
g_msiSpiBase = (typer >> 16) & 0x3FF;
|
||||
g_msiSpiCount = typer & 0x3FF;
|
||||
}
|
||||
}
|
||||
|
||||
KernelLogStream(OK, "GIC") << "GICv2: distributor 0x" << base::hex << gicdPhys
|
||||
<< ", CPU interface 0x" << giccPhys << ", " << base::dec
|
||||
<< (uint64_t)g_intidCount << " interrupt IDs";
|
||||
if (g_msiSpiCount != 0) {
|
||||
KernelLogStream(OK, "GIC") << "GICv2m MSI frame 0x" << base::hex << g_msiFramePhys
|
||||
<< ": SPIs " << base::dec << (uint64_t)g_msiSpiBase << "-"
|
||||
<< (uint64_t)(g_msiSpiBase + g_msiSpiCount - 1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void RegisterIrqHandler(uint8_t irq, IrqHandler handler) {
|
||||
if (irq >= IRQ_COUNT) return;
|
||||
g_irqHandlers[irq] = handler;
|
||||
Kt::KernelLogStream(Kt::DEBUG, "IRQ") << "Registered handler for IRQ "
|
||||
KernelLogStream(DEBUG, "IRQ") << "Registered handler for IRQ "
|
||||
<< base::dec << (uint64_t)irq;
|
||||
}
|
||||
|
||||
// TODO(aarch64): GIC SPI routing (ACPI MADT GICD / interrupt overrides).
|
||||
// TODO(aarch64): PCI INTx needs the pin's _PRT GSI and a GSI-based
|
||||
// routing call (RouteSpi with level/polarity); drivers poll until then.
|
||||
void RouteLegacyIrq(uint8_t irq) {
|
||||
Kt::KernelLogStream(Kt::WARNING, "IRQ") << "aarch64: cannot route IRQ "
|
||||
<< base::dec << (uint64_t)irq << " (no GIC driver)";
|
||||
KernelLogStream(WARNING, "IRQ") << "aarch64: no PC interrupt line "
|
||||
<< base::dec << (uint64_t)irq << " to route";
|
||||
}
|
||||
|
||||
void EnableLegacyIrq(uint8_t irq) {
|
||||
RouteLegacyIrq(irq);
|
||||
}
|
||||
|
||||
// TODO(aarch64): GICv3 ITS / GICv2m doorbell.
|
||||
bool ComposeMsi(uint8_t, MsiMessage&) {
|
||||
return false;
|
||||
bool ComposeMsi(uint8_t irq, MsiMessage& out) {
|
||||
if (g_gicd == nullptr || irq < FirstMsiSlot || irq > LastMsiSlot) return false;
|
||||
uint32_t index = irq - FirstMsiSlot;
|
||||
if (index >= g_msiSpiCount) return false;
|
||||
|
||||
uint32_t spi = g_msiSpiBase + index;
|
||||
if (g_slotToIntid[irq] != spi) {
|
||||
RouteSpi(spi, irq, true);
|
||||
}
|
||||
out = MsiMessage{ g_msiFramePhys + V2M_MSI_SETSPI_NS, spi };
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO(aarch64): GIC software-generated interrupts.
|
||||
bool SendIpi(int, uint8_t) {
|
||||
return false;
|
||||
bool SendIpi(int cpuIndex, uint8_t irq) {
|
||||
if (g_gicd == nullptr) return false;
|
||||
auto* cpu = Smp::GetCpuData(cpuIndex);
|
||||
if (cpu == nullptr || cpu->arch.gicTargetMask == 0) return false;
|
||||
uint32_t sgi = g_slotToIntid[irq];
|
||||
if (sgi >= 16) return false;
|
||||
|
||||
// Make the IPI's memory effects visible before it is raised.
|
||||
asm volatile("dsb ishst" ::: "memory");
|
||||
GicdWrite(GICD_SGIR, ((uint32_t)cpu->arch.gicTargetMask << 16) | sgi);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// IRQ exception vector (Vectors.S).
|
||||
// TODO(aarch64): acknowledge through the GIC CPU interface, translate the
|
||||
// INTID to a slot and call ArchIrqDispatch.
|
||||
extern "C" void ArchIrqException(void*, bool) {
|
||||
static bool reported = false;
|
||||
if (!reported) {
|
||||
reported = true;
|
||||
Kt::KernelLogStream(Kt::WARNING, "IRQ") << "aarch64: IRQ taken with no interrupt controller driver";
|
||||
}
|
||||
}
|
||||
// IRQ exception vector (Vectors.S). As on x86, the interrupt is completed
|
||||
// BEFORE the handler runs: the handler may context-switch away and not
|
||||
// come back here for a long time, and an un-EOId interrupt would hold this
|
||||
// CPU's running priority up (no further ticks). Level-triggered sources
|
||||
// get the chance to drop their line first through an ack hook.
|
||||
extern "C" void ArchIrqException(void*, bool fromUser) {
|
||||
using namespace Arch;
|
||||
uint32_t iar = GiccRead(GICC_IAR);
|
||||
uint32_t intid = iar & 0x3FF;
|
||||
if (intid >= SpuriousIntid) return;
|
||||
|
||||
extern "C" void ArchIrqDispatch(uint64_t irq, bool fromUser) {
|
||||
if (irq < Arch::IRQ_COUNT && Arch::g_irqHandlers[irq] != nullptr) {
|
||||
Arch::g_irqHandlers[irq]((uint8_t)irq, fromUser);
|
||||
uint8_t irq = g_intidToSlot[intid];
|
||||
if (irq != NoSlot && g_ackHooks[irq] != nullptr) {
|
||||
g_ackHooks[irq]();
|
||||
}
|
||||
GiccWrite(GICC_EOIR, iar);
|
||||
|
||||
if (irq != NoSlot && g_irqHandlers[irq] != nullptr) {
|
||||
g_irqHandlers[irq](irq, fromUser);
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,8 @@ namespace Arch {
|
||||
|
||||
struct CpuState {
|
||||
uint64_t mpidr; // MPIDR_EL1 affinity of this CPU
|
||||
uint8_t gicTargetMask; // GICv2 CPU-interface bit (SGI/SPI targets)
|
||||
uint64_t timerPeriod; // tick period in counter ticks, 0 = stopped
|
||||
};
|
||||
|
||||
// TPIDR_EL1 holds the CpuData pointer (zeroed in EarlyInitialize until
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#include <Arch/Platform.hpp>
|
||||
#include <Arch/Cpu.hpp>
|
||||
#include <Arch/Smp.hpp>
|
||||
#include <Arch/aarch64/Gic.hpp>
|
||||
#include <ACPI/FADT.hpp>
|
||||
#include <Terminal/Terminal.hpp>
|
||||
#include <CppLib/Stream.hpp>
|
||||
|
||||
@@ -18,6 +20,42 @@ 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 EarlyInitialize() {
|
||||
// No per-CPU data yet: Smp::TryGetCurrentCpuData must see null.
|
||||
@@ -32,10 +70,11 @@ namespace Arch {
|
||||
asm volatile("msr vbar_el1, %0\n\tisb" :: "r"(ArchExceptionVectors) : "memory");
|
||||
|
||||
// Kernel mappings are RWX (Paging.cpp), so writable must not
|
||||
// imply execute-never.
|
||||
// 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
|
||||
@@ -57,10 +96,12 @@ namespace Arch {
|
||||
void InitializeMemoryAttributes() {
|
||||
}
|
||||
|
||||
// TODO(aarch64): GIC distributor/redistributor from the ACPI MADT.
|
||||
void InitializeInterrupts(Hal::ACPI::CommonSDTHeader*) {
|
||||
void InitializeInterrupts(Hal::ACPI::CommonSDTHeader* xsdt) {
|
||||
Smp::InitBsp();
|
||||
KernelLogStream(WARNING, "Arch") << "aarch64: no interrupt controller driver";
|
||||
if (!Gic::Initialize(xsdt)) {
|
||||
KernelLogStream(ERROR, "Arch") << "aarch64: no interrupt controller, running without IRQs";
|
||||
}
|
||||
InitializePsci(xsdt);
|
||||
}
|
||||
|
||||
void InitializePlatformDevices() {
|
||||
@@ -73,12 +114,22 @@ namespace Arch {
|
||||
void LateInitialize() {
|
||||
}
|
||||
|
||||
void ListPlatformDevices(PlatformDeviceSink, void*) {
|
||||
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)");
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(aarch64): PSCI SYSTEM_RESET through the conduit the FADT names.
|
||||
void Reboot() {
|
||||
PsciCall(PsciSystemReset);
|
||||
HaltForever();
|
||||
}
|
||||
|
||||
void PowerOff() {
|
||||
if (g_psci != PsciConduit::None) {
|
||||
PsciCall(PsciSystemOff);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,24 +1,115 @@
|
||||
/*
|
||||
* Timer.cpp
|
||||
* aarch64 tick timer (implements Arch/Timer.hpp)
|
||||
* aarch64 tick timer: the EL1 virtual generic timer (implements Arch/Timer.hpp)
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#include <Arch/Timer.hpp>
|
||||
#include <Arch/Interrupts.hpp>
|
||||
#include <Arch/Cpu.hpp>
|
||||
#include <Arch/Smp.hpp>
|
||||
#include <Arch/aarch64/Gic.hpp>
|
||||
#include <ACPI/ACPI.hpp>
|
||||
#include <Terminal/Terminal.hpp>
|
||||
#include <CppLib/Stream.hpp>
|
||||
|
||||
// TODO(aarch64): drive the EL1 virtual timer (CNTV_TVAL/CNTV_CTL) from
|
||||
// CNTFRQ_EL0, re-arming on each tick, once the GIC delivers its PPI.
|
||||
// The generic timer counts at CNTFRQ_EL0 on every CPU, so it needs no
|
||||
// calibration. Each CPU's virtual timer raises a PPI (the GTDT names it,
|
||||
// normally INTID 27); the tick is re-armed from the interrupt's ack hook,
|
||||
// which also drops the level-triggered line before the EOI.
|
||||
|
||||
using namespace Kt;
|
||||
|
||||
namespace Arch::Timer {
|
||||
|
||||
bool InitializeBsp(uint32_t) {
|
||||
return false;
|
||||
// CNTV_CTL_EL0
|
||||
static constexpr uint64_t CtlEnable = 1ULL << 0;
|
||||
static constexpr uint64_t CtlMask = 1ULL << 1;
|
||||
|
||||
// GTDT timer flags
|
||||
static constexpr uint32_t GtdtEdgeTriggered = 1u << 0;
|
||||
|
||||
static constexpr uint32_t DefaultVirtualTimerIntid = 27;
|
||||
|
||||
struct Gtdt {
|
||||
Hal::ACPI::CommonSDTHeader Header;
|
||||
uint64_t CntControlBase;
|
||||
uint32_t Reserved;
|
||||
uint32_t SecureEl1Gsiv;
|
||||
uint32_t SecureEl1Flags;
|
||||
uint32_t NonSecureEl1Gsiv;
|
||||
uint32_t NonSecureEl1Flags;
|
||||
uint32_t VirtualTimerGsiv;
|
||||
uint32_t VirtualTimerFlags;
|
||||
} __attribute__((packed));
|
||||
|
||||
static uint64_t g_ticksPerMs = 0;
|
||||
static uint32_t g_intid = DefaultVirtualTimerIntid;
|
||||
static bool g_edgeTriggered = false;
|
||||
|
||||
static inline void WriteCtl(uint64_t value) {
|
||||
asm volatile("msr cntv_ctl_el0, %0\n\tisb" :: "r"(value) : "memory");
|
||||
}
|
||||
|
||||
void StartPeriodic(uint32_t) {
|
||||
static inline uint64_t ReadCval() {
|
||||
uint64_t value;
|
||||
asm volatile("mrs %0, cntv_cval_el0" : "=r"(value));
|
||||
return value;
|
||||
}
|
||||
|
||||
static inline void WriteCval(uint64_t value) {
|
||||
asm volatile("msr cntv_cval_el0, %0\n\tisb" :: "r"(value) : "memory");
|
||||
}
|
||||
|
||||
// Advance the deadline by one period from the previous one (no drift),
|
||||
// skipping ticks the CPU missed rather than replaying them.
|
||||
static void Rearm() {
|
||||
uint64_t period = Smp::GetCurrentCpuData()->arch.timerPeriod;
|
||||
if (period == 0) {
|
||||
WriteCtl(CtlMask);
|
||||
return;
|
||||
}
|
||||
uint64_t now = ReadTimestamp();
|
||||
uint64_t next = ReadCval() + period;
|
||||
if ((int64_t)(next - now) <= 0) next = now + period;
|
||||
WriteCval(next);
|
||||
}
|
||||
|
||||
static void ReadGtdt() {
|
||||
auto* gtdt = (Gtdt*)Gic::FindTable(Gic::CurrentXsdt(), "GTDT");
|
||||
if (gtdt == nullptr || gtdt->Header.Length < sizeof(Gtdt)) return;
|
||||
if (gtdt->VirtualTimerGsiv >= 16 && gtdt->VirtualTimerGsiv < 32) {
|
||||
g_intid = gtdt->VirtualTimerGsiv;
|
||||
g_edgeTriggered = (gtdt->VirtualTimerFlags & GtdtEdgeTriggered) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool InitializeBsp(uint32_t periodMs) {
|
||||
uint64_t frequency;
|
||||
asm volatile("mrs %0, cntfrq_el0" : "=r"(frequency));
|
||||
if (frequency < 1000 || !Gic::IsInitialized()) return false;
|
||||
g_ticksPerMs = frequency / 1000;
|
||||
|
||||
ReadGtdt();
|
||||
Gic::SetAckHook(IRQ_TIMER, Rearm);
|
||||
Gic::RoutePpi(g_intid, IRQ_TIMER, g_edgeTriggered);
|
||||
StartPeriodic(periodMs);
|
||||
|
||||
KernelLogStream(OK, "Timer") << "Generic timer: " << base::dec << frequency
|
||||
<< " Hz counter, virtual timer INTID " << (uint64_t)g_intid;
|
||||
return true;
|
||||
}
|
||||
|
||||
void StartPeriodic(uint32_t periodMs) {
|
||||
uint64_t period = g_ticksPerMs * periodMs;
|
||||
Smp::GetCurrentCpuData()->arch.timerPeriod = period;
|
||||
WriteCval(ReadTimestamp() + period);
|
||||
WriteCtl(CtlEnable);
|
||||
}
|
||||
|
||||
void Stop() {
|
||||
Smp::GetCurrentCpuData()->arch.timerPeriod = 0;
|
||||
WriteCtl(CtlMask);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -79,6 +79,9 @@ namespace Arch {
|
||||
asm volatile("wbinvd" ::: "memory");
|
||||
}
|
||||
|
||||
inline void SyncInstructionCache(const void*, uint64_t) {
|
||||
}
|
||||
|
||||
inline void FlushTlbPage(uint64_t virtualAddress) {
|
||||
asm volatile("invlpg (%0)" :: "r"(virtualAddress) : "memory");
|
||||
}
|
||||
|
||||
@@ -77,4 +77,8 @@ namespace Arch {
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
// x86 powers off through ACPI S5 (AcpiShutdown).
|
||||
void PowerOff() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -220,6 +220,13 @@ namespace Drivers::USB::Xhci {
|
||||
*(volatile uint32_t*)(g_opBase + reg) = value;
|
||||
}
|
||||
|
||||
// PollEvents() acknowledges the Port Status Change event, clearing PRC,
|
||||
// so a controller that resets instantly (QEMU) can complete the reset
|
||||
// between two reads of PRC. A port that left reset enabled is done too.
|
||||
static bool PortResetComplete(uint32_t portsc) {
|
||||
return (portsc & PORTSC_PRC) || ((portsc & PORTSC_PED) && !(portsc & PORTSC_PR));
|
||||
}
|
||||
|
||||
static uint32_t ReadOp(uint32_t reg) {
|
||||
return *(volatile uint32_t*)(g_opBase + reg);
|
||||
}
|
||||
@@ -474,6 +481,16 @@ namespace Drivers::USB::Xhci {
|
||||
BudgetExhausted,
|
||||
};
|
||||
|
||||
// Without MSI nothing interrupts: the deferred-work check (timer and
|
||||
// idle loop) looks at the event ring itself instead. This is the only
|
||||
// mode on platforms whose PCIe has no MSI controller (Raspberry Pi 4).
|
||||
static bool g_pollMode = false;
|
||||
|
||||
static bool EventRingHasWork() {
|
||||
const volatile TRB& evt = g_evtRing[g_evtRingDequeue];
|
||||
return ((evt.Control & TRB_CYCLE_BIT) != 0) == g_evtRingCCS;
|
||||
}
|
||||
|
||||
static PollResult PollEventsBounded(uint32_t maxEvents) {
|
||||
// PollEvents runs from synchronous poll loops (ControlTransfer,
|
||||
// SendCommand, firmware download) and idle bottom halves on different
|
||||
@@ -1396,6 +1413,9 @@ namespace Drivers::USB::Xhci {
|
||||
}
|
||||
|
||||
bool HasDeferredWork() {
|
||||
if (g_initialized && g_pollMode && EventRingHasWork()) {
|
||||
g_irqDrainPending.store(true, std::memory_order_release);
|
||||
}
|
||||
return g_initialized &&
|
||||
(g_irqDrainPending.load(std::memory_order_acquire) ||
|
||||
g_deferredWorkPending.load(std::memory_order_acquire) ||
|
||||
@@ -1537,7 +1557,7 @@ namespace Drivers::USB::Xhci {
|
||||
for (uint32_t i = 0; i < 100000; i++) {
|
||||
PollEvents();
|
||||
uint32_t ps = ReadOp(OP_PORTSC_BASE + port * OP_PORTSC_STRIDE);
|
||||
if (ps & PORTSC_PRC) {
|
||||
if (PortResetComplete(ps)) {
|
||||
resetDone = true;
|
||||
break;
|
||||
}
|
||||
@@ -1732,6 +1752,7 @@ namespace Drivers::USB::Xhci {
|
||||
// MSI setup
|
||||
if (!SetupMsi(dev.Bus, dev.Device, dev.Function)) {
|
||||
KernelLogStream(WARNING, "xHCI") << "MSI not available, using poll mode";
|
||||
g_pollMode = true;
|
||||
}
|
||||
|
||||
// Enable interrupter 0
|
||||
@@ -1772,7 +1793,7 @@ namespace Drivers::USB::Xhci {
|
||||
for (uint32_t i = 0; i < 100000; i++) {
|
||||
PollEvents();
|
||||
uint32_t ps = ReadOp(OP_PORTSC_BASE + port * OP_PORTSC_STRIDE);
|
||||
if (ps & PORTSC_PRC) { resetDone = true; break; }
|
||||
if (PortResetComplete(ps)) { resetDone = true; break; }
|
||||
for (int j = 0; j < 100; j++) Arch::CompilerBarrier();
|
||||
}
|
||||
|
||||
@@ -2045,6 +2066,7 @@ namespace Drivers::USB::Xhci {
|
||||
// -----------------------------------------------------------------
|
||||
if (!SetupMsi(foundDev->Bus, foundDev->Device, foundDev->Function)) {
|
||||
KernelLogStream(WARNING, "xHCI") << "MSI not available, using poll mode";
|
||||
g_pollMode = true;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
@@ -276,6 +276,13 @@ namespace Efi {
|
||||
inline EFI_RESET_SYSTEM g_ResetSystem = nullptr;
|
||||
|
||||
inline void Init(SystemTable* ST, const montauk::boot::EfiInfo& efi) {
|
||||
/* The system table and the runtime regions are firmware-reserved
|
||||
memory, which the HHDM does not cover on every architecture:
|
||||
map them (identity and HHDM) before the first access. */
|
||||
if (Memory::VMM::g_paging) {
|
||||
Memory::VMM::g_paging->MapEfiRuntime(efi);
|
||||
}
|
||||
|
||||
Kt::KernelLogStream(Kt::OK, "UEFI") << "ST Minor Revision: " << ST->Header.Revision.MinorRevision;
|
||||
Kt::KernelLogStream(Kt::OK, "UEFI") << "ST Major Revision: " << ST->Header.Revision.MajorRevision;
|
||||
|
||||
@@ -284,12 +291,6 @@ namespace Efi {
|
||||
if (ST->RuntimeServices != nullptr) {
|
||||
Kt::KernelLogStream(Kt::OK, "UEFI") << "EFI Runtime Service API is available.";
|
||||
|
||||
/* Identity-map EFI runtime service regions so firmware code
|
||||
can reference its own data at physical addresses */
|
||||
if (Memory::VMM::g_paging) {
|
||||
Memory::VMM::g_paging->MapEfiRuntime(efi);
|
||||
}
|
||||
|
||||
EFI_TIME Time;
|
||||
EFI_TIME_CAPABILITIES TimeCapabilities;
|
||||
|
||||
|
||||
@@ -528,6 +528,7 @@ namespace Sched {
|
||||
Fs::Vfs::CloseBackendFile(file);
|
||||
return 0;
|
||||
}
|
||||
Arch::SyncInstructionCache(page, 0x1000);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -718,6 +719,7 @@ namespace Sched {
|
||||
uint8_t* dst = (uint8_t*)Memory::HHDM(physAddr) + pageDataOffset;
|
||||
uint8_t* src = fileData + srcOffset;
|
||||
memcpy(dst, src, copySize);
|
||||
Arch::SyncInstructionCache(dst, copySize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -442,6 +442,7 @@ namespace Sched {
|
||||
|
||||
// User code that calls SYS_EXIT(0)
|
||||
Arch::WriteExitStub((uint8_t*)stubPage);
|
||||
Arch::SyncInstructionCache(stubPage, 0x1000);
|
||||
}
|
||||
|
||||
// Returning from the entry function lands in the exit stub.
|
||||
|
||||
Reference in new issue
Block a user