diff --git a/include/sched.h b/include/sched.h new file mode 100644 index 0000000..e421e4b --- /dev/null +++ b/include/sched.h @@ -0,0 +1,45 @@ +#ifndef FROSTBYTE_SCHED_H +#define FROSTBYTE_SCHED_H + +#include "types.h" +#include "vfs.h" + +typedef enum { + threadUnused = 0, + threadRunnable = 1, + threadRunning = 2, + threadSleeping = 3, + threadDead = 4 +} ThreadState; + +typedef void (*ThreadEntry)(void); + +typedef struct Thread { + uint64 rsp; + uint64 stackBase; + uint64 kernelStackTop; + uint64 sleepUntil; + uint64 brk; + uint32 id; + ThreadState state; + const char* name; + FileDescriptor fds[maxOpenFiles]; +} Thread; + +void schedInit(void); +uint32 threadCreate(const char* name, ThreadEntry entry); +uint32 threadCreateUser(const char* name, uint64 entry, uint64 userStackTop); +uint64 schedule(uint64 currentRsp); +void schedStart(void); + +void threadExit(uint64 code); +void threadSleep(uint64 milliseconds); +void threadSetBrk(uint32 id, uint64 brk); +uint64 currentThreadSbrk(int64 increment); + +uint32 threadCount(void); +Thread* threadAt(uint32 index); +Thread* currentThread(void); +uint32 currentThreadId(void); + +#endif diff --git a/src/sched.c b/src/sched.c new file mode 100644 index 0000000..ad6c639 --- /dev/null +++ b/src/sched.c @@ -0,0 +1,183 @@ +#include "sched.h" +#include "isr.h" +#include "heap.h" +#include "string.h" +#include "ports.h" +#include "gdt.h" +#include "tss.h" +#include "pit.h" +#include "console.h" +#include "paging.h" +#include "vfs.h" + +#define maxThreads 16 +#define threadStackSize 16384 + +static Thread threads[maxThreads]; +static uint32 threadTotal; +static uint32 currentIndex; +static bool schedulerActive; + +void schedInit(void) +{ + threadTotal = 1; + currentIndex = 0; + + threads[0].id = 0; + threads[0].name = "idle"; + threads[0].state = threadRunning; + threads[0].rsp = 0; + threads[0].stackBase = 0; + threads[0].kernelStackTop = 0; + threads[0].sleepUntil = 0; + threads[0].brk = 0; + vfsFdInit(threads[0].fds); +} + +uint32 threadCreate(const char* name, ThreadEntry entry) +{ + uint32 slot = threadTotal; + threadTotal++; + + uint64 stackBase = (uint64)kmalloc(threadStackSize); + uint64 stackTop = (stackBase + threadStackSize) & ~15ull; + + Registers* frame = (Registers*)(stackTop - sizeof(Registers)); + memset(frame, 0, sizeof(Registers)); + frame->rip = (uint64)entry; + frame->cs = kernelCodeSelector; + frame->rflags = 0x202; + frame->rsp = stackTop; + frame->ss = kernelDataSelector; + + threads[slot].id = slot; + threads[slot].name = name; + threads[slot].state = threadRunnable; + threads[slot].stackBase = stackBase; + threads[slot].kernelStackTop = stackTop; + threads[slot].rsp = (uint64)frame; + threads[slot].sleepUntil = 0; + threads[slot].brk = 0; + vfsFdInit(threads[slot].fds); + + return slot; +} + +uint32 threadCreateUser(const char* name, uint64 entry, uint64 userStackTop) +{ + uint32 slot = threadTotal; + threadTotal++; + + uint64 stackBase = (uint64)kmalloc(threadStackSize); + uint64 kernelStackTop = (stackBase + threadStackSize) & ~15ull; + + Registers* frame = (Registers*)(kernelStackTop - sizeof(Registers)); + memset(frame, 0, sizeof(Registers)); + frame->rip = entry; + frame->cs = userCodeSelector | 3; + frame->rflags = 0x202; + frame->rsp = userStackTop; + frame->ss = userDataSelector | 3; + + threads[slot].id = slot; + threads[slot].name = name; + threads[slot].state = threadRunnable; + threads[slot].stackBase = stackBase; + threads[slot].kernelStackTop = kernelStackTop; + threads[slot].rsp = (uint64)frame; + threads[slot].sleepUntil = 0; + threads[slot].brk = 0; + vfsFdInit(threads[slot].fds); + + return slot; +} + +uint64 schedule(uint64 currentRsp) +{ + if (!schedulerActive) + return currentRsp; + + threads[currentIndex].rsp = currentRsp; + if (threads[currentIndex].state == threadRunning) + threads[currentIndex].state = threadRunnable; + + uint64 now = pitTicks(); + for (uint32 i = 0; i < threadTotal; i++) + if (threads[i].state == threadSleeping && now >= threads[i].sleepUntil) + threads[i].state = threadRunnable; + + uint32 next = 0; + for (uint32 step = 1; step <= threadTotal; step++) { + uint32 candidate = (currentIndex + step) % threadTotal; + if (threads[candidate].state == threadRunnable) { + next = candidate; + break; + } + } + + currentIndex = next; + threads[next].state = threadRunning; + tssSetRsp0(threads[next].kernelStackTop); + return threads[next].rsp; +} + +void schedStart(void) +{ + schedulerActive = true; + enableInterrupts(); + for (;;) + halt(); +} + +void threadExit(uint64 code) +{ + Thread* thread = &threads[currentIndex]; + thread->state = threadDead; + kprintf("[user] pid=%u '%s' exited with code %lu\n", thread->id, thread->name, code); +} + +void threadSleep(uint64 milliseconds) +{ + uint64 ticks = milliseconds * pitFrequency() / 1000; + if (ticks == 0) + ticks = 1; + threads[currentIndex].sleepUntil = pitTicks() + ticks; + threads[currentIndex].state = threadSleeping; +} + +void threadSetBrk(uint32 id, uint64 brk) +{ + threads[id].brk = brk; +} + +uint64 currentThreadSbrk(int64 increment) +{ + Thread* thread = &threads[currentIndex]; + uint64 previous = thread->brk; + + if (increment > 0) + pagingMapUserRange(previous, (uint64)increment); + + thread->brk = previous + (uint64)increment; + return previous; +} + +uint32 threadCount(void) +{ + return threadTotal; +} + +Thread* threadAt(uint32 index) +{ + return &threads[index]; +} + +Thread* currentThread(void) +{ + return &threads[currentIndex]; +} + +uint32 currentThreadId(void) +{ + return threads[currentIndex].id; +}