Orion
Barry Moving signal handlers into separate namespace 7ae31b0 (3 years, 1 month ago)
/*
* 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);
}
/* Signal handlers */
if (flags & CLONE_SIGHAND) {
child->signals = parent->signals;
child->signals->usage++;
} else {
child->signals = kmalloc(sizeof(SigHandlers));
child->signals->usage = 1;
}
/* 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 signals */
if (--current->signals->usage == 0)
kfree(current->signals);
/* Clean unread IPC messages */
/* 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;
/* Signals Namespace */
current->signals = kmalloc(sizeof(SigHandlers));
current->signals->usage = 1;
/* Inter-Process Communication Namespace */
register_interrupt(0, timer_handler);
}