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(¤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; +}