diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..3caa328
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+kern/compile/*
+*.depend
+*.swp
+build/*
+
diff --git a/defs.mk b/defs.mk
new file mode 100644
index 0000000..9345229
--- /dev/null
+++ b/defs.mk
@@ -0,0 +1,20 @@
+# This file was generated by configure. Edits will disappear if you rerun
+# configure. If you find that you need to edit this file to make things
+# work, let the course staff know and we'll try to fix the configure script.
+#
+# The purpose of this file is to hold all the makefile definitions
+# needed to adjust the OS/161 build process to any particular
+# environment. If I've done it right, all you need to do is rerun the
+# configure script and make clean if you start working on a different
+# host OS. If I've done it mostly right, you may need to edit this
+# file but you still hopefully won't need to edit any of the
+# makefiles.
+#
+# The things that can be set here are documented in mk/os161.config.mk.
+#
+
+OSTREE=$(HOME)/root
+PLATFORM=sys161
+MACHINE=mips
+COMPAT_CFLAGS=
+COMPAT_TARGETS=
diff --git a/kern/arch/mips/locore/trap.c b/kern/arch/mips/locore/trap.c
index ff39633..42ed641 100644
--- a/kern/arch/mips/locore/trap.c
+++ b/kern/arch/mips/locore/trap.c
@@ -111,6 +111,10 @@ kill_curthread(vaddr_t epc, unsigned code, vaddr_t vaddr)
/*
* You will probably want to change this.
*/
+
+ //simulate an exit call.
+ sys__exit( -1 );
+ return;
kprintf("Fatal user mode trap %u sig %d (%s, epc 0x%x, vaddr 0x%x)\n",
code, sig, trapcodenames[code], epc, vaddr);
diff --git a/kern/arch/mips/syscall/syscall.c b/kern/arch/mips/syscall/syscall.c
index 0f773bd..4e99f50 100644
--- a/kern/arch/mips/syscall/syscall.c
+++ b/kern/arch/mips/syscall/syscall.c
@@ -32,10 +32,14 @@
#include <kern/syscall.h>
#include <lib.h>
#include <mips/trapframe.h>
+#include <copyinout.h>
#include <thread.h>
#include <current.h>
#include <syscall.h>
+#define MAKE_64BIT(x,y) (((int64_t)x) << 32 | y)
+#define GET_LO(x) ((int32_t) x & 0x00000000FFFFFFFF)
+#define GET_HI(x) ((int32_t) x & 0xFFFFFFFF00000000)
/*
* System call dispatcher.
@@ -80,7 +84,11 @@ syscall(struct trapframe *tf)
{
int callno;
int32_t retval;
+ int64_t retval64;
int err;
+ int nextra;
+ bool handle64;
+
KASSERT(curthread != NULL);
KASSERT(curthread->t_curspl == 0);
@@ -98,6 +106,10 @@ syscall(struct trapframe *tf)
*/
retval = 0;
+ handle64 = false;
+
+ //award points for the process that just called the systemcall.
+ curthread->td_proc->p_nsyscalls++;
switch (callno) {
case SYS_reboot:
@@ -109,6 +121,79 @@ syscall(struct trapframe *tf)
(userptr_t)tf->tf_a1);
break;
+ case SYS_open:
+ err = sys_open( (userptr_t) tf->tf_a0,
+ tf->tf_a1, &retval);
+ break;
+
+ case SYS_close:
+ err = sys_close( tf->tf_a0 );
+ break;
+
+ case SYS_write:
+ err = sys_write( tf->tf_a0,
+ (userptr_t) tf->tf_a1,
+ tf->tf_a2, &retval );
+ break;
+
+ case SYS_read:
+ err = sys_read( tf->tf_a0,
+ (userptr_t) tf->tf_a1,
+ tf->tf_a2, &retval);
+ break;
+
+ case SYS___getcwd:
+ err = sys___getcwd( (userptr_t)tf->tf_a0,
+ tf->tf_a1, &retval );
+ break;
+
+ case SYS_lseek:
+ //get the value of whence from sp+16
+ err = copyin( (userptr_t)(tf->tf_sp + 16),
+ &nextra, sizeof( int ) );
+ if( err )
+ break;
+
+ err = sys_lseek(
+ tf->tf_a0,
+ MAKE_64BIT( tf->tf_a2, tf->tf_a3 ),
+ nextra,
+ &retval64
+ );
+
+ //if no errors occurred, we must handle
+ //a 64-bit return value.
+ handle64 = true;
+ break;
+
+ case SYS_dup2:
+ err = sys_dup2( tf->tf_a0, tf->tf_a1, &retval );
+ break;
+
+ case SYS_chdir:
+ err = sys_chdir( (userptr_t)tf->tf_a0 );
+ break;
+
+ case SYS_getpid:
+ err = sys_getpid( &retval );
+ break;
+ case SYS__exit:
+ sys__exit( tf->tf_a0 );
+ err = 0;
+ break;
+
+ case SYS_waitpid:
+ err = sys_waitpid( tf->tf_a0, (userptr_t)tf->tf_a1, tf->tf_a2, &retval );
+ break;
+
+ case SYS_fork:
+ err = sys_fork( tf, &retval );
+ break;
+
+ case SYS_execv:
+ err = sys_execv( (userptr_t) tf->tf_a0, (userptr_t) tf->tf_a1 );
+ break;
+
/* Add stuff here */
default:
@@ -127,6 +212,11 @@ syscall(struct trapframe *tf)
tf->tf_v0 = err;
tf->tf_a3 = 1; /* signal an error */
}
+ else if( handle64 ) {
+ tf->tf_a3 = 0;
+ tf->tf_v0 = GET_HI( retval64 );
+ tf->tf_v1 = GET_LO( retval64 );
+ }
else {
/* Success. */
tf->tf_v0 = retval;
diff --git a/kern/conf/conf.kern b/kern/conf/conf.kern
index d527f61..190b6cf 100644
--- a/kern/conf/conf.kern
+++ b/kern/conf/conf.kern
@@ -368,6 +368,31 @@ file syscall/loadelf.c
file syscall/runprogram.c
file syscall/time_syscalls.c
+#IO SYSCALLS
+file syscall/open.c
+file syscall/write.c
+file syscall/close.c
+file syscall/read.c
+file syscall/__getcwd.c
+file syscall/lseek.c
+file syscall/dup2.c
+file syscall/chdir.c
+
+#PROC SYSCALLS
+file syscall/getpid.c
+file syscall/_exit.c
+file syscall/waitpid.c
+file syscall/fork.c
+file syscall/execv.c
+
+
+#IO
+file io/file.c
+file io/filedesc.c
+
+#PROC
+file proc/proc.c
+
#
# Startup and initialization
#
diff --git a/kern/include/file.h b/kern/include/file.h
new file mode 100644
index 0000000..803ec05
--- /dev/null
+++ b/kern/include/file.h
@@ -0,0 +1,35 @@
+#ifndef __FILEH__
+#define __FILEH__
+
+#include <vnode.h>
+#include <synch.h>
+#include <proc.h>
+
+#define MAX_FILE_NAME 32
+
+struct proc;
+
+struct file {
+ struct vnode *f_vnode; /* vnode associated with the file */
+ uint16_t f_oflags; /* open flags */
+ uint16_t f_refcount; /* reference count */
+ off_t f_offset; /* file offset */
+ struct lock *f_lk; /* lock for IO atomicity */
+};
+
+int file_get(struct proc *, int, struct file ** );
+int file_close_descriptor( struct proc *, int );
+int file_close( struct proc *, struct file * );
+int file_create( struct vnode *, int, struct file ** );
+bool file_descriptor_exists( struct proc *, int );
+void file_destroy( struct file * );
+int file_close_all( struct proc * );
+
+
+//helper function to open() files from inside the kernel.
+int ___open( struct proc *, char *, int, int *);
+
+#define F_LOCK(x) (lock_acquire((x)->f_lk))
+#define F_UNLOCK(x) (lock_release((x)->f_lk))
+
+#endif
diff --git a/kern/include/filedesc.h b/kern/include/filedesc.h
new file mode 100644
index 0000000..9129276
--- /dev/null
+++ b/kern/include/filedesc.h
@@ -0,0 +1,25 @@
+#ifndef __FILEDESCH__
+#define __FILEDESCH__
+
+#include <file.h>
+
+#define MAX_OPEN_FILES 16
+#define FD_RESERVED_SPOT 0xcafebabe
+
+struct filedesc {
+ struct file *fd_ofiles[MAX_OPEN_FILES]; /* array of open files */
+ struct lock *fd_lk; /* a lock protecting the file descriptor table */
+ uint16_t fd_nfiles; /* how many open files we have */
+};
+
+void fd_clone( struct filedesc *, struct filedesc * );
+int fd_create( struct filedesc ** );
+void fd_destroy( struct filedesc * );
+int fd_attach( struct filedesc *, struct file *, int * );
+void fd_detach( struct filedesc *, int );
+int fd_attach_into( struct filedesc *, struct file *, int );
+
+#define FD_LOCK(x) (lock_acquire((x)->fd_lk))
+#define FD_UNLOCK(x) (lock_release((x)->fd_lk))
+
+#endif
diff --git a/kern/include/proc.h b/kern/include/proc.h
new file mode 100644
index 0000000..09b9c4a
--- /dev/null
+++ b/kern/include/proc.h
@@ -0,0 +1,43 @@
+#ifndef __PROC__
+#define __PROC__
+
+#include <types.h>
+#include <filedesc.h>
+#include <synch.h>
+
+#define MAX_PROCESSES 64
+#define PROC_RESERVED_SPOT 0xcafebabe
+
+struct proc {
+ pid_t p_pid; /* pid of the process */
+ struct filedesc *p_fd; /* file descriptor table */
+ struct proc *p_proc; /* parent process */
+ bool p_is_dead; /* are we dead? */
+ int p_retval; /* our return code */
+
+ /* synchronization mechanisms */
+ struct lock *p_lk; /* lock to protect the structure */
+ struct semaphore *p_sem; /* sem used for wait/exit */
+
+ /* scheduler related */
+ uint64_t p_nsyscalls; /* how many system calls we called? */
+ int p_nice; /* our nice value */
+};
+
+extern struct proc *allproc[MAX_PROCESSES];
+extern struct lock *lk_allproc;
+extern struct lock *lk_exec;
+
+int proc_create( struct proc ** );
+int proc_clone(struct proc *, struct proc ** );
+void proc_destroy(struct proc *);
+int proc_get( pid_t, struct proc ** );
+void proc_system_init(void);
+
+//tests.
+void proc_test_pid_allocation(void);
+
+#define PROC_LOCK(x) (lock_acquire( (x)->p_lk ))
+#define PROC_UNLOCK(x) (lock_release( (x)->p_lk ))
+
+#endif
diff --git a/kern/include/synch.h b/kern/include/synch.h
index ac3714b..461eedc 100644
--- a/kern/include/synch.h
+++ b/kern/include/synch.h
@@ -74,8 +74,12 @@ void V(struct semaphore *);
*/
struct lock {
char *lk_name;
- // add what you need here
- // (don't forget to mark things volatile as needed)
+
+ // BEGIN SOLUTION
+ struct wchan *lk_wchan;
+ struct spinlock lk_lock;
+ volatile struct thread *lk_holder;
+ // END SOLUTION
};
struct lock *lock_create(const char *name);
@@ -113,8 +117,10 @@ void lock_destroy(struct lock *);
struct cv {
char *cv_name;
- // add what you need here
- // (don't forget to mark things volatile as needed)
+
+ // BEGIN SOLUTION
+ struct wchan *cv_wchan;
+ // END SOLUTION
};
struct cv *cv_create(const char *name);
diff --git a/kern/include/syscall.h b/kern/include/syscall.h
index befd3d8..851842a 100644
--- a/kern/include/syscall.h
+++ b/kern/include/syscall.h
@@ -58,4 +58,32 @@ void enter_new_process(int argc, userptr_t argv, vaddr_t stackptr,
int sys_reboot(int code);
int sys___time(userptr_t user_seconds, userptr_t user_nanoseconds);
+/*
+ * I/O syscalls
+ */
+
+int sys_open( userptr_t, int, int *);
+int sys_close( int );
+int sys_write( int, userptr_t, size_t, int * );
+int sys_read( int, userptr_t, size_t, int * );
+int sys___getcwd( userptr_t, size_t, int * );
+int sys_lseek( int, off_t, int, int64_t * );
+int sys_dup2( int, int, int * );
+int sys_chdir( userptr_t );
+
+/**
+ * Process System Calls
+ */
+
+int sys_getpid( int * );
+void sys__exit( int );
+int sys_waitpid( int, userptr_t, int, int * );
+int sys_fork( struct trapframe *, int * );
+int sys_execv( userptr_t, userptr_t );
+
+/**
+ * Kernel versions of the system calls.
+ */
+int ___waitpid( int, int *, int );
+
#endif /* _SYSCALL_H_ */
diff --git a/kern/include/thread.h b/kern/include/thread.h
index 86706ca..373453d 100644
--- a/kern/include/thread.h
+++ b/kern/include/thread.h
@@ -38,6 +38,7 @@
#include <spinlock.h>
#include <threadlist.h>
+#include <proc.h>
struct addrspace;
struct cpu;
@@ -111,7 +112,8 @@ struct thread {
/* VFS */
struct vnode *t_cwd; /* current working directory */
- /* add more here as needed */
+ /* modifications for ASST2 */
+ struct proc *td_proc; /* process associated with this thread */
};
/* Call once during system startup to allocate data structures. */
diff --git a/kern/include/version.h b/kern/include/version.h
index 9fec0e8..4734a98 100644
--- a/kern/include/version.h
+++ b/kern/include/version.h
@@ -35,6 +35,7 @@
* code we gave you.
*/
#define BASE_VERSION "1.99.05"
+#define ASST1SOL_VERSION "1.0"
/*
* Change this as you see fit in the course of hacking the system.
diff --git a/kern/io/file.c b/kern/io/file.c
new file mode 100644
index 0000000..299773c
--- /dev/null
+++ b/kern/io/file.c
@@ -0,0 +1,144 @@
+#include <types.h>
+#include <lib.h>
+#include <kern/errno.h>
+#include <file.h>
+#include <vfs.h>
+
+/*
+ * create a new file associated with the given vnode/flags
+ */
+int
+file_create( struct vnode *vn, int flags, struct file **f ) {
+ struct file *res;
+
+ res = kmalloc( sizeof( struct file ) );
+ if( res == NULL )
+ return ENOMEM;
+
+ //fill the basic fields
+ res->f_oflags = flags;
+ res->f_refcount = 0;
+ res->f_vnode = vn;
+ res->f_offset = 0;
+
+ //attempt to create the lock
+ res->f_lk = lock_create( "f_lk" );
+ if( res->f_lk == NULL ) {
+ kfree( res );
+ return ENOMEM;
+ }
+
+ *f = res;
+ return 0;
+}
+
+/*
+ * destroy the given file.
+ * otherwise, lock_destroy will fail.
+ */
+void
+file_destroy( struct file *f ) {
+ //make sure we are not destroying something that is being used
+ KASSERT( f->f_refcount == 0 );
+
+ //close the associated vnode
+ vfs_close( f->f_vnode );
+
+ //release and destroy the lock
+ lock_destroy( f->f_lk );
+
+ //free the memory
+ kfree( f );
+}
+
+/**
+ * close the file given by the descriptor
+ */
+int
+file_close_descriptor( struct proc *p, int fd ) {
+ struct file *f = NULL;
+ int err = 0;
+
+ err = file_get( p, fd, &f );
+ if( err )
+ return err;
+
+ //make sure there are programs using it
+ KASSERT( f->f_refcount > 0 );
+
+ //detach from the file descriptor table
+ fd_detach( p->p_fd, fd );
+
+ //decrease both refcounts
+ f->f_refcount--;
+ VOP_DECREF( f->f_vnode );
+
+ //destroy if we are the only ones using it
+ if( f->f_refcount == 0 ) {
+ F_UNLOCK( f );
+ file_destroy( f );
+ return 0;
+ }
+
+ //unlock the file
+ F_UNLOCK( f );
+ return 0;
+}
+
+
+/**
+ * find and return the file associated with the filedescriptor
+ * inside the process. it will be returned locked.
+ */
+int
+file_get(struct proc *p, int fd, struct file **f ) {
+ if( fd >= MAX_OPEN_FILES || fd < 0 )
+ return EBADF;
+
+ FD_LOCK( p->p_fd );
+ if( p->p_fd->fd_ofiles[fd] != NULL ) {
+ *f = p->p_fd->fd_ofiles[fd];
+ FD_UNLOCK( p->p_fd );
+ return 0;
+ }
+
+ FD_UNLOCK( p->p_fd );
+ return EBADF;
+}
+
+/**
+ * Checks whether the given file descriptor exists in the table.
+ */
+bool
+file_descriptor_exists( struct proc *p, int fd ) {
+ bool exists = false;
+
+ FD_LOCK( p->p_fd );
+ if( p->p_fd->fd_ofiles[fd] != NULL )
+ exists = true;
+ FD_UNLOCK( p->p_fd );
+
+ return exists;
+}
+
+/**
+ * close all open files associated with the given process.
+ */
+int
+file_close_all( struct proc *p ) {
+ int i = 0;
+ int err;
+
+ FD_LOCK( p->p_fd );
+ for( i = 0; i < MAX_OPEN_FILES; ++i ) {
+ if( p->p_fd->fd_ofiles[i] != NULL ) {
+ FD_UNLOCK( p->p_fd );
+ err = file_close_descriptor( p, i );
+ if( err )
+ return -1;
+ FD_LOCK( p->p_fd );
+ }
+ }
+ FD_UNLOCK( p->p_fd );
+ return 0;
+}
diff --git a/kern/io/filedesc.c b/kern/io/filedesc.c
new file mode 100644
index 0000000..2e59a07
--- /dev/null
+++ b/kern/io/filedesc.c
@@ -0,0 +1,133 @@
+#include <types.h>
+#include <lib.h>
+#include <kern/errno.h>
+#include <filedesc.h>
+
+//scan over the given file descriptor table
+//and store the file in a free location, if possible
+int
+fd_attach( struct filedesc *fdesc, struct file *f, int *fd ) {
+ int i = 0;
+
+ //lock the file descriptor table
+ FD_LOCK( fdesc );
+
+ //for each possible spot
+ for( i = 0; i < MAX_OPEN_FILES; ++i ) {
+ if( fdesc->fd_ofiles[i] == NULL ) {
+ fdesc->fd_ofiles[i] = f;
+ fdesc->fd_nfiles++;
+ *fd = i;
+ FD_UNLOCK( fdesc );
+ return 0;
+ }
+ }
+ FD_UNLOCK( fdesc );
+ return ENFILE;
+}
+
+//detaches the given filedescriptor from the given filetable
+void
+fd_detach( struct filedesc *fdesc, int fd ) {
+ FD_LOCK( fdesc );
+ fdesc->fd_ofiles[fd] = NULL;
+ fdesc->fd_nfiles--;
+ FD_UNLOCK( fdesc );
+}
+
+//destroy the given filedescriptor table
+void
+fd_destroy( struct filedesc *fdesc ) {
+ KASSERT( fdesc->fd_nfiles == 0 );
+ lock_destroy( fdesc->fd_lk );
+ kfree( fdesc );
+}
+
+//create a new filedescriptor tabke
+int
+fd_create( struct filedesc **fdesc ) {
+ struct filedesc *fd = NULL;
+ int i = 0;
+
+ fd = kmalloc( sizeof( struct filedesc ) );
+ if( fd == NULL )
+ return ENOMEM;
+
+ //create the lock
+ fd->fd_lk = lock_create( "fd_lk" );
+ if( fd->fd_lk == NULL ) {
+ kfree( fd );
+ return ENOMEM;
+ }
+
+ //initialize all open files
+ for( i = 0; i < MAX_OPEN_FILES; ++i )
+ fd->fd_ofiles[i] = NULL;
+
+ //initially we have no open files.
+ fd->fd_nfiles = 0;
+
+ //we are good to go
+ *fdesc = fd;
+ return 0;
+}
+
+/**
+ * attaches a given filehandle inside a specific location
+ * on the filetable.
+ */
+int
+fd_attach_into( struct filedesc *fdesc, struct file *f, int fd ) {
+ FD_LOCK( fdesc );
+
+ //if the file-table already contains something
+ //inside the desired spot, we cannot continue.
+ if( fdesc->fd_ofiles[fd] != NULL ) {
+ FD_UNLOCK( fdesc );
+ return EMFILE;
+ }
+
+ fdesc->fd_ofiles[fd] = f;
+ fdesc->fd_nfiles++;
+
+ FD_UNLOCK( fdesc );
+ return 0;
+}
+
+/**
+ * clone a filedescriptor table into another.
+ */
+void
+fd_clone( struct filedesc *source, struct filedesc *fdesc ) {
+ struct file *f = NULL;
+ int i = 0;
+
+ //lock the source file-descriptor table.
+ //and for each file, we copy its pointer into the new table.
+ FD_LOCK( source );
+ for( i = 0; i < MAX_OPEN_FILES; ++i ) {
+ if( source->fd_ofiles[i] != NULL ) {
+ //lock the file for atomicity.
+ //this ensures nobody is writing things out while we are transfering.
+ f = source->fd_ofiles[i];
+ F_LOCK( f );
+ fdesc->fd_ofiles[i] = f;
+ fdesc->fd_nfiles++;
+
+ //at this point we also update the file's
+ //reference count.
+ f->f_refcount++;
+ VOP_INCREF( f->f_vnode );
+
+ //we are done with it.
+ F_UNLOCK( f );
+ }
+ }
+
+ //for sakeness, both file-descriptor tables
+ //must have the same number of open files.
+ KASSERT( source->fd_nfiles == fdesc->fd_nfiles );
+
+ //unlock.
+ FD_UNLOCK( source );
+}
diff --git a/kern/proc/proc.c b/kern/proc/proc.c
new file mode 100644
index 0000000..66b354e
--- /dev/null
+++ b/kern/proc/proc.c
@@ -0,0 +1,285 @@
+#include <types.h>
+#include <lib.h>
+#include <kern/errno.h>
+#include <proc.h>
+
+struct proc *allproc[MAX_PROCESSES];
+struct lock *lk_allproc;
+struct lock *lk_exec;
+int next_pid;
+
+/**
+ * add the given function to the allproc array.
+ */
+static
+void
+proc_add_to_allproc( struct proc *p, int spot ) {
+ lock_acquire( lk_allproc );
+
+ KASSERT( allproc[spot] == (void *)PROC_RESERVED_SPOT );
+ allproc[spot] = p;
+ lock_release( lk_allproc );
+}
+
+/**
+ * quick function that simply a spot as reserved,
+ * stores the index of the spot inside the pid
+ * and unlocks allproc.
+ */
+static
+void
+proc_found_spot( pid_t *pid, int spot ) {
+ //if we are being called, allproc[spot] must be null.
+ KASSERT( allproc[spot] == NULL );
+
+ //mark it as reserved.
+ allproc[spot] = (void *)PROC_RESERVED_SPOT;
+ *pid = spot;
+
+ //adjust next_pid to be the one just given.
+ next_pid = spot + 1;
+
+ //release the lock
+ lock_release( lk_allproc );
+}
+
+/**
+ * attempts to allocate a new pid by looping
+ * through allproc, starting from next_pid, until finding an empty spot.
+ * once a spot is given, it is marked as reserved.
+ */
+static
+int
+proc_alloc_pid( pid_t *pid ) {
+ int i = 0;
+
+ //lock allproc, to guarantee atomicity
+ lock_acquire( lk_allproc );
+
+ //if the next_pid is greater than the maximum number of processes
+ //we need to start failing pid allocations.
+ if( next_pid >= MAX_PROCESSES )
+ next_pid = 0;
+
+
+ //first we loop until the end, starting from lastpid.
+ for( i = next_pid; i < MAX_PROCESSES; ++i ) {
+ if( allproc[i] == NULL ) {
+ proc_found_spot( pid, i );
+ return 0;
+ }
+ }
+
+ //now we loop from the beginning until next_pid
+ for( i = 0; i < next_pid; ++i ) {
+ if( allproc[i] == NULL ) {
+ proc_found_spot( pid, i );
+ return 0;
+ }
+ }
+
+ //we couldn't find a spot, so we have to fail.
+ lock_release( lk_allproc );
+ return ENPROC;
+}
+
+/**
+ * de-allocates the given pid.
+ */
+static
+void
+proc_dealloc_pid( pid_t pid ) {
+ //lock for atomicity.
+ lock_acquire( lk_allproc );
+
+ //it cannot be null, it must be either reserved or fulfilled.
+ KASSERT( allproc[pid] != NULL );
+ allproc[pid] = NULL;
+
+ //unlock and be done.
+ lock_release( lk_allproc );
+}
+
+int
+proc_create( struct proc **res ) {
+ struct proc *p = NULL;
+ int err = 0;
+ pid_t pid;
+
+ //first, attempt to allocate a pid.
+ err = proc_alloc_pid( &pid );
+ if( err )
+ return err;
+
+ //alloc memory for the structure
+ p = kmalloc( sizeof( struct proc ) );
+ if( p == NULL ) {
+ proc_dealloc_pid( pid );
+ return ENOMEM;
+ }
+
+ //associcate it with the pid.
+ p->p_pid = pid;
+
+ //create the filedescriptor table
+ err = fd_create( &p->p_fd );
+ if( err ) {
+ kfree( p );
+ proc_dealloc_pid( pid );
+ return err;
+ }
+
+ //create the lock
+ p->p_lk = lock_create( "p_lk" );
+ if( p->p_lk == NULL ) {
+ fd_destroy( p->p_fd );
+ kfree( p );
+ proc_dealloc_pid( pid );
+ return ENOMEM;
+ }
+
+ //create the semaphore
+ p->p_sem = sem_create( "p_sem", 0 );
+ if( p->p_sem == NULL ) {
+ lock_destroy( p->p_lk );
+ fd_destroy( p->p_fd );
+ kfree( p );
+ proc_dealloc_pid( pid );
+ return ENOMEM;
+ }
+
+ //adjust static information
+ p->p_retval = 0;
+ p->p_is_dead = false;
+ p->p_nsyscalls = 0;
+ p->p_nice = 0;
+ p->p_proc = NULL;
+
+ //add to the list of allproc
+ proc_add_to_allproc( p, pid );
+
+ *res = p;
+ return 0;
+}
+
+/**
+ * clone a process into a new process.
+ */
+int
+proc_clone( struct proc *source, struct proc **target ) {
+ struct proc *p = NULL;
+ int err;
+
+ //try to create the process.
+ err = proc_create( &p );
+ if( err )
+ return err;
+
+ //clone all the files from the source file-descriptor table
+ //into the childs file-descriptor table.
+ fd_clone( source->p_fd, p->p_fd );
+
+ //we are done, simply copy the new proc
+ //into the given pointer.
+ *target = p;
+
+ return 0;
+}
+/**
+ * destroy a given process.
+ */
+void
+proc_destroy( struct proc *p ) {
+ pid_t pid;
+
+ //copy the pid for later used.
+ pid = p->p_pid;
+
+ //destroy the cv
+ sem_destroy( p->p_sem );
+
+ //destroy the lock associated with it.
+ lock_destroy( p->p_lk );
+
+ //destroy the filedescriptor table
+ fd_destroy( p->p_fd );
+
+ //free the memory.
+ kfree( p );
+
+ //deallocate the pid.
+ proc_dealloc_pid( pid );
+}
+
+/**
+ * initialize the proc-system
+ */
+void
+proc_system_init( void ) {
+ int i = 0;
+
+ //initialize the array
+ for( i = 0; i < MAX_PROCESSES; ++i ) {
+ allproc[i] = NULL;
+ }
+
+ //create the lock
+ lk_allproc = lock_create( "lk_allproc" );
+ if( lk_allproc == NULL )
+ panic( "could not initialize proc system." );
+
+ //create the lock protecting exec args
+ lk_exec = lock_create( "lk_exec" );
+ if( lk_exec == NULL ) {
+ lock_destroy( lk_allproc );
+ panic( "could not create lk_exec." );
+ }
+ //set last pid to be 0.
+ next_pid = 0;
+}
+
+/**
+ * find a process inside allproc.
+ * if it exists, return it locked.
+ */
+int
+proc_get( pid_t pid, struct proc **res ) {
+ //invalid pid.
+ if( pid >= MAX_PROCESSES || pid <= 0 )
+ return EINVAL;
+
+ //lock allproc.
+ lock_acquire( lk_allproc );
+
+ //if the requested pid is associated with a valid process
+ if( allproc[pid] != NULL && allproc[pid] != (void *)PROC_RESERVED_SPOT ) {
+ PROC_LOCK( allproc[pid] );
+ *res = allproc[pid];
+ lock_release( lk_allproc );
+ return 0;
+ }
+
+ //the requested pid is actually invalid.
+ lock_release( lk_allproc );
+ return ESRCH;
+
+}
+/**
+ * stress tests.
+ */
+void
+proc_test_pid_allocation() {
+ pid_t pid;
+ int err;
+ int i;
+
+ for( i = 0; i < 100; ++i ) {
+ err = proc_alloc_pid( &pid );
+ if( err )
+ panic( "failed to allocate a pid. pid allocation is broken.\n" );
+
+ kprintf( "awarded PID = %d\n", pid );
+ proc_dealloc_pid( pid );
+ }
+}
+
diff --git a/kern/startup/main.c b/kern/startup/main.c
index be4c4b8..535a864 100644
--- a/kern/startup/main.c
+++ b/kern/startup/main.c
@@ -96,7 +96,7 @@ boot(void)
*/
kprintf("\n");
- kprintf("OS/161 base system version %s\n", BASE_VERSION);
+ kprintf("OS/161 base version %s ASST1 solution version %s\n", BASE_VERSION, ASST1SOL_VERSION);
kprintf("%s", harvard_copyright);
kprintf("\n");
diff --git a/kern/startup/menu.c b/kern/startup/menu.c
index 6c71551..ec02bc3 100644
--- a/kern/startup/menu.c
+++ b/kern/startup/menu.c
@@ -39,11 +39,23 @@
#include <vfs.h>
#include <sfs.h>
#include <syscall.h>
+#include <kern/fcntl.h>
#include <test.h>
+#include <proc.h>
+#include <file.h>
+#include <current.h>
+
#include "opt-synchprobs.h"
#include "opt-sfs.h"
#include "opt-net.h"
+struct proc *p0;
+
+struct cmd_progthread_args {
+ char **args;
+ struct proc *p;
+};
+
/*
* In-kernel menu and command dispatcher.
*/
@@ -66,6 +78,40 @@ getinterval(time_t s1, uint32_t ns1, time_t s2, uint32_t ns2,
*rs = s2 - s1;
}
+//open the standard files.
+static
+int
+open_standard_files( struct proc *p ) {
+ int err = 0;
+ int retval;
+ char buf[32];
+
+ KASSERT( p != NULL );
+
+ strcpy( buf, "con:" );
+ err = ___open( p, buf, O_RDONLY, &retval );
+ if( err )
+ return err;
+
+ strcpy( buf, "con:" );
+ err = ___open( p, buf, O_WRONLY, &retval );
+ if( err ) {
+ file_close_all( p );
+ return err;
+ }
+
+ strcpy( buf, "con:" );
+ err = ___open( p, buf, O_WRONLY, &retval );
+ if( err ) {
+ file_close_all( p );
+ return err;
+ }
+
+ return 0;
+}
+
+
+
////////////////////////////////////////////////////////////
//
// Command menu functions
@@ -85,10 +131,11 @@ static
void
cmd_progthread(void *ptr, unsigned long nargs)
{
- char **args = ptr;
+ struct cmd_progthread_args *cargs = ptr;
+ char **args = cargs->args;
char progname[128];
int result;
-
+
KASSERT(nargs >= 1);
if (nargs > 2) {
@@ -99,11 +146,20 @@ cmd_progthread(void *ptr, unsigned long nargs)
KASSERT(strlen(args[0]) < sizeof(progname));
strcpy(progname, args[0]);
+
+ //attach the process to the current thread
+ curthread->td_proc = cargs->p;
+
+ //destroy the cargs struct
+ kfree( cargs );
result = runprogram(progname);
if (result) {
kprintf("Running program %s failed: %s\n", args[0],
strerror(result));
+
+ //exit with a failure.
+ sys__exit( -1 );
return;
}
@@ -127,21 +183,62 @@ int
common_prog(int nargs, char **args)
{
int result;
+ struct proc *p = NULL;
+ struct cmd_progthread_args *cargs = NULL;
+ pid_t pid;
+ int err;
#if OPT_SYNCHPROBS
kprintf("Warning: this probably won't work with a "
"synchronization-problems kernel.\n");
#endif
+
+ //attempt to create the first process.
+ result = proc_create( &p );
+ if( result ) {
+ kprintf( "common_prog: failed creating the first process." );
+ return result;
+ }
+
+ //store the pid.
+ pid = p->p_pid;
+
+ //open the standard files.
+ err = open_standard_files( p );
+ if( err ) {
+ proc_destroy( p );
+ return err;
+ }
+
+ //create the arguments
+ cargs = kmalloc( sizeof( struct cmd_progthread_args ) );
+ if( cargs == NULL ) {
+ file_close_all( p );
+ proc_destroy( p );
+ return ENOMEM;
+ }
+
+ //adjust the parent to reflect p0.
+ p->p_proc = p0;
+
+ cargs->args = args;
+ cargs->p = p;
result = thread_fork(args[0] /* thread name */,
cmd_progthread /* thread function */,
- args /* thread arg */, nargs /* thread arg */,
+ cargs /* thread arg */, nargs /* thread arg */,
NULL);
if (result) {
kprintf("thread_fork failed: %s\n", strerror(result));
+
+ file_close_all( p );
+ proc_destroy( p );
return result;
}
+ //wait for our chid to die.
+ ___waitpid( pid, &result, 0 );
+
return 0;
}
@@ -652,6 +749,32 @@ menu_execute(char *line, int isargs)
}
}
+static
+int
+proc0( struct proc **p0 ) {
+ struct proc *p = NULL;
+ int err;
+
+ //create the new process.
+ err = proc_create( &p );
+ if( err )
+ return err;
+
+ //open the standard files.
+ err = open_standard_files( p );
+ if( err ) {
+ proc_destroy( p );
+ return err;
+ }
+
+ //associate it with the current thread.
+ curthread->td_proc = p;
+
+ //we are good to go.
+ *p0 = p;
+ return 0;
+}
+
/*
* Command menu main loop.
*
@@ -673,8 +796,20 @@ void
menu(char *args)
{
char buf[64];
+ int err;
menu_execute(args, 1);
+
+ //initialize the proc system
+ proc_system_init();
+
+ //kickstart proc0.
+ err = proc0( &p0 );
+ if( err )
+ panic( "failed creating proc0." );
+
+ //test the proc allocation mechanism.
+ //proc_test_pid_allocation();
while (1) {
kprintf("OS/161 kernel [? for menu]: ");
diff --git a/kern/syscall/__getcwd.c b/kern/syscall/__getcwd.c
new file mode 100644
index 0000000..90bf9eb
--- /dev/null
+++ b/kern/syscall/__getcwd.c
@@ -0,0 +1,45 @@
+#include <types.h>
+#include <lib.h>
+#include <vfs.h>
+#include <kern/iovec.h>
+#include <uio.h>
+#include <kern/fcntl.h>
+#include <kern/errno.h>
+#include <vnode.h>
+#include <current.h>
+#include <syscall.h>
+
+int
+sys___getcwd( userptr_t ubuf, size_t ulen, int *retval ) {
+ struct uio ruio;
+ struct iovec iov;
+ int err;
+
+ KASSERT( curthread != NULL );
+ KASSERT( curthread->td_proc != NULL );
+
+ //prepare the uio
+ uio_kinit(
+ &iov,
+ &ruio,
+ ubuf,
+ ulen,
+ 0,
+ UIO_READ
+ );
+
+ //the given pointer lives in userland
+ ruio.uio_space = curthread->t_addrspace;
+ ruio.uio_segflg = UIO_USERSPACE;
+
+ //forward the call to vfs_getcwd
+ err = vfs_getcwd( &ruio );
+ if( err )
+ return err;
+
+ //set the return value to be the length of the
+ //data that was just read
+ *retval = ulen - ruio.uio_resid;
+ return 0;
+}
+
diff --git a/kern/syscall/_exit.c b/kern/syscall/_exit.c
new file mode 100644
index 0000000..ca1a787
--- /dev/null
+++ b/kern/syscall/_exit.c
@@ -0,0 +1,53 @@
+#include <types.h>
+#include <lib.h>
+#include <proc.h>
+#include <file.h>
+#include <filedesc.h>
+#include <thread.h>
+#include <current.h>
+#include <syscall.h>
+
+/**
+ * exit will perform the following tasks:
+ * 1. close all open files (through file_close_all())
+ * 2. set the given exit code inside the proc structure.
+ * 3. mark each child process as orphan.
+ * 4. if orphan, will destroy the proc associated with the current thread.
+ * 4.1 if not orphan, will signal the parent regarding our death.
+ * 5. call thread_exit() so we become a zombie.
+ */
+void
+sys__exit( int code ) {
+ struct proc *p = NULL;
+ int err;
+
+ KASSERT( curthread != NULL );
+ KASSERT( curthread->td_proc != NULL );
+
+ p = curthread->td_proc;
+
+ //close all open files.
+ err = file_close_all( p );
+ if( err )
+ panic( "problem closing a file." );
+
+ //lock so we can adjust the return value.
+ PROC_LOCK( p );
+ p->p_retval = code;
+ p->p_is_dead = true;
+
+ //if we are orphans ourselves, no one is interested
+ //in our return code, so we simply destroy ourselves.
+ if( p->p_proc == NULL ) {
+ PROC_UNLOCK( p );
+ proc_destroy( p );
+ }
+ else {
+ //signal that we are done.
+ V( p->p_sem );
+ PROC_UNLOCK( p );
+ }
+
+ //all that is left now is to kill our thread.
+ thread_exit();
+}
diff --git a/kern/syscall/chdir.c b/kern/syscall/chdir.c
new file mode 100644
index 0000000..db7d113
--- /dev/null
+++ b/kern/syscall/chdir.c
@@ -0,0 +1,43 @@
+#include <types.h>
+#include <lib.h>
+#include <copyinout.h>
+#include <kern/iovec.h>
+#include <kern/fcntl.h>
+#include <uio.h>
+#include <vfs.h>
+#include <thread.h>
+#include <current.h>
+#include <syscall.h>
+
+#define MAX_DIR_LEN 128
+
+int
+sys_chdir( userptr_t ubuf ) {
+ char kbuf[MAX_DIR_LEN];
+ int err;
+ struct vnode *v_dir = NULL;
+
+ KASSERT( curthread != NULL );
+ KASSERT( curthread->td_proc != NULL );
+
+ //copy the buffer from userland into kernel land.
+ err = copyinstr( ubuf, kbuf, sizeof( kbuf ), NULL );
+ if( err )
+ return err;
+
+ //try to open the directory pointed by the path.
+ err = vfs_open( kbuf, O_RDONLY, 0644, &v_dir );
+ if( err )
+ return err;
+
+ //change the current directory.
+ err = vfs_setcurdir( v_dir );
+
+ //close the vnode.
+ vfs_close( v_dir );
+
+ if( err )
+ return err;
+
+ return 0;
+}
diff --git a/kern/syscall/close.c b/kern/syscall/close.c
new file mode 100644
index 0000000..aba47a3
--- /dev/null
+++ b/kern/syscall/close.c
@@ -0,0 +1,21 @@
+#include <types.h>
+#include <lib.h>
+#include <kern/errno.h>
+#include <syscall.h>
+#include <file.h>
+#include <copyinout.h>
+#include <current.h>
+
+/**
+ * close() system call
+ */
+int
+sys_close( int fd ) {
+ struct proc *p = NULL;
+
+ KASSERT( curthread != NULL );
+ KASSERT( curthread->td_proc != NULL );
+
+ p = curthread->td_proc;
+ return file_close_descriptor( p, fd );
+}
diff --git a/kern/syscall/dup2.c b/kern/syscall/dup2.c
new file mode 100644
index 0000000..311b47d
--- /dev/null
+++ b/kern/syscall/dup2.c
@@ -0,0 +1,61 @@
+#include <types.h>
+#include <lib.h>
+#include <kern/errno.h>
+#include <filedesc.h>
+#include <file.h>
+#include <current.h>
+#include <syscall.h>
+
+int
+sys_dup2( int oldfd, int newfd, int *retval ) {
+ struct proc *p = NULL;
+ struct file *f_old = NULL;
+ struct file *f_new = NULL;
+ int err;
+
+ KASSERT( curthread != NULL );
+ KASSERT( curthread->td_proc != NULL );
+
+ p = curthread->td_proc;
+
+ //make sure both file handles are valid
+ if( (oldfd < 0 || newfd < 0) || (newfd >= MAX_OPEN_FILES) )
+ return EBADF;
+
+ //get the old file
+ err = file_get( p, oldfd, &f_old );
+ if( err )
+ return EBADF;
+
+ //if the new-file already exists
+ //we must close it.
+ if( file_descriptor_exists( p, newfd ) ) {
+ err = file_close_descriptor( p, newfd );
+ //if we had a problem closing, dup2()
+ //cannot continue running.
+ if( err ) {
+ F_UNLOCK( f_old );
+ return err;
+ }
+ }
+
+ //at this point, we simply copy the pointers.
+ f_new = f_old;
+
+ //attach f_new into newfd
+ err = fd_attach_into( p->p_fd, f_new, newfd );
+ if( err ) {
+ F_UNLOCK( f_old );
+ return err;
+ }
+
+ //increase the reference count.
+ f_old->f_refcount++;
+ VOP_INCREF(f_old->f_vnode);
+
+ //unlock and return
+ F_UNLOCK( f_old );
+ *retval = newfd;
+
+ return 0;
+}
diff --git a/kern/syscall/execv.c b/kern/syscall/execv.c
new file mode 100644
index 0000000..0d3e7d7
--- /dev/null
+++ b/kern/syscall/execv.c
@@ -0,0 +1,295 @@
+#include <types.h>
+#include <lib.h>
+#include <copyinout.h>
+#include <proc.h>
+#include <thread.h>
+#include <current.h>
+#include <filedesc.h>
+#include <addrspace.h>
+#include <kern/fcntl.h>
+#include <limits.h>
+#include <kern/errno.h>
+#include <machine/trapframe.h>
+#include <synch.h>
+#include <vfs.h>
+#include <syscall.h>
+
+#ifndef NARG_MAX
+#define NARG_MAX 1024
+#endif
+
+static char karg[ARG_MAX];
+static unsigned char kargbuf[ARG_MAX];
+
+#define MAX_PROG_NAME 32
+
+/**
+ * given a string, and an align parameter
+ * this function will align its length (by appending zero) to match the required alignment.
+ */
+static
+int
+align_arg( char arg[ARG_MAX], int align ) {
+ char *p = arg;
+ int len = 0;
+ int diff;
+
+ while( *p++ != '\0' )
+ ++len;
+
+ if( ++len % align == 0 )
+ return len;
+
+ diff = align - ( len % align );
+ while( diff-- ) {
+ *(++p) = '\0';
+ ++len;
+ }
+
+ return len;
+}
+
+/**
+ * return the nearest length aligned to alignment.
+ */
+static
+int
+get_aligned_length( char arg[ARG_MAX], int alignment ) {
+ char *p = arg;
+ int len = 0;
+
+ while( *p++ != '\0' )
+ ++len;
+
+ if( ++len % 4 == 0 )
+ return len;
+
+ return len + (alignment - ( len % alignment ) );
+}
+
+static
+int
+copy_args( userptr_t uargs, int *nargs, int *buflen ) {
+ int i = 0;
+ int err;
+ int nlast = 0;
+ char *ptr;
+ unsigned char *p_begin = NULL;
+ unsigned char *p_end = NULL;
+ uint32_t offset;
+ uint32_t last_offset;
+
+ //check whether we got a valid pointer.
+ if( uargs == NULL )
+ return EFAULT;
+
+ //initialize the numbe of arguments and the buffer size
+ *nargs = 0;
+ *buflen = 0;
+
+ //copy-in kargs.
+ i = 0;
+ while( ( err = copyin( (userptr_t)uargs + i * 4, &ptr, sizeof( ptr ) ) ) == 0 ) {
+ if( ptr == NULL )
+ break;
+ err = copyinstr( (userptr_t)ptr, karg, sizeof( karg ), NULL );
+ if( err )
+ return err;
+
+ ++i;
+ *nargs += 1;
+ *buflen += get_aligned_length( karg, 4 ) + sizeof( char * );
+ }
+
+ //if there is a problem, and we haven't read a single argument
+ //that means the given user argument pointer is invalid.
+ if( i == 0 && err )
+ return err;
+
+ //account for NULL also.
+ *nargs += 1;
+ *buflen += sizeof( char * );
+
+
+ //loop over the arguments again, building karbuf.
+ i = 0;
+ p_begin = kargbuf;
+ p_end = kargbuf + (*nargs * sizeof( char * ));
+ nlast = 0;
+ last_offset = *nargs * sizeof( char * );
+ while( ( err = copyin( (userptr_t)uargs + i * 4, &ptr, sizeof( ptr ) ) ) == 0 ) {
+ if( ptr == NULL )
+ break;
+ err = copyinstr( (userptr_t)ptr, karg, sizeof( karg ), NULL );
+ if( err )
+ return err;
+
+ offset = last_offset + nlast;
+ nlast = align_arg( karg, 4 );
+
+ //copy the integer into 4 bytes.
+ *p_begin = offset & 0xff;
+ *(p_begin + 1) = (offset >> 8) & 0xff;
+ *(p_begin + 2) = (offset >> 16) & 0xff;
+ *(p_begin + 3) = (offset >> 24) & 0xff;
+
+ //copy the string the buffer.
+ memcpy( p_end, karg, nlast );
+ p_end += nlast;
+
+ //advance p_begin by 4 bytes.
+ p_begin += 4;
+
+ //adjust last offset
+ last_offset = offset;
+ ++i;
+ }
+
+ //set the NULL pointer (i.e., it takes 4 zero bytes.)
+ *p_begin = 0;
+ *(p_begin+1) = 0;
+ *(p_begin+2) = 0;
+ *(p_begin+3) = 0;
+
+ return 0;
+}
+
+static
+int
+adjust_kargbuf( int nparams, vaddr_t stack_ptr ) {
+ int i;
+ uint32_t new_offset = 0;
+ uint32_t old_offset = 0;
+ int index;
+
+ for( i = 0; i < nparams-1; ++i ) {
+ index = i * sizeof( char * );
+ //read the old offset.
+ old_offset = (( 0xFF & kargbuf[index+3] ) << 24) | (( 0xFF & kargbuf[index+2]) << 16) |
+ (( 0xFF & kargbuf[index+1]) << 8) | (0xFF & kargbuf[index]);
+
+ //calculate the new offset
+ new_offset = stack_ptr + old_offset;
+
+ //store it instead of the old one.
+ memcpy( kargbuf + index, &new_offset, sizeof( int ) );
+ }
+
+ return 0;
+}
+
+int
+sys_execv( userptr_t upname, userptr_t uargs ) {
+ struct addrspace *as_new = NULL;
+ struct addrspace *as_old = NULL;
+ struct vnode *vn = NULL;
+ vaddr_t entry_ptr;
+ vaddr_t stack_ptr;
+ int err;
+ char kpname[MAX_PROG_NAME];
+ int nargs;
+ int buflen;
+
+ KASSERT( curthread != NULL );
+ KASSERT( curthread->td_proc != NULL );
+
+ (void)uargs;
+
+ //lock the execv args
+ lock_acquire( lk_exec );
+
+ //copy the old addrspace just in case.
+ as_old = curthread->t_addrspace;
+
+ //copyin the program name.
+ err = copyinstr( upname, kpname, sizeof( kpname ), NULL );
+ if( err ) {
+ lock_release( lk_exec );
+ return err;
+ }
+
+ //try to open the given executable.
+ err = vfs_open( kpname, O_RDONLY, 0, &vn );
+ if( err ) {
+ lock_release( lk_exec );
+ return err;
+ }
+
+ //copy the arguments into the kernel buffer.
+ err = copy_args( uargs, &nargs, &buflen );
+ if( err ) {
+ lock_release( lk_exec );
+ vfs_close( vn );
+ return err;
+ }
+
+ //create the new addrspace.
+ as_new = as_create();
+ if( as_new == NULL ) {
+ lock_release( lk_exec );
+ vfs_close( vn );
+ return ENOMEM;
+ }
+
+ //activate the new addrspace.
+ as_activate( as_new );
+
+ //temporarily switch the addrspaces.
+ curthread->t_addrspace = as_new;
+
+ //load the elf executable.
+ err = load_elf( vn, &entry_ptr );
+ if( err ) {
+ curthread->t_addrspace = as_old;
+ as_destroy( as_new );
+ vfs_close( vn );
+ lock_release( lk_exec );
+ return err;
+ }
+
+ //create a stack for the new addrspace.
+ err = as_define_stack( as_new, &stack_ptr );
+ if( err ) {
+ curthread->t_addrspace = as_old;
+ as_destroy( as_new );
+ vfs_close( vn );
+ lock_release( lk_exec );
+ return err;
+ }
+
+ //adjust the stackptr to reflect the change
+ stack_ptr -= buflen;
+ err = adjust_kargbuf( nargs, stack_ptr );
+ if( err ) {
+ curthread->t_addrspace = as_old;
+ as_destroy( as_new );
+ vfs_close( vn );
+ lock_release( lk_exec );
+ return err;
+ }
+
+ //copy the arguments into the new user stack.
+ err = copyout( kargbuf, (userptr_t)stack_ptr, buflen );
+ if( err ) {
+ curthread->t_addrspace = as_old;
+ as_destroy( as_new );
+ vfs_close( vn );
+ lock_release( lk_exec );
+ return err;
+ }
+
+ //reelase lk_exec
+ lock_release( lk_exec );
+
+ //no need for it anymore.
+ vfs_close( vn );
+
+ //we are good to go.
+ as_destroy( as_old );
+
+ //off we go to userland.
+ enter_new_process( nargs-1, (userptr_t)stack_ptr, stack_ptr, entry_ptr );
+
+ panic( "execv: we should not be here." );
+ return EINVAL;
+}
diff --git a/kern/syscall/fork.c b/kern/syscall/fork.c
new file mode 100644
index 0000000..043f740
--- /dev/null
+++ b/kern/syscall/fork.c
@@ -0,0 +1,170 @@
+#include <types.h>
+#include <lib.h>
+#include <copyinout.h>
+#include <proc.h>
+#include <thread.h>
+#include <current.h>
+#include <filedesc.h>
+#include <addrspace.h>
+#include <kern/errno.h>
+#include <machine/trapframe.h>
+#include <synch.h>
+#include <syscall.h>
+
+/**
+ * structure containings all the information
+ * necessary for the child fork thread.
+ */
+struct child_fork_args {
+ struct addrspace *as_source;
+ struct proc *td_proc;
+ struct trapframe *tf;
+};
+
+
+/**
+ * this is the first function that gets executed
+ * when the child thread runs.
+ */
+static
+void
+fork_child_return( void *v_args, unsigned long not_used ) {
+ struct child_fork_args *args = NULL;
+ struct trapframe tf;
+
+ (void)not_used;
+
+ //cast to something we can work with.
+ args = v_args;
+
+ //the return value of fork() for the child is 0.
+ args->tf->tf_v0 = 0;
+ args->tf->tf_a3 = 0;
+
+ //skip to the next instruction to avoid fork()ing again.
+ args->tf->tf_epc += 4;
+
+ //make the current thread aware of its process.
+ curthread->td_proc = args->td_proc;
+
+ //set the current addrspace.
+ curthread->t_addrspace = args->as_source;
+
+ //active its addrspace.
+ as_activate( curthread->t_addrspace );
+
+ //copy from kernel stack into user stack.
+ memcpy( &tf, args->tf, sizeof( struct trapframe ) );
+
+ //clean-up the arguments passed by fork().
+ kfree( args->tf );
+ kfree( args );
+
+ //tell our parent we are OK.
+ V( curthread->td_proc->p_sem );
+
+ //off we go to usermode.
+ mips_usermode( &tf );
+}
+
+/**
+ * helper function to clone a trapframe.
+ */
+static
+int
+trapframe_clone( struct trapframe *tf, struct trapframe **newtf ) {
+ *newtf = kmalloc( sizeof( struct trapframe ) );
+ if( *newtf == NULL )
+ return ENOMEM;
+
+ memcpy( *newtf, tf, sizeof( struct trapframe ) );
+ return 0;
+}
+
+int
+sys_fork( struct trapframe *tf, int *retval ) {
+ struct proc *p_new = NULL;
+ struct trapframe *tf_new = NULL;
+ int err;
+ struct child_fork_args *args;
+ pid_t pid;
+
+ KASSERT( curthread != NULL );
+ KASSERT( curthread->td_proc != NULL );
+ KASSERT( tf != NULL );
+
+ //attempt to create a clone.
+ err = proc_clone( curthread->td_proc, &p_new );
+ if( err )
+ return err;
+
+ //hold on to the pid.
+ pid = p_new->p_pid;
+
+ //set the parent process of the new process to be us.
+ p_new->p_proc = curthread->td_proc;
+
+ //clone the trapframe.
+ err = trapframe_clone( tf, &tf_new );
+ if( err ) {
+ file_close_all( p_new );
+ proc_destroy( p_new );
+ return err;
+ }
+
+ //prepare the arguments for the fork child thread.
+ args = kmalloc( sizeof( struct child_fork_args ) );
+ if( args == NULL ) {
+ kfree( tf_new );
+ file_close_all( p_new );
+ proc_destroy( p_new );
+ return ENOMEM;
+ }
+
+ //copy the trapframe and wrapping proc
+ //into the args structure.
+ args->tf = tf_new;
+ args->td_proc = p_new;
+
+ //copy the addresspace.
+ err = as_copy( curthread->t_addrspace, &args->as_source );
+ if( err ) {
+ //clean after ourselves.
+ kfree( args->tf );
+ kfree( args );
+
+ file_close_all( p_new );
+ proc_destroy( p_new );
+ return err;
+ }
+
+ //finalize the creation of the thread.
+ err = thread_fork(
+ curthread->t_name,
+ fork_child_return,
+ args,
+ 0,
+ NULL
+ );
+
+ //oh well, thread creation failed.
+ //make sure we clean-up after ourselves.
+ if( err ) {
+ as_destroy( args->as_source );
+ kfree( args->tf );
+ kfree( args );
+
+ //close all possible files open by the proc.
+ file_close_all( p_new );
+ proc_destroy( p_new );
+ return err;
+ }
+
+
+ //wait for the child to tell us things are ok.
+ P( p_new->p_sem );
+
+ //parent returns with no errors.
+ *retval = pid;
+ return 0;
+}
diff --git a/kern/syscall/getpid.c b/kern/syscall/getpid.c
new file mode 100644
index 0000000..ba52560
--- /dev/null
+++ b/kern/syscall/getpid.c
@@ -0,0 +1,19 @@
+#include <types.h>
+#include <lib.h>
+#include <proc.h>
+#include <thread.h>
+#include <current.h>
+#include <syscall.h>
+
+int
+sys_getpid( int *retval ) {
+ KASSERT( curthread != NULL );
+ KASSERT( curthread->td_proc != NULL );
+
+ PROC_LOCK( curthread->td_proc );
+ *retval = curthread->td_proc->p_pid;
+ PROC_UNLOCK( curthread->td_proc );
+
+ return 0;
+}
+
diff --git a/kern/syscall/lseek.c b/kern/syscall/lseek.c
new file mode 100644
index 0000000..5b26e8a
--- /dev/null
+++ b/kern/syscall/lseek.c
@@ -0,0 +1,72 @@
+#include <types.h>
+#include <lib.h>
+#include <proc.h>
+#include <file.h>
+#include <vnode.h>
+#include <vfs.h>
+#include <stat.h>
+#include <kern/fcntl.h>
+#include <kern/seek.h>
+#include <kern/errno.h>
+#include <current.h>
+#include <syscall.h>
+
+int
+sys_lseek( int fd, off_t offset, int whence, int64_t *retval ) {
+ struct proc *p = NULL;
+ struct file *f = NULL;
+ int err;
+ struct stat st;
+ off_t new_offset;
+
+ KASSERT( curthread != NULL );
+ KASSERT( curthread->td_proc != NULL );
+
+ p = curthread->td_proc;
+
+ //try to open the file
+ err = file_get( p, fd, &f );
+ if( err )
+ return err;
+
+ //depending on whence, seek to appropriate location
+ switch( whence ) {
+ case SEEK_SET:
+ new_offset = offset;
+ break;
+
+ case SEEK_CUR:
+ new_offset = f->f_offset + offset;
+ break;
+
+ case SEEK_END:
+ //if it is SEEK_END, we use VOP_STAT to figure out
+ //the size of the file, and set the offset to be that size.
+ err = VOP_STAT( f->f_vnode, &st );
+ if( err ) {
+ F_UNLOCK( f );
+ return err;
+ }
+
+ //set the offet to the filesize.
+ new_offset = st.st_size + offset;
+ break;
+ default:
+ F_UNLOCK( f );
+ return EINVAL;
+ }
+
+ //use VOP_TRYSEEK to verify whether the desired
+ //seeking location is proper.
+ err = VOP_TRYSEEK( f->f_vnode, new_offset );
+ if( err ) {
+ F_UNLOCK( f );
+ return err;
+ }
+
+ //adjust the seek.
+ f->f_offset = new_offset;
+ *retval = new_offset;
+ F_UNLOCK( f );
+ return 0;
+}
diff --git a/kern/syscall/open.c b/kern/syscall/open.c
new file mode 100644
index 0000000..42d0d66
--- /dev/null
+++ b/kern/syscall/open.c
@@ -0,0 +1,116 @@
+#include <types.h>
+#include <lib.h>
+#include <kern/errno.h>
+#include <syscall.h>
+#include <file.h>
+#include <filedesc.h>
+#include <copyinout.h>
+#include <kern/fcntl.h>
+#include <stat.h>
+#include <vfs.h>
+#include <current.h>
+
+//check to see if the given flags are valid.
+//valid flags consist of exactly one of O_RDONLY/O_WRONLY/O_RDWR.
+static
+bool
+valid_flags( int flags ) {
+ int count = 0;
+ int accmode = flags & O_ACCMODE;
+
+ if( accmode == O_RDWR )
+ ++count;
+
+ if( accmode == O_RDONLY )
+ ++count;
+
+ if( accmode == O_WRONLY )
+ ++count;
+
+ return count == 1;
+}
+
+//kernel version of the open() systemcall.
+int
+___open( struct proc *p, char *path, int flags, int *retval ) {
+ struct vnode *vn = NULL;
+ int err;
+ struct file *f = NULL;
+ struct stat st;
+
+ //attempt to open the file
+ err = vfs_open( path, flags, 0, &vn );
+ if( err )
+ return err;
+
+ //atempt to create the file object
+ err = file_create( vn, flags, &f );
+ if( err ) {
+ vfs_close( vn );
+ return err;
+ }
+
+ //if we have O_APPEND, we must set the offset
+ //to be the file size.
+ if( flags & O_APPEND ) {
+ err = VOP_STAT( f->f_vnode, &st );
+ if( err ) {
+ vfs_close( vn );
+ file_destroy( f );
+ return err;
+ }
+
+ f->f_offset = st.st_size;
+ }
+
+ //lock the file so we can safely modify
+ F_LOCK( f );
+
+ //we now need to find a spot inside the process' filetable
+ //so we can store our new file
+ err = fd_attach( p->p_fd, f, retval );
+ if( err ) {
+ F_UNLOCK( f );
+ vfs_close( vn );
+ file_destroy( f );
+ return err;
+ }
+
+ //increase both references counts
+ f->f_refcount++;
+ VOP_INCREF( f->f_vnode );
+
+ //we are done if the file, unlock it
+ F_UNLOCK( f );
+ return 0;
+
+}
+
+int
+sys_open( userptr_t upath, int flags, int *retval ) {
+ char k_filename[MAX_FILE_NAME];
+ int err;
+ struct proc *p;
+
+ //make sure the current thread is not null
+ //and that we have a process associated with it
+ KASSERT( curthread != NULL );
+ KASSERT( curthread->td_proc != NULL );
+
+ p = curthread->td_proc;
+
+ //check if we have valid flags
+ if( !valid_flags( flags ) )
+ return EINVAL;
+
+ //copy the path from userland into kernel-land
+ err = copyinstr( upath, k_filename, sizeof( k_filename ), NULL );
+
+ //if we couldn't copy the file name, we probably have an I/O error
+ //simply return that error code
+ if( err )
+ return err;
+
+ //delegate the actual opening to another function.
+ return ___open( p, k_filename, flags, retval );
+}
diff --git a/kern/syscall/read.c b/kern/syscall/read.c
new file mode 100644
index 0000000..7a61ff7
--- /dev/null
+++ b/kern/syscall/read.c
@@ -0,0 +1,68 @@
+#include <types.h>
+#include <lib.h>
+#include <copyinout.h>
+#include <proc.h>
+#include <kern/iovec.h>
+#include <uio.h>
+#include <file.h>
+#include <kern/fcntl.h>
+#include <kern/errno.h>
+#include <vnode.h>
+#include <current.h>
+#include <syscall.h>
+
+int
+sys_read( int fd, userptr_t ubuf, size_t ulen, int *retval ) {
+ struct proc *p = NULL;
+ struct file *f = NULL;
+ struct uio ruio;
+ struct iovec iov;
+ int err;
+
+ KASSERT( curthread != NULL );
+ KASSERT( curthread->td_proc != NULL );
+
+ p = curthread->td_proc;
+
+ //attempt to open the file
+ err = file_get( p, fd, &f );
+ if( err )
+ return err;
+
+ //check whether we have reading permissions
+ if( f->f_oflags & O_WRONLY ) {
+ F_UNLOCK( f );
+ return EBADF;
+ }
+
+ //prepare the uio for read
+ uio_kinit (
+ &iov,
+ &ruio,
+ ubuf,
+ ulen,
+ f->f_offset,
+ UIO_READ
+ );
+
+ ruio.uio_space = curthread->t_addrspace;
+ ruio.uio_segflg = UIO_USERSPACE;
+
+ //perform the read
+ err = VOP_READ( f->f_vnode, &ruio );
+
+ if( err ) {
+ F_UNLOCK( f );
+ return err;
+ }
+
+ //advance the offset
+ f->f_offset = ruio.uio_offset;
+
+ //set the number of bytes read to be retval
+ *retval = ulen - ruio.uio_resid;
+
+ //unlock and quit
+ F_UNLOCK( f );
+ return 0;
+}
diff --git a/kern/syscall/waitpid.c b/kern/syscall/waitpid.c
new file mode 100644
index 0000000..1b67cd5
--- /dev/null
+++ b/kern/syscall/waitpid.c
@@ -0,0 +1,86 @@
+#include <types.h>
+#include <lib.h>
+#include <kern/errno.h>
+#include <kern/wait.h>
+#include <proc.h>
+#include <copyinout.h>
+#include <file.h>
+#include <filedesc.h>
+#include <thread.h>
+#include <current.h>
+#include <syscall.h>
+
+int
+___waitpid( int pid, int *retval, int options ) {
+ struct proc *p = NULL;
+ int err;
+
+ KASSERT( curthread != NULL );
+ KASSERT( curthread->td_proc != NULL );
+
+ //we only support WNOHANG and nothing else.
+ if( options != 0 && options != WNOHANG )
+ return EINVAL;
+
+ //get the process associated with the given pid
+ err = proc_get( pid, &p );
+ if( err )
+ return err;
+
+ //make sure that we are the parent of that process
+ //otherwise we are collecting an the exit code of a process
+ //whose parent might still be interested in.
+ if( p->p_proc != curthread->td_proc ) {
+ PROC_UNLOCK( p );
+ return ECHILD;
+ }
+
+ //if WNOHANG was given, and said process is not yet dead
+ //we immediately, (successfully) return with a value of 0.
+ if( !p->p_is_dead && (options == WNOHANG) ) {
+ PROC_UNLOCK( p );
+ *retval = 0;
+ return 0;
+ }
+
+ //unlock the process, so the child potentially makes progress.
+ //then, consume a signal from the child's semaphore.
+ PROC_UNLOCK( p );
+ P( p->p_sem );
+ PROC_LOCK( p );
+
+ //at this point the child should be certainly dead.
+ KASSERT( p->p_is_dead );
+
+ //copy its exit code to the userland.
+ *retval = _MKWAIT_EXIT(p->p_retval);
+
+ //unlock and destroy.
+ PROC_UNLOCK( p );
+ proc_destroy( p );
+
+ return 0;
+
+}
+
+int
+sys_waitpid( int pid, userptr_t uret, int options, int *retval ) {
+ int kstatus;
+ int err;
+
+ err = ___waitpid( pid, &kstatus, options );
+ if( err )
+ return err;
+
+ //copy its exit code to the userland.
+ err = copyout( &kstatus, uret, sizeof( int ) );
+
+ //if we had an error copying out, we will return
+ //an efault, but we still must destroy the associated
+ //proc since the child is essentially dead.
+ if( err )
+ return err;
+
+ *retval = pid;
+ return 0;
+}
diff --git a/kern/syscall/write.c b/kern/syscall/write.c
new file mode 100644
index 0000000..881ef4c
--- /dev/null
+++ b/kern/syscall/write.c
@@ -0,0 +1,68 @@
+#include <types.h>
+#include <lib.h>
+#include <copyinout.h>
+#include <kern/errno.h>
+#include <file.h>
+#include <filedesc.h>
+#include <kern/iovec.h>
+#include <kern/fcntl.h>
+#include <uio.h>
+#include <proc.h>
+#include <current.h>
+#include <syscall.h>
+
+int
+sys_write( int fd, userptr_t ubuf, size_t ulen, int *retval ) {
+ int err = 0;
+ struct file *f = NULL;
+ struct proc *p = NULL;
+ char kbuf[ulen];
+ struct uio wuio;
+ struct iovec iov;
+
+ KASSERT( curthread != NULL );
+ KASSERT( curthread->td_proc != NULL );
+
+ p = curthread->td_proc;
+
+ //attempt to get a hold of the file
+ err = file_get( p, fd, &f );
+ if( err )
+ return err;
+
+ //make sure it is not readony
+ if( f->f_oflags & O_RDONLY )
+ return EBADF;
+
+ //copy the data from userland into kernel
+ err = copyin( ubuf, kbuf, sizeof( kbuf ) );
+ if( err ) {
+ F_UNLOCK( f );
+ return err;
+ }
+
+ //prepare the iovec/uio
+ uio_kinit(
+ &iov,
+ &wuio,
+ kbuf,
+ sizeof( kbuf ),
+ f->f_offset,
+ UIO_WRITE
+ );
+
+ //perform the IO
+ err = VOP_WRITE( f->f_vnode, &wuio );
+ if( err ) {
+ F_UNLOCK( f );
+ return err;
+ }
+
+ //update the offset
+ f->f_offset = wuio.uio_offset;
+ F_UNLOCK( f );
+
+ //number of bytes written
+ *retval = ulen - wuio.uio_resid;
+ return 0;
+}
diff --git a/kern/thread/synch.c b/kern/thread/synch.c
index 9a7468c..c5f8ef3 100644
--- a/kern/thread/synch.c
+++ b/kern/thread/synch.c
@@ -163,7 +163,14 @@ lock_create(const char *name)
return NULL;
}
- // add stuff here as needed
+ lock->lk_wchan = wchan_create(lock->lk_name);
+ if (lock->lk_wchan == NULL) {
+ kfree(lock->lk_name);
+ kfree(lock);
+ return NULL;
+ }
+ spinlock_init(&lock->lk_lock);
+ lock->lk_holder = NULL;
return lock;
}
@@ -171,9 +178,11 @@ lock_create(const char *name)
void
lock_destroy(struct lock *lock)
{
- KASSERT(lock != NULL);
+ DEBUGASSERT(lock != NULL);
+ DEBUGASSERT(lock->lk_holder == NULL);
- // add stuff here as needed
+ spinlock_cleanup(&lock->lk_lock);
+ wchan_destroy(lock->lk_wchan);
kfree(lock->lk_name);
kfree(lock);
@@ -182,27 +191,44 @@ lock_destroy(struct lock *lock)
void
lock_acquire(struct lock *lock)
{
- // Write this
+ DEBUGASSERT(lock != NULL);
+ DEBUGASSERT(!(lock_do_i_hold(lock)));
+ KASSERT(curthread->t_in_interrupt == false);
+
+ spinlock_acquire(&lock->lk_lock);
+ while (lock->lk_holder != NULL) {
+ wchan_lock(lock->lk_wchan);
+ spinlock_release(&lock->lk_lock);
+ wchan_sleep(lock->lk_wchan);
+ spinlock_acquire(&lock->lk_lock);
+ }
- (void)lock; // suppress warning until code gets written
+ lock->lk_holder = curthread;
+ spinlock_release(&lock->lk_lock);
}
void
lock_release(struct lock *lock)
{
- // Write this
+ DEBUGASSERT(lock_do_i_hold(lock));
- (void)lock; // suppress warning until code gets written
+ spinlock_acquire(&lock->lk_lock);
+ lock->lk_holder = NULL;
+ wchan_wakeone(lock->lk_wchan);
+ spinlock_release(&lock->lk_lock);
}
bool
lock_do_i_hold(struct lock *lock)
{
- // Write this
+ bool ret;
+ DEBUGASSERT(lock != NULL);
- (void)lock; // suppress warning until code gets written
+ spinlock_acquire(&lock->lk_lock);
+ ret = (lock->lk_holder == curthread);
+ spinlock_release(&lock->lk_lock);
- return true; // dummy until code gets written
+ return ret;
}
////////////////////////////////////////////////////////////
@@ -226,7 +252,12 @@ cv_create(const char *name)
return NULL;
}
- // add stuff here as needed
+ cv->cv_wchan = wchan_create(cv->cv_name);
+ if (cv->cv_wchan == NULL) {
+ kfree(cv->cv_name);
+ kfree(cv);
+ return NULL;
+ }
return cv;
}
@@ -236,8 +267,7 @@ cv_destroy(struct cv *cv)
{
KASSERT(cv != NULL);
- // add stuff here as needed
-
+ wchan_destroy(cv->cv_wchan);
kfree(cv->cv_name);
kfree(cv);
}
@@ -245,23 +275,26 @@ cv_destroy(struct cv *cv)
void
cv_wait(struct cv *cv, struct lock *lock)
{
- // Write this
- (void)cv; // suppress warning until code gets written
- (void)lock; // suppress warning until code gets written
-}
+ DEBUGASSERT(lock_do_i_hold(lock));
+ wchan_lock(cv->cv_wchan);
+ lock_release(lock);
+ wchan_sleep(cv->cv_wchan);
+ lock_acquire(lock);
+}
+
void
cv_signal(struct cv *cv, struct lock *lock)
{
- // Write this
- (void)cv; // suppress warning until code gets written
- (void)lock; // suppress warning until code gets written
-}
+ DEBUGASSERT(lock_do_i_hold(lock));
+ wchan_wakeone(cv->cv_wchan);
+}
+
void
cv_broadcast(struct cv *cv, struct lock *lock)
{
- // Write this
- (void)cv; // suppress warning until code gets written
- (void)lock; // suppress warning until code gets written
+ DEBUGASSERT(lock_do_i_hold(lock));
+
+ wchan_wakeall(cv->cv_wchan);
}
diff --git a/kern/thread/synch.c.orig b/kern/thread/synch.c.orig
new file mode 100644
index 0000000..9a7468c
--- /dev/null
+++ b/kern/thread/synch.c.orig
@@ -0,0 +1,267 @@
+/*
+ * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009
+ * The President and Fellows of Harvard College.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Synchronization primitives.
+ * The specifications of the functions are in synch.h.
+ */
+
+#include <types.h>
+#include <lib.h>
+#include <spinlock.h>
+#include <wchan.h>
+#include <thread.h>
+#include <current.h>
+#include <synch.h>
+
+////////////////////////////////////////////////////////////
+//
+// Semaphore.
+
+struct semaphore *
+sem_create(const char *name, int initial_count)
+{
+ struct semaphore *sem;
+
+ KASSERT(initial_count >= 0);
+
+ sem = kmalloc(sizeof(struct semaphore));
+ if (sem == NULL) {
+ return NULL;
+ }
+
+ sem->sem_name = kstrdup(name);
+ if (sem->sem_name == NULL) {
+ kfree(sem);
+ return NULL;
+ }
+
+ sem->sem_wchan = wchan_create(sem->sem_name);
+ if (sem->sem_wchan == NULL) {
+ kfree(sem->sem_name);
+ kfree(sem);
+ return NULL;
+ }
+
+ spinlock_init(&sem->sem_lock);
+ sem->sem_count = initial_count;
+
+ return sem;
+}
+
+void
+sem_destroy(struct semaphore *sem)
+{
+ KASSERT(sem != NULL);
+
+ /* wchan_cleanup will assert if anyone's waiting on it */
+ spinlock_cleanup(&sem->sem_lock);
+ wchan_destroy(sem->sem_wchan);
+ kfree(sem->sem_name);
+ kfree(sem);
+}
+
+void
+P(struct semaphore *sem)
+{
+ KASSERT(sem != NULL);
+
+ /*
+ * May not block in an interrupt handler.
+ *
+ * For robustness, always check, even if we can actually
+ * complete the P without blocking.
+ */
+ KASSERT(curthread->t_in_interrupt == false);
+
+ spinlock_acquire(&sem->sem_lock);
+ while (sem->sem_count == 0) {
+ /*
+ * Bridge to the wchan lock, so if someone else comes
+ * along in V right this instant the wakeup can't go
+ * through on the wchan until we've finished going to
+ * sleep. Note that wchan_sleep unlocks the wchan.
+ *
+ * Note that we don't maintain strict FIFO ordering of
+ * threads going through the semaphore; that is, we
+ * might "get" it on the first try even if other
+ * threads are waiting. Apparently according to some
+ * textbooks semaphores must for some reason have
+ * strict ordering. Too bad. :-)
+ *
+ * Exercise: how would you implement strict FIFO
+ * ordering?
+ */
+ wchan_lock(sem->sem_wchan);
+ spinlock_release(&sem->sem_lock);
+ wchan_sleep(sem->sem_wchan);
+
+ spinlock_acquire(&sem->sem_lock);
+ }
+ KASSERT(sem->sem_count > 0);
+ sem->sem_count--;
+ spinlock_release(&sem->sem_lock);
+}
+
+void
+V(struct semaphore *sem)
+{
+ KASSERT(sem != NULL);
+
+ spinlock_acquire(&sem->sem_lock);
+
+ sem->sem_count++;
+ KASSERT(sem->sem_count > 0);
+ wchan_wakeone(sem->sem_wchan);
+
+ spinlock_release(&sem->sem_lock);
+}
+
+////////////////////////////////////////////////////////////
+//
+// Lock.
+
+struct lock *
+lock_create(const char *name)
+{
+ struct lock *lock;
+
+ lock = kmalloc(sizeof(struct lock));
+ if (lock == NULL) {
+ return NULL;
+ }
+
+ lock->lk_name = kstrdup(name);
+ if (lock->lk_name == NULL) {
+ kfree(lock);
+ return NULL;
+ }
+
+ // add stuff here as needed
+
+ return lock;
+}
+
+void
+lock_destroy(struct lock *lock)
+{
+ KASSERT(lock != NULL);
+
+ // add stuff here as needed
+
+ kfree(lock->lk_name);
+ kfree(lock);
+}
+
+void
+lock_acquire(struct lock *lock)
+{
+ // Write this
+
+ (void)lock; // suppress warning until code gets written
+}
+
+void
+lock_release(struct lock *lock)
+{
+ // Write this
+
+ (void)lock; // suppress warning until code gets written
+}
+
+bool
+lock_do_i_hold(struct lock *lock)
+{
+ // Write this
+
+ (void)lock; // suppress warning until code gets written
+
+ return true; // dummy until code gets written
+}
+
+////////////////////////////////////////////////////////////
+//
+// CV
+
+
+struct cv *
+cv_create(const char *name)
+{
+ struct cv *cv;
+
+ cv = kmalloc(sizeof(struct cv));
+ if (cv == NULL) {
+ return NULL;
+ }
+
+ cv->cv_name = kstrdup(name);
+ if (cv->cv_name==NULL) {
+ kfree(cv);
+ return NULL;
+ }
+
+ // add stuff here as needed
+
+ return cv;
+}
+
+void
+cv_destroy(struct cv *cv)
+{
+ KASSERT(cv != NULL);
+
+ // add stuff here as needed
+
+ kfree(cv->cv_name);
+ kfree(cv);
+}
+
+void
+cv_wait(struct cv *cv, struct lock *lock)
+{
+ // Write this
+ (void)cv; // suppress warning until code gets written
+ (void)lock; // suppress warning until code gets written
+}
+
+void
+cv_signal(struct cv *cv, struct lock *lock)
+{
+ // Write this
+ (void)cv; // suppress warning until code gets written
+ (void)lock; // suppress warning until code gets written
+}
+
+void
+cv_broadcast(struct cv *cv, struct lock *lock)
+{
+ // Write this
+ (void)cv; // suppress warning until code gets written
+ (void)lock; // suppress warning until code gets written
+}
diff --git a/kern/thread/thread.c b/kern/thread/thread.c
index 5b8099e..cb76f07 100644
--- a/kern/thread/thread.c
+++ b/kern/thread/thread.c
@@ -852,7 +852,58 @@ schedule(void)
{
// 28 Feb 2012 : GWA : Implement your scheduler that prioritizes
// "interactive" threads here.
+
+ /*
+ * the idea behind the new scheduler
+ * is that interactive threads call system-calls more often
+ * then cpu-bound threads, therefore we will give preference to those.
+ */
+
+ struct cpu *cpu = NULL;
+ struct thread *it = NULL;
+ struct thread *t_max = NULL;
+
+ //get the current cpu.
+ cpu = curcpu;
+
+ //lock its running queue.
+ spinlock_acquire( &cpu->c_runqueue_lock );
+
+ //loop over all the threads, and choose the one that has the
+ //maximum syscalls number. once chosen, we remove that thread and
+ //add it to the front of the runqueue.
+ for ( it = cpu->c_runqueue.tl_head.tln_next->tln_self;
+ it != NULL && it->t_listnode.tln_next != NULL;
+ it = it->t_listnode.tln_next->tln_self) {
+
+ //if we're in the first iteration ...
+ if( t_max == NULL ) {
+ t_max = it;
+ continue;
+ }
+
+ if( t_max->td_proc != NULL && it->td_proc != NULL ) {
+ if( t_max->td_proc->p_nsyscalls < it->td_proc->p_nsyscalls ) {
+ t_max = it;
+ continue;
+ }
+ }
+ }
+
+ //remove t_max from the runqueue.
+ if( t_max != NULL ) {
+ threadlist_remove( &cpu->c_runqueue, t_max );
+
+ //add it to the head.
+ threadlist_addhead( &cpu->c_runqueue, t_max );
+ }
+
+ KASSERT( t_max->t_state == S_READY );
+ //release the lock
+ spinlock_release( &cpu->c_runqueue_lock );
+
}
+
#endif
/*
diff --git a/patches/ASST1-sol.tgz b/patches/ASST1-sol.tgz
new file mode 100644
index 0000000..b56c5e7
Binary files /dev/null and b/patches/ASST1-sol.tgz differ
diff --git a/patches/ASST1-sol/README b/patches/ASST1-sol/README
new file mode 100644
index 0000000..7cfed18
--- /dev/null
+++ b/patches/ASST1-sol/README
@@ -0,0 +1,58 @@
+01 Mar 2012 : GWA : This is ASST1 solution v. 1.0.
+05 Mar 2012 : GWA : This is ASST1 solution v. 1.1.
+
+It contains:
+ 1) Working locks.
+ 2) Working CVs.
+
+It does not contain:
+ 1) Working reader-writer locks.
+ 2) Solutions to the synchronization problems.
+
+Reader-writer locks may be included in a future ASST1 solution archive, but
+for now neither of the things that it does *not* contain should prevent you
+from making forward progress on ASST2.
+
+There are two ways to bring your Git repository into line with your solution.
+The first thing to do is commit anything you have uncommitted and make sure
+that your git status shows a clean directory. After that, here are the two
+options:
+
+1) git revert + patch
+
+The sol10.patch file is against commit 02a93045, or the same one that starts
+ASST2. If you have not received this commit yet do a git pull to do so. It's
+probably a good idea to do a git pull anyway, just in case.
+
+The sol11.path is against the sol10.path, so you should apply them in order.
+
+The first thing you have to do is locate the commit that contains your ASST0
+changes: adding your username and debug messages. You may have tagged this
+commit to begin ASST1. Use git log to locate the appropriate commit tag.
+
+Once you have located it, ask git to revert your ASST1 changes and then apply
+our patches.
+
+$ git revert <ASST1 start tag>
+$ patch -p1 sol10.patch
+$ patch -p1 sol11.patch
+$ git commit -a
+
+If you have previously applied the sol10.patch you should be able to simply
+apply the sol11.patch on top of your current source tree.
+
+2) copy files
+
+The other option is to do the merge by hand starting with your current
+working tree. All the files we changed are included in this archive. Several
+can be copied; for others, you will need to do the merge by hand, but the
+changes are very limited.
+
+ 1) synch.c, synch.h: solution primitives. These files can be copied.
+ 2) version.h, main.c: add solution set version to kernel boot messages.
+ These files must be merged by hand or you will break changes you made for
+ ASST0. (Specifically, making sure that your group or username prints during
+ bootup, which our grading scripts will check.)
+
+After committing any changes, simply move these files into place and commit
+the resulting changes.
diff --git a/patches/ASST1-sol/files/main.c b/patches/ASST1-sol/files/main.c
new file mode 100644
index 0000000..535a864
--- /dev/null
+++ b/patches/ASST1-sol/files/main.c
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009
+ * The President and Fellows of Harvard College.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Main.
+ */
+
+#include <types.h>
+#include <kern/errno.h>
+#include <kern/reboot.h>
+#include <kern/unistd.h>
+#include <lib.h>
+#include <spl.h>
+#include <clock.h>
+#include <thread.h>
+#include <current.h>
+#include <synch.h>
+#include <vm.h>
+#include <mainbus.h>
+#include <vfs.h>
+#include <device.h>
+#include <syscall.h>
+#include <test.h>
+#include <version.h>
+#include "autoconf.h" // for pseudoconfig
+
+
+/*
+ * These two pieces of data are maintained by the makefiles and build system.
+ * buildconfig is the name of the config file the kernel was configured with.
+ * buildversion starts at 1 and is incremented every time you link a kernel.
+ *
+ * The purpose is not to show off how many kernels you've linked, but
+ * to make it easy to make sure that the kernel you just booted is the
+ * same one you just built.
+ */
+extern const int buildversion;
+extern const char buildconfig[];
+
+/*
+ * Copyright message for the OS/161 base code.
+ */
+static const char harvard_copyright[] =
+ "Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009\n"
+ " President and Fellows of Harvard College. All rights reserved.\n";
+
+
+/*
+ * Initial boot sequence.
+ */
+static
+void
+boot(void)
+{
+ /*
+ * The order of these is important!
+ * Don't go changing it without thinking about the consequences.
+ *
+ * Among other things, be aware that console output gets
+ * buffered up at first and does not actually appear until
+ * mainbus_bootstrap() attaches the console device. This can
+ * be remarkably confusing if a bug occurs at this point. So
+ * don't put new code before mainbus_bootstrap if you don't
+ * absolutely have to.
+ *
+ * Also note that the buffer for this is only 1k. If you
+ * overflow it, the system will crash without printing
+ * anything at all. You can make it larger though (it's in
+ * dev/generic/console.c).
+ */
+
+ kprintf("\n");
+ kprintf("OS/161 base version %s ASST1 solution version %s\n", BASE_VERSION, ASST1SOL_VERSION);
+ kprintf("%s", harvard_copyright);
+ kprintf("\n");
+
+ kprintf("Put-your-group-name-here's system version %s (%s #%d)\n",
+ GROUP_VERSION, buildconfig, buildversion);
+ kprintf("\n");
+
+ /* Early initialization. */
+ ram_bootstrap();
+ thread_bootstrap();
+ hardclock_bootstrap();
+ vfs_bootstrap();
+
+ /* Probe and initialize devices. Interrupts should come on. */
+ kprintf("Device probe...\n");
+ KASSERT(curthread->t_curspl > 0);
+ mainbus_bootstrap();
+ KASSERT(curthread->t_curspl == 0);
+ /* Now do pseudo-devices. */
+ pseudoconfig();
+ kprintf("\n");
+
+ /* Late phase of initialization. */
+ vm_bootstrap();
+ kprintf_bootstrap();
+ thread_start_cpus();
+
+ /* Default bootfs - but ignore failure, in case emu0 doesn't exist */
+ vfs_setbootfs("emu0");
+
+
+ /*
+ * Make sure various things aren't screwed up.
+ */
+ COMPILE_ASSERT(sizeof(userptr_t) == sizeof(char *));
+ COMPILE_ASSERT(sizeof(*(userptr_t)0) == sizeof(char));
+}
+
+/*
+ * Shutdown sequence. Opposite to boot().
+ */
+static
+void
+shutdown(void)
+{
+
+ kprintf("Shutting down.\n");
+
+ vfs_clearbootfs();
+ vfs_clearcurdir();
+ vfs_unmountall();
+
+ thread_shutdown();
+
+ splhigh();
+}
+
+/*****************************************/
+
+/*
+ * reboot() system call.
+ *
+ * Note: this is here because it's directly related to the code above,
+ * not because this is where system call code should go. Other syscall
+ * code should probably live in the "syscall" directory.
+ */
+int
+sys_reboot(int code)
+{
+ switch (code) {
+ case RB_REBOOT:
+ case RB_HALT:
+ case RB_POWEROFF:
+ break;
+ default:
+ return EINVAL;
+ }
+
+ shutdown();
+
+ switch (code) {
+ case RB_HALT:
+ kprintf("The system is halted.\n");
+ mainbus_halt();
+ break;
+ case RB_REBOOT:
+ kprintf("Rebooting...\n");
+ mainbus_reboot();
+ break;
+ case RB_POWEROFF:
+ kprintf("The system is halted.\n");
+ mainbus_poweroff();
+ break;
+ }
+
+ panic("reboot operation failed\n");
+ return 0;
+}
+
+/*
+ * Kernel main. Boot up, then fork the menu thread; wait for a reboot
+ * request, and then shut down.
+ */
+void
+kmain(char *arguments)
+{
+ boot();
+
+ menu(arguments);
+
+ /* Should not get here */
+}
diff --git a/patches/ASST1-sol/files/synch.c b/patches/ASST1-sol/files/synch.c
new file mode 100644
index 0000000..c5f8ef3
--- /dev/null
+++ b/patches/ASST1-sol/files/synch.c
@@ -0,0 +1,300 @@
+/*
+ * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009
+ * The President and Fellows of Harvard College.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Synchronization primitives.
+ * The specifications of the functions are in synch.h.
+ */
+
+#include <types.h>
+#include <lib.h>
+#include <spinlock.h>
+#include <wchan.h>
+#include <thread.h>
+#include <current.h>
+#include <synch.h>
+
+////////////////////////////////////////////////////////////
+//
+// Semaphore.
+
+struct semaphore *
+sem_create(const char *name, int initial_count)
+{
+ struct semaphore *sem;
+
+ KASSERT(initial_count >= 0);
+
+ sem = kmalloc(sizeof(struct semaphore));
+ if (sem == NULL) {
+ return NULL;
+ }
+
+ sem->sem_name = kstrdup(name);
+ if (sem->sem_name == NULL) {
+ kfree(sem);
+ return NULL;
+ }
+
+ sem->sem_wchan = wchan_create(sem->sem_name);
+ if (sem->sem_wchan == NULL) {
+ kfree(sem->sem_name);
+ kfree(sem);
+ return NULL;
+ }
+
+ spinlock_init(&sem->sem_lock);
+ sem->sem_count = initial_count;
+
+ return sem;
+}
+
+void
+sem_destroy(struct semaphore *sem)
+{
+ KASSERT(sem != NULL);
+
+ /* wchan_cleanup will assert if anyone's waiting on it */
+ spinlock_cleanup(&sem->sem_lock);
+ wchan_destroy(sem->sem_wchan);
+ kfree(sem->sem_name);
+ kfree(sem);
+}
+
+void
+P(struct semaphore *sem)
+{
+ KASSERT(sem != NULL);
+
+ /*
+ * May not block in an interrupt handler.
+ *
+ * For robustness, always check, even if we can actually
+ * complete the P without blocking.
+ */
+ KASSERT(curthread->t_in_interrupt == false);
+
+ spinlock_acquire(&sem->sem_lock);
+ while (sem->sem_count == 0) {
+ /*
+ * Bridge to the wchan lock, so if someone else comes
+ * along in V right this instant the wakeup can't go
+ * through on the wchan until we've finished going to
+ * sleep. Note that wchan_sleep unlocks the wchan.
+ *
+ * Note that we don't maintain strict FIFO ordering of
+ * threads going through the semaphore; that is, we
+ * might "get" it on the first try even if other
+ * threads are waiting. Apparently according to some
+ * textbooks semaphores must for some reason have
+ * strict ordering. Too bad. :-)
+ *
+ * Exercise: how would you implement strict FIFO
+ * ordering?
+ */
+ wchan_lock(sem->sem_wchan);
+ spinlock_release(&sem->sem_lock);
+ wchan_sleep(sem->sem_wchan);
+
+ spinlock_acquire(&sem->sem_lock);
+ }
+ KASSERT(sem->sem_count > 0);
+ sem->sem_count--;
+ spinlock_release(&sem->sem_lock);
+}
+
+void
+V(struct semaphore *sem)
+{
+ KASSERT(sem != NULL);
+
+ spinlock_acquire(&sem->sem_lock);
+
+ sem->sem_count++;
+ KASSERT(sem->sem_count > 0);
+ wchan_wakeone(sem->sem_wchan);
+
+ spinlock_release(&sem->sem_lock);
+}
+
+////////////////////////////////////////////////////////////
+//
+// Lock.
+
+struct lock *
+lock_create(const char *name)
+{
+ struct lock *lock;
+
+ lock = kmalloc(sizeof(struct lock));
+ if (lock == NULL) {
+ return NULL;
+ }
+
+ lock->lk_name = kstrdup(name);
+ if (lock->lk_name == NULL) {
+ kfree(lock);
+ return NULL;
+ }
+
+ lock->lk_wchan = wchan_create(lock->lk_name);
+ if (lock->lk_wchan == NULL) {
+ kfree(lock->lk_name);
+ kfree(lock);
+ return NULL;
+ }
+ spinlock_init(&lock->lk_lock);
+ lock->lk_holder = NULL;
+
+ return lock;
+}
+
+void
+lock_destroy(struct lock *lock)
+{
+ DEBUGASSERT(lock != NULL);
+ DEBUGASSERT(lock->lk_holder == NULL);
+
+ spinlock_cleanup(&lock->lk_lock);
+ wchan_destroy(lock->lk_wchan);
+
+ kfree(lock->lk_name);
+ kfree(lock);
+}
+
+void
+lock_acquire(struct lock *lock)
+{
+ DEBUGASSERT(lock != NULL);
+ DEBUGASSERT(!(lock_do_i_hold(lock)));
+ KASSERT(curthread->t_in_interrupt == false);
+
+ spinlock_acquire(&lock->lk_lock);
+ while (lock->lk_holder != NULL) {
+ wchan_lock(lock->lk_wchan);
+ spinlock_release(&lock->lk_lock);
+ wchan_sleep(lock->lk_wchan);
+ spinlock_acquire(&lock->lk_lock);
+ }
+
+ lock->lk_holder = curthread;
+ spinlock_release(&lock->lk_lock);
+}
+
+void
+lock_release(struct lock *lock)
+{
+ DEBUGASSERT(lock_do_i_hold(lock));
+
+ spinlock_acquire(&lock->lk_lock);
+ lock->lk_holder = NULL;
+ wchan_wakeone(lock->lk_wchan);
+ spinlock_release(&lock->lk_lock);
+}
+
+bool
+lock_do_i_hold(struct lock *lock)
+{
+ bool ret;
+ DEBUGASSERT(lock != NULL);
+
+ spinlock_acquire(&lock->lk_lock);
+ ret = (lock->lk_holder == curthread);
+ spinlock_release(&lock->lk_lock);
+
+ return ret;
+}
+
+////////////////////////////////////////////////////////////
+//
+// CV
+
+
+struct cv *
+cv_create(const char *name)
+{
+ struct cv *cv;
+
+ cv = kmalloc(sizeof(struct cv));
+ if (cv == NULL) {
+ return NULL;
+ }
+
+ cv->cv_name = kstrdup(name);
+ if (cv->cv_name==NULL) {
+ kfree(cv);
+ return NULL;
+ }
+
+ cv->cv_wchan = wchan_create(cv->cv_name);
+ if (cv->cv_wchan == NULL) {
+ kfree(cv->cv_name);
+ kfree(cv);
+ return NULL;
+ }
+
+ return cv;
+}
+
+void
+cv_destroy(struct cv *cv)
+{
+ KASSERT(cv != NULL);
+
+ wchan_destroy(cv->cv_wchan);
+ kfree(cv->cv_name);
+ kfree(cv);
+}
+
+void
+cv_wait(struct cv *cv, struct lock *lock)
+{
+ DEBUGASSERT(lock_do_i_hold(lock));
+
+ wchan_lock(cv->cv_wchan);
+ lock_release(lock);
+ wchan_sleep(cv->cv_wchan);
+ lock_acquire(lock);
+}
+
+void
+cv_signal(struct cv *cv, struct lock *lock)
+{
+ DEBUGASSERT(lock_do_i_hold(lock));
+
+ wchan_wakeone(cv->cv_wchan);
+}
+
+void
+cv_broadcast(struct cv *cv, struct lock *lock)
+{
+ DEBUGASSERT(lock_do_i_hold(lock));
+
+ wchan_wakeall(cv->cv_wchan);
+}
diff --git a/patches/ASST1-sol/files/synch.h b/patches/ASST1-sol/files/synch.h
new file mode 100644
index 0000000..461eedc
--- /dev/null
+++ b/patches/ASST1-sol/files/synch.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009
+ * The President and Fellows of Harvard College.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _SYNCH_H_
+#define _SYNCH_H_
+
+/*
+ * Header file for synchronization primitives.
+ */
+
+
+#include <spinlock.h>
+
+/*
+ * Dijkstra-style semaphore.
+ *
+ * The name field is for easier debugging. A copy of the name is made
+ * internally.
+ */
+struct semaphore {
+ char *sem_name;
+ struct wchan *sem_wchan;
+ struct spinlock sem_lock;
+ volatile int sem_count;
+};
+
+struct semaphore *sem_create(const char *name, int initial_count);
+void sem_destroy(struct semaphore *);
+
+/*
+ * Operations (both atomic):
+ * P (proberen): decrement count. If the count is 0, block until
+ * the count is 1 again before decrementing.
+ * V (verhogen): increment count.
+ */
+void P(struct semaphore *);
+void V(struct semaphore *);
+
+
+/*
+ * Simple lock for mutual exclusion.
+ *
+ * When the lock is created, no thread should be holding it. Likewise,
+ * when the lock is destroyed, no thread should be holding it.
+ *
+ * The name field is for easier debugging. A copy of the name is
+ * (should be) made internally.
+ */
+struct lock {
+ char *lk_name;
+
+ // BEGIN SOLUTION
+ struct wchan *lk_wchan;
+ struct spinlock lk_lock;
+ volatile struct thread *lk_holder;
+ // END SOLUTION
+};
+
+struct lock *lock_create(const char *name);
+void lock_acquire(struct lock *);
+
+/*
+ * Operations:
+ * lock_acquire - Get the lock. Only one thread can hold the lock at the
+ * same time.
+ * lock_release - Free the lock. Only the thread holding the lock may do
+ * this.
+ * lock_do_i_hold - Return true if the current thread holds the lock;
+ * false otherwise.
+ *
+ * These operations must be atomic. You get to write them.
+ */
+void lock_release(struct lock *);
+bool lock_do_i_hold(struct lock *);
+void lock_destroy(struct lock *);
+
+
+/*
+ * Condition variable.
+ *
+ * Note that the "variable" is a bit of a misnomer: a CV is normally used
+ * to wait until a variable meets a particular condition, but there's no
+ * actual variable, as such, in the CV.
+ *
+ * These CVs are expected to support Mesa semantics, that is, no
+ * guarantees are made about scheduling.
+ *
+ * The name field is for easier debugging. A copy of the name is
+ * (should be) made internally.
+ */
+
+struct cv {
+ char *cv_name;
+
+ // BEGIN SOLUTION
+ struct wchan *cv_wchan;
+ // END SOLUTION
+};
+
+struct cv *cv_create(const char *name);
+void cv_destroy(struct cv *);
+
+/*
+ * Operations:
+ * cv_wait - Release the supplied lock, go to sleep, and, after
+ * waking up again, re-acquire the lock.
+ * cv_signal - Wake up one thread that's sleeping on this CV.
+ * cv_broadcast - Wake up all threads sleeping on this CV.
+ *
+ * For all three operations, the current thread must hold the lock passed
+ * in. Note that under normal circumstances the same lock should be used
+ * on all operations with any particular CV.
+ *
+ * These operations must be atomic. You get to write them.
+ */
+void cv_wait(struct cv *cv, struct lock *lock);
+void cv_signal(struct cv *cv, struct lock *lock);
+void cv_broadcast(struct cv *cv, struct lock *lock);
+
+/*
+ * 13 Feb 2012 : GWA : Reader-writer locks.
+ */
+
+struct rwlock {
+ char *rwlock_name;
+};
+
+struct rwlock * rwlock_create(const char *);
+void rwlock_destroy(struct rwlock *);
+
+void rwlock_acquire_read(struct rwlock *);
+void rwlock_release_read(struct rwlock *);
+void rwlock_acquire_write(struct rwlock *);
+void rwlock_release_write(struct rwlock *);
+
+#endif /* _SYNCH_H_ */
diff --git a/patches/ASST1-sol/files/version.h b/patches/ASST1-sol/files/version.h
new file mode 100644
index 0000000..4734a98
--- /dev/null
+++ b/patches/ASST1-sol/files/version.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009
+ * The President and Fellows of Harvard College.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _VERSION_H_
+#define _VERSION_H_
+
+/*
+ * Leave this alone, so we can tell what version of the OS/161 base
+ * code we gave you.
+ */
+#define BASE_VERSION "1.99.05"
+#define ASST1SOL_VERSION "1.0"
+
+/*
+ * Change this as you see fit in the course of hacking the system.
+ */
+#define GROUP_VERSION "0"
+
+
+#endif /* _VERSION_H_ */
diff --git a/patches/ASST1-sol/sol10.patch b/patches/ASST1-sol/sol10.patch
new file mode 100644
index 0000000..6dea880
--- /dev/null
+++ b/patches/ASST1-sol/sol10.patch
@@ -0,0 +1,220 @@
+diff --git a/kern/include/synch.h b/kern/include/synch.h
+index ac3714b..461eedc 100644
+--- a/kern/include/synch.h
++++ b/kern/include/synch.h
+@@ -74,8 +74,12 @@ void V(struct semaphore *);
+ */
+ struct lock {
+ char *lk_name;
+- // add what you need here
+- // (don't forget to mark things volatile as needed)
++
++ // BEGIN SOLUTION
++ struct wchan *lk_wchan;
++ struct spinlock lk_lock;
++ volatile struct thread *lk_holder;
++ // END SOLUTION
+ };
+
+ struct lock *lock_create(const char *name);
+@@ -113,8 +117,10 @@ void lock_destroy(struct lock *);
+
+ struct cv {
+ char *cv_name;
+- // add what you need here
+- // (don't forget to mark things volatile as needed)
++
++ // BEGIN SOLUTION
++ struct wchan *cv_wchan;
++ // END SOLUTION
+ };
+
+ struct cv *cv_create(const char *name);
+diff --git a/kern/include/version.h b/kern/include/version.h
+index 9fec0e8..4734a98 100644
+--- a/kern/include/version.h
++++ b/kern/include/version.h
+@@ -35,6 +35,7 @@
+ * code we gave you.
+ */
+ #define BASE_VERSION "1.99.05"
++#define ASST1SOL_VERSION "1.0"
+
+ /*
+ * Change this as you see fit in the course of hacking the system.
+diff --git a/kern/startup/main.c b/kern/startup/main.c
+index be4c4b8..535a864 100644
+--- a/kern/startup/main.c
++++ b/kern/startup/main.c
+@@ -96,7 +96,7 @@ boot(void)
+ */
+
+ kprintf("\n");
+- kprintf("OS/161 base system version %s\n", BASE_VERSION);
++ kprintf("OS/161 base version %s ASST1 solution version %s\n", BASE_VERSION, ASST1SOL_VERSION);
+ kprintf("%s", harvard_copyright);
+ kprintf("\n");
+
+diff --git a/kern/thread/synch.c b/kern/thread/synch.c
+index 9a7468c..fb983b4 100644
+--- a/kern/thread/synch.c
++++ b/kern/thread/synch.c
+@@ -163,7 +163,16 @@ lock_create(const char *name)
+ return NULL;
+ }
+
+- // add stuff here as needed
++ // BEGIN SOLUTION
++ lock->lk_wchan = wchan_create(lock->lk_name);
++ if (lock->lk_wchan == NULL) {
++ kfree(lock->lk_name);
++ kfree(lock);
++ return NULL;
++ }
++ spinlock_init(&lock->lk_lock);
++ lock->lk_holder = NULL;
++ // END SOLUTION
+
+ return lock;
+ }
+@@ -173,7 +182,11 @@ lock_destroy(struct lock *lock)
+ {
+ KASSERT(lock != NULL);
+
+- // add stuff here as needed
++ // BEGIN SOLUTION
++ KASSERT(lock->lk_holder == NULL);
++ spinlock_cleanup(&lock->lk_lock);
++ wchan_destroy(lock->lk_wchan);
++ // END SOLUTION
+
+ kfree(lock->lk_name);
+ kfree(lock);
+@@ -182,27 +195,52 @@ lock_destroy(struct lock *lock)
+ void
+ lock_acquire(struct lock *lock)
+ {
+- // Write this
++ // BEGIN SOLUTION
++ DEBUGASSERT(lock != NULL);
++ KASSERT(curthread->t_in_interrupt == false);
++
++ spinlock_acquire(&lock->lk_lock);
++ while (lock->lk_holder != NULL) {
++ /* As in the semaphore. */
++ wchan_lock(lock->lk_wchan);
++ spinlock_release(&lock->lk_lock);
++ wchan_sleep(lock->lk_wchan);
++
++ spinlock_acquire(&lock->lk_lock);
++ }
+
+- (void)lock; // suppress warning until code gets written
++ lock->lk_holder = curthread;
++ spinlock_release(&lock->lk_lock);
++ // END SOLUTION
+ }
+
+ void
+ lock_release(struct lock *lock)
+ {
+- // Write this
+-
+- (void)lock; // suppress warning until code gets written
++ // BEGIN SOLUTION
++ DEBUGASSERT(lock != NULL);
++
++ spinlock_acquire(&lock->lk_lock);
++ lock->lk_holder = NULL;
++ wchan_wakeone(lock->lk_wchan);
++ spinlock_release(&lock->lk_lock);
++ // END SOLUTION
+ }
+
+ bool
+ lock_do_i_hold(struct lock *lock)
+ {
+- // Write this
++ // BEGIN SOLUTION
++ bool ret;
+
+- (void)lock; // suppress warning until code gets written
++ DEBUGASSERT(lock != NULL);
+
+- return true; // dummy until code gets written
++ spinlock_acquire(&lock->lk_lock);
++ ret = (lock->lk_holder == curthread);
++ spinlock_release(&lock->lk_lock);
++
++ return ret;
++ // END SOLUTION
+ }
+
+ ////////////////////////////////////////////////////////////
+@@ -226,7 +264,14 @@ cv_create(const char *name)
+ return NULL;
+ }
+
+- // add stuff here as needed
++ // BEGIN SOLUTION
++ cv->cv_wchan = wchan_create(cv->cv_name);
++ if (cv->cv_wchan == NULL) {
++ kfree(cv->cv_name);
++ kfree(cv);
++ return NULL;
++ }
++ // END SOLUTION
+
+ return cv;
+ }
+@@ -236,7 +281,9 @@ cv_destroy(struct cv *cv)
+ {
+ KASSERT(cv != NULL);
+
+- // add stuff here as needed
++ // BEGIN SOLUTION
++ wchan_destroy(cv->cv_wchan);
++ // END SOLUTION
+
+ kfree(cv->cv_name);
+ kfree(cv);
+@@ -245,23 +292,28 @@ cv_destroy(struct cv *cv)
+ void
+ cv_wait(struct cv *cv, struct lock *lock)
+ {
+- // Write this
+- (void)cv; // suppress warning until code gets written
+- (void)lock; // suppress warning until code gets written
++ // BEGIN SOLUTION
++ wchan_lock(cv->cv_wchan);
++ lock_release(lock);
++ wchan_sleep(cv->cv_wchan);
++ lock_release(lock);
++ // END SOLUTION
+ }
+-
++
+ void
+ cv_signal(struct cv *cv, struct lock *lock)
+ {
+- // Write this
+- (void)cv; // suppress warning until code gets written
+- (void)lock; // suppress warning until code gets written
++ // BEGIN SOLUTION
++ (void)lock;
++ wchan_wakeone(cv->cv_wchan);
++ // END SOLUTION
+ }
+-
++
+ void
+ cv_broadcast(struct cv *cv, struct lock *lock)
+ {
+- // Write this
+- (void)cv; // suppress warning until code gets written
+- (void)lock; // suppress warning until code gets written
++ // BEGIN SOLUTION
++ (void)lock;
++ wchan_wakeall(cv->cv_wchan);
++ // END SOLUTION
+ }
diff --git a/user/testbin/Makefile b/user/testbin/Makefile
index c3b97a8..d02221f 100644
--- a/user/testbin/Makefile
+++ b/user/testbin/Makefile
@@ -6,10 +6,10 @@ TOP=../..
.include "$(TOP)/mk/os161.config.mk"
SUBDIRS=add argtest badcall bigfile conman crash ctest dirconc dirseek \
- dirtest f_test farm faulter filetest forkbomb forktest guzzle \
+ dirtest f_test farm faulter fileonlytest filetest forkbomb forktest guzzle \
hash hog huge kitchen malloctest matmult palin parallelvm psort \
randcall rmdirtest rmtest sink sort sty tail tictac triplehuge \
- triplemat triplesort
+ triplemat triplesort ft1 ft2 ft3 ft4 pt1 pt2 pt3 pt4 pt5
# But not:
# userthreads (no support in kernel API in base system)
diff --git a/user/testbin/badcall/bad_execv.c b/user/testbin/badcall/bad_execv.c
index 287a678..2312381 100644
--- a/user/testbin/badcall/bad_execv.c
+++ b/user/testbin/badcall/bad_execv.c
@@ -34,6 +34,7 @@
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
+#include <stdio.h>
#include <errno.h>
#include <err.h>
@@ -62,6 +63,7 @@ exec_common_fork(void)
warn("UH-OH: waitpid failed");
return -1;
}
+
if (!WIFEXITED(status) || WEXITSTATUS(status) != MAGIC_STATUS) {
warnx("FAILURE: wrong exit code of subprocess");
}
diff --git a/user/testbin/fileonlytest/Makefile b/user/testbin/fileonlytest/Makefile
new file mode 100644
index 0000000..156fc41
--- /dev/null
+++ b/user/testbin/fileonlytest/Makefile
@@ -0,0 +1,11 @@
+# Makefile for fileonlytest
+
+TOP=../../..
+.include "$(TOP)/mk/os161.config.mk"
+
+PROG=fileonlytest
+SRCS=fileonlytest.c
+BINDIR=/testbin
+
+.include "$(TOP)/mk/os161.prog.mk"
+
diff --git a/user/testbin/fileonlytest/fileonlytest.c b/user/testbin/fileonlytest/fileonlytest.c
new file mode 100644
index 0000000..b807ff6
--- /dev/null
+++ b/user/testbin/fileonlytest/fileonlytest.c
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009
+ * The President and Fellows of Harvard College.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * rmtest.c
+ *
+ * Tests file system synchronization by deleting an open file and
+ * then attempting to read it.
+ *
+ * This should run correctly when the file system assignment is complete.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <err.h>
+
+// 23 Mar 2012 : GWA : BUFFER_COUNT must be even.
+
+#define BUFFER_COUNT 128
+#define BUFFER_SIZE 128
+
+static int writebuf[BUFFER_SIZE];
+static int readbuf[BUFFER_SIZE];
+
+int
+main(int argc, char **argv)
+{
+
+ // 23 Mar 2012 : GWA : Assume argument passing is *not* supported.
+
+ (void) argc;
+ (void) argv;
+ int i, j;
+ int fh, len;
+ off_t pos, target;
+
+ const char * filename = "fileonlytest.dat";
+
+ // 23 Mar 2012 : GWA : Test that open works.
+
+ printf("Opening %s\n", filename);
+
+ fh = open(filename, O_RDWR|O_CREAT|O_TRUNC);
+ if (fh < 0) {
+ err(1, "create failed");
+ }
+
+ printf("Writing %d bytes.\n", BUFFER_SIZE * BUFFER_COUNT);
+
+ // 23 Mar 2012 : GWA : Do the even-numbered writes. Test read() and
+ // lseek(SEEK_END).
+
+ for (i = 0; i < BUFFER_COUNT / 2; i++) {
+ for (j = 0; j < BUFFER_SIZE; j++) {
+ writebuf[j] = i * 2 * j;
+ }
+ len = write(fh, writebuf, sizeof(writebuf));
+ if (len != sizeof(writebuf)) {
+ err(1, "write failed");
+ }
+
+ // 23 Mar 2012 : GWA : Use lseek() to skip the odd guys.
+
+ target = (i + 1) * 2 * sizeof(writebuf);
+ pos = lseek(fh, sizeof(writebuf), SEEK_END);
+ if (pos != target) {
+ err(1, "(even) lseek failed: %llu != %llu", pos, target);
+ }
+ }
+
+ target = 0;
+ pos = lseek(fh, target, SEEK_SET);
+ if (pos != target) {
+ err(1, "(reset) lseek failed: %llu != %llu", pos, target);
+ }
+
+ // 23 Mar 2012 : GWA : Do the odd-numbered writes. Test write() and
+ // lseek(SEEK_CUR).
+
+ for (i = 0; i < BUFFER_COUNT / 2; i++) {
+
+ // 23 Mar 2012 : GWA : Use lseek() to skip the even guys.
+
+ target = ((i * 2) + 1) * sizeof(writebuf);
+ pos = lseek(fh, sizeof(writebuf), SEEK_CUR);
+ if (pos != target) {
+ err(1, "(odd) lseek failed: %llu != %llu", pos, target);
+ }
+
+ for (j = 0; j < BUFFER_SIZE; j++) {
+ writebuf[j] = ((i * 2) + 1) * j;
+ }
+ len = write(fh, writebuf, sizeof(writebuf));
+ if (len != sizeof(writebuf)) {
+ err(1, "write failed");
+ }
+ }
+
+ // 23 Mar 2012 : GWA : Read the test data back and make sure what we wrote
+ // ended up where we wrote it. Tests read() and lseek(SEEK_SET).
+
+ printf("Verifying write.\n");
+
+ for (i = BUFFER_COUNT - 1; i >= 0; i--) {
+ target = i * sizeof(writebuf);
+ pos = lseek(fh, target, SEEK_SET);
+ if (pos != target) {
+ err(1, "(verify) lseek failed: %llu != %llu", pos, target);
+ }
+ len = read(fh, readbuf, sizeof(readbuf));
+ if (len != sizeof(readbuf)) {
+ err(1, "read failed");
+ }
+ for (j = BUFFER_SIZE - 1; j >= 0; j--) {
+ if (readbuf[j] != i * j) {
+ err(1, "read mismatch: pos=%llu, readbuf[j]=%d, i*j=%d, i=%d, j=%d", pos, readbuf[j], i * j, i, j);
+ }
+ }
+ }
+
+ // 23 Mar 2012 : GWA : Close the file.
+
+ printf("Closing %s\n", filename);
+ close(fh);
+
+ // 23 Mar 2012 : GWA : Make sure the file is actually closed.
+
+ pos = lseek(fh, (off_t) 0, SEEK_SET);
+ if (pos == 0) {
+ err(1, "seek after close succeeded");
+ }
+
+ // 23 Mar 2012 : GWA : FIXME : Spin until exit() works.
+
+ printf("Spinning in case exit() doesn't work.\n");
+ while (1) {};
+
+ return 0;
+}
diff --git a/user/testbin/ft1/Makefile b/user/testbin/ft1/Makefile
new file mode 100644
index 0000000..318ed15
--- /dev/null
+++ b/user/testbin/ft1/Makefile
@@ -0,0 +1,11 @@
+# Makefile for tail
+
+TOP=../../..
+.include "$(TOP)/mk/os161.config.mk"
+
+PROG=ft1
+SRCS=ft1.c
+BINDIR=/testbin
+
+.include "$(TOP)/mk/os161.prog.mk"
+
diff --git a/user/testbin/ft1/ft1.c b/user/testbin/ft1/ft1.c
new file mode 100644
index 0000000..17b110a
--- /dev/null
+++ b/user/testbin/ft1/ft1.c
@@ -0,0 +1,24 @@
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int main( int argc, char *argv[] ) {
+ int fd_stdout = 0;
+
+ (void)argc;
+ (void)argv;
+
+ //attempt to open stdout
+ fd_stdout = open( "con:", O_WRONLY );
+ if( fd_stdout == -1 )
+ return -1;
+
+ //now we can write
+ char message[] = "Hello World!";
+ write( fd_stdout, message, sizeof( message ) );
+
+ //close the file
+ close( fd_stdout );
+
+ return 0;
+}
diff --git a/user/testbin/ft2/Makefile b/user/testbin/ft2/Makefile
new file mode 100644
index 0000000..8662011
--- /dev/null
+++ b/user/testbin/ft2/Makefile
@@ -0,0 +1,11 @@
+# Makefile for tail
+
+TOP=../../..
+.include "$(TOP)/mk/os161.config.mk"
+
+PROG=ft2
+SRCS=ft2.c
+BINDIR=/testbin
+
+.include "$(TOP)/mk/os161.prog.mk"
+
diff --git a/user/testbin/ft2/ft2.c b/user/testbin/ft2/ft2.c
new file mode 100644
index 0000000..c9f8020
--- /dev/null
+++ b/user/testbin/ft2/ft2.c
@@ -0,0 +1,37 @@
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define BUF_SIZE 128
+
+int main( int argc, char *argv[] ) {
+ int fd_stdout;
+ int fd_in;
+ char buf[BUF_SIZE];
+ int nread;
+
+ (void)argc;
+ (void)argv;
+
+ //attempt to open stdout
+ fd_stdout = open( "con:", O_WRONLY );
+ if( fd_stdout == -1 )
+ return -1;
+
+ //attempt to open input file
+ fd_in = open( "/hello.txt", O_RDONLY );
+ if( fd_in == -1 ) {
+ close( fd_stdout );
+ return -1;
+ }
+
+ //read and output to stdout
+ while( ( nread = read( fd_in, buf, sizeof( buf ) ) ) > 0 )
+ write( fd_stdout, buf, nread );
+
+ //close the files
+ close( fd_in );
+ close( fd_stdout );
+
+ return 0;
+}
diff --git a/user/testbin/ft3/Makefile b/user/testbin/ft3/Makefile
new file mode 100644
index 0000000..0ba365b
--- /dev/null
+++ b/user/testbin/ft3/Makefile
@@ -0,0 +1,11 @@
+# Makefile for tail
+
+TOP=../../..
+.include "$(TOP)/mk/os161.config.mk"
+
+PROG=ft3
+SRCS=ft3.c
+BINDIR=/testbin
+
+.include "$(TOP)/mk/os161.prog.mk"
+
diff --git a/user/testbin/ft3/ft3.c b/user/testbin/ft3/ft3.c
new file mode 100644
index 0000000..8a96c0e
--- /dev/null
+++ b/user/testbin/ft3/ft3.c
@@ -0,0 +1,42 @@
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define BUF_SIZE 128
+
+int main( int argc, char *argv[] ) {
+ int fd_out;
+ int err;
+
+ (void)argc;
+ (void)argv;
+
+ //try to open a sample output file.
+ fd_out = open( "/hello.txt", O_WRONLY );
+ if( fd_out < 0 ) {
+ printf( "open: error opening the file." );
+ return -1;
+ }
+
+ //close stdout.
+ err = close( STDOUT_FILENO );
+ if( err ) {
+ printf( "close: could not close stdout." );
+ return -1;
+ }
+
+ //route stdout to fd_out.
+ dup2( fd_out, STDOUT_FILENO );
+
+ //print a sample message to STDOUT.
+ //this should end up inside the file.
+ printf( "I should be seeing this message inside hello.txt\n" );
+
+ //close fd_out
+ err = close( fd_out );
+ if( err )
+ return err;
+
+ //stdout will be closed by exit().
+ return 0;
+}
diff --git a/user/testbin/ft4/Makefile b/user/testbin/ft4/Makefile
new file mode 100644
index 0000000..6d532e4
--- /dev/null
+++ b/user/testbin/ft4/Makefile
@@ -0,0 +1,11 @@
+# Makefile for tail
+
+TOP=../../..
+.include "$(TOP)/mk/os161.config.mk"
+
+PROG=ft4
+SRCS=ft4.c
+BINDIR=/testbin
+
+.include "$(TOP)/mk/os161.prog.mk"
+
diff --git a/user/testbin/ft4/ft4.c b/user/testbin/ft4/ft4.c
new file mode 100644
index 0000000..3b18e6a
--- /dev/null
+++ b/user/testbin/ft4/ft4.c
@@ -0,0 +1,37 @@
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define BUF_SIZE 128
+
+static
+int
+test_chdir( const char *path ) {
+ int err;
+
+ putchar( '\n' );
+
+ printf( "attempting to switch directories to %s\n", path );
+ err = chdir( path );
+ if( err ) {
+ printf( "failed to switch directories to %s\n", path );
+ return -1;
+ }
+
+ printf( "successfully switched to %s\n", path );
+ return 0;
+}
+
+int main( int argc, char *argv[] ) {
+ (void)argc;
+ (void)argv;
+
+ if( test_chdir( "/testbin" ) )
+ return -1;
+
+ if( test_chdir( "/" ) )
+ return -1;
+
+
+ return 0;
+}
diff --git a/user/testbin/pt1/Makefile b/user/testbin/pt1/Makefile
new file mode 100644
index 0000000..6ba378a
--- /dev/null
+++ b/user/testbin/pt1/Makefile
@@ -0,0 +1,11 @@
+# Makefile for tail
+
+TOP=../../..
+.include "$(TOP)/mk/os161.config.mk"
+
+PROG=pt1
+SRCS=pt1.c
+BINDIR=/testbin
+
+.include "$(TOP)/mk/os161.prog.mk"
+
diff --git a/user/testbin/pt1/pt1.c b/user/testbin/pt1/pt1.c
new file mode 100644
index 0000000..edae1a7
--- /dev/null
+++ b/user/testbin/pt1/pt1.c
@@ -0,0 +1,11 @@
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int main( int argc, char *argv[] ) {
+ (void)argc;
+ (void)argv;
+
+ printf( "my pid is %d\n", getpid() );
+ return 0;
+}
diff --git a/user/testbin/pt2/Makefile b/user/testbin/pt2/Makefile
new file mode 100644
index 0000000..515e24e
--- /dev/null
+++ b/user/testbin/pt2/Makefile
@@ -0,0 +1,11 @@
+# Makefile for tail
+
+TOP=../../..
+.include "$(TOP)/mk/os161.config.mk"
+
+PROG=pt2
+SRCS=pt2.c
+BINDIR=/testbin
+
+.include "$(TOP)/mk/os161.prog.mk"
+
diff --git a/user/testbin/pt2/pt2.c b/user/testbin/pt2/pt2.c
new file mode 100644
index 0000000..5b0a41e
--- /dev/null
+++ b/user/testbin/pt2/pt2.c
@@ -0,0 +1,23 @@
+#include <unistd.h>
+#include <sys/wait.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int main( int argc, char *argv[] ) {
+ pid_t pid;
+ int res;
+
+ (void)argc;
+ (void)argv;
+
+ pid = fork();
+ if( pid == 0 ) {
+ printf( "Hi. I'm the child. My PID is: %d\n", getpid() );
+ }
+ else {
+ waitpid( pid, &res, 0 );
+ printf( "Hi. I'm the parent. My child returned: %d\n", res );
+ }
+
+ return 0;
+}
diff --git a/user/testbin/pt3/Makefile b/user/testbin/pt3/Makefile
new file mode 100644
index 0000000..2cc84f9
--- /dev/null
+++ b/user/testbin/pt3/Makefile
@@ -0,0 +1,11 @@
+# Makefile for tail
+
+TOP=../../..
+.include "$(TOP)/mk/os161.config.mk"
+
+PROG=pt3
+SRCS=pt3.c
+BINDIR=/testbin
+
+.include "$(TOP)/mk/os161.prog.mk"
+
diff --git a/user/testbin/pt3/pt3.c b/user/testbin/pt3/pt3.c
new file mode 100644
index 0000000..07da59f
--- /dev/null
+++ b/user/testbin/pt3/pt3.c
@@ -0,0 +1,30 @@
+#include <unistd.h>
+#include <sys/wait.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int main( int argc, char *argv[] ) {
+ pid_t pid;
+ int res;
+ const char *args[] = { "hello", "world", NULL };
+
+ (void)argc;
+ (void)argv;
+
+ pid = fork();
+ if( pid == 0 ) {
+ printf( "Hi. I'm the child. My PID is: %d\n", getpid() );
+ printf( "I'm about to call exec ....\n" );
+ res = execv( "/testbin/pt1", (char **)args );
+ if( res ) {
+ printf( "execv failed." );
+ return -1;
+ }
+ }
+ else {
+ waitpid( pid, &res, 0 );
+ printf( "Hi. I'm the parent. My child returned: %d\n", res );
+ }
+
+ return 0;
+}
diff --git a/user/testbin/pt4/Makefile b/user/testbin/pt4/Makefile
new file mode 100644
index 0000000..9ac3773
--- /dev/null
+++ b/user/testbin/pt4/Makefile
@@ -0,0 +1,11 @@
+# Makefile for tail
+
+TOP=../../..
+.include "$(TOP)/mk/os161.config.mk"
+
+PROG=pt4
+SRCS=pt4.c
+BINDIR=/testbin
+
+.include "$(TOP)/mk/os161.prog.mk"
+
diff --git a/user/testbin/pt4/pt4.c b/user/testbin/pt4/pt4.c
new file mode 100644
index 0000000..60867c8
--- /dev/null
+++ b/user/testbin/pt4/pt4.c
@@ -0,0 +1,16 @@
+#include <unistd.h>
+#include <sys/wait.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int main( int argc, char *argv[] ) {
+ const char *args[] = { "foo", "os161", "execv", NULL };
+
+ (void) argc;
+ (void) argv;
+
+ printf( "about to call execv\n" );
+ execv( "/testbin/pt5", (char **)args );
+
+ return 0;
+}
diff --git a/user/testbin/pt5/Makefile b/user/testbin/pt5/Makefile
new file mode 100644
index 0000000..8fc5a80
--- /dev/null
+++ b/user/testbin/pt5/Makefile
@@ -0,0 +1,11 @@
+# Makefile for tail
+
+TOP=../../..
+.include "$(TOP)/mk/os161.config.mk"
+
+PROG=pt5
+SRCS=pt5.c
+BINDIR=/testbin
+
+.include "$(TOP)/mk/os161.prog.mk"
+
diff --git a/user/testbin/pt5/pt5.c b/user/testbin/pt5/pt5.c
new file mode 100644
index 0000000..2e25a4f
--- /dev/null
+++ b/user/testbin/pt5/pt5.c
@@ -0,0 +1,16 @@
+#include <unistd.h>
+#include <sys/wait.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int main( int argc, char *argv[] ) {
+ int i;
+
+ printf( "\ngot %d arguments\n", argc );
+ printf( "the address of argv is: %p\n", argv );
+
+ for( i = 0; i < argc; ++i )
+ printf( "argv[%d] = %s\n", i, argv[i] );
+
+ return 0;
+}