summaryrefslogtreecommitdiff
path: root/kernel/sched.c
diff options
context:
space:
mode:
authorJake Mannens <jake72360@gmail.com>2018-07-11 00:18:49 +1000
committerJake Mannens <jake72360@gmail.com>2018-07-11 00:18:49 +1000
commit99d16e98e04c73e108160d7d70244bafdb33b6fc (patch)
tree4bdd78c2340e4c9ff2a875ad07908b5f258357fc /kernel/sched.c
parentb99a1b58d63dfefb319eecb564c07a4029e02233 (diff)
Implemented a basic scheduler using an array of task structures in which
each structure has a TSS inside, as well as several descriptor entries in the GDT pointing to each task's TSS. Task switching is performed in hardware by means of far jumping to the new task's TSS descriptor in the GDT. Task states are saved manually by copying the saved state from the interrupt handler's stack, back into the task's TSS. Added the function save_state() to sched.c which can be called from assembly to copy the state information of the suspended task from the current stack, to it's respective task structure. NOTE: this assumes that the pointer cstate has been set ahead of time to point to the state information on the stack, and that the pointer ctask has not yet been modified. Added the function reschedule() which is currently called from the timer's tick handler. Currently, this function merely alternates between the two tasks created in sched_init(), but in future, will be expanded to decided independently which task will run next. The function userspace_init() was renamed to the more appropriate sched_init(). Moved most of the code from the original userspace_init() to the new function create_proc() which sets up a virtual address space and state information, copies the specified binary into the new address space, then returns a pointer to the newly created tasks entry in the task table. Defined the type pid_t in unistd.h as a 16-bit unsigned integer. Added two new debugging system calls for checking which task is currently active. The getpid() system calls returns the caller's PID whilst the getpdir() call returns the physical location of the caller's page directory (the value of CR3). The getpdir() call can be used as a fallback to determine which task is *actually* running if the information in ctask or the getpid() call is faulty. Added two new subroutines; set_tss() and clear_tss() (whose prototypes are defined in asm/system.h) for managing the task-state descriptor entries in the GDT. set_tss() initializes a TSS descriptor and sets it's base pointer to the supplied TSS pointer whilst clear_tss() simply marks a TSS descriptor as 'not present' without clearing it.
Diffstat (limited to 'kernel/sched.c')
-rw-r--r--kernel/sched.c151
1 files changed, 126 insertions, 25 deletions
diff --git a/kernel/sched.c b/kernel/sched.c
index 3e25de0..dd75d47 100644
--- a/kernel/sched.c
+++ b/kernel/sched.c
@@ -1,10 +1,17 @@
#include <asm/system.h>
-#include <kernel/con.h>
#include <kernel/memory.h>
#include <kernel/sched.h>
#include <string.h>
#include <sys/types.h>
+/*
+ * WARNING!
+ * don't change this without also updating
+ * the number of TSS descriptors in the GDT
+ * (located in boot.s)
+ */
+#define NRTASKS 64
+
#define USRSTART ((void*) 0x40000000)
#define pages(n) \
@@ -23,9 +30,29 @@
extern char _usrbin_start;
extern char _usrbin_size;
+struct task_state {
+ uint16_t ds;
+ uint32_t edi;
+ uint32_t esi;
+ uint32_t ebp;
+ uint32_t esp_garbage;
+ uint32_t ebx;
+ uint32_t edx;
+ uint32_t ecx;
+ uint32_t eax;
+ uint32_t eip;
+ uint32_t cs;
+ uint32_t eflags;
+ uint32_t esp;
+ uint32_t ss;
+} __attribute__((packed));
+
struct task_struct *ctask;
+struct task_state *cstate;
-static struct task_struct task;
+static struct task_struct tasks[NRTASKS];
+
+static pid_t nextpid;
static inline void empty_table(void *t) {
int i;
@@ -34,19 +61,41 @@ static inline void empty_table(void *t) {
((uint32_t*) t)[i] = 0;
}
-void userspace_init(void) {
+struct task_struct *create_proc(void *bin, size_t len) {
int i, j;
int c;
size_t totpages, npages, ntables;
void *p, *page, *tab, *map;
void *pdir, *stackt, *stackp;
+ struct task_struct *task = NULL;
- size_t usrsize = (size_t) &_usrbin_size;
-
- npages = pages(usrsize);
- ntables = tables(pages(usrsize));
+ npages = pages(len);
+ ntables = tables(pages(len));
totpages = npages + ntables + 3;
+ /* find an unused task structure */
+ for(i = 0; i < NRTASKS; i++) {
+ if(!tasks[i].pid) {
+ task = &tasks[i];
+ /* update the GDT */
+ set_tss(i, &tasks[i].state);
+ break;
+ }
+ }
+ if(task == NULL)
+ return NULL;
+
+ /* find an unused PID */
+ for(i = 0; i < NRTASKS; i++) {
+ if(tasks[i].pid == nextpid) {
+ /* TODO: handle PID exhaustion */
+ nextpid++;
+ if(nextpid == 0)
+ nextpid++;
+ i = 0;
+ }
+ }
+
/* here we allocate pages to setup the process'
* address space. in addition to the pages that
* will hold the binary image, the following
@@ -57,19 +106,12 @@ void userspace_init(void) {
* - stack table (1 page)
*/
p = alloc_physical_pages(totpages);
- printk("Allocating %x pages for userspace process\n", totpages);
- if(p == NULL) {
- printk("Failed to allocate memory for userspace!\n");
- return;
- }
+ if(p == NULL)
+ return NULL;
getpage(pdir, p);
getpage(stackt, p);
getpage(stackp, p);
- printk(" Page directory: 0x%08x\n", (uint32_t) pdir);
- printk(" Stack table: 0x%08x\n", (uint32_t) stackt);
- printk(" Stack page: 0x%08x\n", (uint32_t) stackp);
- printk(" Page tables:\n");
/* populate the page directory */
c = npages;
@@ -78,7 +120,6 @@ void userspace_init(void) {
for(i = 0; i < ntables; i++) {
/* add the table entry to the directory */
getpage(tab, p);
- printk(" 0x%08x\n", (uint32_t) tab);
map = map_page(pdir);
((uint32_t*) map)[i + (PGENT / 4)] = (uint32_t) tab | 0x007;
@@ -109,23 +150,83 @@ void userspace_init(void) {
__asm__ volatile ("mov %%eax, %%cr3" :: "a" (pdir));
- memcpy(USRSTART, &_usrbin_start, usrsize);
+ /* load in the user binary */
+ memcpy(USRSTART, bin, len);
/*
* here we initialize the task struct.
* currently, we only setup state information
* necessary to begin execution.
*/
- memset(&task, 0, sizeof(task));
- task.state.cs = 0x1B;
- task.state.ss = 0x23;
- task.state.eip = (uint32_t) USRSTART;
- task.state.esp = 0xFFFFFFFF;
+ memset(task, 0, sizeof(struct task_struct));
+ task->pid = nextpid;
+ task->state.cr3 = (uint32_t) pdir;
+ task->state.cs = 0x1B;
+ task->state.ds = 0x23;
+ task->state.es = 0x23;
+ task->state.fs = 0x23;
+ task->state.gs = 0x23;
+ task->state.ss = 0x23;
+ task->state.eip = (uint32_t) USRSTART;
+ task->state.esp = 0xFFFFFFFF;
+ task->state.esp0 = 0x80000;
+ task->state.ss0 = 0x10;
__asm__ (
"pushf\n" \
"pop %%eax" \
- : "=a" (task.state.eflags) \
+ : "=a" (task->state.eflags) \
::);
- switch_to(&task, &task.state);
+ return task;
+}
+
+void sched_init(void) {
+ int i;
+ struct task_struct *task;
+
+ memset(&tasks, 0, sizeof(tasks));
+ for(i = 0; i < NRTASKS; i++)
+ clear_tss(i);
+
+ nextpid = 1;
+ ctask = NULL;
+ cstate = NULL;
+
+ create_proc(&_usrbin_start, (size_t) &_usrbin_size);
+ create_proc(&_usrbin_start, (size_t) &_usrbin_size);
+
+ switch_to(0, &tasks[0]);
+}
+
+void save_state(void) {
+ if(ctask == NULL || cstate == NULL)
+ return;
+
+ ctask->state.eax = cstate->eax;
+ ctask->state.ebx = cstate->ebx;
+ ctask->state.ecx = cstate->ecx;
+ ctask->state.edx = cstate->edx;
+ ctask->state.esi = cstate->esi;
+ ctask->state.edi = cstate->edi;
+ ctask->state.esp = cstate->esp;
+ ctask->state.ebp = cstate->ebp;
+ ctask->state.cs = cstate->cs;
+ ctask->state.ds = cstate->ds;
+ ctask->state.es = cstate->ds;
+ ctask->state.fs = cstate->ds;
+ ctask->state.gs = cstate->ds;
+ ctask->state.ss = cstate->ss;
+ ctask->state.eflags = cstate->eflags;
+ ctask->state.eip = cstate->eip;
+}
+
+void reschedule(void) {
+ if(ctask == NULL)
+ return;
+
+ if(ctask == &tasks[0]) {
+ switch_to(1, &tasks[1]);
+ } else {
+ switch_to(0, &tasks[0]);
+ }
}