Orion
Barry Importing existing Orion kernel d41a53c (3 years, 2 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(¤t->lock);
+// msg = current->msgQueue;
+// if (msg && (msg->from == from || from == ANY)) {
+// current->msgQueue = msg->next;
+// memcpy(buf, msg, sizeof(Message));
+// kfree(msg);
+// }
+// release(¤t->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(¤t->lock);
+ current->eip = eip;
+ current->esp = esp;
+ current->ebp = ebp;
+ release(¤t->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(¤t->lock);
+ current->state = reason;
+ release(¤t->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(¤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);
+}
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;
+}