BarryServer : Git

All the code for all my projects
// BarryServer : Git / Orion / blob / 1628fcfdfdf2978ed9ccac96ee7d13bb3dc43a01 / task / task.c

// Related

Orion

Barry Moving signal handlers into separate namespace 7ae31b0 (2 years, 4 months ago)
/*
 * This file is responsible for the core functions related to multitasking.  It
 * relies on a decent paging implementation.  It contains several routines which
 * are related to the creation, modification and deletion of tasks.
 */

#include <stdint.h>
#include <sys/sched.h>
#include <errno.h>
#include "task.h"
#include "../vfs/vfs.h"
#include "../vfs/inode.h"
#include "../vfs/cache.h"
#include "../vfs/tmpfs/fs.h"
#include "../mem/mem.h"
#include "../mem/heap.h"
#include "../mem/paging.h"
#include "../proc/proc.h"
#include "../screen.h"

extern TaskQueue readyQueue, tasks;
pid_t nextPid = 1;

extern uint32_t initialStack;
extern page_dir_t kernelDir;

uintptr_t read_eip(void);

/* Move the initial stack to a new position */
static void
move_stack(void *start, size_t size)
{
	uintptr_t i, tmp, *tmpp;
	uintptr_t oldStackPointer, oldBasePointer, offset;
	uintptr_t newStackPointer, newBasePointer;

	for (i = (size_t) start - size; i <= (size_t) start; i += 0x1000) {
		page_t *pg = get_page((void *) i);
		alloc_page(pg, PTE_PRESENT | PTE_WRITE | PTE_USER, -1);
	}

	asm volatile("mov %%esp, %0" : "=r" (oldStackPointer));
	asm volatile("mov %%ebp, %0" : "=r" (oldBasePointer));
	offset = (uint32_t) start - initialStack;
	newStackPointer = oldStackPointer + offset;
	newBasePointer = oldBasePointer + offset;

	memcpy((void *) newStackPointer, (void *) oldStackPointer,
	       initialStack - oldStackPointer);

	/* Update pointers on stack */
	for (i = (size_t) start; i > (size_t) start - size; i -= 4) {
		tmp = *(uint32_t *) i;
		if ((tmp > oldStackPointer) && (tmp < initialStack)) {
			tmp += offset;
			tmpp = (uintptr_t *) i;
			*tmpp = tmp;
		}
	}

	asm volatile("mov %0, %%esp" :: "r" (newStackPointer));
	asm volatile("mov %0, %%ebp" :: "r" (newBasePointer));
}

/* Fork a task */
pid_t
fork(void)
{
	return clone(CLONE_NONE);
}

