diff --git a/context/list.rs b/context/list.rs
index b9cb97284b57e76493763c398718f1bb1c0649f6..2154861635eeab5a47e672b2867769d5738da742 100644
--- a/context/list.rs
+++ b/context/list.rs
@@ -77,4 +77,8 @@ impl ContextList {
         }
         Ok(context_lock)
     }
+
+    pub fn remove(&mut self, id: usize) -> Option<RwLock<Context>> {
+        self.map.remove(&id)
+    }
 }
diff --git a/syscall/call.rs b/syscall/call.rs
new file mode 100644
index 0000000000000000000000000000000000000000..e62b27d84ad7b30421dd2189b5f0afe9a1ab8637
--- /dev/null
+++ b/syscall/call.rs
@@ -0,0 +1,57 @@
+use super::{Error, Result};
+
+/// System call list
+/// See http://syscalls.kernelgrok.com/ for numbers
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+#[repr(C)]
+pub enum Call {
+    /// Exit syscall
+    Exit = 1,
+    /// Read syscall
+    Read = 3,
+    /// Write syscall
+    Write = 4,
+    /// Open syscall
+    Open = 5,
+    /// Close syscall
+    Close = 6,
+    /// Wait for a process
+    WaitPid = 7,
+    /// Execute syscall
+    Exec = 11,
+    /// Get process ID
+    GetPid = 20,
+    /// Duplicate file descriptor
+    Dup = 41,
+    /// Set process break
+    Brk = 45,
+    /// Set process I/O privilege level
+    Iopl = 110,
+    /// Clone process
+    Clone = 120,
+    /// Yield to scheduler
+    SchedYield = 158
+}
+
+/// Convert numbers to calls
+/// See http://syscalls.kernelgrok.com/
+impl Call {
+    pub fn from(number: usize) -> Result<Call> {
+        match number {
+            1 => Ok(Call::Exit),
+            3 => Ok(Call::Read),
+            4 => Ok(Call::Write),
+            5 => Ok(Call::Open),
+            6 => Ok(Call::Close),
+            7 => Ok(Call::WaitPid),
+            11 => Ok(Call::Exec),
+            20 => Ok(Call::GetPid),
+            41 => Ok(Call::Dup),
+            45 => Ok(Call::Brk),
+            110 => Ok(Call::Iopl),
+            120 => Ok(Call::Clone),
+            158 => Ok(Call::SchedYield),
+            _ => Err(Error::NoCall)
+        }
+    }
+}
diff --git a/syscall/error.rs b/syscall/error.rs
new file mode 100644
index 0000000000000000000000000000000000000000..ad37b336e0fdf80cd1d237ddbf2bac8a5cd9ce88
--- /dev/null
+++ b/syscall/error.rs
@@ -0,0 +1,28 @@
+/// The error number for an invalid value
+/// See http://www-numi.fnal.gov/offline_software/srt_public_context/WebDocs/Errors/unix_system_errors.html for numbers
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+#[repr(C)]
+pub enum Error {
+    /// Operation not permitted
+    NotPermitted = 1,
+    /// No such file or directory
+    NoEntry = 2,
+    /// No such process
+    NoProcess = 3,
+    /// Invalid executable format
+    NoExec = 8,
+    /// Bad file number
+    BadFile = 9,
+    /// Try again
+    TryAgain = 11,
+    /// File exists
+    FileExists = 17,
+    /// Invalid argument
+    InvalidValue = 22,
+    /// Too many open files
+    TooManyFiles = 24,
+    /// Syscall not implemented
+    NoCall = 38
+}
+
+pub type Result<T> = ::core::result::Result<T, Error>;
diff --git a/syscall/mod.rs b/syscall/mod.rs
index bd53ad479f3dbe283d2145922bf6cbd3555f5771..f27d009089c5bbcffc4f4fd0ab06b9374164437f 100644
--- a/syscall/mod.rs
+++ b/syscall/mod.rs
@@ -1,109 +1,25 @@
 ///! Syscall handlers
 
-use core::slice;
-
+pub use self::call::*;
+pub use self::error::*;
 pub use self::fs::*;
 pub use self::process::*;
+pub use self::validate::*;
 
-/// Filesystem syscalls
-pub mod fs;
-
-/// Process syscalls
-pub mod process;
+/// System call numbers
+mod call;
 
-/// System call list
-/// See http://syscalls.kernelgrok.com/ for numbers
-#[derive(Copy, Clone, Debug, Eq, PartialEq)]
-#[repr(C)]
-pub enum Call {
-    /// Exit syscall
-    Exit = 1,
-    /// Read syscall
-    Read = 3,
-    /// Write syscall
-    Write = 4,
-    /// Open syscall
-    Open = 5,
-    /// Close syscall
-    Close = 6,
-    /// Execute syscall
-    Exec = 11,
-    /// Get process ID
-    GetPid = 20,
-    /// Duplicate file descriptor
-    Dup = 41,
-    /// Set process break
-    Brk = 45,
-    /// Set process I/O privilege level
-    Iopl = 110,
-    /// Clone process
-    Clone = 120,
-    /// Yield to scheduler
-    SchedYield = 158
-}
+/// System error codes
+mod error;
 
