Implement kernel threads and a round-robin scheduler

This commit is contained in:
Ali Mashtizadeh 2014-07-23 18:07:07 -07:00
parent a3cf5d9ad9
commit c184e7fa0a
13 changed files with 378 additions and 13 deletions

View File

@ -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",

View File

@ -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);

View 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__ */

View File

@ -81,6 +81,7 @@ typedef struct TrapFrame
void Trap_Init();
void Trap_Dump(TrapFrame *tf);
void Trap_Pop(TrapFrame *tf);
#endif /* __TRAP_H__ */

View File

@ -144,12 +144,12 @@ SECTIONS
}
.data1 : { *(.data1) }
/* Kernel Debugger */
__kdbgcmd_start = .;
.kdbgcmd :
.kdbgcmd ALIGN(CONSTANT(MAXPAGESIZE)) :
{
__kdbgcmd_start = .;
*(.kdbgcmd)
__kdbgcmd_end = .;
}
__kdbgcmd_end = .;
_edata = .; PROVIDE (edata = .);
. = .;
__bss_start = .;

View File

@ -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();
}

View File

@ -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
View 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
View 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);
}

View File

@ -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;
}

View File

@ -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
View 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
View 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);