/* * 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 #include #include #include "task.h" #include "../vfs/vfs.h" #include "../vfs/inode.h" #include "../vfs/cache.h" #include "../vfs/tmpfs/fs.h" #include "../mem/mem.h" #include "../mem/heap.h" #include "../mem/paging.h" #include "../proc/proc.h" #include "../screen.h" extern TaskQueue readyQueue, tasks; pid_t nextPid = 1; extern uint32_t initialStack; extern page_dir_t kernelDir; uintptr_t read_eip(void); /* Move the initial stack to a new position */ static void move_stack(void *start, size_t size) { uintptr_t i, tmp, *tmpp; uintptr_t oldStackPointer, oldBasePointer, offset; uintptr_t newStackPointer, newBasePointer; for (i = (size_t) start - size; i <= (size_t) start; i += 0x1000) { page_t *pg = get_page((void *) i); alloc_page(pg, PTE_PRESENT | PTE_WRITE | PTE_USER, -1); } asm volatile("mov %%esp, %0" : "=r" (oldStackPointer)); asm volatile("mov %%ebp, %0" : "=r" (oldBasePointer)); offset = (uint32_t) start - initialStack; newStackPointer = oldStackPointer + offset; newBasePointer = oldBasePointer + offset; memcpy((void *) newStackPointer, (void *) oldStackPointer, initialStack - oldStackPointer); /* Update pointers on stack */ for (i = (size_t) start; i > (size_t) start - size; i -= 4) { tmp = *(uint32_t *) i; if ((tmp > oldStackPointer) && (tmp < initialStack)) { tmp += offset; tmpp = (uintptr_t *) i; *tmpp = tmp; } } asm volatile("mov %0, %%esp" :: "r" (newStackPointer)); asm volatile("mov %0, %%ebp" :: "r" (newBasePointer)); } /* Fork a task */ pid_t fork(void) { return clone(CLONE_NONE); } /* Clone a task */ pid_t clone(int flags) { Task *parent = current, *child = kmalloc(sizeof(Task)), *tmp; if (flags & CLONE_THREAD) { flags |= CLONE_PARENT; flags |= CLONE_VM; } child->tid = nextPid++; if (flags & CLONE_THREAD) child->tgid = parent->tgid; else child->tgid = child->tid; child->priority = NORMAL; child->state = READY; child->status = 0; child->inSyscall = parent->inSyscall; child->name = kmalloc(strlen(parent->name)+1); memcpy(child->name, parent->name, strlen(parent->name)+1); /* Set parent */ child->parent = parent; if (flags & CLONE_PARENT) child->parent = parent->parent; child->ppid = child->parent->tgid; child->executable = file_get(parent->executable); /* Add to list of tasks */ tasks.end->tnext = child; tasks.end = child; /* Clone parent's file descriptors */ int fd; File *file; if (flags & CLONE_FILES) { child->files = parent->files; child->files->usage++; } else { child->files = kmalloc(sizeof(Files)); child->files->usage = 1; for (fd = 0; fd < NFILES; fd++) { file = parent->files->fd[fd]; if (!file) continue; child->files->fd[fd] = file_get(file); } } /* Clone parent's file system context */ if (flags & CLONE_FS) { child->fs = parent->fs; child->fs->usage++; } else { child->fs = kmalloc(sizeof(FileSystem)); child->fs->usage = 1; child->fs->cwd = parent->fs->cwd; child->fs->cwd->usage++; init_custody_chain(&child->fs->cwdCustody); copy_custody_chain(&parent->fs->cwdCustody, &child->fs->cwdCustody); child->fs->root = parent->fs->root; child->fs->root->usage++; } /* Clone page directory */ if (flags & CLONE_VM) { child->vm = parent->vm; child->vm->usage++; } else { child->vm = kmalloc(sizeof(VirtualMemory)); child->vm->usage = 1; } child->pageDir = clone_dir(); /* Clone parent's VM Regions in child */ VMRegion *head; Page *page; off_t i; if (child->vm != parent->vm) child->vm->regions = vm_clone_regions(parent->vm->regions); child->stack = kmalloc(sizeof(VMRegion)); memcpy(child->stack, parent->stack, sizeof(VMRegion)); child->stack->next = child->stack->prev = NULL; /* Copy stack */ if (parent->stack && parent->stack->front) { file = kmalloc(sizeof(File)); file->inode = inode_get(kmalloc(sizeof(Inode))); file->ops = &tmpfsFileOps; child->stack->front = file_get(file); for (i = 0; i < child->stack->end - child->stack->start; i += 0x1000) { page = page_find(parent->stack->front->inode, i); if (page) page_add(file->inode, page); } } /* Copy thread local storage */ if (parent->tls) { child->tls = kmalloc(sizeof(VMRegion)); memcpy(child->tls, parent->tls, sizeof(VMRegion)); child->tls->next = child->tls->prev = NULL; if (parent->tls->front) { file = kmalloc(sizeof(File)); file->inode = inode_get(kmalloc(sizeof(Inode))); file->ops = &tmpfsFileOps; child->tls->front = file_get(file); for (i = 0; i < child->tls->end - child->tls->start; i += 0x1000) { page = page_find(parent->tls->front->inode, i); if (page) page_add(file->inode, page); } } if (parent->tls->back) child->tls->back = file_get(parent->tls->back); } /* Signal handlers */ if (flags & CLONE_SIGHAND) { child->signals = parent->signals; child->signals->usage++; } else { child->signals = kmalloc(sizeof(SigHandlers)); child->signals->usage = 1; } /* Split tasks here */ uintptr_t esp, ebp, eip; eip = read_eip(); if (current == parent) { asm volatile("mov %%esp, %0" : "=r" (esp)); asm volatile("mov %%ebp, %0" : "=r" (ebp)); child->esp = esp; child->ebp = ebp; child->eip = eip; add_to_queue(&readyQueue, child); return child->tid; } return 0; } /* Terminate the current task */ void terminate(void) { /* Close files */ int fd; if (--current->files->usage == 0) { for (fd = 0; fd < NFILES; fd++) if (current->files->fd[fd]) close(fd); kfree(current->files); } if (current->executable) file_put(current->executable); /* Clean File System info */ if (--current->fs->usage == 0) kfree(current->fs); /* Clean VM Regions and unreferenced VM Objects */ VMRegion *head; if (--current->vm->usage == 0) { for (head = current->vm->regions; head; head = head->next) vm_destroy_region(head); kfree(current->vm); } vm_destroy_region(current->stack); /* Clean signals */ if (--current->signals->usage == 0) kfree(current->signals); /* Clean unread IPC messages */ /* TODO */ /* Deschedule */ current->state = TERMINATED; acquire(&readyQueue.lock); Task *tmp, *next; for (tmp = current->waiting.start; tmp; tmp = next) { next = tmp->next; add_to_queue(&readyQueue, tmp); } release(&readyQueue.lock); schedule(); panic("Unreached"); /* Clean task - FIXME */ kfree(current->name); kfree(current); clean_dir(); schedule(); /* UNREACHED */ } /* Exit the current task */ void exit(int status) { if (current->tid == 1) panic("Attempted to exit init! Exit code %d", status); current->status = (1 << 31) | (status & 0x0F); terminate(); } /* Wait for a child process to change state */ pid_t waitpid(pid_t pid, int *wstatus, int options) { if (!verify_access(wstatus, sizeof(int), PROT_WRITE)) return -EFAULT; Task *task = find_task(pid); if (!task) return -ECHILD; if (task->ppid != current->tgid && task->tgid != current->tgid) return -ECHILD; if (task->state != TERMINATED) { add_to_queue(&task->waiting, current); block_task(WAITING_FOR_CHILD); } if (wstatus) *wstatus = task->status; return task->tid; } /* Get current task's PID */ pid_t getpid(void) { return current->tgid; } /* Get current task's UID */ uid_t getuid(void) { return current->uid; } /* Set current task's (E)UID */ int setuid(uid_t uid) { if (uid != current->uid && uid != current->suid && !super_user()) return -EPERM; if (super_user()) { current->uid = uid; current->suid = uid; } current->euid = uid; return 0; } /* Get current task's EUID */ uid_t geteuid(void) { return current->euid; } /* Set the current task's EUID */ int seteuid(uid_t euid) { if (euid != current->uid && euid != current->euid && euid != current->suid && !super_user()) return -EPERM; current->euid = euid; return 0; } /* Get current task's GID */ gid_t getgid(void) { return current->gid; } /* Set current task's (E)GID */ int setgid(gid_t gid) { if (gid != current->gid && gid != current->sgid && !super_user()) return -EPERM; if (super_user()) { current->gid = gid; current->sgid = gid; } current->egid = gid; return 0; } /* Get current task's EGID */ gid_t getegid(void) { return current->egid; } /* Set the current task's EUID */ int setegid(gid_t egid) { if (egid != current->gid && egid != current->egid && egid != current->sgid && !super_user()) return -EPERM; current->egid = egid; return 0; } /* Initialse tasking */ void init_tasking(void) { move_stack((void *) (0xF0800000 - sizeof(uintptr_t)), 0x2000); /* Initialise the Kernel Task */ tasks.start = tasks.end = current = kmalloc(sizeof(Task)); current->tid = nextPid++; current->tgid = current->tid; current->priority = NORMAL; current->name = kmalloc(7); current->state = RUNNING; memcpy(current->name, "kernel", 7); current->pageDir = kernelDir; /* Files Namespace */ current->files = kmalloc(sizeof(Files)); current->files->usage = 1; /* File System Namespace */ current->fs = kmalloc(sizeof(FileSystem)); init_custody_chain(¤t->fs->cwdCustody); current->fs->usage = 1; /* Virtual Memory Namespace */ current->vm = kmalloc(sizeof(Files)); current->vm->regions = NULL; current->vm->usage = 1; /* Signals Namespace */ current->signals = kmalloc(sizeof(SigHandlers)); current->signals->usage = 1; /* Inter-Process Communication Namespace */ register_interrupt(0, timer_handler); }