-/// Convert numbers to calls
-/// See http://syscalls.kernelgrok.com/
-impl Call {
-    fn from(number: usize) -> Result<Call> {
-        match number {
-            1 => Ok(Call::Exit),
-            3 => Ok(Call::Read),
-            4 => Ok(Call::Write),
-            5 => Ok(Call::Open),
-            6 => Ok(Call::Close),
-            11 => Ok(Call::Exec),
-            20 => Ok(Call::GetPid),
-            41 => Ok(Call::Dup),
-            45 => Ok(Call::Brk),
-            110 => Ok(Call::Iopl),
-            120 => Ok(Call::Clone),
-            158 => Ok(Call::SchedYield),
-            _ => Err(Error::NoCall)
-        }
-    }
-}
-
-/// The error number for an invalid value
-/// See http://www-numi.fnal.gov/offline_software/srt_public_context/WebDocs/Errors/unix_system_errors.html for numbers
-#[derive(Copy, Clone, Debug, Eq, PartialEq)]
-#[repr(C)]
-pub enum Error {
-    /// Operation not permitted
-    NotPermitted = 1,
-    /// No such file or directory
-    NoEntry = 2,
-    /// No such process
-    NoProcess = 3,
-    /// Invalid executable format
-    NoExec = 8,
-    /// Bad file number
-    BadFile = 9,
-    /// Try again
-    TryAgain = 11,
-    /// File exists
-    FileExists = 17,
-    /// Invalid argument
-    InvalidValue = 22,
-    /// Too many open files
-    TooManyFiles = 24,
-    /// Syscall not implemented
-    NoCall = 38
-}
-
-pub type Result<T> = ::core::result::Result<T, Error>;
+/// Filesystem syscalls
+mod fs;
 
-/// Convert a pointer and length to slice, if valid
-/// TODO: Check validity
-pub fn convert_slice<T>(ptr: *const T, len: usize) -> Result<&'static [T]> {
-    Ok(unsafe { slice::from_raw_parts(ptr, len) })
-}
+/// Process syscalls
+mod process;
 
-/// Convert a pointer and length to slice, if valid
-/// TODO: Check validity
-pub fn convert_slice_mut<T>(ptr: *mut T, len: usize) -> Result<&'static mut [T]> {
-    Ok(unsafe { slice::from_raw_parts_mut(ptr, len) })
-}
+/// Validate input
+mod validate;
 
 #[no_mangle]
 pub extern fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, stack: usize) -> usize {
@@ -112,11 +28,12 @@ pub extern fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize
         match Call::from(a) {
             Ok(call) => match call {
                 Call::Exit => exit(b),
-                Call::Read => read(b, convert_slice_mut(c as *mut u8, d)?),
-                Call::Write => write(b, convert_slice(c as *const u8, d)?),
-                Call::Open => open(convert_slice(b as *const u8, c)?, d),
+                Call::Read => read(b, validate_slice_mut(c as *mut u8, d)?),
+                Call::Write => write(b, validate_slice(c as *const u8, d)?),
+                Call::Open => open(validate_slice(b as *const u8, c)?, d),
                 Call::Close => close(b),
-                Call::Exec => exec(convert_slice(b as *const u8, c)?, convert_slice(d as *const [usize; 2], e)?),
+                Call::WaitPid => waitpid(b, c, d),
+                Call::Exec => exec(validate_slice(b as *const u8, c)?, validate_slice(d as *const [usize; 2], e)?),
                 Call::GetPid => getpid(),
                 Call::Dup => dup(b),
                 Call::Brk => brk(b),
diff --git a/syscall/process.rs b/syscall/process.rs
index 38ed84b6cc4c24e2b3612573644be8bf80d151e4..4530297a7470ab014d4794bc5859815f2d7644ca 100644
--- a/syscall/process.rs
+++ b/syscall/process.rs
@@ -363,3 +363,27 @@ pub fn sched_yield() -> Result<usize> {
     unsafe { context::switch(); }
     Ok(0)
 }
+
+pub fn waitpid(pid: usize, _status_ptr: usize, _options: usize) -> Result<usize> {
+    loop {
+        {
+            let mut exited = false;
+
+            {
+                let contexts = context::contexts();
+                let context_lock = contexts.get(pid).ok_or(Error::NoProcess)?;
+                let context = context_lock.read();
+                if context.status == context::Status::Exited {
+                    exited = true;
+                }
+            }
+
+            if exited {
+                let mut contexts = context::contexts_mut();
+                return contexts.remove(pid).ok_or(Error::NoProcess).and(Ok(pid));
+            }
+        }
+
+        unsafe { context::switch(); }
+    }
+}
diff --git a/syscall/validate.rs b/syscall/validate.rs
new file mode 100644
index 0000000000000000000000000000000000000000..61c73841f34b454f9346bbb39b938d0af94cd6cb
--- /dev/null
+++ b/syscall/validate.rs
@@ -0,0 +1,15 @@
+use core::slice;
+
+use super::Result;
+
+/// Convert a pointer and length to slice, if valid
+/// TODO: Check validity
+pub fn validate_slice<T>(ptr: *const T, len: usize) -> Result<&'static [T]> {
+    Ok(unsafe { slice::from_raw_parts(ptr, len) })
+}
+
+/// Convert a pointer and length to slice, if valid
+/// TODO: Check validity
+pub fn validate_slice_mut<T>(ptr: *mut T, len: usize) -> Result<&'static mut [T]> {
+    Ok(unsafe { slice::from_raw_parts_mut(ptr, len) })
+}