Orion
Barry Importing existing Orion kernel d41a53c (2 years, 4 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); +}