diff --git a/src/arch/x86_64/start.rs b/src/arch/x86_64/start.rs
index cdf213759a5010fcba5825019cd8d18c1f933e9c..8c70df825d21e5d10d04284dd4336ab0b0e1b042 100644
--- a/src/arch/x86_64/start.rs
+++ b/src/arch/x86_64/start.rs
@@ -3,6 +3,7 @@
 /// It must create the IDT with the correct entries, those entries are
 /// defined in other files inside of the `arch` module
 
+use core::slice;
 use core::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
 
 use acpi;
@@ -32,31 +33,28 @@ pub static CPU_COUNT: AtomicUsize = ATOMIC_USIZE_INIT;
 pub static AP_READY: AtomicBool = ATOMIC_BOOL_INIT;
 static BSP_READY: AtomicBool = ATOMIC_BOOL_INIT;
 
-extern {
-    /// Kernel main function
-    fn kmain(cpus: usize) -> !;
-    /// Kernel main for APs
-    fn kmain_ap(id: usize) -> !;
-}
-
 #[repr(packed)]
 pub struct KernelArgs {
     kernel_base: u64,
     kernel_size: u64,
     stack_base: u64,
     stack_size: u64,
+    env_base: u64,
+    env_size: u64,
 }
 
 /// The entry to Rust, all things must be initialized
 #[no_mangle]
 pub unsafe extern fn kstart(args_ptr: *const KernelArgs) -> ! {
-    {
+    let env = {
         let args = &*args_ptr;
 
         let kernel_base = args.kernel_base as usize;
         let kernel_size = args.kernel_size as usize;
         let stack_base = args.stack_base as usize;
         let stack_size = args.stack_size as usize;
+        let env_base = args.env_base as usize;
+        let env_size = args.env_size as usize;
 
         // BSS should already be zero
         {
@@ -69,6 +67,7 @@ pub unsafe extern fn kstart(args_ptr: *const KernelArgs) -> ! {
 
         println!("Kernel: {:X}:{:X}", kernel_base, kernel_base + kernel_size);
         println!("Stack: {:X}:{:X}", stack_base, stack_base + stack_size);
+        println!("Env: {:X}:{:X}", env_base, env_base + env_size);
 
         // Initialize memory management
         memory::init(0, kernel_base + ((kernel_size + 4095)/4096) * 4096);
@@ -128,9 +127,11 @@ pub unsafe extern fn kstart(args_ptr: *const KernelArgs) -> ! {
         memory::init_noncore();
 
         BSP_READY.store(true, Ordering::SeqCst);
-    }
 
-    kmain(CPU_COUNT.load(Ordering::SeqCst));
+        slice::from_raw_parts(env_base as *const u8, env_size)
+    };
+
+    ::kmain(CPU_COUNT.load(Ordering::SeqCst), env);
 }
 
 #[repr(packed)]
@@ -184,7 +185,7 @@ pub unsafe extern fn kstart_ap(args_ptr: *const KernelArgsAp) -> ! {
         interrupt::pause();
     }
 
-    kmain_ap(cpu_id);
+    ::kmain_ap(cpu_id);
 }
 
 pub unsafe fn usermode(ip: usize, sp: usize, arg: usize) -> ! {
diff --git a/src/lib.rs b/src/lib.rs
index a848177bcbc78721edbb888d243bbb9eac1f9ec1..92c2dbbceaf7e072982ea029f26daa778e3a37ce 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -36,7 +36,10 @@ extern crate bitflags;
 extern crate goblin;
 extern crate spin;
 
+use alloc::arc::Arc;
 use core::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
+use spin::Mutex;
+
 use scheme::FileHandle;
 
 pub use consts::*;
@@ -129,8 +132,7 @@ pub extern fn userspace_init() {
 }
 
 /// This is the kernel entry point for the primary CPU. The arch crate is responsible for calling this
-#[no_mangle]
-pub extern fn kmain(cpus: usize) {
+pub fn kmain(cpus: usize, env: &[u8]) -> ! {
     CPU_ID.store(0, Ordering::SeqCst);
     CPU_COUNT.store(cpus, Ordering::SeqCst);
 
@@ -138,11 +140,25 @@ pub extern fn kmain(cpus: usize) {
 
     let pid = syscall::getpid();
     println!("BSP: {:?} {}", pid, cpus);
+    println!("Env: {:?}", ::core::str::from_utf8(env));
 
     match context::contexts_mut().spawn(userspace_init) {
         Ok(context_lock) => {
             let mut context = context_lock.write();
             context.status = context::Status::Runnable;
+
+            let mut context_env = context.env.lock();
+            for line in env.split(|b| *b == b'\n') {
+                let mut parts = line.splitn(2, |b| *b == b'=');
+                if let Some(name) = parts.next() {
+                    if let Some(data) = parts.next() {
+                        context_env.insert(
+                            name.to_vec().into_boxed_slice(),
+                            Arc::new(Mutex::new(data.to_vec()))
+                        );
+                    }
+                }
+            }
         },
         Err(err) => {
             panic!("failed to spawn userspace_init: {:?}", err);
@@ -163,21 +179,11 @@ pub extern fn kmain(cpus: usize) {
 }
 
 /// This is the main kernel entry point for secondary CPUs
-#[no_mangle]
 #[allow(unreachable_code, unused_variables)]
-pub extern fn kmain_ap(id: usize) {
-    println!("AP {}: Disabled", id);
-
-    loop {
-        unsafe {
-            interrupt::disable();
-            interrupt::halt();
-        }
-    }
+pub fn kmain_ap(id: usize) -> ! {
+    CPU_ID.store(id, Ordering::SeqCst);
 
     if cfg!(feature = "multi_core"){
-        CPU_ID.store(id, Ordering::SeqCst);
-
         context::init();
 
         let pid = syscall::getpid();
@@ -194,8 +200,16 @@ pub extern fn kmain_ap(id: usize) {
                 }
             }
         }
-    }
+    } else {
+        println!("AP {}: Disabled", id);
 
+        loop {
+            unsafe {
+                interrupt::disable();
+                interrupt::halt();
+            }
+        }
+    }
 }
 
 /// Allow exception handlers to send signal to arch-independant kernel