diff options
Diffstat (limited to 'src/kernel/task/task.c')
-rw-r--r-- | src/kernel/task/task.c | 245 |
1 files changed, 245 insertions, 0 deletions
diff --git a/src/kernel/task/task.c b/src/kernel/task/task.c new file mode 100644 index 0000000..804adc2 --- /dev/null +++ b/src/kernel/task/task.c @@ -0,0 +1,245 @@ +#include "task.h" +#include <core/sys.h> +#include <core/monitor.h> +#include <mem/mem.h> +#include "timer.h" + +#define KSTACKSIZE 0x8000 + +//Static routines for handling threads exiting and all cleanup +static void thread_exit_stackJmp(uint32_t reason); +static void thread_exit2(uint32_t reason); +static void thread_delete(struct thread *th); +static void process_delete(struct process *pr); + +//From task_.asm +extern uint32_t read_eip(); +extern void task_idle(void*); + +static uint32_t thread_runnable(struct thread *th); + +static uint32_t nextpid = 1; + +struct process *processes = 0, *kernel_process; +struct thread *threads = 0, *current_thread = 0, *idle_thread; + +uint32_t tasking_tmpStack[0x4000]; + +void tasking_init() { + cli(); + kernel_process = kmalloc(sizeof(struct process)); //This process must be hidden to users + kernel_process->pid = kernel_process->uid = kernel_process->threads = 0; + kernel_process->privilege = PL_KERNEL; + kernel_process->parent = kernel_process; + kernel_process->pagedir = kernel_pagedir; + kernel_process->next = 0; + current_thread = 0; + idle_thread = thread_new(kernel_process, task_idle, 0); + threads = 0; //Do not include idle thread in threads + sti(); + monitor_write("Tasking set up\n"); +} + +static struct thread *thread_next() { + if (current_thread == 0 || current_thread == idle_thread) current_thread = threads; + struct thread *ret = current_thread; + while (1) { + ret = ret->next; + if (ret == 0) ret = threads; + if (thread_runnable(ret)) { + return ret; + } + if (ret == current_thread) return idle_thread; + } +} + +void tasking_switch() { + if (threads == 0) PANIC("No more threads to run !"); + asm volatile("cli"); + + uint32_t esp, ebp, eip; + + asm volatile("mov %%esp, %0" : "=r"(esp)); + asm volatile("mov %%ebp, %0" : "=r"(ebp)); + eip = read_eip(); + + if (eip == 0x12345) { + return; + } + + if (current_thread != 0) { + current_thread->esp = esp; + current_thread->ebp = ebp; + current_thread->eip = eip; + } + + current_thread = thread_next(); + + asm volatile(" \ + mov %0, %%ebp; \ + mov %1, %%esp; \ + mov %2, %%ecx; \ + mov $0x12345, %%eax; \ + jmp *%%ecx;" + : : "r"(current_thread->ebp), "r"(current_thread->esp), "r"(current_thread->eip)); +} + +void tasking_updateKernelPagetable(uint32_t idx, struct page_table *table, uint32_t tablephysical) { + if (idx < 896) return; + struct process* it = processes; + while (it != 0) { + it->pagedir->tables[idx] = table; + it->pagedir->tablesPhysical[idx] = tablephysical; + it = it->next; + } +} + +uint32_t tasking_handleException(struct registers *regs) { + if (current_thread == 0) return 0; //No tasking yet + monitor_write("\n(task.c:99) Unhandled exception : "); + char *exception_messages[] = {"Division By Zero","Debug","Non Maskable Interrupt","Breakpoint", + "Into Detected Overflow","Out of Bounds","Invalid Opcode","No Coprocessor", "Double Fault", + "Coprocessor Segment Overrun","Bad TSS","Segment Not Present","Stack Fault","General Protection Fault", + "Page Fault","Unknown Interrupt","Coprocessor Fault","Alignment Check","Machine Check"}; + monitor_write(exception_messages[regs->int_no]); + monitor_write(" at "); monitor_writeHex(regs->eip); + monitor_write(" >>> Thread exiting.\n"); + thread_exit_stackJmp(EX_TH_EXCEPTION); + PANIC("This should never have happened. Please report this."); +} + +void thread_sleep(uint32_t msecs) { + if (current_thread == 0) return; + current_thread->state = TS_SLEEPING; + current_thread->timeWait = timer_time() + msecs; + asm volatile("int $64" : : "a"(1)); +} + +void thread_exit2(uint32_t reason) { //See EX_TH_* defines in task.h + /* + * if reason == EX_TH_NORMAL, it is just one thread exiting because it has to + * if reason == EX_TH_EXCEPTION, it is just one thread exiting because of an exception + * if reason is none of the two cases above, it is the whole process exiting (with error code = reason) + */ + struct thread *th = current_thread; + struct process *pr = th->process; + if ((reason == EX_TH_NORMAL || reason == EX_TH_EXCEPTION) && pr->threads > 1) { + thread_delete(th); + } else { + process_delete(pr); + } + sti(); + tasking_switch(); +} + +void thread_exit_stackJmp(uint32_t reason) { + uint32_t *stack; + cli(); + stack = tasking_tmpStack + 0x4000; + stack--; *stack = reason; + stack--; *stack = 0; + asm volatile(" \ + mov %0, %%esp; \ + mov %1, %%ebp; \ + mov %2, %%ecx; \ + mov %3, %%cr3; \ + jmp *%%ecx;" : : + "r"(stack), "r"(stack), "r"(thread_exit2), "r"(kernel_pagedir->physicalAddr)); +} + +void thread_exit() { + thread_exit_stackJmp(EX_TH_NORMAL); +} + +void process_exit(uint32_t retval) { + if (retval == EX_TH_NORMAL || retval == EX_TH_EXCEPTION) retval = EX_PR_EXCEPTION; + thread_exit_stackJmp(retval); +} + +static uint32_t thread_runnable(struct thread *t) { + if (t->state == TS_RUNNING) return 1; + if (t->state == TS_SLEEPING && timer_time() >= t->timeWait) return 1; + return 0; +} + +static void thread_run(struct thread *thread, thread_entry entry_point, void *data) { + pagedir_switch(thread->process->pagedir); //TODO : take into account privilege level + asm volatile("sti"); + entry_point(data); + thread_exit(0); +} + +struct thread *thread_new(struct process *proc, thread_entry entry_point, void *data) { + struct thread *t = kmalloc(sizeof(struct thread)); + t->process = proc; + proc->threads++; + t->kernelStack_addr = kmalloc(KSTACKSIZE); + t->kernelStack_size = KSTACKSIZE; + + uint32_t *stack = (uint32_t*)((size_t)t->kernelStack_addr + t->kernelStack_size); + + //Pass parameters + stack--; *stack = (uint32_t)data; + stack--; *stack = (uint32_t)entry_point; + stack--; *stack = (uint32_t)t; + stack--; *stack = 0; + t->esp = (uint32_t)stack; + t->ebp = t->esp + 8; + t->eip = (uint32_t)thread_run; + + t->state = TS_RUNNING; + + if (threads == 0) { + threads = t; + } else { + struct thread *i = threads; + while (i->next != 0) i = i->next; + i->next = t; + } + return t; +} + +struct process *process_new(struct process* parent, uint32_t uid, uint32_t privilege) { + struct process* p = kmalloc(sizeof(struct process)); + p->pid = (nextpid++); + p->uid = uid; + p->threads = 0; + p->privilege = privilege; + p->parent = parent; + p->pagedir = pagedir_new(); + p->next = processes; + processes = p; + return p; +} + +static void thread_delete(struct thread *th) { + kfree(th->kernelStack_addr); + th->process->threads--; + if (threads == th) { + threads = th->next; + } else { + struct thread *it = threads; + while (it->next != th && it->next != 0) it = it->next; + if (it->next == th) it->next = th->next; + } + kfree(th); +} + +static void process_delete(struct process *pr) { + struct thread *it; + while (threads != 0 && threads->process == pr) thread_delete(threads); + it = threads; + while (it != 0) { + while (it->next->process == pr) thread_delete(it->next); + it = it->next; + } + pagedir_delete(pr->pagedir); + if (processes == pr) { + processes = pr->next; + } else { + struct process *it = processes; + while (it != 0 && it->next != pr) it = it->next; + if (it != 0 && it->next == pr) it->next = pr->next; + } + kfree(pr); +} |