Working kernel written on C and userspace-ready #1
@@ -73,3 +73,16 @@ static inline void CPUEnableMMU(Address l0PhysicalAddress) {
|
|||||||
: "=r"(sctlr) : "r"(sctlr_flags) : "memory"
|
: "=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")
|
||||||
@@ -36,6 +36,8 @@ typedef struct ExceptionsContext {
|
|||||||
UInt64 elr_el1; // pc
|
UInt64 elr_el1; // pc
|
||||||
UInt64 spsr_el1; // cpu status
|
UInt64 spsr_el1; // cpu status
|
||||||
UInt64 esr_el1; // error reason
|
UInt64 esr_el1; // error reason
|
||||||
|
UInt64 sp_el0; // user's stack
|
||||||
|
UInt64 padding; // align to 16 bytes
|
||||||
} ExceptionsContext;
|
} ExceptionsContext;
|
||||||
|
|
||||||
typedef enum ExceptionsType {
|
typedef enum ExceptionsType {
|
||||||
|
|||||||
@@ -25,4 +25,4 @@ enum {
|
|||||||
|
|
||||||
void GICInitialize(Pointer gicdVirtBase, Pointer giccVirtBase);
|
void GICInitialize(Pointer gicdVirtBase, Pointer giccVirtBase);
|
||||||
void GICEnableInterrupt(UInt32 irqID);
|
void GICEnableInterrupt(UInt32 irqID);
|
||||||
void GICDispatch(ExceptionsType type);
|
Address GICDispatch(ExceptionsContext* frame, ExceptionsType type);
|
||||||
@@ -1,8 +1,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <Types.h>
|
#include <Types.h>
|
||||||
|
#include <Arch/Exceptions.h>
|
||||||
|
|
||||||
static const UInt64 kTimerFrequency = 1000; // 1ms
|
static const UInt64 kTimerFrequency = 1000; // 1ms
|
||||||
static const UInt8 kTimerIRQ = 27;
|
static const UInt8 kTimerIRQ = 27;
|
||||||
|
|
||||||
void TimerInitialize();
|
void TimerInitialize();
|
||||||
void TimerReset(UInt64 interval);
|
void TimerReset(UInt64 interval);
|
||||||
|
Address TimerHandler(ExceptionsContext* frame);
|
||||||
|
UInt64 TimerGetCounter();
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <Types.h>
|
||||||
|
|
||||||
|
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);
|
||||||
@@ -4,8 +4,8 @@
|
|||||||
#include <IO/Serial.h>
|
#include <IO/Serial.h>
|
||||||
#include <OS/Panic.h>
|
#include <OS/Panic.h>
|
||||||
|
|
||||||
void ExceptionsHandler(ExceptionsContext* frame, ExceptionsType type) {
|
Address ExceptionsHandler(ExceptionsContext* frame, ExceptionsType type) {
|
||||||
if (type == ExceptionsIRQEl1h || type == ExceptionsIRQEl064) return GICDispatch(type);
|
if (type == ExceptionsIRQEl1h || type == ExceptionsIRQEl064) return GICDispatch(frame, type);
|
||||||
OSPanicException(frame);
|
OSPanicException(frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -52,14 +52,18 @@ void GICCWriteEOIR(UInt32 irqID) {
|
|||||||
GICC[kGICCEOIR / 4] = irqID;
|
GICC[kGICCEOIR / 4] = irqID;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GICDispatch(ExceptionsType type) {
|
Address GICDispatch(ExceptionsContext* frame, ExceptionsType type) {
|
||||||
if (type != ExceptionsIRQEl1h && type != ExceptionsIRQEl064) return;
|
if (type != ExceptionsIRQEl1h && type != ExceptionsIRQEl064) return (Address)frame;
|
||||||
UInt32 irqID = GICCReadIAR() & 0x3FF;
|
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) {
|
if (irqID == kTimerIRQ) {
|
||||||
TimerReset(kTimerFrequency);
|
newStackPointer = TimerHandler(frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
GICCWriteEOIR(irqID);
|
GICCWriteEOIR(irqID);
|
||||||
|
return newStackPointer;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
#include <Arch/Timer.h>
|
#include <Arch/Timer.h>
|
||||||
#include <Arch/GIC.h>
|
#include <Arch/GIC.h>
|
||||||
|
#include <OS/Scheduler.h>
|
||||||
|
#include <Arch/Exceptions.h>
|
||||||
|
|
||||||
|
static volatile UInt64 sTimerCounter = 0;
|
||||||
|
|
||||||
void TimerInitialize() {
|
void TimerInitialize() {
|
||||||
GICEnableInterrupt(kTimerIRQ);
|
GICEnableInterrupt(kTimerIRQ);
|
||||||
@@ -12,3 +16,13 @@ void TimerReset(UInt64 interval) {
|
|||||||
__asm__ volatile ("msr cntv_tval_el0, %0" :: "r"(frequency /interval));
|
__asm__ volatile ("msr cntv_tval_el0, %0" :: "r"(frequency /interval));
|
||||||
__asm__ volatile ("msr cntv_ctl_el0, %0" :: "r"((UInt64)1));
|
__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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
.macro ventry type
|
.macro ventry type
|
||||||
.align 7
|
.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
|
stp x0, x1, [sp, #0] // move stack
|
||||||
mov x1, #\type // move type to x1
|
mov x1, #\type // move type to x1
|
||||||
b ExceptionsTrapEntry
|
b ExceptionsTrapEntry
|
||||||
@@ -50,12 +50,17 @@ ExceptionsTrapEntry:
|
|||||||
mrs x21, elr_el1
|
mrs x21, elr_el1
|
||||||
mrs x22, spsr_el1
|
mrs x22, spsr_el1
|
||||||
mrs x23, esr_el1
|
mrs x23, esr_el1
|
||||||
|
mrs x24, sp_el0
|
||||||
|
|
||||||
stp x30, x21, [sp, #16 * 15]
|
stp x30, x21, [sp, #16 * 15]
|
||||||
stp x22, x23, [sp, #16 * 16]
|
stp x22, x23, [sp, #16 * 16]
|
||||||
|
|
||||||
mov x0, sp
|
mov x0, sp
|
||||||
bl ExceptionsHandler
|
bl ExceptionsHandler
|
||||||
|
mov sp, x0
|
||||||
|
|
||||||
|
ldp x24, xzr, [sp, #16 * 17]
|
||||||
|
msr sp_el0, x24
|
||||||
|
|
||||||
ldp x22, x23, [sp, #16 * 16]
|
ldp x22, x23, [sp, #16 * 16]
|
||||||
msr spsr_el1, x22
|
msr spsr_el1, x22
|
||||||
@@ -79,7 +84,7 @@ ExceptionsTrapEntry:
|
|||||||
ldp x2, x3, [sp, #16 * 1]
|
ldp x2, x3, [sp, #16 * 1]
|
||||||
ldp x0, x1, [sp, #0]
|
ldp x0, x1, [sp, #0]
|
||||||
|
|
||||||
add sp, sp, #272
|
add sp, sp, #288
|
||||||
eret
|
eret
|
||||||
|
|
||||||
.global ExceptionsVectorsInit
|
.global ExceptionsVectorsInit
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include <VM/Heap.h>
|
#include <VM/Heap.h>
|
||||||
#include <OS/Log.h>
|
#include <OS/Log.h>
|
||||||
#include <OS/Panic.h>
|
#include <OS/Panic.h>
|
||||||
|
#include <OS/Scheduler.h>
|
||||||
|
|
||||||
void KernelMain(Bootinfo* bootinfo) {
|
void KernelMain(Bootinfo* bootinfo) {
|
||||||
OSLog("Kernel started.\n");
|
OSLog("Kernel started.\n");
|
||||||
@@ -16,6 +17,7 @@ void KernelMain(Bootinfo* bootinfo) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
VMBootMemoryMap bootMap = {0};
|
VMBootMemoryMap bootMap = {0};
|
||||||
|
|
||||||
bootMap.reservedCount = 0;
|
bootMap.reservedCount = 0;
|
||||||
DTBParse(bootinfo->dtb, &bootMap);
|
DTBParse(bootinfo->dtb, &bootMap);
|
||||||
|
|
||||||
@@ -30,5 +32,7 @@ void KernelMain(Bootinfo* bootinfo) {
|
|||||||
TimerInitialize();
|
TimerInitialize();
|
||||||
CPUEnableInterrupts();
|
CPUEnableInterrupts();
|
||||||
|
|
||||||
|
SchedulerInitialize();
|
||||||
|
|
||||||
OSLog("Kernel initialized.\n");
|
OSLog("Kernel initialized.\n");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,11 +93,11 @@ __attribute__((noreturn)) void OSPanicException(ExceptionsContext* frame) {
|
|||||||
PrintSeparator();
|
PrintSeparator();
|
||||||
OSLog("Registers:\n");
|
OSLog("Registers:\n");
|
||||||
PrintSeparator();
|
PrintSeparator();
|
||||||
OSLog("x0 = 0x%X; x1 = 0x%X\n", frame->x0, frame->x1);
|
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("x2 = 0x%X; x3 = 0x%X\n", frame->x2, frame->x3);
|
||||||
OSLog("x4 = 0x%X; x5 = 0x%X\n", frame->x4, frame->x5);
|
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("x6 = 0x%X; x7 = 0x%X\n", frame->x6, frame->x7);
|
||||||
OSLog("x8 = 0x%X; x9 = 0x%X\n", frame->x8, frame->x9);
|
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("x10 = 0x%X; x11 = 0x%X\n", frame->x10, frame->x11);
|
||||||
OSLog("x12 = 0x%X; x13 = 0x%X\n", frame->x12, frame->x13);
|
OSLog("x12 = 0x%X; x13 = 0x%X\n", frame->x12, frame->x13);
|
||||||
OSLog("x14 = 0x%X; x15 = 0x%X\n", frame->x14, frame->x15);
|
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("x22 = 0x%X; x23 = 0x%X\n", frame->x22, frame->x23);
|
||||||
OSLog("x24 = 0x%X; x25 = 0x%X\n", frame->x24, frame->x25);
|
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("x26 = 0x%X; x27 = 0x%X\n", frame->x26, frame->x27);
|
||||||
OSLog("\t\tx28 = 0x%X\n", frame->x28);
|
OSLog("\t x28 = 0x%X\n", frame->x28);
|
||||||
OSLog("FP = 0x%X; LR = 0x%X\n", frame->x29, frame->x30);
|
OSLog("FP = 0x%X; LR = 0x%X\n", frame->x29, frame->x30);
|
||||||
|
|
||||||
DrawPanicFooter();
|
DrawPanicFooter();
|
||||||
Halt();
|
Halt();
|
||||||
|
|||||||
@@ -0,0 +1,116 @@
|
|||||||
|
#include <OS/Scheduler.h>
|
||||||
|
#include <OS/Panic.h>
|
||||||
|
#include <Arch/Exceptions.h>
|
||||||
|
#include <Arch/CPU.h>
|
||||||
|
#include <VM/VMM.h>
|
||||||
|
#include <VM/Heap.h>
|
||||||
|
#include <Lib/Align.h>
|
||||||
|
#include <Lib/String.h>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user