diff --git a/context/memory.rs b/context/memory.rs
index 3121794dc5706034e34bc82e03d33d06a1ec9a96..143ee531b37d8fc9db19b756fce5cfe26e160c1a 100644
--- a/context/memory.rs
+++ b/context/memory.rs
@@ -3,7 +3,8 @@ use collections::VecDeque;
 use spin::Mutex;
 
 use arch::externs::memset;
-use arch::paging::{ActivePageTable, InactivePageTable, Page, PageIter, VirtualAddress};
+use arch::memory::Frame;
+use arch::paging::{ActivePageTable, InactivePageTable, Page, PageIter, PhysicalAddress, VirtualAddress};
 use arch::paging::entry::{self, EntryFlags};
 use arch::paging::temporary_page::TemporaryPage;
 
@@ -55,6 +56,47 @@ impl Grant {
         });
     }
 
+    pub fn physmap(from: PhysicalAddress, to: VirtualAddress, size: usize, flags: EntryFlags) -> Grant {
+        let mut active_table = unsafe { ActivePageTable::new() };
+
+        let mut flush_all = false;
+
+        let start_page = Page::containing_address(to);
+        let end_page = Page::containing_address(VirtualAddress::new(to.get() + size - 1));
+        for page in Page::range_inclusive(start_page, end_page) {
+            let frame = Frame::containing_address(PhysicalAddress::new(page.start_address().get() - to.get() + from.get()));
+            active_table.map_to(page, frame, flags);
+            flush_all = true;
+        }
+
+        if flush_all {
+            active_table.flush_all();
+        }
+
+        Grant {
+            start: to,
+            size: size,
+            flags: flags
+        }
+    }
+
+    pub fn physunmap(self) {
+        let mut active_table = unsafe { ActivePageTable::new() };
+
+        let mut flush_all = false;
+
+        let start_page = Page::containing_address(self.start);
+        let end_page = Page::containing_address(VirtualAddress::new(self.start.get() + self.size - 1));
+        for page in Page::range_inclusive(start_page, end_page) {
+            active_table.unmap_return(page);
+            flush_all = true;
+        }
+
+        if flush_all {
+            active_table.flush_all();
+        }
+    }
+
     pub fn start_address(&self) -> VirtualAddress {
         self.start
     }
diff --git a/scheme/initfs.rs b/scheme/initfs.rs
index 84df203dc461eb0c91efd97c57c8b4c439a90ace..525e0a7280020f900245702da99e42f8e4946707 100644
--- a/scheme/initfs.rs
+++ b/scheme/initfs.rs
@@ -7,6 +7,9 @@ use syscall::error::*;
 use syscall::flag::{SEEK_SET, SEEK_CUR, SEEK_END};
 use syscall::scheme::Scheme;
 
