diff --git a/Kernel/Include/Arch/CPU.h b/Kernel/Include/Arch/CPU.h index ad69c8a..db026f2 100644 --- a/Kernel/Include/Arch/CPU.h +++ b/Kernel/Include/Arch/CPU.h @@ -73,3 +73,16 @@ static inline void CPUEnableMMU(Address l0PhysicalAddress) { : "=r"(sctlr) : "r"(sctlr_flags) : "memory" ); } + +static inline void CPUSwitchAddressSpace(Address l0Physical) { + __asm__ volatile( + "dsb ishst\n" // wait till all previous writes are finished physically + "msr ttbr0_el1, %0\n" // Update TTBR0_EL1 (userspace) + "tlbi vmalle1is\n" // Reset TLB cache + "dsb ish\n" // wait for tlb cache to reset + "isb\n" // Clear instruction pipeline + :: "r" (l0Physical) : "memory" + ); +} + +#define CPUException(number) __asm__ volatile ("svc %0" :: "i" (number) : "memory") \ No newline at end of file diff --git a/Kernel/Include/Arch/Exceptions.h b/Kernel/Include/Arch/Exceptions.h index fe069c1..1a68681 100644 --- a/Kernel/Include/Arch/Exceptions.h +++ b/Kernel/Include/Arch/Exceptions.h @@ -36,6 +36,8 @@ typedef struct ExceptionsContext { UInt64 elr_el1; // pc UInt64 spsr_el1; // cpu status UInt64 esr_el1; // error reason + UInt64 sp_el0; // user's stack + UInt64 padding; // align to 16 bytes } ExceptionsContext; typedef enum ExceptionsType { diff --git a/Kernel/Include/Arch/GIC.h b/Kernel/Include/Arch/GIC.h index 0978e16..1f06c56 100644 --- a/Kernel/Include/Arch/GIC.h +++ b/Kernel/Include/Arch/GIC.h @@ -25,4 +25,4 @@ enum { void GICInitialize(Pointer gicdVirtBase, Pointer giccVirtBase); void GICEnableInterrupt(UInt32 irqID); -void GICDispatch(ExceptionsType type); +Address GICDispatch(ExceptionsContext* frame, ExceptionsType type); \ No newline at end of file diff --git a/Kernel/Include/Arch/Timer.h b/Kernel/Include/Arch/Timer.h index 3fbe2e4..4951a05 100644 --- a/Kernel/Include/Arch/Timer.h +++ b/Kernel/Include/Arch/Timer.h @@ -1,8 +1,11 @@ #pragma once #include +#include static const UInt64 kTimerFrequency = 1000; // 1ms static const UInt8 kTimerIRQ = 27; void TimerInitialize(); -void TimerReset(UInt64 interval); \ No newline at end of file +void TimerReset(UInt64 interval); +Address TimerHandler(ExceptionsContext* frame); +UInt64 TimerGetCounter(); \ No newline at end of file diff --git a/Kernel/Include/OS/Scheduler.h b/Kernel/Include/OS/Scheduler.h new file mode 100644 index 0000000..2dc4d5f --- /dev/null +++ b/Kernel/Include/OS/Scheduler.h @@ -0,0 +1,42 @@ +#pragma once +#include + +typedef enum OSTaskState { + OSTaskStateDead, + OSTaskStateRunning, + OSTaskStateReady, + OSTaskStateBlocked, + OSTaskStateSleeping, +} OSTaskState; + +typedef struct OSProcess { + UInt64 id; + OSTaskState state; + Address* l0Table; + Address heapStart; + Address heapCurrent; + struct OSProcess* parent; + ASCII name[32]; +} OSProcess; + +typedef struct OSTask { + Address stackPointer; + struct OSTask* next; + UInt32 id; + UInt32 sleepTicks; + OSTaskState state; + Address kernelStackTop; + Pointer kernelStackBase; + OSProcess* process; + Int32 waitingForProcessId; +} OSTask; + +enum { + kOSSchedulerTaskStackSize = 16 * 1024, + kOSSchedulerExceptionNumber = 0xF0F0 +}; + +void SchedulerInitialize(); +OSTask* SchedulerSpawn(void(*entryPoint)(), OSProcess* owner, Boolean isUser, Address fixedUserStackAddress); +Address SchedulerNext(Address stackPointer); +void SchedulerYield(UInt64 ticks); \ No newline at end of file diff --git a/Kernel/Source/Arch/Exceptions.c b/Kernel/Source/Arch/Exceptions.c index a2b93fd..20692ad 100644 --- a/Kernel/Source/Arch/Exceptions.c +++ b/Kernel/Source/Arch/Exceptions.c @@ -4,8 +4,8 @@ #include #include -void ExceptionsHandler(ExceptionsContext* frame, ExceptionsType type) { - if (type == ExceptionsIRQEl1h || type == ExceptionsIRQEl064) return GICDispatch(type); +Address ExceptionsHandler(ExceptionsContext* frame, ExceptionsType type) { + if (type == ExceptionsIRQEl1h || type == ExceptionsIRQEl064) return GICDispatch(frame, type); OSPanicException(frame); } diff --git a/Kernel/Source/Arch/GIC.c b/Kernel/Source/Arch/GIC.c index 9cf1a48..d751cea 100644 --- a/Kernel/Source/Arch/GIC.c +++ b/Kernel/Source/Arch/GIC.c @@ -52,14 +52,18 @@ void GICCWriteEOIR(UInt32 irqID) { GICC[kGICCEOIR / 4] = irqID; } -void GICDispatch(ExceptionsType type) { - if (type != ExceptionsIRQEl1h && type != ExceptionsIRQEl064) return; +Address GICDispatch(ExceptionsContext* frame, ExceptionsType type) { + if (type != ExceptionsIRQEl1h && type != ExceptionsIRQEl064) return (Address)frame; UInt32 irqID = GICCReadIAR() & 0x3FF; - if (irqID == 1023) return; // spurious interrupt + if (irqID == 1023) return (Address)frame; // spurious interrupt + + + Address newStackPointer = (Address)frame; if (irqID == kTimerIRQ) { - TimerReset(kTimerFrequency); + newStackPointer = TimerHandler(frame); } GICCWriteEOIR(irqID); + return newStackPointer; } diff --git a/Kernel/Source/Arch/Timer.c b/Kernel/Source/Arch/Timer.c index 5553a60..62b2e74 100644 --- a/Kernel/Source/Arch/Timer.c +++ b/Kernel/Source/Arch/Timer.c @@ -1,5 +1,9 @@ #include #include +#include +#include + +static volatile UInt64 sTimerCounter = 0; void TimerInitialize() { GICEnableInterrupt(kTimerIRQ); @@ -12,3 +16,13 @@ void TimerReset(UInt64 interval) { __asm__ volatile ("msr cntv_tval_el0, %0" :: "r"(frequency /interval)); __asm__ volatile ("msr cntv_ctl_el0, %0" :: "r"((UInt64)1)); } + +Address TimerHandler(ExceptionsContext* frame) { + sTimerCounter++; + TimerReset(kTimerFrequency); + return SchedulerNext((Address)frame); +} + +UInt64 TimerGetCounter() { + return sTimerCounter; +} diff --git a/Kernel/Source/Arch/vectors.S b/Kernel/Source/Arch/vectors.S index 05e183e..59db0d4 100644 --- a/Kernel/Source/Arch/vectors.S +++ b/Kernel/Source/Arch/vectors.S @@ -1,6 +1,6 @@ .macro ventry type .align 7 - sub sp, sp, #272 // save 272 bytes of stack + sub sp, sp, #288 // save 288 bytes of stack stp x0, x1, [sp, #0] // move stack mov x1, #\type // move type to x1 b ExceptionsTrapEntry @@ -50,12 +50,17 @@ ExceptionsTrapEntry: mrs x21, elr_el1 mrs x22, spsr_el1 mrs x23, esr_el1 + mrs x24, sp_el0 stp x30, x21, [sp, #16 * 15] stp x22, x23, [sp, #16 * 16] mov x0, sp bl ExceptionsHandler + mov sp, x0 + + ldp x24, xzr, [sp, #16 * 17] + msr sp_el0, x24 ldp x22, x23, [sp, #16 * 16] msr spsr_el1, x22 @@ -79,7 +84,7 @@ ExceptionsTrapEntry: ldp x2, x3, [sp, #16 * 1] ldp x0, x1, [sp, #0] - add sp, sp, #272 + add sp, sp, #288 eret .global ExceptionsVectorsInit diff --git a/Kernel/Source/KernelMain.c b/Kernel/Source/KernelMain.c index 60c6007..e72433e 100644 --- a/Kernel/Source/KernelMain.c +++ b/Kernel/Source/KernelMain.c @@ -8,6 +8,7 @@ #include #include #include +#include void KernelMain(Bootinfo* bootinfo) { OSLog("Kernel started.\n"); @@ -16,6 +17,7 @@ void KernelMain(Bootinfo* bootinfo) { } VMBootMemoryMap bootMap = {0}; + bootMap.reservedCount = 0; DTBParse(bootinfo->dtb, &bootMap); @@ -30,5 +32,7 @@ void KernelMain(Bootinfo* bootinfo) { TimerInitialize(); CPUEnableInterrupts(); + SchedulerInitialize(); + OSLog("Kernel initialized.\n"); } diff --git a/Kernel/Source/OS/Panic.c b/Kernel/Source/OS/Panic.c index 56ed5d2..4d962da 100644 --- a/Kernel/Source/OS/Panic.c +++ b/Kernel/Source/OS/Panic.c @@ -93,11 +93,11 @@ __attribute__((noreturn)) void OSPanicException(ExceptionsContext* frame) { PrintSeparator(); OSLog("Registers:\n"); PrintSeparator(); - OSLog("x0 = 0x%X; x1 = 0x%X\n", frame->x0, frame->x1); - OSLog("x2 = 0x%X; x3 = 0x%X\n", frame->x2, frame->x3); - OSLog("x4 = 0x%X; x5 = 0x%X\n", frame->x4, frame->x5); - OSLog("x6 = 0x%X; x7 = 0x%X\n", frame->x6, frame->x7); - OSLog("x8 = 0x%X; x9 = 0x%X\n", frame->x8, frame->x9); + OSLog("x0 = 0x%X; x1 = 0x%X\n", frame->x0, frame->x1); + OSLog("x2 = 0x%X; x3 = 0x%X\n", frame->x2, frame->x3); + OSLog("x4 = 0x%X; x5 = 0x%X\n", frame->x4, frame->x5); + OSLog("x6 = 0x%X; x7 = 0x%X\n", frame->x6, frame->x7); + OSLog("x8 = 0x%X; x9 = 0x%X\n", frame->x8, frame->x9); OSLog("x10 = 0x%X; x11 = 0x%X\n", frame->x10, frame->x11); OSLog("x12 = 0x%X; x13 = 0x%X\n", frame->x12, frame->x13); OSLog("x14 = 0x%X; x15 = 0x%X\n", frame->x14, frame->x15); @@ -107,8 +107,8 @@ __attribute__((noreturn)) void OSPanicException(ExceptionsContext* frame) { OSLog("x22 = 0x%X; x23 = 0x%X\n", frame->x22, frame->x23); OSLog("x24 = 0x%X; x25 = 0x%X\n", frame->x24, frame->x25); OSLog("x26 = 0x%X; x27 = 0x%X\n", frame->x26, frame->x27); - OSLog("\t\tx28 = 0x%X\n", frame->x28); - OSLog("FP = 0x%X; LR = 0x%X\n", frame->x29, frame->x30); + OSLog("\t x28 = 0x%X\n", frame->x28); + OSLog("FP = 0x%X; LR = 0x%X\n", frame->x29, frame->x30); DrawPanicFooter(); Halt(); diff --git a/Kernel/Source/OS/Scheduler.c b/Kernel/Source/OS/Scheduler.c new file mode 100644 index 0000000..a818f13 --- /dev/null +++ b/Kernel/Source/OS/Scheduler.c @@ -0,0 +1,116 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +OSTask* gOSSchedulerCurrentTask = nullptr; +UInt32 gOSSchedulerNextProcessID = 1; + +static OSProcess sOSSchedulerKernelProcess; + +void SchedulerInitialize() { + sOSSchedulerKernelProcess.id = 0; + sOSSchedulerKernelProcess.state = OSTaskStateRunning; + sOSSchedulerKernelProcess.l0Table = gVMKernelL0Table; + StringCopy(sOSSchedulerKernelProcess.name, "kernel"); + + OSTask* kernelTask = (OSTask*)HeapAllocate(sizeof(OSTask)); + if (!kernelTask) OSPanic("Failed to allocate kernel task"); + + MemorySet(kernelTask, 0, sizeof(OSTask)); + kernelTask->id = 0; + kernelTask->process = &sOSSchedulerKernelProcess; + kernelTask->sleepTicks = 0; + kernelTask->next = kernelTask; + kernelTask->state = OSTaskStateRunning; + kernelTask->waitingForProcessId = -1; + + gOSSchedulerCurrentTask = kernelTask; +} + +OSTask* SchedulerSpawn(void(*entryPoint)(), OSProcess* owner, Boolean isUser, Address fixedUserStackAddress) { + OSTask* task = (OSTask*)HeapAllocate(sizeof(OSTask)); + Pointer stackBase = nullptr; + if (!task) goto cleanup; + + if (!owner) owner = &sOSSchedulerKernelProcess; + Size stackSize = kOSSchedulerTaskStackSize; + stackBase = HeapAllocate(stackSize); + if (!stackBase) goto cleanup; + + Address stackPointer = (Address)stackBase + stackSize; + stackPointer -= sizeof(ExceptionsContext); + stackPointer = AlignDown64(stackPointer, 16); + ExceptionsContext* context = (ExceptionsContext*)stackPointer; + MemorySet(context, 0, sizeof(ExceptionsContext)); + context->elr_el1 = (Address)entryPoint; + + if (isUser) { + context->spsr_el1 = 0x00000000; // El0t (M[3:0] = 0b0000) allow interrupts + context->sp_el0 = fixedUserStackAddress; + } else { + context->spsr_el1 = 0x00000005; // El1h (M[3:0] = 0b0101) allow interrupts + } + + task->stackPointer = stackPointer; + task->process = owner; + task->id = owner->id; + task->sleepTicks = 0; + task->kernelStackBase = stackBase; + task->kernelStackTop = (Address)stackBase + stackSize; + task->state = OSTaskStateRunning; + task->waitingForProcessId = -1; + + task->next = gOSSchedulerCurrentTask->next; + gOSSchedulerCurrentTask->next = task; + + return task; +cleanup: + if (task) HeapFree(task); + if (stackBase) HeapFree(stackBase); + return nullptr; +} + +Address SchedulerNext(Address stackPointer) { + if (!gOSSchedulerCurrentTask) return stackPointer; + + gOSSchedulerCurrentTask->stackPointer = stackPointer; + OSTask* taskIterator = gOSSchedulerCurrentTask->next; + + do { + if (taskIterator->sleepTicks > 0) taskIterator->sleepTicks--; + taskIterator = taskIterator->next; + } while (taskIterator != gOSSchedulerCurrentTask->next); + + if (gOSSchedulerCurrentTask->sleepTicks > 0) gOSSchedulerCurrentTask->sleepTicks--; + + OSTask* nextTask = gOSSchedulerCurrentTask->next; + loop { + if (nextTask->state == OSTaskStateSleeping && nextTask->sleepTicks == 0) nextTask->state = OSTaskStateRunning; + if (nextTask->state == OSTaskStateRunning) break; + nextTask = nextTask->next; + + if (nextTask == gOSSchedulerCurrentTask) { + if (gOSSchedulerCurrentTask->state == OSTaskStateRunning) break; + OSPanic("No running tasks"); + } + } + + if (nextTask->process->l0Table != gOSSchedulerCurrentTask->process->l0Table) { + Address physicalL0Table = VMHHDMToPhys((Address)nextTask->process->l0Table); + CPUSwitchAddressSpace(physicalL0Table); + } + gOSSchedulerCurrentTask = nextTask; + + return gOSSchedulerCurrentTask->stackPointer; +} + +void SchedulerYield(UInt64 ticks) { + gOSSchedulerCurrentTask->sleepTicks = ticks; + gOSSchedulerCurrentTask->state = OSTaskStateSleeping; + CPUException(kOSSchedulerExceptionNumber); +} \ No newline at end of file