diff --git a/Kernel/Include/Arch/CPU.h b/Kernel/Include/Arch/CPU.h index df5c60c..ad69c8a 100644 --- a/Kernel/Include/Arch/CPU.h +++ b/Kernel/Include/Arch/CPU.h @@ -72,4 +72,4 @@ static inline void CPUEnableMMU(Address l0PhysicalAddress) { "isb\n" : "=r"(sctlr) : "r"(sctlr_flags) : "memory" ); -} \ No newline at end of file +} diff --git a/Kernel/Include/Arch/GIC.h b/Kernel/Include/Arch/GIC.h new file mode 100644 index 0000000..0978e16 --- /dev/null +++ b/Kernel/Include/Arch/GIC.h @@ -0,0 +1,28 @@ +#pragma once +#include +#include +#include + +enum { + kGICDBaseAddress = 0x08000000, + kGICCBaseAddress = kGICDBaseAddress + 0x10000, + + // GIC Distributor (GICD), offsets from GICD base + kGICDCTLR = 0x000, // control + kGICDTYPER = 0x004, // controller type + kGICDISENABLER = 0x100, // interrupt set-enable + kGICDICENABLER = 0x180, // interrupt clear-enable + kGICDICPENDR = 0x280, // interrupt clear-pending + kGICDIPRIORITYR = 0x400, // interrupt priority + kGICDITARGETSR = 0x800, // interrupt processor targets + + // GIC CPU interface (GICC), offsets from CPU interface base + kGICCCTLR = 0x000, // CPU interface control + kGICCPMR = 0x004, // priority mask + kGICCIAR = 0x00C, // interrupt acknowledge + kGICCEOIR = 0x010, // end of interrupt +}; + +void GICInitialize(Pointer gicdVirtBase, Pointer giccVirtBase); +void GICEnableInterrupt(UInt32 irqID); +void GICDispatch(ExceptionsType type); diff --git a/Kernel/Include/Arch/Timer.h b/Kernel/Include/Arch/Timer.h new file mode 100644 index 0000000..3fbe2e4 --- /dev/null +++ b/Kernel/Include/Arch/Timer.h @@ -0,0 +1,8 @@ +#pragma once +#include + +static const UInt64 kTimerFrequency = 1000; // 1ms +static const UInt8 kTimerIRQ = 27; + +void TimerInitialize(); +void TimerReset(UInt64 interval); \ No newline at end of file diff --git a/Kernel/Include/VM/PMM.h b/Kernel/Include/VM/PMM.h index 1ee4399..cd45223 100644 --- a/Kernel/Include/VM/PMM.h +++ b/Kernel/Include/VM/PMM.h @@ -13,12 +13,18 @@ typedef struct { Size size; } VMMemoryRegion; +typedef struct { + VMMemoryRegion GICD; + VMMemoryRegion GICC; +} GICRegion; + typedef struct { VMMemoryRegion totalRAM; VMMemoryRegion reserved[kVMMaxReservedRegions]; UInt32 reservedCount; VMMemoryRegion UART; + GICRegion GIC; } VMBootMemoryMap; void PMMInitialize(VMBootMemoryMap* bootMap); diff --git a/Kernel/Source/Arch/DTB.c b/Kernel/Source/Arch/DTB.c index 238fa97..9d638a2 100644 --- a/Kernel/Source/Arch/DTB.c +++ b/Kernel/Source/Arch/DTB.c @@ -77,6 +77,15 @@ void DTBParse(Pointer dtb, VMBootMemoryMap* bootMap) { bootMap->UART.base = Merge32To64(BytesSwap32(cells[1]), BytesSwap32(cells[0])); bootMap->UART.size = Merge32To64(BytesSwap32(cells[3]), BytesSwap32(cells[2])); } + else if (StringStartsWith(currentNode, "intc")) { + UInt32* cells = (UInt32*)structs; + + bootMap->GIC.GICD.base = Merge32To64(BytesSwap32(cells[1]), BytesSwap32(cells[0])); + bootMap->GIC.GICD.size = Merge32To64(BytesSwap32(cells[3]), BytesSwap32(cells[2])); + + bootMap->GIC.GICC.base = Merge32To64(BytesSwap32(cells[5]), BytesSwap32(cells[4])); + bootMap->GIC.GICC.size = Merge32To64(BytesSwap32(cells[7]), BytesSwap32(cells[6])); + } } structs += propertyLength; diff --git a/Kernel/Source/Arch/Exceptions.c b/Kernel/Source/Arch/Exceptions.c index b3aa22d..a2b93fd 100644 --- a/Kernel/Source/Arch/Exceptions.c +++ b/Kernel/Source/Arch/Exceptions.c @@ -1,8 +1,11 @@ #include #include +#include #include #include -void ExceptionsHandler(ExceptionsContext* frame, [[maybe_unused]]ExceptionsType type) { +void ExceptionsHandler(ExceptionsContext* frame, ExceptionsType type) { + if (type == ExceptionsIRQEl1h || type == ExceptionsIRQEl064) return GICDispatch(type); OSPanicException(frame); -} \ No newline at end of file +} + diff --git a/Kernel/Source/Arch/GIC.c b/Kernel/Source/Arch/GIC.c new file mode 100644 index 0000000..9cf1a48 --- /dev/null +++ b/Kernel/Source/Arch/GIC.c @@ -0,0 +1,65 @@ +#include +#include +#include +#include + +static volatile UInt32* GICD = nullptr; +static volatile UInt32* GICC = nullptr; + +void GICInitialize(Pointer gicdVirtBase, Pointer giccVirtBase) { + GICD = (volatile UInt32*)gicdVirtBase; + GICC = (volatile UInt32*)giccVirtBase; + + GICD[kGICDCTLR / 4] = 0; // disable Distributor (gicd) + UInt32 typer = GICD[kGICDTYPER / 4]; // how many interrupts are supported + UInt32 maxIRQS = 32 * ((typer & 0x1F) + 1); // total number of interrupts + OSLog("GIC: maxIRQS: %d\n", maxIRQS); + + for (UInt32 i = 0; i < maxIRQS / 32; i++) { + GICD[kGICDICENABLER / 4 + i] = 0xFFFFFFFF; // clear enable + GICD[kGICDICPENDR / 4 + i] = 0xFFFFFFFF; // clear pending + } + + for (UInt32 i = 0; i < maxIRQS / 4; i++) { + GICD[kGICDIPRIORITYR / 4 + i] = 0xA0A0A0A0; // set priority 0xA0 for all interrupts + } + + for (UInt32 i = 8; i < maxIRQS / 4; i++) { + GICD[kGICDITARGETSR / 4 + i] = 0x01010101; // set interrupts id >= 32 for CPU 0 + } + + GICD[kGICDCTLR / 4] = 1; // enable Distributor (gicd) + + GICC[kGICCCTLR / 4] = 0; // disable cpu interface + GICC[kGICCPMR / 4] = 0xFF; // set lowest priority (accept all interrupts) + GICC[kGICCCTLR / 4] = 1; // enable CPU interface (gicc) + + OSLog("GICv2 initialized.\n"); +} + +void GICEnableInterrupt(UInt32 irqID) { + UInt32 regOffset = irqID / 32; + UInt32 bitMask = 1 << (irqID % 32); + + GICD[kGICDISENABLER / 4 + regOffset] = bitMask; +} + +UInt32 GICCReadIAR(void) { + return GICC[kGICCIAR / 4]; +} + +void GICCWriteEOIR(UInt32 irqID) { + GICC[kGICCEOIR / 4] = irqID; +} + +void GICDispatch(ExceptionsType type) { + if (type != ExceptionsIRQEl1h && type != ExceptionsIRQEl064) return; + UInt32 irqID = GICCReadIAR() & 0x3FF; + if (irqID == 1023) return; // spurious interrupt + + if (irqID == kTimerIRQ) { + TimerReset(kTimerFrequency); + } + + GICCWriteEOIR(irqID); +} diff --git a/Kernel/Source/Arch/Timer.c b/Kernel/Source/Arch/Timer.c new file mode 100644 index 0000000..7d875f3 --- /dev/null +++ b/Kernel/Source/Arch/Timer.c @@ -0,0 +1,14 @@ +#include +#include + +void TimerInitialize() { + GICEnableInterrupt(kTimerIRQ); + TimerReset(kTimerFrequency); +} + +void TimerReset(UInt64 interval) { + UInt64 frequency; + __asm__ volatile ("mrs %0, cntfrq_el0" : "=r"(frequency)); + __asm__ volatile ("msr cntv_tval_el0, %0" :: "r"(frequency /interval)); + __asm__ volatile ("msr cntv_ctl_el0, %0" :: "r" (1)); +} diff --git a/Kernel/Source/KernelMain.c b/Kernel/Source/KernelMain.c index fc0ba27..60c6007 100644 --- a/Kernel/Source/KernelMain.c +++ b/Kernel/Source/KernelMain.c @@ -1,5 +1,8 @@ #include "../Common/bootinfo.h" #include +#include +#include +#include #include #include #include @@ -15,9 +18,17 @@ void KernelMain(Bootinfo* bootinfo) { VMBootMemoryMap bootMap = {0}; bootMap.reservedCount = 0; DTBParse(bootinfo->dtb, &bootMap); + PMMInitialize(&bootMap); VMMInitialize(&bootMap, bootinfo); HeapInitialize(); + GICInitialize( + (Pointer)VMPhysToHHDM(bootMap.GIC.GICD.base), + (Pointer)VMPhysToHHDM(bootMap.GIC.GICC.base) + ); + TimerInitialize(); + CPUEnableInterrupts(); + OSLog("Kernel initialized.\n"); -} \ No newline at end of file +} diff --git a/Kernel/Source/VM/VMM.c b/Kernel/Source/VM/VMM.c index 7e4cec2..ccbf659 100644 --- a/Kernel/Source/VM/VMM.c +++ b/Kernel/Source/VM/VMM.c @@ -197,7 +197,39 @@ void VMMInitialize(VMBootMemoryMap* bootMap, Bootinfo* info) { ); OSLog("UART mapped\n"); - info->framebuffer.base = (BIUInt32*)kVMFbVirtBase;; + Address gicdPhys = bootMap->GIC.GICD.base; + Size gicdSize = bootMap->GIC.GICD.size; + if (!gicdPhys) { + gicdPhys = 0x08000000; // QEMU fallback + gicdSize = 0x10000; + } + + for (Address offset = 0; offset < gicdSize; offset += kVMPageSize) { + VMMMapPage( + gVMKernelL0Table, gicdPhys + offset, + VMPhysToHHDM(gicdPhys + offset), + kPTEDeviceMem | kPTEAccessRW | kPTEUserNX | kPTEPrivNX + ); + } + OSLog("GICD mapped\n"); + + Address giccPhys = bootMap->GIC.GICC.base; + Size giccSize = bootMap->GIC.GICC.size; + if (!giccPhys) { + giccPhys = 0x08001000; // QEMU fallback + giccSize = 0x10000; + } + + for (Address offset = 0; offset < giccSize; offset += kVMPageSize) { + VMMMapPage( + gVMKernelL0Table, giccPhys + offset, + VMPhysToHHDM(giccPhys + offset), + kPTEDeviceMem | kPTEAccessRW | kPTEUserNX | kPTEPrivNX + ); + } + OSLog("GICC mapped\n"); + + info->framebuffer.base = (BIUInt32*)kVMFbVirtBase; OSLog("Enabling MMU...\n"); CPUEnableMMU(gVMKernelL0Physical); isInitialized = true;