summaryrefslogtreecommitdiff
path: root/src/kernel/task/task.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/kernel/task/task.cpp')
-rw-r--r--src/kernel/task/task.cpp411
1 files changed, 411 insertions, 0 deletions
diff --git a/src/kernel/task/task.cpp b/src/kernel/task/task.cpp
new file mode 100644
index 0000000..b7c8f45
--- /dev/null
+++ b/src/kernel/task/task.cpp
@@ -0,0 +1,411 @@
+#include "task.h"
+#include "sched.h"
+#include <core/sys.h>
+#include <core/monitor.h>
+#include <lib/cpp.h>
+#include <mem/seg.h>
+#include <mem/gdt.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(thread *th);
+static void process_delete(process *pr);
+
+//From task_.asm
+extern "C" uint32_t read_eip();
+extern "C" void task_idle(void*);
+
+static uint32_t nextpid = 1;
+process *processes = 0, *kernel_process;
+thread *current_thread = 0, *idle_thread = 0;
+
+uint32_t tasking_tmpStack[KSTACKSIZE];
+
+/* Sets up tasking. Called by kmain on startup.
+ Creates a kernel process and an IDLE thread in it. */
+void tasking_init() {
+ cli();
+ kernel_process = new process(); //This process must be hidden to users
+ kernel_process->pid = kernel_process->uid = kernel_process->thread_count = 0;
+ kernel_process->privilege = PL_KERNEL;
+ kernel_process->parent = kernel_process;
+ kernel_process->pagedir = kernel_pagedir;
+ kernel_process->next = 0;
+ kernel_process->threads = 0;
+ current_thread = 0;
+ idle_thread = thread_new(kernel_process, task_idle, 0, 0);
+ sti();
+ monitor_write("[Tasking] ");
+}
+
+/* Called by the paging functions when a page table is allocated in the kernel space (>K_HIGHHALF_ADDR).
+ Updates the page directories of all the processes. */
+void tasking_updateKernelPagetable(uint32_t idx, page_table *table, uint32_t tablephysical) {
+ if (idx < FIRST_KERNEL_PAGETABLE) return;
+ process* it = processes;
+ while (it != 0) {
+ it->pagedir->tables[idx] = table;
+ it->pagedir->tablesPhysical[idx] = tablephysical;
+ it = it->next;
+ }
+}
+
+/* Called when a timer IRQ fires. Does a context switch. */
+void schedule() {
+ if (processes == 0) PANIC("No processes are running !");
+ 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;
+ if (current_thread->state == TS_RUNNING) sched_enqueue(current_thread);
+ }
+
+ current_thread = sched_dequeue();
+ ASSERT(current_thread != 0);
+
+ pagedir_switch(current_thread->process->pagedir);
+
+ gdt_setKernelStack(((uint32_t)current_thread->kernelStack_addr) + current_thread->kernelStack_size);
+
+ 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));
+}
+
+/* Called when an exception happens. Provides a stack trace if it was in kernel land.
+ Ends the thread for most exceptions, ends the whole process for page faults. */
+uint32_t tasking_handleException(registers *regs) {
+ if (current_thread == 0) return 0; //No tasking yet
+ NL; WHERE; monitor_write("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("'\teip:"); monitor_writeHex(regs->eip);
+ if (regs->eip >= K_HIGHHALF_ADDR) {
+ monitor_write("\n Exception stack trace :\n");
+ stack_trace(regs->ebp);
+ PANIC("Kernel error'd.");
+ }
+ if (regs->int_no == 14) {
+ monitor_write("\n>>> Process exiting.\n");
+ thread_exit_stackJmp(EX_PR_EXCEPTION);
+ } else {
+ monitor_write("\n>>> Thread exiting.\n");
+ thread_exit_stackJmp(EX_TH_EXCEPTION);
+ }
+ PANIC("This should never have happened. Please report this.");
+ return 0;
+}
+
+/* Puts the current thread in an inactive state. */
+void thread_goInactive() {
+ current_thread->state = TS_WAKEWAIT;
+ schedule();
+}
+
+/* Wakes up the given thread. */
+void thread_wakeUp(thread* t) {
+ if (t->state == TS_WAKEWAIT) {
+ t->state = TS_RUNNING;
+ sched_enqueue(t);
+ }
+}
+
+/* Returns the privilege level of the current process. */
+int proc_priv() {
+ if (current_thread == 0 || current_thread->process == 0) return PL_UNKNOWN;
+ return current_thread->process->privilege;
+}
+
+/* For internal use only. Called by thread_exit_stackJmp on a stack that will not be deleted.
+ Exits current thread or process, depending on the reason. */
+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)
+ */
+ thread *th = current_thread;
+ process* pr;
+ if (th == 0 || th->process == 0) goto retrn;
+ pr = th->process;
+ if ((reason == EX_TH_NORMAL || reason == EX_TH_EXCEPTION) && pr->thread_count > 1) {
+ thread_delete(th);
+ } else {
+ process_delete(pr);
+ }
+ retrn:
+ sti();
+ schedule();
+}
+
+/* For internal use only. Called by thread_exit and process_exit.
+ Switches to a stack that will not be deleted when current thread is deleted. */
+void thread_exit_stackJmp(uint32_t reason) {
+ cli();
+ uint32_t *stack;
+ stack = tasking_tmpStack + (KSTACKSIZE / 4);
+ 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));
+}
+
+/* System call. Exit the current thread. */
+void thread_exit() {
+ thread_exit_stackJmp(EX_TH_NORMAL);
+}
+
+/* System call. Exit the current process. */
+void process_exit(size_t retval) {
+ if (retval == EX_TH_NORMAL || retval == EX_TH_EXCEPTION) retval = EX_PR_EXCEPTION;
+ thread_exit_stackJmp(retval);
+}
+
+/* For internal use only. This is called when a newly created thread first runs
+ (its address is the value given for EIP).
+ It switches to user mode if necessary and calls the entry point. */
+static void thread_run(void* u_esp, thread *thread, thread_entry entry_point, void *data) {
+ pagedir_switch(thread->process->pagedir);
+ if (thread->process->privilege >= PL_USER) { //User mode !
+ uint32_t *stack = (uint32_t*)u_esp;
+
+ stack--; *stack = (uint32_t)data;
+ stack--; *stack = 0;
+ size_t esp = (size_t)stack, eip = (size_t)entry_point;
+ //Setup a false structure for returning from an interrupt :
+ //value for esp is in ebx, for eip is in ecx
+ //- update data segments to 0x23 = user data segment with RPL=3
+ //- push value for ss : 0x23 (user data seg rpl3)
+ //- push value for esp
+ //- push flags
+ //- update flags, set IF = 1 (interrupts flag)
+ //- push value for cs : 0x1B = user code segment with RPL=3
+ //- push eip
+ //- return from fake interrupt
+ asm volatile(" \
+ mov $0x23, %%ax; \
+ mov %%ax, %%ds; \
+ mov %%ax, %%es; \
+ mov %%ax, %%fs; \
+ mov %%ax, %%gs; \
+ \
+ pushl $0x23; \
+ pushl %%ebx; \
+ pushf; \
+ pop %%eax; \
+ or $0x200, %%eax; \
+ push %%eax; \
+ pushl $0x1B; \
+ push %%ecx; \
+ iret; \
+ " : : "b"(esp), "c"(eip));
+ } else {
+ asm volatile("sti");
+ entry_point(data);
+ }
+ thread_exit();
+}
+
+/* Creates a new thread for given process.
+ Allocates a kernel stack and a user stack if necessary.
+ Sets up the kernel stack for values to be passed to thread_run. */
+thread *thread_new(process *proc, thread_entry entry_point, void *data, void *u_esp) {
+ thread *t = new thread();
+ t->process = proc;
+ t->next = 0;
+ proc->thread_count++;
+
+ if (u_esp == 0) u_esp = (void*)proc->stack;
+
+ 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 = (uint32_t)u_esp;
+ stack--; *stack = 0;
+ t->esp = (uint32_t)stack;
+ t->ebp = t->esp + 8;
+ t->eip = (uint32_t)thread_run;
+
+ t->state = TS_RUNNING;
+ sched_enqueue(t);
+
+ if (proc->threads == 0) {
+ proc->threads = t;
+ } else {
+ thread *i = proc->threads;
+ while (i->next != 0) i = i->next;
+ i->next = t;
+ }
+ return t;
+}
+
+/* Creates a new process. Creates a struct process and fills it up. */
+process *process_new(process* parent, uint32_t uid, uint32_t privilege) {
+ process* p = new process();
+ p->pid = (nextpid++);
+ p->uid = uid;
+ p->thread_count = 0;
+ p->threads = 0;
+ p->privilege = privilege;
+ p->parent = parent;
+ p->pagedir = pagedir_new();
+ p->next = processes;
+ p->data = 0;
+ p->dataseg = 0;
+
+ p->stack = 0;
+ if (p->privilege >= PL_USER) { //We are running in user mode
+ size_t stacksBottom = K_HIGHHALF_ADDR - 0x01000000;
+ seg_map(simpleseg_make(stacksBottom, USER_STACK_SIZE, 1), p->pagedir, 0);
+ p->stack = stacksBottom + USER_STACK_SIZE - 4;
+ }
+
+ processes = p;
+ return p;
+}
+
+/* Deletes given thread, freeing the stack(s). */
+static void thread_delete(thread *th) {
+ if (th->process->threads == th) {
+ th->process->threads = th->next;
+ } else {
+ thread *it = th->process->threads;
+ while (it) {
+ if (it->next == th) {
+ it->next = th->next;
+ break;
+ }
+ it = it->next;
+ }
+ }
+ if (current_thread == th) current_thread = 0;
+ th->process->thread_count--;
+ kfree(th->kernelStack_addr);
+ kfree(th);
+}
+
+/* Deletes a process. First, deletes all its threads. Also deletes the corresponding page directory. */
+static void process_delete(process *pr) {
+ thread *it = pr->threads;
+ while (it != 0) {
+ thread_delete(it);
+ it = it->next;
+ }
+ if (processes == pr) {
+ processes = pr->next;
+ } else {
+ process *it = processes;
+ while (it) {
+ if (it->next == pr) {
+ it->next = pr->next;
+ break;
+ }
+ it = it->next;
+ }
+ }
+ pagedir_delete(pr->pagedir);
+ kfree(pr);
+}
+
+/* System call. Called by the app to define the place for the heap. */
+/*int process_setheapseg(size_t start, size_t end) { //syscall
+ struct process *p = current_thread->process;
+ if (start >= K_HIGHHALF_ADDR || end >= K_HIGHHALF_ADDR) return -1;
+ if (p->heapseg == 0) {
+ struct segment *s = simpleseg_make(start, end - start, 1);
+ if (s == 0) return -5;
+ p->heapseg = seg_map(s, p->pagedir, 0);
+ if (p->heapseg == 0) return -1;
+ return 0;
+ } else if (p->heapseg->start != start) {
+ seg_unmap(p->heapseg);
+ struct segment *s = simpleseg_make(start, end - start, 1);
+ if (s == 0) return -5;
+ p->heapseg = seg_map(s, p->pagedir, 0);
+ if (p->heapseg == 0) return -1;
+ return 0;
+ } else {
+ return simpleseg_resize(p->heapseg, end - start);
+ }
+}*/
+
+size_t process_sbrk(size_t size) {
+ process *p = current_thread->process;
+ if (p->data == 0) return -1;
+ ASSERT(p->data < K_HIGHHALF_ADDR);
+ if (p->data + size >= K_HIGHHALF_ADDR) return -1;
+
+ size_t ret;
+ if (p->dataseg == 0) {
+ size_t start = p->data;
+ if (start & 0x0FFF) start = (start & 0xFFFFF000) + 0x1000;
+ size_t end = start + size;
+ if (end & 0x0FFF) end = (end & 0xFFFFF000) + 0x1000;
+
+ segment *s = simpleseg_make(start, end - start, 1);
+ if (s == 0) return -5;
+ p->dataseg = seg_map(s, p->pagedir, 0);
+ if (p->dataseg == 0) return -1;
+
+ ret = start;
+ p->data = start + size;
+ } else {
+ size_t start = p->dataseg->start;
+ size_t end = p->data + size;
+ if (end <= start) return -1;
+
+ if (end & 0x0FFF) end = (end & 0xFFFFF000) + 0x1000;
+ simpleseg_resize(p->dataseg, end - start);
+
+ ret = p->data;
+ p->data += size;
+ }
+ /* (DBG) monitor_write("(sbrk ");
+ monitor_writeHex(size);
+ monitor_write(" ");
+ monitor_writeHex(ret);
+ monitor_write(")"); */
+ return ret;
+}
+
+void process_brk(size_t ptr) {
+ process *p = current_thread->process;
+
+ ASSERT(ptr < K_HIGHHALF_ADDR);
+ process_sbrk(ptr - p->data);
+}
+