/* Clone a task */
pid_t
clone(int flags)
{
	Task *parent = current, *child = kmalloc(sizeof(Task)), *tmp;

	if (flags & CLONE_THREAD) {
		flags |= CLONE_PARENT;
		flags |= CLONE_VM;
	}

	child->tid = nextPid++;
	if (flags & CLONE_THREAD)
		child->tgid = parent->tgid;
	else
		child->tgid = child->tid;
	child->priority = NORMAL;
	child->state = READY;
	child->status = 0;
	child->inSyscall = parent->inSyscall;
	child->name = kmalloc(strlen(parent->name)+1);
	memcpy(child->name, parent->name, strlen(parent->name)+1);

	/* Set parent */
	child->parent = parent;
	if (flags & CLONE_PARENT)
		child->parent = parent->parent;
	child->ppid = child->parent->tgid;

	child->executable = file_get(parent->executable);

	/* Add to list of tasks */
	tasks.end->tnext = child;
	tasks.end = child;

	/* Clone parent's file descriptors */
	int fd;
	File *file;
	if (flags & CLONE_FILES) {
		child->files = parent->files;
		child->files->usage++;
	} else {
		child->files = kmalloc(sizeof(Files));
		child->files->usage = 1;
		for (fd = 0; fd < NFILES; fd++) {
			file = parent->files->fd[fd];
			if (!file) continue;
			child->files->fd[fd] = file_get(file);
		}
	}

	/* Clone parent's file system context */
	if (flags & CLONE_FS) {
		child->fs = parent->fs;
		child->fs->usage++;
	} else {
		child->fs = kmalloc(sizeof(FileSystem));
		child->fs->usage = 1;
		child->fs->cwd = parent->fs->cwd;
		child->fs->cwd->usage++;
		init_custody_chain(&child->fs->cwdCustody);
		copy_custody_chain(&parent->fs->cwdCustody,
		                   &child->fs->cwdCustody);
		child->fs->root = parent->fs->root;
		child->fs->root->usage++;
	}

	/* Clone page directory */
	if (flags & CLONE_VM) {
		child->vm = parent->vm;
		child->vm->usage++;
	} else {
		child->vm = kmalloc(sizeof(VirtualMemory));
		child->vm->usage = 1;
	}
	child->pageDir = clone_dir();

	/* Clone parent's VM Regions in child */
	VMRegion *head;
	Page *page;
	off_t i;
	if (child->vm != parent->vm)
		child->vm->regions = vm_clone_regions(parent->vm->regions);
	child->stack = kmalloc(sizeof(VMRegion));
	memcpy(child->stack, parent->stack, sizeof(VMRegion));
	child->stack->next = child->stack->prev = NULL;
	/* Copy stack */
	if (parent->stack && parent->stack->front) {
		file = kmalloc(sizeof(File));
		file->inode = inode_get(kmalloc(sizeof(Inode)));
		file->ops = &tmpfsFileOps;
		child->stack->front = file_get(file);
		for (i = 0; i < child->stack->end - child->stack->start;
		     i += 0x1000) {
			page = page_find(parent->stack->front->inode, i);
			if (page)
				page_add(file->inode, page);
		}
	}
	/* Copy thread local storage */
	if (parent->tls) {
		child->tls = kmalloc(sizeof(VMRegion));
		memcpy(child->tls, parent->tls, sizeof(VMRegion));
		child->tls->next = child->tls->prev = NULL;
		if (parent->tls->front) {
			file = kmalloc(sizeof(File));
			file->inode = inode_get(kmalloc(sizeof(Inode)));
			file->ops = &tmpfsFileOps;
			child->tls->front = file_get(file);
			for (i = 0; i < child->tls->end - child->tls->start;
			     i += 0x1000) {
				page = page_find(parent->tls->front->inode, i);
				if (page)
					page_add(file->inode, page);
			}
		}
		if (parent->tls->back)
			child->tls->back = file_get(parent->tls->back);
	}

	/* Signal handlers */
	if (flags & CLONE_SIGHAND) {
		child->signals = parent->signals;
		child->signals->usage++;
	} else {
		child->signals = kmalloc(sizeof(SigHandlers));
		child->signals->usage = 1;
	}

	/* Split tasks here */
	uintptr_t esp, ebp, eip;
	eip = read_eip();
	if (current == parent) {
		asm volatile("mov %%esp, %0" : "=r" (esp));
		asm volatile("mov %%ebp, %0" : "=r" (ebp));
		child->esp = esp;
		child->ebp = ebp;
		child->eip = eip;
		add_to_queue(&readyQueue, child);
		return child->tid;
	}
	return 0;
}

