From d24b3806edddbfff587f1cf91a64338e0fadd48f Mon Sep 17 00:00:00 2001 From: Alex Auvolat Date: Sat, 13 Dec 2014 13:05:10 +0100 Subject: Terminology change : task -> thread. --- kernel/Makefile | 2 +- kernel/include/task.h | 47 ---------- kernel/include/thread.h | 47 ++++++++++ kernel/l0/kmain.c | 12 +-- kernel/l0/paging.c | 12 +-- kernel/l0/task.c | 222 ------------------------------------------------ kernel/l0/thread.c | 222 ++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 282 insertions(+), 282 deletions(-) delete mode 100644 kernel/include/task.h create mode 100644 kernel/include/thread.h delete mode 100644 kernel/l0/task.c create mode 100644 kernel/l0/thread.c diff --git a/kernel/Makefile b/kernel/Makefile index 0088085..5571543 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -11,7 +11,7 @@ LDFLAGS = -T linker.ld -ffreestanding -O2 -nostdlib -lgcc -Xlinker -Map=kernel.m OBJ = lib/string.o lib/printf.o lib/slab_alloc.o lib/mutex.o \ l0/loader.o l0/kmain.o l0/dbglog.o l0/sys.o \ - l0/gdt.o l0/idt.o l0/interrupt.o l0/context_switch.o l0/task.o \ + l0/gdt.o l0/idt.o l0/interrupt.o l0/context_switch.o l0/thread.o \ l0/frame.o l0/paging.o l0/region.o l0/kmalloc.o OUT = kernel.bin diff --git a/kernel/include/task.h b/kernel/include/task.h deleted file mode 100644 index 0faa8b3..0000000 --- a/kernel/include/task.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include -#include -#include - -#define T_STATE_RUNNING 1 -#define T_STATE_FINISHED 2 -#define T_STATE_WAITING 3 - -#define KPROC_STACK_SIZE 0x8000 // 8Kb - -#define TASK_SWITCH_FREQUENCY 100 // in herz - -typedef struct saved_context { - uint32_t *esp; - void (*eip)(); -} saved_context_t; - -typedef struct task { - saved_context_t ctx; - pagedir_t *current_pd_d; - - uint32_t state; - void* result; - bool has_result; - - region_info_t *stack_region; - - void* more_data; - - struct task *next_in_queue; -} task_t; - -typedef void (*entry_t)(void*); - -void tasking_setup(entry_t cont, void* data); // never returns -task_t *new_task(entry_t entry); // task is PAUSED, and must be resume_task_with_result'ed - -extern task_t *current_task; - -void yield(); -void* wait_for_result(); - -void resume_task_with_result(task_t *task, void* data, bool run_at_once); - -/* vim: set ts=4 sw=4 tw=0 noet :*/ diff --git a/kernel/include/thread.h b/kernel/include/thread.h new file mode 100644 index 0000000..03277a2 --- /dev/null +++ b/kernel/include/thread.h @@ -0,0 +1,47 @@ +#pragma once + +#include +#include +#include + +#define T_STATE_RUNNING 1 +#define T_STATE_FINISHED 2 +#define T_STATE_WAITING 3 + +#define KPROC_STACK_SIZE 0x8000 // 8Kb + +#define TASK_SWITCH_FREQUENCY 100 // in herz + +typedef struct saved_context { + uint32_t *esp; + void (*eip)(); +} saved_context_t; + +typedef struct thread { + saved_context_t ctx; + pagedir_t *current_pd_d; + + uint32_t state; + void* result; + bool has_result; + + region_info_t *stack_region; + + void* more_data; + + struct thread *next_in_queue; +} thread_t; + +typedef void (*entry_t)(void*); + +void threading_setup(entry_t cont, void* data); // never returns +thread_t *new_thread(entry_t entry); // thread is PAUSED, and must be resume_thread_with_result'ed + +extern thread_t *current_thread; + +void yield(); +void* wait_for_result(); + +void resume_thread_with_result(thread_t *thread, void* data, bool run_at_once); + +/* vim: set ts=4 sw=4 tw=0 noet :*/ diff --git a/kernel/l0/kmain.c b/kernel/l0/kmain.c index 2ce3749..416b802 100644 --- a/kernel/l0/kmain.c +++ b/kernel/l0/kmain.c @@ -10,7 +10,7 @@ #include #include -#include +#include #include @@ -100,7 +100,7 @@ void kmalloc_test(void* kernel_data_end) { dbg_print_region_info(); } -void test_task(void* a) { +void test_thread(void* a) { int i = 0; while(1) { dbg_printf("b"); @@ -112,8 +112,8 @@ void test_task(void* a) { } } void kernel_init_stage2(void* data) { - task_t *tb = new_task(test_task); - resume_task_with_result(tb, 0, false); + thread_t *tb = new_thread(test_thread); + resume_thread_with_result(tb, 0, false); dbg_print_region_info(); dbg_print_frame_stats(); @@ -163,10 +163,10 @@ void kmain(struct multiboot_info_t *mbd, int32_t mb_magic) { kmalloc_setup(); kmalloc_test(kernel_data_end); - // enter multi-tasking mode + // enter multi-threading mode // interrupts are enabled at this moment, so all // code run from now on should be preemtible (ie thread-safe) - tasking_setup(kernel_init_stage2, 0); + threading_setup(kernel_init_stage2, 0); PANIC("Should never come here."); } diff --git a/kernel/l0/paging.c b/kernel/l0/paging.c index b8fd454..683abe6 100644 --- a/kernel/l0/paging.c +++ b/kernel/l0/paging.c @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include #define PAGE_OF_ADDR(x) (((size_t)x >> PAGE_SHIFT) % N_PAGES_IN_PT) @@ -143,8 +143,8 @@ void paging_setup(void* kernel_data_end) { } pagedir_t *get_current_pagedir() { - if (current_task == 0) return &kernel_pd_d; - return current_task->current_pd_d; + if (current_thread == 0) return &kernel_pd_d; + return current_thread->current_pd_d; } pagedir_t *get_kernel_pagedir() { @@ -153,7 +153,7 @@ pagedir_t *get_kernel_pagedir() { void switch_pagedir(pagedir_t *pd) { asm volatile("movl %0, %%cr3":: "r"(pd->phys_addr)); - if (current_task != 0) current_task->current_pd_d = pd; + if (current_thread != 0) current_thread->current_pd_d = pd; } // ============================== // @@ -177,8 +177,8 @@ int pd_map_page(void* vaddr, uint32_t frame_id, bool rw) { ASSERT((size_t)vaddr < PD_MIRROR_ADDR); - pagedir_t *pdd = ((size_t)vaddr >= K_HIGHHALF_ADDR || current_task == 0 - ? &kernel_pd_d : current_task->current_pd_d); + pagedir_t *pdd = ((size_t)vaddr >= K_HIGHHALF_ADDR || current_thread == 0 + ? &kernel_pd_d : current_thread->current_pd_d); pagetable_t *pd = ((size_t)vaddr >= K_HIGHHALF_ADDR ? &kernel_pd : current_pd); mutex_lock(&pdd->mutex); diff --git a/kernel/l0/task.c b/kernel/l0/task.c deleted file mode 100644 index cf22f9d..0000000 --- a/kernel/l0/task.c +++ /dev/null @@ -1,222 +0,0 @@ -#include -#include -#include -#include - -#include -#include - -void save_context_and_enter_scheduler(saved_context_t *ctx); -void irq0_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); -} - -// ============================= // -// HELPER : IF FLAG MANIPULATION // -// ============================= // - -static inline bool disable_interrupts() { - uint32_t eflags; - asm volatile("pushf; pop %0" : "=r"(eflags)); - asm volatile("cli"); - return (eflags & EFLAGS_IF) != 0; -} - -static inline void resume_interrupts(bool st) { - if (st) asm volatile("sti"); -} - -// ================== // -// 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() { - // At this point, interrupts are disabled - // 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); - - switch_pagedir(get_kernel_pagedir()); - - 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->current_pd_d = get_kernel_pagedir(); - - t->more_data = 0; // free for use by L1 functions - - return t; -} - -// ========== // -// SETUP CODE // -// ========== // - -static void irq0_handler(registers_t *regs) { - if (current_task != 0) - irq0_save_context_and_enter_scheduler(¤t_task->ctx); -} -void tasking_setup(entry_t cont, void* arg) { - set_pit_frequency(TASK_SWITCH_FREQUENCY); - idt_set_irq_handler(IRQ0, irq0_handler); - - task_t *t = new_task(cont); - ASSERT(t != 0); - - resume_task_with_result(t, arg, false); - - run_scheduler(); // never returns - ASSERT(false); -} - -// ======================= // -// TASK STATE MANIPULATION // -// ======================= // - -void yield() { - if (current_task == 0) { - // might happen before tasking is initialized - // (but should not...) - dbg_printf("Warning: probable deadlock."); - } else { - save_context_and_enter_scheduler(¤t_task->ctx); - } -} - -void* wait_for_result() { - bool st = disable_interrupts(); - - 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; - - void *result = current_task->result; - - resume_interrupts(st); - return result; -} - -void resume_task_with_result(task_t *task, void* data, bool run_at_once) { - bool st = disable_interrupts(); - - 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(); - - resume_interrupts(st); -} - -/* vim: set ts=4 sw=4 tw=0 noet :*/ diff --git a/kernel/l0/thread.c b/kernel/l0/thread.c new file mode 100644 index 0000000..b14ad9b --- /dev/null +++ b/kernel/l0/thread.c @@ -0,0 +1,222 @@ +#include +#include +#include +#include + +#include +#include + +void save_context_and_enter_scheduler(saved_context_t *ctx); +void irq0_save_context_and_enter_scheduler(saved_context_t *ctx); +void resume_context(saved_context_t *ctx); + +thread_t *current_thread = 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); +} + +// ============================= // +// HELPER : IF FLAG MANIPULATION // +// ============================= // + +static inline bool disable_interrupts() { + uint32_t eflags; + asm volatile("pushf; pop %0" : "=r"(eflags)); + asm volatile("cli"); + return (eflags & EFLAGS_IF) != 0; +} + +static inline void resume_interrupts(bool st) { + if (st) asm volatile("sti"); +} + +// ================== // +// THE TASK SCHEDULER // +// ================== // + +static thread_t *queue_first_thread = 0, *queue_last_thread = 0; + +void enqueue_thread(thread_t *t, bool just_ran) { + ASSERT(t->state == T_STATE_RUNNING); + if (queue_first_thread == 0) { + queue_first_thread = queue_last_thread = t; + t->next_in_queue = 0; + } else if (just_ran) { + t->next_in_queue = 0; + queue_last_thread->next_in_queue = t; + queue_last_thread = t; + } else { + t->next_in_queue = queue_first_thread; + queue_first_thread = t; + } +} + +thread_t* dequeue_thread() { + thread_t *t = queue_first_thread; + if (t == 0) return 0; + + queue_first_thread = t->next_in_queue; + if (queue_first_thread == 0) queue_last_thread = 0; + + return t; +} + +// ================ // +// THE TASKING CODE // +// ================ // + +void run_scheduler() { + // At this point, interrupts are disabled + // This function is expected NEVER TO RETURN + + if (current_thread != 0 && current_thread->state == T_STATE_RUNNING) { + enqueue_thread(current_thread, true); + } + + current_thread = dequeue_thread(); + if (current_thread != 0) { + resume_context(¤t_thread->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_thread(void (*entry)(void*)) { + ASSERT(current_thread->state == T_STATE_RUNNING); + ASSERT(current_thread->has_result); + + switch_pagedir(get_kernel_pagedir()); + + current_thread->has_result = false; + + asm volatile("sti"); + entry(current_thread->result); + + current_thread->state = T_STATE_FINISHED; + // TODO : add job for deleting the thread, or whatever + yield(); // expected never to return! + ASSERT(false); +} +thread_t *new_thread(entry_t entry) { + thread_t *t = (thread_t*)kmalloc(sizeof(thread_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_thread function never returns) + + t->ctx.eip = (void(*)())run_thread; + t->state = T_STATE_WAITING; + t->result = 0; + t->has_result = false; + + t->current_pd_d = get_kernel_pagedir(); + + t->more_data = 0; // free for use by L1 functions + + return t; +} + +// ========== // +// SETUP CODE // +// ========== // + +static void irq0_handler(registers_t *regs) { + if (current_thread != 0) + irq0_save_context_and_enter_scheduler(¤t_thread->ctx); +} +void threading_setup(entry_t cont, void* arg) { + set_pit_frequency(TASK_SWITCH_FREQUENCY); + idt_set_irq_handler(IRQ0, irq0_handler); + + thread_t *t = new_thread(cont); + ASSERT(t != 0); + + resume_thread_with_result(t, arg, false); + + run_scheduler(); // never returns + ASSERT(false); +} + +// ======================= // +// TASK STATE MANIPULATION // +// ======================= // + +void yield() { + if (current_thread == 0) { + // might happen before threading is initialized + // (but should not...) + dbg_printf("Warning: probable deadlock."); + } else { + save_context_and_enter_scheduler(¤t_thread->ctx); + } +} + +void* wait_for_result() { + bool st = disable_interrupts(); + + if (!current_thread->has_result) { + current_thread->state = T_STATE_WAITING; + save_context_and_enter_scheduler(¤t_thread->ctx); + } + ASSERT(current_thread->has_result); + current_thread->has_result = false; + + void *result = current_thread->result; + + resume_interrupts(st); + return result; +} + +void resume_thread_with_result(thread_t *thread, void* data, bool run_at_once) { + bool st = disable_interrupts(); + + thread->has_result = true; + thread->result = data; + + if (thread->state == T_STATE_WAITING) { + thread->state = T_STATE_RUNNING; + enqueue_thread(thread, false); + } + if (run_at_once) yield(); + + resume_interrupts(st); +} + +/* vim: set ts=4 sw=4 tw=0 noet :*/ -- cgit v1.2.3