BarryServer : Git

All the code for all my projects
// BarryServer : Git / Orion / commit / d41a53cbc7d055b1c00cf0a339dbed6925f4f02c / task

// Related

Orion

Barry Importing existing Orion kernel d41a53c (2 years, 6 months ago)
diff --git a/task/exec.c b/task/exec.c
new file mode 100644
index 0000000..dffc92b
--- /dev/null
+++ b/task/exec.c
@@ -0,0 +1,321 @@
+/*
+ * This file deals with loading programs from the Kernel File System.  The
+ * programs in KernelFS are statically linked, so they just need to be copied
+ * into their address space and run.  Since KernelFS is present in memory, this
+ * process is very simple.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include "../proc/proc.h"
+#include "../task/task.h"
+#include "../mem/heap.h"
+#include "../mem/paging.h"
+#include "../mem/vm.h"
+#include "../vfs/vfs.h"
+
+#define KFS_START ((void *) 0x100000)
+
+/* Structure of a KernelFS file listing */
+typedef struct KFSEntry {
+	char name[27];
+	uint8_t size;
+	uint32_t start;
+} __attribute__((packed)) KFSEntry;
+
+/* ELF File Header */
+typedef struct ELFHeader {
+	char magic[4];
+	uint8_t arch;
+	uint8_t endian;
+	uint8_t headerVersion;
+	uint8_t osAbi;
+	uint8_t reserved[8];
+	uint16_t type;
+	uint16_t isa;
+	uint32_t version;
+	uint32_t entry;
+	uint32_t programHeader;
+	uint32_t sectionHeader;
+	uint32_t flags;
+	uint16_t headerSize;
+	uint16_t programEntrySize;
+	uint16_t numProgramEntries;
+	uint16_t sectionEntrySize;
+	uint16_t numSectionEntries;
+	uint16_t sectionNames;
+} ELFHeader;
+
+/* ELF Section Header */
+typedef struct SectionHeader {
+	uint32_t name;
+	uint32_t type;
+	uint32_t flags;
+	uint32_t address;
+	uint32_t offset;
+	uint32_t size;
+	uint32_t link;
+	uint32_t info;
+	uint32_t align;
+	uint32_t entrySize;
+} SectionHeader;
+
+/* ELF Program Header */
+typedef struct ProgramHeader {
+	uint32_t type;
+	uint32_t offset;
+	uint32_t address;
+	uint32_t reserved;
+	uint32_t filesz;
+	uint32_t memsz;
+	uint32_t flags;
+	uint32_t align;
+} ProgramHeader;
+
+static KFSEntry *
+get_file_by_name(char *name)
+{
+	KFSEntry *index;
+	for (index = KFS_START;
+	     index < (KFSEntry *) (KFS_START + 512);
+	     index++) {
+		if (!strcmp(name, index->name))
+			return index;
+	}
+	return (KFSEntry *) 0;
+}
+
+/* Get SectionHeader by index */
+static SectionHeader *
+section_header(ELFHeader *header, uint16_t index)
+{
+	SectionHeader *section;
+	section = (void *) ((char *) header + header->sectionHeader +
+	          (index * header->sectionEntrySize));
+	return section;
+}
+
+/* Get a Section name */
+static char *
+section_name(ELFHeader *header, uint32_t index)
+{
+	char *data = (char *) header + section_header(header,
+	             header->sectionNames)->offset;
+	return data + index;
+}
+
+/* Get ProgramHeader by index */
+static ProgramHeader *
+program_header(ELFHeader *header, uint16_t index)
+{
+	ProgramHeader *program;
+	program = (void *) ((char *) header + header->programHeader +
+	          (index * header->programEntrySize));
+	return program;
+}
+
+extern TaskQueue tasks;
+
+/* Execute a program */
+int
+execve(const char *file, char *argv[], char *envp[])
+{
+	if (current->tid != current->tgid) {
+		/*
+		 * TODO: This should execute the program to execute in the
+		 * "thread group leader" (the Task where tid = current->tgid)
+		 * and all other threads in the group should be exited.
+		 */
+		return -EFAULT;
+	}
+
+	if (!verify_access(file, strnlen(file, PATH_MAX), PROT_READ))
+		return -EFAULT;
+
+	/* Count argv and envp */
+	int argc, argi, envc, envi;
+	if (argv == NULL) argc = 0;
+	else for (argc = 0; argv[argc] != NULL; argc++);
+	if (envp == NULL) envc = 0;
+	else for (envc = 0; envp[envc] != NULL; envc++);
+	/* Find size of argv and envp strings */
+	size_t ssz = sizeof(int)
+	           + (sizeof(uintptr_t) * (argc + 1))
+	           + (sizeof(uintptr_t) * (envc + 1));
+	for (argi = 0; argi < argc; argi++) {
+		if (!verify_access(argv[argi], strlen(argv[argi]), PROT_READ))
+			return -EFAULT;
+		ssz += strlen(argv[argi]) + 1;
+	}
+	for (envi = 0; envi < envc; envi++) {
+		if (!verify_access(envp[envi], strlen(envp[envi]), PROT_READ))
+			return -EFAULT;
+		ssz += strlen(envp[envi]) + 1;
+	}
+
+	/* Read ELF header */
+	ELFHeader header;
+	int fd = open(file, O_RDONLY);
+	if (fd < 0)
+		return fd;
+
+	current->inSyscall = 0;
+
+	/* Only execute regular files */
+	if (!S_ISREG(current->files->fd[fd]->mode)) {
+		close(fd);
+		return -EACCES;
+	}
+
+	/* Read file header */
+	read(fd, &header, sizeof(ELFHeader));
+	if (memcmp(header.magic, "\x7F""ELF", 4) || header.isa != 3) {
+		close(fd);
+		return -ENOEXEC;
+	}
+	if (header.type != 2) { /* 1: relocatable, 2: executable */
+		close(fd);
+		return -ENOEXEC;
+	}
+
+	/*
+	 * POINT OF NO RETURN
+	 */
+
+	/* Set process name */
+	kfree(current->name);
+	current->name = kmalloc(strlen(file)+1);
+	memcpy(current->name, file, strlen(file)+1);
+
+	/* Store everything (pointers adjusted) in temporary buffer */
+	uintptr_t esp = 0xE0000000 - ssz;
+	char *istack = kmalloc(ssz);
+	char *isp = istack + ssz;
+	for (envi = envc - 1; envi >= 0; envi--) {
+		isp -= strlen(envp[envi]) + 1;
+		memcpy(isp, envp[envi], strlen(envp[envi]) + 1);
+		envp[envi] = (char *) (isp - istack) + esp;
+	}
+	if (envp)
+		envp[envc] = NULL;
+	for (argi = argc - 1; argi >= 0; argi--) {
+		isp -= strlen(argv[argi]) + 1;
+		memcpy(isp, argv[argi], strlen(argv[argi]) + 1);
+		argv[argi] = (char *) (isp - istack) + esp;
+	}
+	if (argv)
+		argv[argc] = NULL;
+	isp -= sizeof(uintptr_t);
+	*((uintptr_t *) isp ) = (uintptr_t) NULL;
+	isp -= sizeof(uintptr_t) * envc;
+	memcpy(isp, envp, sizeof(uintptr_t) * envc);
+	isp -= sizeof(uintptr_t);
+	*((uintptr_t *) isp ) = (uintptr_t) NULL;
+	isp -= sizeof(uintptr_t) * argc;
+	memcpy(isp, argv, sizeof(uintptr_t) * argc);
+	isp -= sizeof(int);
+	*((int *) isp) = argc;
+
+	/* Destroy previous executable */
+	VMRegion *head;
+	for (head = current->vm->regions; head; head = head->next)
+		vm_destroy_region(head);
+
+	/* Program headers */
+	size_t p;
+	off_t off;
+	uintptr_t pgbrk, heapEnd;
+	ProgramHeader ph, tlsph;
+	memset(&tlsph, 0, sizeof(ProgramHeader));
+	for (p = 0; p < header.numProgramEntries; p++) {
+		off = header.programHeader + (p * header.programEntrySize);
+		lseek(fd, off, 0);
+		read(fd, &ph, sizeof(ProgramHeader));
+		if (ph.type != 1) {
+			if (ph.type == 7)
+				memcpy(&tlsph, &ph, sizeof(ProgramHeader));
+			continue;
+		}
+
+		/* Map data into region */
+		mmap((void *) ph.address, ph.filesz, ph.flags,
+		     MAP_PRIVATE, fd, ph.offset & ~0xFFF);
+		/* Space left before */
+		if (ph.address & 0xFFF) {
+			memset((void *) (ph.address & ~0xFFF), 0,
+			       ph.address - (ph.address & ~0xFFF));
+		}
+		/* Unset memory */
+		if (ph.memsz > ph.filesz) {
+			pgbrk = (ph.address + ph.filesz + 0xFFF) & ~0xFFF;
+			memset((void *) (ph.address + ph.filesz), 0,
+			       pgbrk - ph.address - ph.filesz);
+			if (ph.memsz > pgbrk - ph.address)
+				mmap((void *) pgbrk,
+				     ph.memsz - (pgbrk - ph.address),
+				     ph.flags, MAP_PRIVATE | MAP_ANONYMOUS,
+				     -1, 0);
+		}
+		if (ph.address + ph.memsz > heapEnd)
+			heapEnd = ph.address + ph.memsz;
+	}
+
+	/* Store executable */
+	current->executable = file_get(current->files->fd[fd]);
+	close(fd);
+
+	/* Thread Local Storage */
+	/* FIXME */
+	if (current->tls)
+		vm_destroy_region(current->tls);
+	if (tlsph.type == 7) {
+		/* should be filesz not memsz */
+		tlsph.flags |= PROT_WRITE;
+		current->tls = vm_create_region((void *) ((heapEnd + 0xFFF) & ~0xFFF),
+		                                tlsph.memsz + 4, tlsph.flags,
+		                                MAP_PRIVATE | MAP_ANONYMOUS,
+		                                0, NULL);
+//		                                tlsph.offset, current->executable);
+		vm_remove_region(current->tls);
+		*((uint32_t *) current->tls->start + 1) = current->tls->start + 4;
+		if (tlsph.filesz)
+			memcpy((void *) current->tls->start,
+			       (void *) tlsph.address, tlsph.filesz);
+	}
+
+	/* Stack area */
+	VMRegion *oldstack = current->stack;
+	current->stack = vm_create_region((void *) 0xDFC00000, 0x400000,
+	                                  PROT_READ | PROT_WRITE,
+	                                  MAP_PRIVATE | MAP_ANONYMOUS, 0, NULL);
+	vm_remove_region(current->stack);
+	if (oldstack)
+		vm_destroy_region(oldstack);
+
+	memcpy((void *) esp, istack, ssz);
+	kfree(istack);
+
+	/* Switch to user-mode */
+	asm volatile(
+		"cli;"
+		"mov $0x23, %%ax;"
+		"mov %%ax, %%ds;"
+		"mov %%ax, %%es;"
+		"mov %%ax, %%fs;"
+		"mov %%ax, %%gs;"
+		"mov %%esi, %%eax;" // "movl %%esp, %%eax;"
+		"pushl $0x23;"
+		"pushl %%eax;"
+		"pushf;"
+		"pop %%eax;"
+		"or $0x200, %%eax;" /* Enable interrupts */
+		"push %%eax;"
+		"pushl $0x1B;"
+		"pushl %%ebx;"
+		"iret;"
+		: : "b" (header.entry), "S" (esp)
+	);
+	/* UNREACHED */
+}
diff --git a/task/ipc.c b/task/ipc.c
new file mode 100644
index 0000000..1faafd9
--- /dev/null
+++ b/task/ipc.c
@@ -0,0 +1,131 @@
+/*
+ * This file contains all the functions related to message passing and
+ * inter-process communication.
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+#include <sys/ipc.h>
+#include "task.h"
+#include "../proc/proc.h"
+#include "../mem/mem.h"
+#include "../mem/heap.h"
+#include "../spinlock.h"
+
+extern TaskQueue readyQueue;
+
+/* Block until a message is received */
+static uint8_t
+block_recv(Task *task, uint32_t from)
+{
+//	Message *head, *prev;
+//	/* Blocking for RECEIVE from any */
+//	if (!task->msgQueue)
+//		return 0;
+//	/* Block for RECEIVE from specific process */
+//	if (task->msgQueue->from != from && from != ANY) {
+//		for (head = task->msgQueue;
+//		     head->from != from;
+//		     prev = head, head = head->next);
+//		/* End of list */
+//		if (!head) return 0;
+//		/* Move message to start of queue */
+//		if (head != task->msgQueue) {
+//			prev->next = head->next;
+//			head->next = task->msgQueue;
+//			task->msgQueue = head;
+//		}
+//	}
+//
+//	return 1;
+}
+
+/* Send a message */
+Message *
+nb_send_msg(pid_t to, uint16_t type, MessageContent *msg)
+{
+//	Message *item, *msgSearch;
+//	Task *taskSearch;
+//
+//	item = kmalloc(sizeof(Message));
+//	memcpy(&item->msg, msg, sizeof(MessageContent));
+//	item->from = current->tid;
+//	item->type = type;
+//	item->next = 0;
+//
+//	/* Find target process */
+//	for (taskSearch = readyQueue;
+//	     taskSearch->tid != to && taskSearch;
+//	     taskSearch = taskSearch->next);
+//	/* Add message to queue */
+//	if (taskSearch) {
+//		acquire(&taskSearch->lock);
+//		if (taskSearch->msgQueue) {
+//			for (msgSearch = taskSearch->msgQueue;
+//			     msgSearch->next;
+//			     msgSearch = msgSearch->next);
+//			msgSearch->next = item;
+//		} else {
+//			taskSearch->msgQueue = item;
+//		}
+//		release(&taskSearch->lock);
+//	}
+//
+//	if (taskSearch)
+//		return item;
+//	kfree(item);
+//	return NULL;
+}
+
+/* Send a message and block until it is delivered */
+Message *
+send_msg(pid_t to, uint16_t type, MessageContent *msg)
+{
+//	Message *nb = nb_send_msg(to, type, msg);
+//	if (!nb) return NULL;
+//	block(block_send, (uint32_t) nb);
+//	return nb;
+}
+
+/* Receive a message */
+pid_t
+nb_recv_msg(Message *buf, pid_t from)
+{
+//	Message *msg;
+//	Task *taskSearch;
+//
+//	acquire(&current->lock);
+//	msg = current->msgQueue;
+//	if (msg && (msg->from == from || from == ANY)) {
+//		current->msgQueue = msg->next;
+//		memcpy(buf, msg, sizeof(Message));
+//		kfree(msg);
+//	}
+//	release(&current->lock);
+//
+//	if (msg && (buf->from == from || from == ANY)) {
+//		/* Find sending process */
+//		for (taskSearch = readyQueue;
+//		     taskSearch->tid != buf->from && taskSearch;
+//		     taskSearch = taskSearch->next);
+//		if (taskSearch) {
+//			if (taskSearch->block.function == block_send
+//			 && taskSearch->block.condition == (uint32_t) msg)
+//				taskSearch->block.function = NULL;
+//		}
+//		return buf->from;
+//	}
+//	return 0;
+}
+
+/* Block until a message is received */
+pid_t
+recv_msg(Message *buf, pid_t from)
+{
+//	pid_t nb;
+//check:
+//	nb = nb_recv_msg(buf, from);
+//	if (nb) return nb;
+//	block(block_recv, from);
+//	goto check;
+}
diff --git a/task/process.S b/task/process.S
new file mode 100644
index 0000000..131481f
--- /dev/null
+++ b/task/process.S
@@ -0,0 +1,23 @@
+; This file contains a few assembly routines related to tasking.  These routines
+; should be minimal, and get called from within various functions from the
+; tasking system.  See the relevant C source for a better description of how the
+; functions work.
+
+; Get the return address
+[global read_eip]
+read_eip:
+	mov eax, [esp]
+	ret
+
+; Switch to a task's context
+[global context_switch]
+context_switch:
+	cli
+	mov ecx, [esp + 4]
+	mov eax, [esp + 8]
+	mov ebp, [esp + 12]
+	mov esp, [esp + 16]
+	mov cr3, eax
+	mov eax, 0x10032004 ; Magic number
+	sti
+	jmp ecx
diff --git a/task/schedule.c b/task/schedule.c
new file mode 100644
index 0000000..1c00576
--- /dev/null
+++ b/task/schedule.c
@@ -0,0 +1,182 @@
+/*
+ * This file controls the Kernel's scheduler.  It decides when a new task must
+ * be scheduled, and which tasks can actually be scheduled.
+ */
+
+#include <stdint.h>
+#include "../proc/proc.h"
+#include "../mem/paging.h"
+#include "task.h"
+
+uintptr_t read_eip(void);
+void context_switch(uintptr_t eip, page_dir_t phys,
+                    uintptr_t ebp, uintptr_t esp);
+void handle_signals(void);
+
+TaskQueue readyQueue, tasks;
+Task *currentTask[MAX_CPUS];
+
+/* Switch to a task */
+static void
+switch_to_task(Task *task)
+{
+	uintptr_t esp, ebp, eip;
+	asm volatile("mov %%esp, %0" : "=r" (esp));
+	asm volatile("mov %%ebp, %0" : "=r" (ebp));
+	eip = read_eip();
+	if (eip == 0x10032004) /* Magic number */
+		return;
+
+	acquire(&current->lock);
+	current->eip = eip;
+	current->esp = esp;
+	current->ebp = ebp;
+	release(&current->lock);
+	current = task;
+	eip = current->eip;
+	esp = current->esp;
+	ebp = current->ebp;
+
+	context_switch(eip, current->pageDir, ebp, esp);
+	/* UNREACHED */
+
+	/*
+	 * This code actually returns to the read_eip() call above.  This is due
+	 * to how the call works.  The context switch jumps to the address
+	 * stored in the eip variable.  This address is the return address of
+	 * the read_eip() call.  The context_switch() function sets the return
+	 * value before jumping, so it appears as though read_eip() returned
+	 * that value.  The code uses the magic number 0x10032004 to tell when
+	 * this is occurring and just returns early.
+	 */
+}
+
+/* Find task by ID */
+Task *
+find_task(pid_t tid)
+{
+	Task *task;
+	for (task = tasks.start; task && task->tid != tid; task = task->tnext);
+	return task;
+}
+
+/* Add a task to a task queue */
+void
+add_to_queue(TaskQueue *queue, Task *task)
+{
+	if (!queue->start) {
+		queue->start = task;
+		queue->end = task;
+	} else {
+		queue->end->next = task;
+		queue->end = task;
+	}
+	task->next = NULL;
+}
+
+/* Remove a task from a task queue */
+void
+remove_from_queue(TaskQueue *queue, Task *task)
+{
+	/* Start of list */
+	if (queue->start == task) {
+		queue->start = task->next;
+		if (!queue->start)
+			queue->end = NULL;
+		return;
+	}
+
+	/* Search */
+	Task *prev;
+	for (prev = queue->start; prev->next; prev = prev->next)
+		if (prev->next == task)
+			break;
+	if (prev->next) {
+		prev->next = task->next;
+		if (queue->end == task)
+			queue->end = prev;
+	}
+}
+
+/* Remove the first task from a task queue */
+Task *
+pop_from_queue(TaskQueue *queue)
+{
+	Task *head = queue->start;
+	queue->start = head->next;
+	if (!queue->start)
+		queue->end = NULL;
+	return head;
+}
+
+/* Block a task */
+void
+block_task(int reason)
+{
+	acquire(&current->lock);
+	current->state = reason;
+	release(&current->lock);
+	schedule();
+}
+
+/* Unblock a task */
+void
+unblock_task(Task *task)
+{
+	if (task->state == READY || task->state == RUNNING)
+		return;
+	task->state = READY;
+	if (!readyQueue.start || task->priority > current->priority) {
+		acquire(&readyQueue.lock);
+		task->next = readyQueue.start;
+		readyQueue.start = task;
+		if (!readyQueue.end)
+			readyQueue.end = task;
+		release(&readyQueue.lock);
+	} else {
+		add_to_queue(&readyQueue, task);
+	}
+}
+
+/* Schedule next task */
+void
+schedule(void)
+{
+	Task *task = current;
+
+	/* Next schedulable task */
+	if (readyQueue.start) {
+		acquire(&readyQueue.lock);
+		task = readyQueue.start;
+		readyQueue.start = task->next;
+		if (!readyQueue.start)
+			readyQueue.end = NULL;
+		task->state = RUNNING;
+		task->next = NULL;
+		if (current->state == RUNNING) {
+			current->state = READY;
+			add_to_queue(&readyQueue, current);
+		}
+		release(&readyQueue.lock);
+		switch_to_task(task);
+	/* Idle task */
+	} else if (current->state != RUNNING) {
+		current = NULL;
+		asm volatile("sti");
+		while (!readyQueue.start)
+			asm volatile("hlt");
+		asm volatile("cli");
+		current = task;
+		acquire(&readyQueue.lock);
+		task = readyQueue.start;
+		readyQueue.start = task->next;
+		if (!readyQueue.start)
+			readyQueue.end = NULL;
+		task->state = RUNNING;
+		task->next = NULL;
+		release(&readyQueue.lock);
+		switch_to_task(task);
+	}
+
+	handle_signals();
+}
diff --git a/task/signal.c b/task/signal.c
new file mode 100644
index 0000000..c80c279
--- /dev/null
+++ b/task/signal.c
@@ -0,0 +1,63 @@
+/*
+ * This file handles signals to tasks.  It handles blocking signals, and
+ * registering the signal handlers.  It send/dispatches signals to tasks and
+ * runs the registered signal handlers when appropriate.
+ */
+
+#include <stdint.h>
+#include <errno.h>
+#include "task.h"
+
+extern TaskQueue tasks;
+
+/* Handle the signals for a task */
+void
+handle_signals(void)
+{
+	if (!(current->sigset & ~current->blockedSignals))
+		return;
+
+	int signal;
+	for (signal = 0; signal < 32; signal++) {
+		if (!(current->sigset & (1 << (signal - 1))))
+			continue;
+		current->status = signal;
+		terminate();
+	}
+}
+
+/* Send a signal to a thread */
+int
+tgkill(pid_t tgid, pid_t tid, int sig)
+{
+	if (sig < 0 || sig > 31)
+		return -EINVAL;
+
+	Task *task = find_task(tid);
+	if (!task || task->tgid != tgid)
+		return -ESRCH;
+	if (sig)
+		task->sigset |= (1 << (sig - 1));
+
+	return 0;
+}
+
+/* Send a signal to a process */
+int
+kill(pid_t pid, int sig)
+{
+	if (sig < 0 || sig > 31)
+		return -EINVAL;
+
+	int sent = 0;
+	Task *task;
+	for (task = tasks.start; task; task = task->tnext) {
+		if (task->tgid != pid)
+			continue;
+		if (sig)
+			task->sigset |= (1 << (sig - 1));
+		sent++;
+	}
+
+	return sent ? 0 : -ESRCH;
+}
diff --git a/task/syscall.c b/task/syscall.c
new file mode 100644
index 0000000..d43542d
--- /dev/null
+++ b/task/syscall.c
@@ -0,0 +1,96 @@
+/*
+ * This file handles system calls.  Every system call gets passed to the
+ * syscall_handler() function, which decides which Kernel function to run.
+ * There is an array of syscalls, which are indexed numerically, these are the
+ * syscall numbers.
+ */
+
+#include <sys/syscall.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include "task.h"
+#include "../screen.h"
+
+/* List of syscalls */
+void *syscalls[] = {
+	/* Tasking */
+	[SYSCALL_DBGPRINTF] = dbgprintf,
+	[SYSCALL_CLONE]     = clone,
+	[SYSCALL_EXIT]      = exit,
+	[SYSCALL_GETPID]    = getpid,
+	[SYSCALL_GETUID]    = getuid,
+	[SYSCALL_SETUID]    = setuid,
+	[SYSCALL_GETEUID]   = geteuid,
+	[SYSCALL_SETEUID]   = seteuid,
+	[SYSCALL_GETGID]    = getgid,
+	[SYSCALL_SETGID]    = setgid,
+	[SYSCALL_GETEGID]   = getegid,
+	[SYSCALL_SETEGID]   = setegid,
+	[SYSCALL_EXECVE]    = execve,
+	[SYSCALL_WAITPID]   = waitpid,
+	[SYSCALL_TGKILL]    = tgkill,
+	[SYSCALL_KILL]      = kill,
+	[SYSCALL_TIME]      = time,
+	[SYSCALL_TIMES]     = times,
+	[SYSCALL_SLEEP]     = sleep,
+
+	/* Files */
+	[SYSCALL_OPEN]     = open,
+	[SYSCALL_CLOSE]    = close,
+	[SYSCALL_READ]     = read,
+	[SYSCALL_WRITE]    = write,
+	[SYSCALL_IOCTL]    = ioctl,
+	[SYSCALL_LSEEK]    = lseek,
+	[SYSCALL_STAT]     = stat,
+	[SYSCALL_GETDENTS] = getdents,
+	[SYSCALL_MKDIR]    = mkdir,
+	[SYSCALL_RMDIR]    = rmdir,
+	[SYSCALL_MKNOD]    = mknod,
+	[SYSCALL_RENAME]   = rename,
+	[SYSCALL_DUP]      = dup,
+	[SYSCALL_DUP2]     = dup2,
+	[SYSCALL_ISATTY]   = isatty,
+
+	/* File System */
+	[SYSCALL_MOUNT]  = mount,
+	[SYSCALL_CHDIR]  = chdir,
+	[SYSCALL_CHROOT] = chroot,
+	[SYSCALL_GETCWD] = getcwd,
+
+	/* Memory */
+	[SYSCALL_MMAP] = mmap,
+
+	/* Messaging */
+	[SYSCALL_NB_SEND_MSG] = nb_send_msg,
+	[SYSCALL_SEND_MSG]    = send_msg,
+	[SYSCALL_NB_RECV_MSG] = nb_recv_msg,
+	[SYSCALL_RECV_MSG]    = recv_msg,
+};
+
+/* Handle a syscall */
+void
+syscall_handler(InterruptFrame *frame)
+{
+	if (frame->eax >= sizeof(syscalls)/sizeof(syscalls[0])) {
+		frame->eax = -EINVAL;
+		return;
+	}
+	void *handler = syscalls[frame->eax];
+	int ret;
+	current->inSyscall = 1;
+
+	size_t num;
+	int *val = (int *) frame->esi;
+	if (!verify_access(val, frame->ecx * sizeof(int), PROT_READ)) {
+		frame->eax = -EINVAL;
+		return;
+	}
+	/* Push args onto stack */
+	for (num = frame->ecx; num; num--)
+		asm volatile("pushl %0" :: "r" (val[num - 1]));
+	/* Call handler */
+	asm volatile("call *%1" : "=a" (ret) : "r" (handler));
+
+	frame->eax = ret;
+	current->inSyscall = 0;
+}
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(&current->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);
+}
diff --git a/task/task.h b/task/task.h
new file mode 100644
index 0000000..228c3d8
--- /dev/null
+++ b/task/task.h
@@ -0,0 +1,128 @@
+#ifndef KERNEL_TASK_H
+#define KERNEL_TASK_H
+
+#include <stdint.h>
+#include <sys/ipc.h>
+#include <sys/times.h>
+#include <time.h>
+#include <signal.h>
+#include "../mem/paging.h"
+#include "../mem/vm.h"
+#include "../proc/proc.h"
+#include "../vfs/vfs.h"
+#include "../spinlock.h"
+
+typedef struct Task Task;
+typedef struct TaskQueue TaskQueue;
+
+/* Process priorities */
+enum Priority {
+	NONE,
+	LOWEST,
+	LOW,
+	NORMAL,
+	HIGH,
+	HIGHEST,
+};
+
+/* Task states */
+enum States {
+	RUNNING,
+	READY,
+	TERMINATED,
+	WAITING_FOR_CHILD,
+	WAITING_FOR_READ,
+	SLEEP,
+};
+
+/* Structure for a Task Queue */
+struct TaskQueue {
+	Task *start, *end;
+	Spinlock lock;
+};
+
+/* Structure of a Task */
+struct Task {
+	pid_t tid, tgid;
+	uid_t uid, euid, suid;
+	gid_t gid, egid, sgid;
+	uint8_t priority;
+	char *name;
+	uint32_t usertime, systime;
+	int state;
+	uint64_t sleepExpiry;
+	int status;
+	uintptr_t esp, ebp, eip;
+	Task *next, *tnext, *parent;
+	pid_t ppid;
+	page_dir_t pageDir;
+	Spinlock lock;
+	uint8_t inSyscall;
+	File *executable;
+	VMRegion *stack, *tls;
+	TaskQueue waiting;
+	sigset_t sigset;
+	sigset_t blockedSignals;
+	void (*sig_handler[32])(int);
+
+	/* Messages */
+	Message *msgQueue;
+
+	/* Namespaces */
+	FileSystem *fs;
+	Files *files;
+	VirtualMemory *vm;
+//	Messages *ipc;
+//	Signals *signals;
+};
+
+extern Task *currentTask[];
+#define current currentTask[CPUID]
+
+/* Check if a routine is running as a syscall */
+static inline uint8_t
+in_syscall(void)
+{
+	if (!current)
+		return 0;
+	return (current->inSyscall);
+}
+
+/* Check if super-user */
+static inline int
+super_user(void)
+{
+	return (current->euid == 0);
+}
+
+void init_tasking(void);
+void syscall_handler(InterruptFrame *frame);
+Task *find_task(pid_t tid);
+void add_to_queue(TaskQueue *queue, Task *task);
+void remove_from_queue(TaskQueue *queue, Task *task);
+Task *pop_from_queue(TaskQueue *queue);
+void timer_handler(InterruptFrame *frame);
+void block_task(int reason);
+void unblock_task(Task *task);
+void schedule(void);
+pid_t fork(void);
+pid_t clone(int flags);
+void terminate(void);
+void exit(int status);
+pid_t waitpid(pid_t pid, int *wstatus, int options);
+pid_t getpid(void);
+uid_t getuid(void);
+int setuid(uid_t uid);
+uid_t geteuid(void);
+int seteuid(uid_t euid);
+gid_t getgid(void);
+int setgid(gid_t gid);
+gid_t getegid(void);
+int setegid(gid_t egid);
+int isatty(int fd);
+int execve(const char *file, char *argv[], char *envp[]);
+
+int tgkill(pid_t tgid, pid_t tid, int sig);
+int kill(pid_t pid, int sig);
+
+#endif
diff --git a/task/time.c b/task/time.c
new file mode 100644
index 0000000..f81d496
--- /dev/null
+++ b/task/time.c
@@ -0,0 +1,183 @@
+/*
+ * This file controls the system clock and contains the functions related to
+ * getting and setting the time from various sources.  It keeps a monotonic
+ * clock internally, but can also make use of the tasks' clocks, and the RTC.
+ */
+
+#include <stdint.h>
+#include <sys/times.h>
+#include <errno.h>
+#include "task.h"
+#include "../proc/proc.h"
+#include "../io.h"
+
+#define RTC_SECONDS 0x00
+#define RTC_MINUTES 0x02
+#define RTC_HOURS   0x04
+#define RTC_WEEKDAY 0x06
+#define RTC_DAY     0x07
+#define RTC_MONTH   0x08
+#define RTC_YEAR    0x09
+#define RTC_CENTURY 0x32
+
+#define LEAP_YEAR(x) ((((x % 4) == 0) && ((x % 100) != 0)) || ((x % 400) == 0))
+
+extern TaskQueue readyQueue;
+
+uint32_t monotonicClock = 0, millis = 0;
+uint8_t slice[MAX_CPUS] = {0};
+TaskQueue sleepQueue;
+
+/* Read an RTC register */
+static uint8_t
+read_rtc(uint8_t index)
+{
+	outb(0x70, 0x80 | index);
+	io_wait();
+	return inb(0x71);
+}
+
+/* Timer interrupt */
+void
+timer_handler(InterruptFrame *frame)
+{
+	/* Monotonic clock */
+	millis++;
+	if (millis == 1000) {
+		monotonicClock++;
+		millis = 0;
+	}
+
+	/* Sleeping processes */
+	Task *proc = sleepQueue.start, *prev = NULL;
+	for (proc = sleepQueue.start; proc; prev = proc, proc = proc->next) {
+		if (proc->sleepExpiry > (monotonicClock * 1000) + millis)
+			break;
+		if (prev)
+			prev->next = proc->next;
+		if (sleepQueue.start == proc)
+			sleepQueue.start = proc->next;
+		if (sleepQueue.end == proc)
+			sleepQueue.end = NULL;
+		add_to_queue(&readyQueue, proc);
+	}
+
+	/* Account timeslice */
+	slice[CPUID]++;
+	if (!current)
+		return;
+
+	/* Account task time */
+	if (in_syscall())
+		current->systime++;
+	else
+		current->usertime++;
+
+	/* Call scheduler */
+	if (slice[CPUID] < current->priority)
+		return;
+	slice[CPUID] = 0;
+	schedule();
+}
+
+/* Sleep for a specified time (milliseconds) */
+int
+sleep(uint32_t ms)
+{
+	current->sleepExpiry = (monotonicClock * 1000) + millis + ms;
+
+	/* Add to sorted sleep TaskQueue */
+	TaskQueue *q = &sleepQueue;
+	Task *task, *prev = NULL;
+	acquire(&q->lock);
+	if (!q->start) {
+		q->start = current;
+		q->end = current;
+		current->next = NULL;
+	} else {
+		for (task = q->start; task; prev = task, task = task->next)
+			if (task->sleepExpiry > current->sleepExpiry)
+				break;
+		if (!prev) {
+			current->next = q->start;
+			q->start = current;
+		} else {
+			current->next = task;
+			prev->next = current;
+		}
+	}
+	release(&q->lock);
+
+	block_task(SLEEP);
+	return 0;
+}
+
+/* Get epoch time */
+time_t
+time(time_t *tloc)
+{
+	if (!verify_access(tloc, sizeof(time_t), PROT_WRITE))
+		return -EFAULT;
+
+	/* Length of months for normal and leap years */
+	const uint16_t monthLen[2][12] = {
+		{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
+		{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
+	};
+
+	uint16_t i;
+	uint8_t second, minute, hour, day, month, year, cent;
+	time_t time = 0;
+	uint8_t years = 0, leapYears = 0;
+
+	second = read_rtc(RTC_SECONDS);
+	minute = read_rtc(RTC_MINUTES);
+	hour   = read_rtc(RTC_HOURS);
+	day    = read_rtc(RTC_DAY);
+	month  = read_rtc(RTC_MONTH);
+	year   = read_rtc(RTC_YEAR);
+	cent   = read_rtc(RTC_CENTURY);
+
+	second = (second & 0x0F) + ((second        / 16) * 10);
+	minute = (minute & 0x0F) + ((minute        / 16) * 10);
+	hour   = ((hour  & 0x0F) + (((hour & 0x70) / 16) * 10)) | (hour & 0x80);
+	day    = (day    & 0x0F) + ((day           / 16) * 10);
+	month  = (month  & 0x0F) + ((month         / 16) * 10);
+	year   = (year   & 0x0F) + ((year          / 16) * 10);
+	cent   = (cent   & 0x0F) + ((cent          / 16) * 10);
+
+	for (i = 1970; i < (cent * 100) + year; i++)
+		if (LEAP_YEAR(i))
+			leapYears++;
+		else
+			years++;
+	time += ((years * 365) + (leapYears * 366)) * 86400;
+
+	for (i = 0; i < month - 1; i++)
+		time += monthLen[LEAP_YEAR(year)][i] * 86400;
+
+	time += (day - 1) * 86400;
+	time += hour * 3600;
+	time += minute * 60;
+	time += second;
+
+	if (tloc)
+		*tloc = time;
+
+	return time;
+}
+
+/* Get process times */
+clock_t
+times(Times *buf)
+{
+	if (!verify_access(buf, sizeof(Times), PROT_WRITE))
+		return -EFAULT;
+	if (buf) {
+		buf->utime = current->usertime;
+		buf->stime = current->systime;
+		buf->cutime = current->usertime;
+		buf->cstime = current->systime;
+	}
+	return (monotonicClock * 1000) + millis;
+}