/* Terminate the current task */
void
terminate(void)
{
	/* Close files */
	int fd;
	if (--current->files->usage == 0) {
		for (fd = 0; fd < NFILES; fd++)
			if (current->files->fd[fd])
				close(fd);
		kfree(current->files);
	}
	if (current->executable)
		file_put(current->executable);

	/* Clean File System info */
	if (--current->fs->usage == 0)
		kfree(current->fs);

	/* Clean VM Regions and unreferenced VM Objects */
	VMRegion *head;
	if (--current->vm->usage == 0) {
		for (head = current->vm->regions; head; head = head->next)
			vm_destroy_region(head);
		kfree(current->vm);
	}
	vm_destroy_region(current->stack);

	/* Clean signals */
	if (--current->signals->usage == 0)
		kfree(current->signals);

	/* Clean unread IPC messages */
	/* TODO */

	/* Deschedule */
	current->state = TERMINATED;
	acquire(&readyQueue.lock);
	Task *tmp, *next;
	for (tmp = current->waiting.start; tmp; tmp = next) {
		next = tmp->next;
		add_to_queue(&readyQueue, tmp);
	}
	release(&readyQueue.lock);
	schedule();
	panic("Unreached");

	/* Clean task - FIXME */
	kfree(current->name);
	kfree(current);
	clean_dir();
	schedule();
	/* UNREACHED */
}

/* Exit the current task */
void
exit(int status)
{
	if (current->tid == 1)
		panic("Attempted to exit init! Exit code %d", status);
	current->status = (1 << 31) | (status & 0x0F);
	terminate();
}

/* Wait for a child process to change state */
pid_t
waitpid(pid_t pid, int *wstatus, int options)
{
	if (!verify_access(wstatus, sizeof(int), PROT_WRITE))
		return -EFAULT;

	Task *task = find_task(pid);
	if (!task)
		return -ECHILD;
	if (task->ppid != current->tgid && task->tgid != current->tgid)
		return -ECHILD;

	if (task->state != TERMINATED) {
		add_to_queue(&task->waiting, current);
		block_task(WAITING_FOR_CHILD);
	}

	if (wstatus)
		*wstatus = task->status;
	return task->tid;
}

/* Get current task's PID */
pid_t
getpid(void)
{
	return current->tgid;
}

/* Get current task's UID */
uid_t
getuid(void)
{
	return current->uid;
}

/* Set current task's (E)UID */
int
setuid(uid_t uid)
{
	if (uid != current->uid && uid != current->suid && !super_user())
		return -EPERM;
	if (super_user()) {
		current->uid = uid;
		current->suid = uid;
	}
	current->euid = uid;
	return 0;
}

/* Get current task's EUID */
uid_t
geteuid(void)
{
	return current->euid;
}

/* Set the current task's EUID */
int
seteuid(uid_t euid)
{
	if (euid != current->uid
	 && euid != current->euid
	 && euid != current->suid
	 && !super_user())
		return -EPERM;
	current->euid = euid;
	return 0;
}

/* Get current task's GID */
gid_t
getgid(void)
{
	return current->gid;
}

/* Set current task's (E)GID */
int
setgid(gid_t gid)
{
	if (gid != current->gid
	 && gid != current->sgid
	 && !super_user())
		return -EPERM;
	if (super_user()) {
		current->gid = gid;
		current->sgid = gid;
	}
	current->egid = gid;
	return 0;
}

/* Get current task's EGID */
gid_t
getegid(void)
{
	return current->egid;
}

/* Set the current task's EUID */
int
setegid(gid_t egid)
{
	if (egid != current->gid
	 && egid != current->egid
	 && egid != current->sgid
	 && !super_user())
		return -EPERM;
	current->egid = egid;
	return 0;
}

/* Initialse tasking */
void
init_tasking(void)
{
	move_stack((void *) (0xF0800000 - sizeof(uintptr_t)), 0x2000);

	/* Initialise the Kernel Task */
	tasks.start = tasks.end = current = kmalloc(sizeof(Task));
	current->tid = nextPid++;
	current->tgid = current->tid;
	current->priority = NORMAL;
	current->name = kmalloc(7);
	current->state = RUNNING;
	memcpy(current->name, "kernel", 7);
	current->pageDir = kernelDir;

	/* Files Namespace */
	current->files = kmalloc(sizeof(Files));
	current->files->usage = 1;

	/* File System Namespace */
	current->fs = kmalloc(sizeof(FileSystem));
	init_custody_chain(&current->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);
}