+#[path="../../build/userspace/initfs.rs"]
+mod gen;
+
 struct Handle {
     data: &'static [u8],
     seek: usize
@@ -20,18 +23,9 @@ pub struct InitFsScheme {
 
 impl InitFsScheme {
     pub fn new() -> InitFsScheme {
-        let mut files: BTreeMap<&'static [u8], &'static [u8]> = BTreeMap::new();
-
-        files.insert(b"bin/init", include_bytes!("../../build/userspace/init"));
-        files.insert(b"bin/ion", include_bytes!("../../build/userspace/ion"));
-        files.insert(b"bin/pcid", include_bytes!("../../build/userspace/pcid"));
-        files.insert(b"bin/ps2d", include_bytes!("../../build/userspace/ps2d"));
-        files.insert(b"bin/example", include_bytes!("../../build/userspace/example"));
-        files.insert(b"etc/init.rc", b"initfs:bin/pcid\ninitfs:bin/ps2d\ninitfs:bin/example\ninitfs:bin/ion");
-
         InitFsScheme {
             next_id: AtomicUsize::new(0),
-            files: files,
+            files: gen::gen(),
             handles: RwLock::new(BTreeMap::new())
         }
     }
diff --git a/syscall/mod.rs b/syscall/mod.rs
index b37a056be9bc4e00b96492a32f55b4c16003a7b2..5ade1675006aaca31ac4631f6e1695cee138fac2 100644
--- a/syscall/mod.rs
+++ b/syscall/mod.rs
@@ -44,6 +44,8 @@ pub extern fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize
             SYS_CLONE => clone(b, stack),
             SYS_YIELD => sched_yield(),
             SYS_GETCWD => getcwd(validate_slice_mut(b as *mut u8, c)?),
+            SYS_PHYSMAP => physmap(b, c, d),
+            SYS_PHYSUNMAP => physunmap(b),
             _ => {
                 println!("Unknown syscall {}", a);
                 Err(Error::new(ENOSYS))
diff --git a/syscall/process.rs b/syscall/process.rs
index 3d94a1170563073b5a29c08c81b0e4b051cb84ff..6a91e473823e4238f018efeaec99906ada3e1bda 100644
--- a/syscall/process.rs
+++ b/syscall/process.rs
@@ -9,14 +9,16 @@ use spin::Mutex;
 use arch;
 use arch::externs::memcpy;
 use arch::memory::allocate_frame;
-use arch::paging::{ActivePageTable, InactivePageTable, Page, VirtualAddress, entry};
+use arch::paging::{ActivePageTable, InactivePageTable, Page, PhysicalAddress, VirtualAddress, entry};
 use arch::paging::temporary_page::TemporaryPage;
 use arch::start::usermode;
 use context;
+use context::memory::Grant;
 use elf::{self, program_header};
 use scheme;
 use syscall;
 use syscall::error::*;
+use syscall::flag::{CLONE_VM, CLONE_FS, CLONE_FILES, MAP_WRITE, MAP_WRITE_COMBINE};
 use syscall::validate::{validate_slice, validate_slice_mut};
 
 pub fn brk(address: usize) -> Result<usize> {
@@ -51,16 +53,7 @@ pub fn brk(address: usize) -> Result<usize> {
     }
 }
 
-pub const CLONE_VM: usize = 0x100;
-pub const CLONE_FS: usize = 0x200;
-pub const CLONE_FILES: usize = 0x400;
-pub const CLONE_VFORK: usize = 0x4000;
 pub fn clone(flags: usize, stack_base: usize) -> Result<usize> {
-    //TODO: Copy on write?
-
-    // vfork not supported
-    assert!(flags & CLONE_VFORK == 0);
-
     let ppid;
     let pid;
     {
@@ -500,6 +493,83 @@ pub fn iopl(_level: usize) -> Result<usize> {
     Ok(0)
 }
 
+//TODO: verify exlusive access to physical memory
+pub fn physmap(physical_address: usize, size: usize, flags: usize) -> Result<usize> {
+    if size == 0 {
+        Ok(0)
+    } else {
+        let contexts = context::contexts();
+        let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
+        let context = context_lock.read();
+
+        let mut grants = context.grants.lock();
+
+        let from_address = (physical_address/4096) * 4096;
+        let offset = physical_address - from_address;
+        let full_size = ((offset + size + 4095)/4096) * 4096;
+        let mut to_address = arch::USER_GRANT_OFFSET;
+
+        let mut entry_flags = entry::PRESENT | entry::NO_EXECUTE | entry::USER_ACCESSIBLE;
+        if flags & MAP_WRITE == MAP_WRITE {
+            entry_flags |= entry::WRITABLE;
+        }
+        if flags & MAP_WRITE_COMBINE == MAP_WRITE_COMBINE {
+            entry_flags |= entry::HUGE_PAGE;
+        }
+
+        for i in 0 .. grants.len() {
+            let start = grants[i].start_address().get();
+            if to_address + full_size < start {
+                grants.insert(i, Grant::physmap(
+                    PhysicalAddress::new(from_address),
+                    VirtualAddress::new(to_address),
+                    full_size,
+                    entry_flags
+                ));
+
+                return Ok(to_address + offset);
+            } else {
+                let pages = (grants[i].size() + 4095) / 4096;
+                let end = start + pages * 4096;
+                to_address = end;
+            }
+        }
+
+        grants.push(Grant::physmap(
+            PhysicalAddress::new(from_address),
+            VirtualAddress::new(to_address),
+            full_size,
+            entry_flags
+        ));
+
+        Ok(to_address + offset)
+    }
+}
+
+pub fn physunmap(virtual_address: usize) -> Result<usize> {
+    if virtual_address == 0 {
+        Ok(0)
+    } else {
+        let contexts = context::contexts();
+        let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
+        let context = context_lock.read();
+
+        let mut grants = context.grants.lock();
+
+        for i in 0 .. grants.len() {
+            let start = grants[i].start_address().get();
+            let end = start + grants[i].size();
+            if virtual_address >= start && virtual_address < end {
+                grants.remove(i).physunmap();
+
+                return Ok(0);
+            }
+        }
+
+        Err(Error::new(EFAULT))
+    }
+}
+
 pub fn sched_yield() -> Result<usize> {
     unsafe { context::switch(); }
     Ok(0)