diff --git a/kernel/syscall_handler.c b/kernel/syscall_handler.c
new file mode 100644
index 0000000..f1bda5e
--- /dev/null
+++ b/kernel/syscall_handler.c
@@ -0,0 +1,639 @@
+/*
+Copyright (C) 2015-2019 The University of Notre Dame
+This software is distributed under the GNU General Public License.
+See the file LICENSE for details.
+*/
+
+#include "kernel/syscall.h"
+#include "kernel/gfxstream.h"
+#include "syscall_handler.h"
+#include "console.h"
+#include "keyboard.h"
+#include "process.h"
+#include "kmalloc.h"
+#include "kobject.h"
+#include "cdromfs.h"
+#include "string.h"
+#include "memorylayout.h"
+#include "main.h"
+#include "fs.h"
+#include "kobject.h"
+#include "pagetable.h"
+#include "clock.h"
+#include "rtc.h"
+#include "elf.h"
+#include "kmalloc.h"
+#include "page.h"
+#include "ata.h"
+#include "window.h"
+#include "is_valid.h"
+#include "bcache.h"
+
+/*
+syscall_handler() is responsible for decoding system calls
+as they arrive, converting raw integers into the appropriate
+types, depending on the system call number.  Then, each
+individual handler routine checks the validity of each
+argument (fd in range, valid path, etc) and invokes the
+underlying system within the kernel.  Ideally, each of these
+handler functions should be short (only a few lines)
+and simply make use of functionality within other kernel modules.
+
+sys_run/fork/exec are notable exceptions and could benefit
+from simplification.
+*/
+
+/*
+Here follow the handlers for each individual system call
+For all of these system calls, a return value of zero or
+greater indiciates success, and return of less than zero
+indicates an error and the reason.
+*/
+
+int sys_debug(const char *str)
+{
+	if(!is_valid_string(str)) return KERROR_INVALID_ADDRESS;
+	printf("%s", str);
+	return 0;
+}
+
+int sys_process_yield()
+{
+	process_yield();
+	return 0;
+}
+
+int sys_process_exit(int status)
+{
+	process_exit(status);
+	return 0;
+}
+
+/* Helper routines to duplicate/free an argv array locally */
+
+static char **argv_copy(int argc, const char **argv)
+{
+	char **pp;
+
+	pp = kmalloc(sizeof(char *) * argc);
+	int i;
+	for(i = 0; i < argc; i++) {
+		pp[i] = strdup(argv[i]);
+	}
+
+	return pp;
+}
+
+static void argv_delete(int argc, char **argv)
+{
+	int i;
+	for(i = 0; i < argc; i++) {
+		kfree(argv[i]);
+	}
+	kfree(argv);
+}
+
+/*
+process_run() creates a child process in a more efficient
+way than fork/exec by creating the child without duplicating
+the memory state, then loading
+*/
+
+int sys_process_run( int fd, int argc, const char **argv)
+{
+	if(!is_valid_object_type(fd,KOBJECT_FILE)) return KERROR_INVALID_OBJECT;
+
+	struct kobject *k = current->ktable[fd];
+
+	/* Copy argv into kernel memory. */
+	char **copy_argv = argv_copy(argc, argv);
+
+	/* Create the child process */
+	struct process *p = process_create();
+	process_inherit(current, p);
+
+	/* SWITCH TO ADDRESS SPACE OF CHILD PROCESS */
+	struct pagetable *old_pagetable = current->pagetable;
+	current->pagetable = p->pagetable;
+	pagetable_load(p->pagetable);
+
+	/* Attempt to load the program image. */
+	addr_t entry;
+	int r = elf_load(p, k->data.file, &entry);
+	if(r >= 0) {
+		/* If load succeeded, reset stack and pass arguments */
+		process_stack_reset(p, PAGE_SIZE);
+		process_kstack_reset(p, entry);
+		process_pass_arguments(p, argc, copy_argv);
+	}
+
+	/* SWITCH BACK TO ADDRESS SPACE OF PARENT PROCESS */
+	current->pagetable = old_pagetable;
+	pagetable_load(old_pagetable);
+
+	/* Delete the argument and path copies. */
+	argv_delete(argc, copy_argv);
+
+	/* If any error happened, return in the context of the parent */
+	if(r < 0) {
+		if(r == KERROR_EXECUTION_FAILED) {
+			process_delete(p);
+		}
+		return r;
+	}
+
+	/* Otherwise, launch the new child process. */
+	process_launch(p);
+	return p->pid;
+}
+
+/* Function creates a child process with the standard window replaced by wd */
+int sys_process_wrun( int fd, int argc, const char **argv, int *fds, int fd_len)
+{
+	if(!is_valid_object_type(fd,KOBJECT_FILE)) return KERROR_INVALID_OBJECT;
+	struct kobject *k = current->ktable[fd];
+
+	/* Copy argv array into kernel memory. */
+	char **copy_argv = argv_copy(argc, argv);
+
+	/* Create the child process */
+	struct process *p = process_create();
+
+	process_selective_inherit(current, p, fds, fd_len);
+
+	/* SWITCH TO ADDRESS SPACE OF CHILD PROCESS */
+	struct pagetable *old_pagetable = current->pagetable;
+	current->pagetable = p->pagetable;
+	pagetable_load(p->pagetable);
+
+	/* Attempt to load the program image. */
+	addr_t entry;
+	int r = elf_load(p, k->data.file, &entry);
+	if(r >= 0) {
+		/* If load succeeded, reset stack and pass arguments */
+		process_stack_reset(p, PAGE_SIZE);
+		process_kstack_reset(p, entry);
+		process_pass_arguments(p, argc, copy_argv);
+	}
+
+	/* SWITCH BACK TO ADDRESS SPACE OF PARENT PROCESS */
+	current->pagetable = old_pagetable;
+	pagetable_load(old_pagetable);
+
+	/* Delete the argument copy. */
+	argv_delete(argc, copy_argv);
+
+	/* If any error happened, return in the context of the parent */
+	if(r < 0) {
+		if(r == KERROR_EXECUTION_FAILED) {
+			process_delete(p);
+		}
+		return r;
+	}
+
+	/* Otherwise, launch the new child process. */
+	process_launch(p);
+	return p->pid;
+}
+
+int sys_process_exec( int fd, int argc, const char **argv)
+{
+	if(!is_valid_object_type(fd,KOBJECT_FILE)) return KERROR_INVALID_OBJECT;
+	struct kobject *k = current->ktable[fd];
+
+	addr_t entry;
+
+	/* Duplicate the arguments into kernel space */
+	char **copy_argv = argv_copy(argc, argv);
+
+	/* Attempt to load the program image into this process. */
+	int r = elf_load(current, k->data.file, &entry);
+
+	/* On failure, return only if our address space is not corrupted. */
+	if(r < 0) {
+		if(r == KERROR_EXECUTION_FAILED) {
+			process_kill(current->pid);
+		}
+		argv_delete(argc, copy_argv);
+		return r;
+	}
+
+	/* Reset the stack and pass in the program arguments */
+	process_stack_reset(current, PAGE_SIZE);
+	process_kstack_reset(current, entry);
+	process_pass_arguments(current, argc, copy_argv);
+
+	/* Delete the local copy of the arguments. */
+	argv_delete(argc, copy_argv);
+
+	/*
+	   IMPORTANT: Following a successful exec, we cannot return via
+	   the normal path, because our stack has been reset to that
+	   of a fresh process.  We must switch in order to jump
+	   to the new stack properly.
+	 */
+
+	process_yield();
+
+	/* NOTREACHED */
+	return 0;
+}
+
+int sys_process_fork()
+{
+	struct process *p = process_create();
+	p->ppid = current->pid;
+	pagetable_delete(p->pagetable);
+	p->pagetable = pagetable_duplicate(current->pagetable);
+	process_inherit(current, p);
+	process_kstack_copy(current, p);
+	process_launch(p);
+	return p->pid;
+}
+
+int sys_process_self()
+{
+	return current->pid;
+}
+
+int sys_process_parent()
+{
+	return current->ppid;
+}
+
+int sys_process_kill(int pid)
+{
+	return process_kill(pid);
+}
+
+int sys_process_reap(int pid)
+{
+	return process_reap(pid);
+}
+
+int sys_process_wait(struct process_info *info, int timeout)
+{
+	if(!is_valid_pointer(info,sizeof(*info))) return KERROR_INVALID_ADDRESS;
+	return process_wait_child(0, info, timeout);
+}
+
+int sys_process_sleep(unsigned int ms)
+{
+	clock_wait(ms);
+	return 0;
+}
+
+int sys_process_stats(struct process_stats *s, int pid)
+{
+	if(!is_valid_pointer(s,sizeof(*s))) return KERROR_INVALID_ADDRESS;
+	return process_stats(pid, s);
+}
+
+int sys_process_heap(int delta)
+{
+	process_data_size_set(current, current->vm_data_size + delta);
+	return PROCESS_ENTRY_POINT + current->vm_data_size;
+}
+
+int sys_object_list( int fd, char *buffer, int length)
+{
+	if(!is_valid_object(fd)) return KERROR_INVALID_OBJECT;
+	if(!is_valid_pointer(buffer,length)) return KERROR_INVALID_ADDRESS;
+	if(kobject_get_type(current->ktable[fd])!=KOBJECT_DIR) return KERROR_NOT_A_DIRECTORY;
+	return kobject_list(current->ktable[fd],buffer,length);
+}
+
+int sys_open_file( int fd, const char *path, int mode, kernel_flags_t flags)
+{
+	if(!is_valid_object(fd)) return KERROR_INVALID_OBJECT;
+	if(!is_valid_path(path)) return KERROR_INVALID_PATH;
+
+	int newfd = process_available_fd(current);
+	if(newfd<0) return KERROR_OUT_OF_OBJECTS;
+
+	struct kobject *newobj;
+	int result = kobject_lookup(current->ktable[fd],path,&newobj);
+
+	if(result>=0) {
+		current->ktable[newfd] = newobj;
+		return newfd;
+	} else {
+		return result;
+	}
+}
+
+int sys_open_dir( int fd, const char *path, kernel_flags_t flags )
+{
+	if(!is_valid_object(fd)) return KERROR_INVALID_OBJECT;
+	if(!is_valid_path(path)) return KERROR_INVALID_PATH;
+
+       	int newfd = process_available_fd(current);
+	if(newfd<0) return KERROR_OUT_OF_OBJECTS;
+
+	struct kobject *newobj;
+	int result;
+
+	if(flags&KERNEL_FLAGS_CREATE) {
+		newobj = kobject_create_dir_from_dir( current->ktable[fd], path );
+		if(newobj) {
+			result = 0;
+		} else {
+			result = KERROR_NOT_FOUND;
+		}
+	} else {
+		result = kobject_lookup( current->ktable[fd], path, &newobj );
+	}
+
+	if(result>=0) {
+		current->ktable[newfd] = newobj;
+		return newfd;
+	} else {
+		return result;
+	}
+}
+
+int sys_open_console(int wd)
+{
+	if(!is_valid_object_type(wd,KOBJECT_WINDOW)) return KERROR_INVALID_OBJECT;
+
+	int fd = process_available_fd(current);
+	if(fd<0) return KERROR_OUT_OF_OBJECTS;
+
+	current->ktable[fd] = kobject_create_console_from_window(current->ktable[wd]);
+	return fd;
+}
+
+
+int sys_open_window(int wd, int x, int y, int w, int h)
+{
+	if(!is_valid_object_type(wd,KOBJECT_WINDOW)) return KERROR_INVALID_OBJECT;
+
+	struct kobject *k = current->ktable[wd];
+
+	int fd = process_available_fd(current);
+	if(fd<0) return KERROR_OUT_OF_OBJECTS;
+
+	k = kobject_create_window_from_window(k,x,y,w,h);
+	if(!k) {
+		// XXX choose better errno
+		return KERROR_INVALID_REQUEST;
+	}
+
+	current->ktable[fd] = k;
+
+	return fd;
+}
+
+int sys_open_pipe()
+{
+	int fd = process_available_fd(current);
+	if(fd < 0) {
+		return KERROR_NOT_FOUND;
+	}
+	struct pipe *p = pipe_create();
+	if(!p) {
+		return KERROR_NOT_FOUND;
+	}
+	current->ktable[fd] = kobject_create_pipe(p);
+	return fd;
+}
+
+int sys_object_type(int fd)
+{
+	if(!is_valid_object(fd)) return KERROR_INVALID_OBJECT;
+
+	int fd_type = kobject_get_type(current->ktable[fd]);
+	if(!fd_type)
+		return 0;
+	return fd_type;
+}
+
+int sys_object_copy( int src, int dst )
+{
+	if(!is_valid_object(src)) return KERROR_INVALID_OBJECT;
+	if(dst>PROCESS_MAX_OBJECTS) return KERROR_INVALID_OBJECT;
+
+	if(dst < 0) {
+		dst = process_available_fd(current);
+		if(dst<0) return KERROR_NOT_FOUND;
+	}
+
+	sys_object_close(dst);
+
+	current->ktable[dst] = kobject_copy(current->ktable[src]);
+
+	return src;
+}
+
+int sys_object_read(int fd, void *data, int length, kernel_io_flags_t flags )
+{
+	if(!is_valid_object(fd)) return KERROR_INVALID_OBJECT;
+	if(!is_valid_pointer(data,length)) return KERROR_INVALID_ADDRESS;
+
+	struct kobject *p = current->ktable[fd];
+	return kobject_read(p, data, length, flags);
+}
+
+int sys_object_write(int fd, void *data, int length, kernel_io_flags_t flags )
+{
+	if(!is_valid_object(fd)) return KERROR_INVALID_OBJECT;
+	if(!is_valid_pointer(data,length)) return KERROR_INVALID_ADDRESS;
+
+	struct kobject *p = current->ktable[fd];
+	return kobject_write(p, data, length, flags);
+}
+
+int sys_object_seek(int fd, int offset, int whence)
+{
+	if(!is_valid_object(fd)) return KERROR_INVALID_OBJECT;
+
+	// XXX add kobject method here
+	return KERROR_NOT_IMPLEMENTED;
+}
+
+int sys_object_remove( int fd, const char *name )
+{
+	if(!is_valid_object(fd)) return KERROR_INVALID_OBJECT;
+	if(!is_valid_path(name)) return KERROR_INVALID_PATH;
+	return kobject_remove( current->ktable[fd], name );
+}
+
+int sys_object_close(int fd)
+{
+	if(!is_valid_object(fd)) return KERROR_INVALID_OBJECT;
+
+	struct kobject *p = current->ktable[fd];
+	kobject_close(p);
+	current->ktable[fd] = 0;
+	return 0;
+}
+
+int sys_object_set_tag(int fd, char *tag)
+{
+	if(!is_valid_object(fd)) return KERROR_INVALID_OBJECT;
+	kobject_set_tag(current->ktable[fd], tag);
+	return 0;
+}
+
+int sys_object_get_tag(int fd, char *buffer, int buffer_size)
+{
+	if(!is_valid_object(fd)) return KERROR_INVALID_OBJECT;
+	return kobject_get_tag(current->ktable[fd], buffer, buffer_size);
+}
+
+int sys_object_size(int fd, int *dims, int n)
+{
+	if(!is_valid_object(fd)) return KERROR_INVALID_OBJECT;
+	if(!is_valid_pointer(dims,sizeof(*dims)*n)) return KERROR_INVALID_ADDRESS;
+
+	struct kobject *p = current->ktable[fd];
+	return kobject_size(p, dims, n);
+}
+
+int sys_object_max()
+{
+	int max_fd = process_object_max(current);
+	return max_fd;
+}
+
+int sys_system_stats(struct system_stats *s)
+{
+	if(!is_valid_pointer(s,sizeof(*s))) return KERROR_INVALID_ADDRESS;
+
+	struct rtc_time t = { 0 };
+	rtc_read(&t);
+	s->time = rtc_time_to_timestamp(&t) - boottime;
+
+	struct ata_count a = ata_stats();
+	for(int i = 0; i < 4; i++) {
+		s->blocks_written[i] = a.blocks_written[i];
+		s->blocks_read[i] = a.blocks_read[i];
+	}
+
+	return 0;
+}
+
+int sys_bcache_stats(struct bcache_stats * s)
+{
+	if(!is_valid_pointer(s,sizeof(*s))) return KERROR_INVALID_ADDRESS;
+	bcache_get_stats( s );
+	return 0;
+}
+
+int sys_bcache_flush()
+{
+	bcache_flush_all();
+	return 0;
+}
+
+int sys_system_time( uint32_t *tm )
+{
+	if(!is_valid_pointer(tm,sizeof(*tm))) return KERROR_INVALID_ADDRESS;
+	struct rtc_time t;
+	rtc_read(&t);
+	*tm = rtc_time_to_timestamp(&t);
+	return 0;
+}
+
+int sys_system_rtc( struct rtc_time *t )
+{
+	if(!is_valid_pointer(t,sizeof(*t))) return KERROR_INVALID_ADDRESS;
+	rtc_read(t);
+	return 0;
+}
+
+int sys_device_driver_stats(const char * name, struct device_driver_stats * stats)
+{
+	if(!is_valid_string(name)) return KERROR_INVALID_ADDRESS;
+	if(!is_valid_pointer(stats,sizeof(*stats))) return KERROR_INVALID_ADDRESS;
+
+	device_driver_get_stats(name, stats);
+	return 0;
+}
+
+int32_t syscall_handler(syscall_t n, uint32_t a, uint32_t b, uint32_t c, uint32_t d, uint32_t e)
+{
+	if((n < MAX_SYSCALL) && current) {
+		current->stats.syscall_count[n]++;
+	}
+	switch (n) {
+	case SYSCALL_DEBUG:
+		return sys_debug((const char *) a);
+	case SYSCALL_PROCESS_YIELD:
+		return sys_process_yield();
+	case SYSCALL_PROCESS_EXIT:
+		return sys_process_exit(a);
+	case SYSCALL_PROCESS_RUN:
+		return sys_process_run(a, b, (const char **)c);
+	case SYSCALL_PROCESS_WRUN:
+		return sys_process_wrun(a, b, (const char **) c, (int *) d, e);
+	case SYSCALL_PROCESS_FORK:
+		return sys_process_fork();
+	case SYSCALL_PROCESS_EXEC:
+		return sys_process_exec(a, b, (const char **) c);
+	case SYSCALL_PROCESS_SELF:
+		return sys_process_self();
+	case SYSCALL_PROCESS_PARENT:
+		return sys_process_parent();
+	case SYSCALL_PROCESS_KILL:
+		return sys_process_kill(a);
+	case SYSCALL_PROCESS_REAP:
+		return sys_process_reap(a);
+	case SYSCALL_PROCESS_WAIT:
+		return sys_process_wait((struct process_info *) a, b);
+	case SYSCALL_PROCESS_SLEEP:
+		return sys_process_sleep(a);
+	case SYSCALL_PROCESS_STATS:
+		return sys_process_stats((struct process_stats *) a, b);
+	case SYSCALL_PROCESS_HEAP:
+		return sys_process_heap(a);
+	case SYSCALL_OPEN_FILE:
+		return sys_open_file(a, (const char *)b, c, d);
+	case SYSCALL_OPEN_DIR:
+		return sys_open_dir(a, (const char *)b, c );
+	case SYSCALL_OPEN_WINDOW:
+		return sys_open_window(a, b, c, d, e);
+	case SYSCALL_OPEN_CONSOLE:
+		return sys_open_console(a);
+	case SYSCALL_OPEN_PIPE:
+		return sys_open_pipe();
+	case SYSCALL_OBJECT_TYPE:
+		return sys_object_type(a);
+	case SYSCALL_OBJECT_COPY:
+		return sys_object_copy(a,b);
+	case SYSCALL_OBJECT_READ:
+		return sys_object_read(a, (void *) b, c, d );
+	case SYSCALL_OBJECT_LIST:
+		return sys_object_list(a, (char *) b, (int) c);
+	case SYSCALL_OBJECT_WRITE:
+		return sys_object_write(a, (void *) b, c, d);
+	case SYSCALL_OBJECT_SEEK:
+		return sys_object_seek(a, b, c);
+	case SYSCALL_OBJECT_REMOVE:
+		return sys_object_remove(a,(const char*)b);
+	case SYSCALL_OBJECT_CLOSE:
+		return sys_object_close(a);
+	case SYSCALL_OBJECT_SET_TAG:
+		return sys_object_set_tag(a, (char *) b);
+	case SYSCALL_OBJECT_GET_TAG:
+		return sys_object_get_tag(a, (char *) b, c);
+	case SYSCALL_OBJECT_SIZE:
+		return sys_object_size(a, (int *) b, c);
+	case SYSCALL_OBJECT_MAX:
+		return sys_object_max(a);
+	case SYSCALL_SYSTEM_STATS:
+		return sys_system_stats((struct system_stats *) a);
+	case SYSCALL_BCACHE_STATS:
+		return sys_bcache_stats((struct bcache_stats *) a);
+	case SYSCALL_BCACHE_FLUSH:
+		return sys_bcache_flush();
+	case SYSCALL_SYSTEM_TIME:
+		return sys_system_time((uint32_t*)a);
+	case SYSCALL_SYSTEM_RTC:
+		return sys_system_rtc((struct rtc_time *) a);
+	case SYSCALL_DEVICE_DRIVER_STATS:
+		return sys_device_driver_stats((char *) a, (struct device_driver_stats *) b);
+	default:
+		return KERROR_INVALID_SYSCALL;
+	}
+}