Orion
Barry Importing existing Orion kernel d41a53c (3 years, 2 months ago)
diff --git a/task/task.c b/task/task.c
new file mode 100644
index 0000000..294b3cc
--- /dev/null
+++ b/task/task.c
@@ -0,0 +1,420 @@
+/*
+ * This file is responsible for the core functions related to multitasking. It
+ * relies on a decent paging implementation. It contains several routines which
+ * are related to the creation, modification and deletion of tasks.
+ */
+
+#include <stdint.h>
+#include <sys/sched.h>
+#include <errno.h>
+#include "task.h"
+#include "../vfs/vfs.h"
+#include "../vfs/inode.h"
+#include "../vfs/cache.h"
+#include "../vfs/tmpfs/fs.h"
+#include "../mem/mem.h"
+#include "../mem/heap.h"
+#include "../mem/paging.h"
+#include "../proc/proc.h"
+#include "../screen.h"
+
+extern TaskQueue readyQueue, tasks;
+pid_t nextPid = 1;
+
+extern uint32_t initialStack;
+extern page_dir_t kernelDir;
+
+uintptr_t read_eip(void);
+
+/* Move the initial stack to a new position */
+static void
+move_stack(void *start, size_t size)
+{
+ uintptr_t i, tmp, *tmpp;
+ uintptr_t oldStackPointer, oldBasePointer, offset;
+ uintptr_t newStackPointer, newBasePointer;
+
+ for (i = (size_t) start - size; i <= (size_t) start; i += 0x1000) {
+ page_t *pg = get_page((void *) i);
+ alloc_page(pg, PTE_PRESENT | PTE_WRITE | PTE_USER, -1);
+ }
+
+ asm volatile("mov %%esp, %0" : "=r" (oldStackPointer));
+ asm volatile("mov %%ebp, %0" : "=r" (oldBasePointer));
+ offset = (uint32_t) start - initialStack;
+ newStackPointer = oldStackPointer + offset;
+ newBasePointer = oldBasePointer + offset;
+
+ memcpy((void *) newStackPointer, (void *) oldStackPointer,
+ initialStack - oldStackPointer);
+
+ /* Update pointers on stack */
+ for (i = (size_t) start; i > (size_t) start - size; i -= 4) {
+ tmp = *(uint32_t *) i;
+ if ((tmp > oldStackPointer) && (tmp < initialStack)) {
+ tmp += offset;
+ tmpp = (uintptr_t *) i;
+ *tmpp = tmp;
+ }
+ }
+
+ asm volatile("mov %0, %%esp" :: "r" (newStackPointer));
+ asm volatile("mov %0, %%ebp" :: "r" (newBasePointer));
+}
+
+/* Fork a task */
+pid_t
+fork(void)
+{
+ return clone(CLONE_NONE);
+}
+
+/* Clone a task */
+pid_t
+clone(int flags)
+{
+ Task *parent = current, *child = kmalloc(sizeof(Task)), *tmp;
+
+ if (flags & CLONE_THREAD) {
+ flags |= CLONE_PARENT;
+ flags |= CLONE_VM;
+ }
+
+ child->tid = nextPid++;
+ if (flags & CLONE_THREAD)
+ child->tgid = parent->tgid;
+ else
+ child->tgid = child->tid;
+ child->priority = NORMAL;
+ child->state = READY;
+ child->status = 0;
+ child->inSyscall = parent->inSyscall;
+ child->name = kmalloc(strlen(parent->name)+1);
+ memcpy(child->name, parent->name, strlen(parent->name)+1);
+
+ /* Set parent */
+ child->parent = parent;
+ if (flags & CLONE_PARENT)
+ child->parent = parent->parent;
+ child->ppid = child->parent->tgid;
+
+ child->executable = file_get(parent->executable);
+
+ /* Add to list of tasks */
+ tasks.end->tnext = child;
+ tasks.end = child;
+
+ /* Clone parent's file descriptors */
+ int fd;
+ File *file;
+ if (flags & CLONE_FILES) {
+ child->files = parent->files;
+ child->files->usage++;
+ } else {
+ child->files = kmalloc(sizeof(Files));
+ child->files->usage = 1;
+ for (fd = 0; fd < NFILES; fd++) {
+ file = parent->files->fd[fd];
+ if (!file) continue;
+ child->files->fd[fd] = file_get(file);
+ }
+ }
+
+ /* Clone parent's file system context */
+ if (flags & CLONE_FS) {
+ child->fs = parent->fs;
+ child->fs->usage++;
+ } else {
+ child->fs = kmalloc(sizeof(FileSystem));
+ child->fs->usage = 1;
+ child->fs->cwd = parent->fs->cwd;
+ child->fs->cwd->usage++;
+ init_custody_chain(&child->fs->cwdCustody);
+ copy_custody_chain(&parent->fs->cwdCustody,
+ &child->fs->cwdCustody);
+ child->fs->root = parent->fs->root;
+ child->fs->root->usage++;
+ }
+
+ /* Clone page directory */
+ if (flags & CLONE_VM) {
+ child->vm = parent->vm;
+ child->vm->usage++;
+ } else {
+ child->vm = kmalloc(sizeof(VirtualMemory));
+ child->vm->usage = 1;
+ }
+ child->pageDir = clone_dir();
+
+ /* Clone parent's VM Regions in child */
+ VMRegion *head;
+ Page *page;
+ off_t i;
+ if (child->vm != parent->vm)
+ child->vm->regions = vm_clone_regions(parent->vm->regions);
+ child->stack = kmalloc(sizeof(VMRegion));
+ memcpy(child->stack, parent->stack, sizeof(VMRegion));
+ child->stack->next = child->stack->prev = NULL;
+ /* Copy stack */
+ if (parent->stack && parent->stack->front) {
+ file = kmalloc(sizeof(File));
+ file->inode = inode_get(kmalloc(sizeof(Inode)));
+ file->ops = &tmpfsFileOps;
+ child->stack->front = file_get(file);
+ for (i = 0; i < child->stack->end - child->stack->start;
+ i += 0x1000) {
+ page = page_find(parent->stack->front->inode, i);
+ if (page)
+ page_add(file->inode, page);
+ }
+ }
+ /* Copy thread local storage */
+ if (parent->tls) {
+ child->tls = kmalloc(sizeof(VMRegion));
+ memcpy(child->tls, parent->tls, sizeof(VMRegion));
+ child->tls->next = child->tls->prev = NULL;
+ if (parent->tls->front) {
+ file = kmalloc(sizeof(File));
+ file->inode = inode_get(kmalloc(sizeof(Inode)));
+ file->ops = &tmpfsFileOps;
+ child->tls->front = file_get(file);
+ for (i = 0; i < child->tls->end - child->tls->start;
+ i += 0x1000) {
+ page = page_find(parent->tls->front->inode, i);
+ if (page)
+ page_add(file->inode, page);
+ }
+ }
+ if (parent->tls->back)
+ child->tls->back = file_get(parent->tls->back);
+ }
+
+ /* Split tasks here */
+ uintptr_t esp, ebp, eip;
+ eip = read_eip();
+ if (current == parent) {
+ asm volatile("mov %%esp, %0" : "=r" (esp));
+ asm volatile("mov %%ebp, %0" : "=r" (ebp));
+ child->esp = esp;
+ child->ebp = ebp;
+ child->eip = eip;
+ add_to_queue(&readyQueue, child);
+ return child->tid;
+ }
+ return 0;
+}
+
+/* Terminate the current task */
+void
+terminate(void)
+{
+ /* Close files */
+ int fd;
+ if (--current->files->usage == 0) {
+ for (fd = 0; fd < NFILES; fd++)
+ if (current->files->fd[fd])
+ close(fd);
+ kfree(current->files);
+ }
+ if (current->executable)
+ file_put(current->executable);
+
+ /* Clean File System info */
+ if (--current->fs->usage == 0)
+ kfree(current->fs);
+
+ /* Clean VM Regions and unreferenced VM Objects */
+ VMRegion *head;
+ if (--current->vm->usage == 0) {
+ for (head = current->vm->regions; head; head = head->next)
+ vm_destroy_region(head);
+ kfree(current->vm);
+ }
+ vm_destroy_region(current->stack);
+
+ /* Clean unread IPC messages */
+ /* TODO */
+
+ /* Clean signals */
+ /* TODO */
+
+ /* Deschedule */
+ current->state = TERMINATED;
+ acquire(&readyQueue.lock);
+ Task *tmp, *next;
+ for (tmp = current->waiting.start; tmp; tmp = next) {
+ next = tmp->next;
+ add_to_queue(&readyQueue, tmp);
+ }
+ release(&readyQueue.lock);
+ schedule();
+ panic("Unreached");
+
+ /* Clean task - FIXME */
+ kfree(current->name);
+ kfree(current);
+ clean_dir();
+ schedule();
+ /* UNREACHED */
+}
+
+/* Exit the current task */
+void
+exit(int status)
+{
+ if (current->tid == 1)
+ panic("Attempted to exit init! Exit code %d", status);
+ current->status = (1 << 31) | (status & 0x0F);
+ terminate();
+}
+
+/* Wait for a child process to change state */
+pid_t
+waitpid(pid_t pid, int *wstatus, int options)
+{
+ if (!verify_access(wstatus, sizeof(int), PROT_WRITE))
+ return -EFAULT;
+
+ Task *task = find_task(pid);
+ if (!task)
+ return -ECHILD;
+ if (task->ppid != current->tgid && task->tgid != current->tgid)
+ return -ECHILD;
+
+ if (task->state != TERMINATED) {
+ add_to_queue(&task->waiting, current);
+ block_task(WAITING_FOR_CHILD);
+ }
+
+ if (wstatus)
+ *wstatus = task->status;
+ return task->tid;
+}
+
+/* Get current task's PID */
+pid_t
+getpid(void)
+{
+ return current->tgid;
+}
+
+/* Get current task's UID */
+uid_t
+getuid(void)
+{
+ return current->uid;
+}
+
+/* Set current task's (E)UID */
+int
+setuid(uid_t uid)
+{
+ if (uid != current->uid && uid != current->suid && !super_user())
+ return -EPERM;
+ if (super_user()) {
+ current->uid = uid;
+ current->suid = uid;
+ }
+ current->euid = uid;
+ return 0;
+}
+
+/* Get current task's EUID */
+uid_t
+geteuid(void)
+{
+ return current->euid;
+}
+
+/* Set the current task's EUID */
+int
+seteuid(uid_t euid)
+{
+ if (euid != current->uid
+ && euid != current->euid
+ && euid != current->suid
+ && !super_user())
+ return -EPERM;
+ current->euid = euid;
+ return 0;
+}
+
+/* Get current task's GID */
+gid_t
+getgid(void)
+{
+ return current->gid;
+}
+
+/* Set current task's (E)GID */
+int
+setgid(gid_t gid)
+{
+ if (gid != current->gid
+ && gid != current->sgid
+ && !super_user())
+ return -EPERM;
+ if (super_user()) {
+ current->gid = gid;
+ current->sgid = gid;
+ }
+ current->egid = gid;
+ return 0;
+}
+
+/* Get current task's EGID */
+gid_t
+getegid(void)
+{
+ return current->egid;
+}
+
+/* Set the current task's EUID */
+int
+setegid(gid_t egid)
+{
+ if (egid != current->gid
+ && egid != current->egid
+ && egid != current->sgid
+ && !super_user())
+ return -EPERM;
+ current->egid = egid;
+ return 0;
+}
+
+/* Initialse tasking */
+void
+init_tasking(void)
+{
+ move_stack((void *) (0xF0800000 - sizeof(uintptr_t)), 0x2000);
+
+ /* Initialise the Kernel Task */
+ tasks.start = tasks.end = current = kmalloc(sizeof(Task));
+ current->tid = nextPid++;
+ current->tgid = current->tid;
+ current->priority = NORMAL;
+ current->name = kmalloc(7);
+ current->state = RUNNING;
+ memcpy(current->name, "kernel", 7);
+ current->pageDir = kernelDir;
+
+ /* Files Namespace */
+ current->files = kmalloc(sizeof(Files));
+ current->files->usage = 1;
+
+ /* File System Namespace */
+ current->fs = kmalloc(sizeof(FileSystem));
+ init_custody_chain(¤t->fs->cwdCustody);
+ current->fs->usage = 1;
+
+ /* Virtual Memory Namespace */
+ current->vm = kmalloc(sizeof(Files));
+ current->vm->regions = NULL;
+ current->vm->usage = 1;
+
+ /* Inter-Process Communication Namespace */
+
+ /* Signals Namespace */
+
+ register_interrupt(0, timer_handler);
+}