diff options
author | Alex Auvolat <alex.auvolat@ens.fr> | 2014-12-07 19:59:34 +0100 |
---|---|---|
committer | Alex Auvolat <alex.auvolat@ens.fr> | 2014-12-07 19:59:34 +0100 |
commit | f0475a3d517b4df8eb2b73f22eaec91a72bcc51f (patch) | |
tree | 084e6cd1db812cafdd67ba650796eda3091c2ea7 /kernel/l0/task.c | |
parent | d7aaba8c85cc71f3184cddcf20b740c5157c864d (diff) | |
download | kogata-f0475a3d517b4df8eb2b73f22eaec91a72bcc51f.tar.gz kogata-f0475a3d517b4df8eb2b73f22eaec91a72bcc51f.zip |
Implement multitasking.
Diffstat (limited to 'kernel/l0/task.c')
-rw-r--r-- | kernel/l0/task.c | 189 |
1 files changed, 189 insertions, 0 deletions
diff --git a/kernel/l0/task.c b/kernel/l0/task.c new file mode 100644 index 0000000..e02dcf6 --- /dev/null +++ b/kernel/l0/task.c @@ -0,0 +1,189 @@ +#include <task.h> +#include <kmalloc.h> +#include <dbglog.h> +#include <idt.h> + +#include <frame.h> +#include <paging.h> + +void save_context_and_enter_scheduler(saved_context_t *ctx); +void resume_context(saved_context_t *ctx); + +task_t *current_task = 0; + +// ====================== // +// THE PROGRAMMABLE TIMER // +// ====================== // + +void set_pit_frequency(uint32_t freq) { + uint32_t divisor = 1193180 / freq; + ASSERT(divisor < 65536); // must fit on 16 bits + + uint8_t l = (divisor & 0xFF); + uint8_t h = ((divisor >> 8) & 0xFF); + + outb(0x43, 0x36); + outb(0x40, l); + outb(0x40, h); +} + +// ================== // +// THE TASK SCHEDULER // +// ================== // + +static task_t *queue_first_task = 0, *queue_last_task = 0; + +void enqueue_task(task_t *t, bool just_ran) { + ASSERT(t->state == T_STATE_RUNNING); + if (queue_first_task == 0) { + queue_first_task = queue_last_task = t; + t->next_in_queue = 0; + } else if (just_ran) { + t->next_in_queue = 0; + queue_last_task->next_in_queue = t; + queue_last_task = t; + } else { + t->next_in_queue = queue_first_task; + queue_first_task = t; + } +} + +task_t* dequeue_task() { + task_t *t = queue_first_task; + if (t == 0) return 0; + + queue_first_task = t->next_in_queue; + if (queue_first_task == 0) queue_last_task = 0; + + return t; +} + +// ================ // +// THE TASKING CODE // +// ================ // + +void run_scheduler() { + // This function is expected NEVER TO RETURN + if (current_task != 0 && current_task->state == T_STATE_RUNNING) { + enqueue_task(current_task, true); + } + current_task = dequeue_task(); + if (current_task != 0) { + resume_context(¤t_task->ctx); + } else { + // Wait for an IRQ + asm volatile("sti; hlt"); + // At this point an IRQ has happenned + // and has been processed. Loop around. + run_scheduler(); + ASSERT(false); + } +} + +static void run_task(void (*entry)(void*)) { + ASSERT(current_task->state == T_STATE_RUNNING); + ASSERT(current_task->has_result); + + current_task->has_result = false; + + asm volatile("sti"); + entry(current_task->result); + + current_task->state = T_STATE_FINISHED; + // TODO : add job for deleting the task, or whatever + yield(); // expected never to return! + ASSERT(false); +} +task_t *new_task(entry_t entry) { + task_t *t = (task_t*)kmalloc(sizeof(task_t)); + if (t == 0) return 0; + + void* stack = region_alloc(KPROC_STACK_SIZE, REGION_T_KPROC_STACK, 0); + if (stack == 0) { + kfree(t); + return 0; + } + + for (void* i = stack + PAGE_SIZE; i < stack + KPROC_STACK_SIZE; i += PAGE_SIZE) { + uint32_t f = frame_alloc(1); + if (f == 0) { + region_free_unmap_free(stack); + kfree(t); + return 0; + } + pd_map_page(i, f, true); + } + + t->stack_region = find_region(stack); + + t->ctx.esp = (uint32_t*)(t->stack_region->addr + t->stack_region->size); + *(--t->ctx.esp) = (uint32_t)entry; // push first argument : entry point + *(--t->ctx.esp) = 0; // push invalid return address (the run_task function never returns) + + t->ctx.eip = (void(*)())run_task; + t->state = T_STATE_WAITING; + t->result = 0; + t->has_result = false; + + t->more_data = 0; + + return t; +} + +void tasking_setup(entry_t cont, void* arg) { + set_pit_frequency(100); + idt_set_irq_handler(IRQ0, yield); + + task_t *t = new_task(cont); + ASSERT(t != 0); + + resume_with_result(t, arg, false); + + run_scheduler(); // never returns + ASSERT(false); +} + +void yield() { + if (current_task == 0) { + // might happen before tasking is initialized + dbg_printf("Warning: probable deadlock."); + } else { + save_context_and_enter_scheduler(¤t_task->ctx); + } +} + +void* wait_for_result() { + uint32_t eflags; + asm volatile("pushf; pop %0" : "=r"(eflags)); + asm volatile("cli"); + + if (!current_task->has_result) { + current_task->state = T_STATE_WAITING; + save_context_and_enter_scheduler(¤t_task->ctx); + } + ASSERT(current_task->has_result); + current_task->has_result = false; + + if (eflags & EFLAGS_IF) asm volatile("sti"); + + return current_task->result; +} + +void resume_with_result(task_t *task, void* data, bool run_at_once) { + uint32_t eflags; + asm volatile("pushf; pop %0" : "=r"(eflags)); + asm volatile("cli"); + + task->has_result = true; + task->result = data; + + if (task->state == T_STATE_WAITING) { + task->state = T_STATE_RUNNING; + enqueue_task(task, false); + } + if (run_at_once) yield(); + + if (eflags & EFLAGS_IF) asm volatile("sti"); +} + +/* vim: set ts=4 sw=4 tw=0 noet :*/ |