Implement kernel threads and a round-robin scheduler
This commit is contained in:
parent
a3cf5d9ad9
commit
c184e7fa0a
@ -14,14 +14,16 @@ src_amd64 = [
|
||||
"amd64/critical.c",
|
||||
"amd64/debug.c",
|
||||
"amd64/disasm.c",
|
||||
"amd64/trap.c",
|
||||
"amd64/trapentry.S",
|
||||
"amd64/ioapic.c",
|
||||
"amd64/irq.c",
|
||||
"amd64/lapic.c",
|
||||
"amd64/machine.c",
|
||||
"amd64/pci.c",
|
||||
"amd64/pmap.c",
|
||||
"amd64/lapic.c",
|
||||
"amd64/ioapic.c",
|
||||
"amd64/irq.c",
|
||||
"amd64/switch.S",
|
||||
"amd64/thread.c",
|
||||
"amd64/trap.c",
|
||||
"amd64/trapentry.S",
|
||||
"amd64/xmem.c",
|
||||
# Devices
|
||||
"dev/x86/debugcons.c",
|
||||
@ -36,6 +38,7 @@ src_common = [
|
||||
"kern/palloc.c",
|
||||
"kern/printf.c",
|
||||
"kern/spinlock.c",
|
||||
"kern/thread.c",
|
||||
"dev/ahci.c",
|
||||
"dev/console.c",
|
||||
"dev/pci.c",
|
||||
|
@ -2,6 +2,8 @@
|
||||
#ifndef __PMAP_H__
|
||||
#define __PMAP_H__
|
||||
|
||||
#include <machine/amd64.h>
|
||||
|
||||
/*
|
||||
* +----------------------+
|
||||
* | Zero Page (Unmapped) |
|
||||
@ -44,8 +46,9 @@
|
||||
typedef struct AS AS;
|
||||
|
||||
void PMap_Init();
|
||||
AS* Map_NewAS();
|
||||
AS* PMap_NewAS();
|
||||
void PMap_DestroyAS(AS *space);
|
||||
void PMap_LoadAS(AS *space);
|
||||
void PMap_SystemLookup(uint64_t va, PageEntry **entry, int size);
|
||||
bool PMap_SystemLMap(uint64_t phys, uint64_t virt, uint64_t lpages, uint64_t flags);
|
||||
bool PMap_SystemMap(uint64_t phys, uint64_t virt, uint64_t pages);
|
||||
|
26
sys/amd64/include/thread.h
Normal file
26
sys/amd64/include/thread.h
Normal file
@ -0,0 +1,26 @@
|
||||
|
||||
#ifndef __MACHINE_THREAD_H__
|
||||
#define __MACHINE_THREAD_H__
|
||||
|
||||
#include <machine/amd64.h>
|
||||
#include <machine/amd64op.h>
|
||||
|
||||
typedef struct ThreadArchStackFrame {
|
||||
uint64_t r15;
|
||||
uint64_t r14;
|
||||
uint64_t r13;
|
||||
uint64_t r12;
|
||||
uint64_t rbx;
|
||||
uint64_t rdi; // First argument
|
||||
uint64_t rbp;
|
||||
uint64_t rip;
|
||||
} ThreadArchStackFrame;
|
||||
|
||||
typedef struct ThreadArch {
|
||||
XSAVEArea xsa;
|
||||
bool useFP;
|
||||
uint64_t rsp;
|
||||
} ThreadArch;
|
||||
|
||||
#endif /* __MACHINE_THREAD_H__ */
|
||||
|
@ -81,6 +81,7 @@ typedef struct TrapFrame
|
||||
|
||||
void Trap_Init();
|
||||
void Trap_Dump(TrapFrame *tf);
|
||||
void Trap_Pop(TrapFrame *tf);
|
||||
|
||||
#endif /* __TRAP_H__ */
|
||||
|
||||
|
@ -144,12 +144,12 @@ SECTIONS
|
||||
}
|
||||
.data1 : { *(.data1) }
|
||||
/* Kernel Debugger */
|
||||
__kdbgcmd_start = .;
|
||||
.kdbgcmd :
|
||||
.kdbgcmd ALIGN(CONSTANT(MAXPAGESIZE)) :
|
||||
{
|
||||
__kdbgcmd_start = .;
|
||||
*(.kdbgcmd)
|
||||
}
|
||||
__kdbgcmd_end = .;
|
||||
}
|
||||
_edata = .; PROVIDE (edata = .);
|
||||
. = .;
|
||||
__bss_start = .;
|
||||
|
@ -14,6 +14,8 @@
|
||||
#include <machine/trap.h>
|
||||
#include <machine/pmap.h>
|
||||
|
||||
#include <sys/thread.h>
|
||||
|
||||
#include "../dev/console.h"
|
||||
|
||||
extern void PCI_Init();
|
||||
@ -109,6 +111,11 @@ void Machine_EarlyInit()
|
||||
PAlloc_Init();
|
||||
}
|
||||
|
||||
void Machine_IdleThread(void *test)
|
||||
{
|
||||
while (1) { enable_interrupts(); hlt(); }
|
||||
}
|
||||
|
||||
void Machine_Init()
|
||||
{
|
||||
Machine_GDTInit();
|
||||
@ -124,11 +131,19 @@ void Machine_Init()
|
||||
IRQ_Init();
|
||||
LAPIC_Init();
|
||||
IOAPIC_Init();
|
||||
Thread_Init();
|
||||
|
||||
PCI_Init();
|
||||
IDE_Init();
|
||||
|
||||
Critical_Exit();
|
||||
|
||||
Thread *thr = Thread_KThreadCreate(&Machine_IdleThread, NULL);
|
||||
if (thr == NULL) {
|
||||
kprintf("Couldn't create idle thread!\n");
|
||||
}
|
||||
Thread_SetRunnable(thr);
|
||||
|
||||
breakpoint();
|
||||
}
|
||||
|
||||
|
@ -88,10 +88,13 @@ PMap_NewAS()
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < PAGETABLE_ENTRIES; i++)
|
||||
for (i = 0; i < PAGETABLE_ENTRIES / 2; i++)
|
||||
{
|
||||
as->root->entries[i] = 0;
|
||||
}
|
||||
for (i = PAGETABLE_ENTRIES / 2; i < PAGETABLE_ENTRIES; i++) {
|
||||
as->root->entries[i] = systemAS.root->entries[i];
|
||||
}
|
||||
|
||||
return as;
|
||||
}
|
||||
@ -99,12 +102,21 @@ PMap_NewAS()
|
||||
void
|
||||
PMap_DestroyAS(AS *space)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < PAGETABLE_ENTRIES / 2; i++)
|
||||
{
|
||||
if (space->root->entries[i] != 0) {
|
||||
// Remove subpages
|
||||
PAlloc_FreePage((void *)DMPA2VA(space->root->entries[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PMap_LoadAS(AS *space)
|
||||
{
|
||||
write_cr3((uint64_t)space->root);
|
||||
write_cr3(DMVA2PA((uint64_t)space->root));
|
||||
currentAS[THISCPU()] = space;
|
||||
}
|
||||
|
||||
|
34
sys/amd64/switch.S
Normal file
34
sys/amd64/switch.S
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Trap Handlers
|
||||
*/
|
||||
|
||||
#include <machine/asm.h>
|
||||
|
||||
.text
|
||||
|
||||
# switch(uint64_t *oldsp, uint64_t newsp)
|
||||
# %rdi: oldsp
|
||||
# %rsi: newsp
|
||||
FUNC_BEGIN(switchstack)
|
||||
pushq %rbp
|
||||
pushq %rdi
|
||||
pushq %rbx
|
||||
pushq %r12
|
||||
pushq %r13
|
||||
pushq %r14
|
||||
pushq %r15
|
||||
|
||||
# Switch stack
|
||||
movq %rsp, (%rdi)
|
||||
movq %rsi, %rsp
|
||||
|
||||
popq %r15
|
||||
popq %r14
|
||||
popq %r13
|
||||
popq %r12
|
||||
popq %rbx
|
||||
popq %rdi
|
||||
popq %rbp
|
||||
ret
|
||||
FUNC_END(switchstack)
|
||||
|
65
sys/amd64/thread.c
Normal file
65
sys/amd64/thread.c
Normal file
@ -0,0 +1,65 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/kassert.h>
|
||||
#include <sys/thread.h>
|
||||
#include <machine/amd64.h>
|
||||
#include <machine/amd64op.h>
|
||||
#include <machine/trap.h>
|
||||
|
||||
extern void ThreadKThreadEntry(TrapFrame *tf);
|
||||
extern void switchstack(uint64_t *oldrsp, uint64_t rsp);
|
||||
|
||||
void
|
||||
Thread_InitArch(Thread *thr)
|
||||
{
|
||||
thr->arch.useFP = false;
|
||||
}
|
||||
|
||||
void
|
||||
Thread_SetupKThread(Thread *thr, void (*f)(void *), void *arg)
|
||||
{
|
||||
// Initialize stack
|
||||
uint64_t stacktop = thr->kstack + PGSIZE;
|
||||
ThreadArchStackFrame *sf;
|
||||
TrapFrame *tf;
|
||||
|
||||
tf = (TrapFrame *)(stacktop - sizeof(*tf));
|
||||
sf = (ThreadArchStackFrame *)(stacktop - sizeof(*tf) - sizeof(*sf));
|
||||
thr->arch.rsp = (uint64_t)sf;
|
||||
|
||||
memset(tf, 0, sizeof(*tf));
|
||||
memset(sf, 0, sizeof(*sf));
|
||||
|
||||
// Setup thread exit function on stack
|
||||
|
||||
sf->rip = (uint64_t)&ThreadKThreadEntry;
|
||||
sf->rdi = (uint64_t)tf;
|
||||
|
||||
tf->ss = SEL_KDS;
|
||||
tf->rsp = stacktop;
|
||||
tf->cs = SEL_KCS;
|
||||
tf->rip = (uint64_t)f;
|
||||
tf->rdi = (uint64_t)arg;
|
||||
tf->rflags = RFLAGS_IF;
|
||||
}
|
||||
|
||||
void
|
||||
Thread_SwitchArch(Thread *oldthr, Thread *newthr)
|
||||
{
|
||||
if (oldthr->arch.useFP)
|
||||
{
|
||||
fxsave(&oldthr->arch.xsa);
|
||||
}
|
||||
|
||||
if (newthr->arch.useFP)
|
||||
{
|
||||
fxrstor(&newthr->arch.xsa);
|
||||
}
|
||||
|
||||
// Jump to trapframe
|
||||
switchstack(&oldthr->arch.rsp, newthr->arch.rsp);
|
||||
}
|
||||
|
@ -147,6 +147,9 @@ trap_entry(TrapFrame *tf)
|
||||
//kprintf("IRQ: %d\n", tf->vector);
|
||||
IRQ_Handler(tf->vector - T_IRQ_BASE);
|
||||
LAPIC_SendEOI();
|
||||
if (tf->vector == T_IRQ_TIMER) {
|
||||
Thread_Scheduler();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -170,6 +170,7 @@ trap_common:
|
||||
pushq %r15
|
||||
movq %rsp, %rdi
|
||||
call trap_entry
|
||||
.globl trap_return
|
||||
trap_return:
|
||||
popq %r15
|
||||
popq %r14
|
||||
@ -189,8 +190,8 @@ trap_return:
|
||||
addq $16, %rsp // Skip error code and vector number
|
||||
iretq
|
||||
|
||||
.globl trap_pop
|
||||
trap_pop:
|
||||
.globl Trap_Pop
|
||||
Trap_Pop:
|
||||
movq %rdi, %rsp
|
||||
jmp trap_return
|
||||
|
||||
|
47
sys/include/thread.h
Normal file
47
sys/include/thread.h
Normal file
@ -0,0 +1,47 @@
|
||||
|
||||
#ifndef __THREAD_H__
|
||||
#define __THREAD_H__
|
||||
|
||||
#include <sys/queue.h>
|
||||
|
||||
struct Thread;
|
||||
typedef struct Thread Thread;
|
||||
|
||||
#include <machine/pmap.h>
|
||||
#include <machine/thread.h>
|
||||
|
||||
#define SCHED_STATE_NULL 0
|
||||
#define SCHED_STATE_RUNNABLE 1
|
||||
#define SCHED_STATE_RUNNING 2
|
||||
#define SCHED_STATE_WAITING 3
|
||||
#define SCHED_STATE_ZOMBIE 4
|
||||
|
||||
typedef struct Thread {
|
||||
ThreadArch arch;
|
||||
AS *space;
|
||||
uintptr_t kstack;
|
||||
// Scheduler
|
||||
int schedState;
|
||||
TAILQ_ENTRY(Thread) schedQueue;
|
||||
// Statistics
|
||||
uint64_t ctxSwitches;
|
||||
uint64_t userTime;
|
||||
uint64_t kernTime;
|
||||
uint64_t waitTime;
|
||||
} Thread;
|
||||
|
||||
void Thread_Init();
|
||||
Thread *Thread_Create();
|
||||
Thread *Thread_KThreadCreate(void (*f)(void*), void *arg);
|
||||
void Thread_SetRunnable(Thread *thr);
|
||||
void Thread_Destroy(Thread *thr);
|
||||
void Thread_Switch(Thread *oldthr, Thread *newthr);
|
||||
void Thread_Scheduler();
|
||||
|
||||
// Platform functions
|
||||
void Thread_InitArch(Thread *thr);
|
||||
void Thread_SetupKThread(Thread *thr, void (*f)(void *), void *arg);
|
||||
void Thread_SwitchArch(Thread *oldthr, Thread *newthr);
|
||||
|
||||
#endif /* __THREAD_H__ */
|
||||
|
155
sys/kern/thread.c
Normal file
155
sys/kern/thread.c
Normal file
@ -0,0 +1,155 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/kassert.h>
|
||||
#include <sys/kconfig.h>
|
||||
#include <sys/kdebug.h>
|
||||
#include <sys/kmem.h>
|
||||
#include <sys/spinlock.h>
|
||||
#include <sys/thread.h>
|
||||
|
||||
#include <machine/trap.h>
|
||||
#include <machine/pmap.h>
|
||||
|
||||
Spinlock threadLock;
|
||||
Thread *curProc;
|
||||
TAILQ_HEAD(ThreadQueueHead, Thread) threadQueue;
|
||||
|
||||
void
|
||||
Thread_Init()
|
||||
{
|
||||
// Create an thread object for current context
|
||||
curProc = Thread_Create();
|
||||
curProc->schedState = SCHED_STATE_RUNNING;
|
||||
|
||||
Spinlock_Init(&threadLock, "Thread Lock");
|
||||
|
||||
TAILQ_INIT(&threadQueue);
|
||||
}
|
||||
|
||||
Thread *
|
||||
Thread_Create()
|
||||
{
|
||||
Thread *thr = PAlloc_AllocPage();
|
||||
|
||||
if (!thr)
|
||||
return NULL;
|
||||
|
||||
memset(thr, 0, sizeof(*thr));
|
||||
|
||||
thr->kstack = (uintptr_t)PAlloc_AllocPage();
|
||||
if (thr->kstack == 0) {
|
||||
PAlloc_FreePage(thr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
thr->space = PMap_NewAS();
|
||||
if (thr->space == NULL) {
|
||||
PAlloc_FreePage((void *)thr->kstack);
|
||||
PAlloc_FreePage(thr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
thr->schedState = SCHED_STATE_NULL;
|
||||
|
||||
Thread_InitArch(thr);
|
||||
// Initialize queue
|
||||
|
||||
return thr;
|
||||
}
|
||||
|
||||
Thread *
|
||||
Thread_KThreadCreate(void (*f)(void *), void *arg)
|
||||
{
|
||||
Thread *thr = Thread_Create();
|
||||
if (!thr)
|
||||
return NULL;
|
||||
|
||||
Thread_SetupKThread(thr, f, arg);
|
||||
|
||||
return thr;
|
||||
}
|
||||
|
||||
void
|
||||
Thread_SetRunnable(Thread *thr)
|
||||
{
|
||||
Spinlock_Lock(&threadLock);
|
||||
|
||||
thr->schedState = SCHED_STATE_RUNNABLE;
|
||||
TAILQ_INSERT_TAIL(&threadQueue, thr, schedQueue);
|
||||
|
||||
Spinlock_Unlock(&threadLock);
|
||||
}
|
||||
|
||||
void
|
||||
Thread_Destroy(Thread *thr)
|
||||
{
|
||||
// Remove from queue
|
||||
|
||||
// Free AS
|
||||
PAlloc_FreePage((void *)thr->kstack);
|
||||
PAlloc_FreePage(thr);
|
||||
}
|
||||
|
||||
void
|
||||
Thread_Switch(Thread *oldthr, Thread *newthr)
|
||||
{
|
||||
// Load AS
|
||||
PMap_LoadAS(newthr->space);
|
||||
|
||||
Thread_SwitchArch(oldthr, newthr);
|
||||
}
|
||||
|
||||
void
|
||||
Thread_Scheduler()
|
||||
{
|
||||
Thread *prev;
|
||||
Thread *next;
|
||||
|
||||
Spinlock_Lock(&threadLock);
|
||||
|
||||
// Select next thread
|
||||
next = TAILQ_FIRST(&threadQueue);
|
||||
TAILQ_REMOVE(&threadQueue, next, schedQueue);
|
||||
|
||||
prev = curProc;
|
||||
curProc = next;
|
||||
prev->schedState = SCHED_STATE_RUNNABLE;
|
||||
next->schedState = SCHED_STATE_RUNNING;
|
||||
next->ctxSwitches++;
|
||||
|
||||
TAILQ_INSERT_TAIL(&threadQueue, prev, schedQueue);
|
||||
|
||||
Thread_Switch(prev, next);
|
||||
|
||||
Spinlock_Unlock(&threadLock);
|
||||
}
|
||||
|
||||
void
|
||||
ThreadKThreadEntry(TrapFrame *tf)
|
||||
{
|
||||
Spinlock_Unlock(&threadLock);
|
||||
|
||||
Trap_Pop(tf);
|
||||
}
|
||||
|
||||
void
|
||||
Debug_Threads(int argc, const char *argv[])
|
||||
{
|
||||
Thread *thr;
|
||||
|
||||
Spinlock_Lock(&threadLock);
|
||||
|
||||
kprintf("Current: %016llx %d\n", curProc, curProc->ctxSwitches);
|
||||
TAILQ_FOREACH(thr, &threadQueue, schedQueue)
|
||||
{
|
||||
kprintf("Thread: %016llx %d\n", thr, thr->ctxSwitches);
|
||||
}
|
||||
|
||||
Spinlock_Unlock(&threadLock);
|
||||
}
|
||||
|
||||
REGISTER_DBGCMD(threads, "Display list of threads", Debug_Threads);
|
||||
|
Loading…
Reference in New Issue
Block a user