diff --git a/asm/x86-unknown-none/gdt.asm b/asm/x86-unknown-none/gdt.asm
index 773139a79a999087b7127580f0d48477d020e5d0..925954ae17d12431a4a1a5f5addf41e89ce22505 100644
--- a/asm/x86-unknown-none/gdt.asm
+++ b/asm/x86-unknown-none/gdt.asm
@@ -58,7 +58,7 @@ gdt_flag:
 
 gdtr:
     dw gdt.end + 1  ; size
-    dd gdt          ; offset
+    dq gdt          ; offset
 
 gdt:
 .null equ $ - gdt
diff --git a/asm/x86-unknown-none/long_mode.asm b/asm/x86-unknown-none/long_mode.asm
index de5f80203845e4d728888db6a39e3d49474f475c..3a01d21e973faef750d1ebf22e4fdfe95ac7a9e7 100644
--- a/asm/x86-unknown-none/long_mode.asm
+++ b/asm/x86-unknown-none/long_mode.asm
@@ -9,16 +9,12 @@ long_mode:
     ; disable interrupts
     cli
 
-    ;disable paging
+    ; disable paging
     mov eax, cr0
     and eax, 0x7FFFFFFF
     mov cr0, eax
 
-    ;cr3 holds pointer to PML4
-    mov eax, [.page_table]
-    mov cr3, eax
-
-    ;enable OSXSAVE, FXSAVE/FXRSTOR, Page Global, Page Address Extension, and Page Size Extension
+    ; enable OSXSAVE, FXSAVE/FXRSTOR, Page Global, Page Address Extension, and Page Size Extension
     mov eax, cr4
     or eax, 1 << 18 | 1 << 9 | 1 << 7 | 1 << 5 | 1 << 4
     mov cr4, eax
@@ -32,6 +28,10 @@ long_mode:
     or eax, 1 << 11 | 1 << 8          ; Set the Long-Mode-Enable and NXE bit.
     wrmsr
 
+    ; set page table
+    mov eax, [.page_table]
+    mov cr3, eax
+
     ; enabling paging and protection simultaneously
     mov eax, cr0
     or eax, 1 << 31 | 1 << 16 | 1                ;Bit 31: Paging, Bit 16: write protect kernel, Bit 0: Protected Mode
diff --git a/asm/x86-unknown-none/memory_map.asm b/asm/x86-unknown-none/memory_map.asm
new file mode 100644
index 0000000000000000000000000000000000000000..460ea27b746200d233eb8418f842aece1c0b2d50
--- /dev/null
+++ b/asm/x86-unknown-none/memory_map.asm
@@ -0,0 +1,33 @@
+SECTION .text
+USE16
+
+;Generate a memory map at 0x500 to 0x5000 (available memory not used for kernel or bootloader)
+memory_map:
+.start  equ 0x0500
+.end    equ 0x5000
+.length equ .end - .start
+
+    xor eax, eax
+    mov di, .start
+    mov ecx, .length / 4 ; moving 4 Bytes at once
+    cld
+    rep stosd
+
+    mov di, .start
+    mov edx, 0x534D4150
+    xor ebx, ebx
+.lp:
+    mov eax, 0xE820
+    mov ecx, 24
+
+    int 0x15
+    jc .done ; Error or finished
+
+    cmp ebx, 0
+    je .done ; Finished
+
+    add di, 24
+    cmp di, .end
+    jb .lp ; Still have buffer space
+.done:
+    ret
diff --git a/asm/x86-unknown-none/stage2.asm b/asm/x86-unknown-none/stage2.asm
index 7455820188f5ae7459eb39800ad300eb08bafcb6..69cfc4d6adf190e85ea4b71ef06384078ca5a74a 100644
--- a/asm/x86-unknown-none/stage2.asm
+++ b/asm/x86-unknown-none/stage2.asm
@@ -10,12 +10,17 @@ stage2.entry:
     or al, 2
     out 0x92, al
 
+    ; load memory map
+    ;TODO: rewrite this in Rust
+    call memory_map
+
     mov dword [protected_mode.func], stage3.entry
     jmp protected_mode.entry
 
 %include "cpuid.asm"
 %include "gdt.asm"
 %include "long_mode.asm"
+%include "memory_map.asm"
 %include "protected_mode.asm"
 %include "thunk.asm"
 
@@ -37,9 +42,53 @@ stage3.entry:
     xor eax, eax
     mov al, [disk]
     push eax
+    mov eax, kernel.entry
+    push eax
     mov eax, [stage3 + 0x18]
     call eax
 .halt:
     cli
     hlt
     jmp .halt
+
+kernel:
+.stack: dq 0
+.func: dq 0
+.args: dq 0
+
+.entry:
+    ; page_table: usize
+    mov eax, [esp + 4]
+    mov [long_mode.page_table], eax
+
+    ; stack: u64
+    mov eax, [esp + 8]
+    mov [.stack], eax
+    mov eax, [esp + 12]
+    mov [.stack + 4], eax
+
+    ; func: u64
+    mov eax, [esp + 16]
+    mov [.func], eax
+    mov eax, [esp + 20]
+    mov [.func + 4], eax
+
+    ; args: *const KernelArgs
+    mov eax, [esp + 24]
+    mov [.args], eax
+
+    mov eax, .inner
+    mov [long_mode.func], eax
+    jmp long_mode.entry
+
+USE64
+
+.inner:
+    mov rsp, [.stack]
+    mov rax, [.func]
+    mov rdi, [.args]
+    call rax
+.halt:
+    cli
+    hlt
+    jmp .halt
diff --git a/src/disk.rs b/src/disk.rs
index e6dcac8af05eca0b55389d2079061b0ce120bba5..bb8a3207b6b9f0f8866cac37097c1c0369522b41 100644
--- a/src/disk.rs
+++ b/src/disk.rs
@@ -44,7 +44,7 @@ impl Disk for DiskBios {
     fn read_at(&mut self, block: u64, buffer: &mut [u8]) -> Result<usize> {
         for (i, chunk) in buffer.chunks_mut(BLOCK_SIZE as usize).enumerate() {
             unsafe {
-                let mut dap = DiskAddressPacket::from_block(block);
+                let mut dap = DiskAddressPacket::from_block(block + i as u64);
                 ptr::write(DISK_ADDRESS_PACKET_ADDR as *mut DiskAddressPacket, dap);
 
                 let mut data = ThunkData::new();
diff --git a/src/lib.rs b/src/lib.rs
index dcb365d7b8dcfa41a1ce957e75222be438a04cda..e5571e333b1095e9effc345c44908f68036c26cb 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -46,6 +46,8 @@ const DISK_BIOS_ADDR: usize = 0x1000; // 4096 bytes, ends at 0x1FFF
 const THUNK_STACK_ADDR: usize = 0x7C00; // Grows downwards
 const VGA_ADDR: usize = 0xB8000;
 
+const PHYS_OFFSET: u64 = 0xFFFF800000000000;
+
 #[global_allocator]
 static ALLOCATOR: LockedHeap = LockedHeap::empty();
 
@@ -53,10 +55,35 @@ static VGA: Mutex<Vga> = Mutex::new(
     unsafe { Vga::new(VGA_ADDR, 80, 25) }
 );
 
-static mut KERNEL_PHYS: u64 = 0;
+#[repr(packed)]
+pub struct KernelArgs {
+    kernel_base: u64,
+    kernel_size: u64,
+    stack_base: u64,
+    stack_size: u64,
+    env_base: u64,
+    env_size: u64,
+
+    /// The base 64-bit pointer to an array of saved RSDPs. It's up to the kernel (and possibly
+    /// userspace), to decide which RSDP to use. The buffer will be a linked list containing a
+    /// 32-bit relative (to this field) next, and the actual struct afterwards.
+    ///
+    /// This field can be NULL, and if so, the system has not booted with UEFI or in some other way
+    /// retrieved the RSDPs. The kernel or a userspace driver will thus try searching the BIOS
+    /// memory instead. On UEFI systems, searching is not guaranteed to actually work though.
+    acpi_rsdps_base: u64,
+    /// The size of the RSDPs region.
+    acpi_rsdps_size: u64,
+}
 
 #[no_mangle]
 pub unsafe extern "C" fn kstart(
+    kernel_entry: extern "C" fn(
+        page_table: usize,
+        stack: u64,
+        func: u64,
+        args: *const KernelArgs,
+    ) -> !,
     boot_disk: usize,
     thunk10: extern "C" fn(),
     thunk13: extern "C" fn(),
@@ -98,6 +125,15 @@ pub unsafe extern "C" fn kstart(
     println!("HEAP: {:X}:{:X}", heap_start, heap_size);
     ALLOCATOR.lock().init(heap_start, heap_size);
 
+
+    let stack_size = 0x20000;
+    let stack_base = ALLOCATOR.alloc_zeroed(
+        Layout::from_size_align(stack_size, 4096).unwrap()
+    );
+    if stack_base.is_null() {
+        panic!("Failed to allocate memory for stack");
+    }
+
     // Locate kernel on RedoxFS
     let kernel = {
         //TODO: ensure boot_disk is 8-bit
@@ -142,9 +178,27 @@ pub unsafe extern "C" fn kstart(
         kernel
     };
 
-    println!("Kernel Phys: 0x{:X}", kernel.as_ptr() as u64);
-    let page_phys = paging::paging_create(kernel.as_ptr() as u64);
-    panic!("kernel entry not implemented");
+    println!("Kernel Phys: 0x{:X}", kernel.as_ptr() as usize);
+    let page_phys = paging::paging_create(kernel.as_ptr() as usize)
+        .expect("Failed to set up paging");
+
+    let args = KernelArgs {
+        kernel_base: kernel.as_ptr() as u64,
+        kernel_size: kernel.len() as u64,
+        stack_base: stack_base as u64,
+        stack_size: stack_size as u64,
+        env_base: 0,
+        env_size: 0,
+        acpi_rsdps_base: 0,
+        acpi_rsdps_size: 0,
+    };
+
+    kernel_entry(
+        page_phys,
+        args.stack_base + args.stack_size + PHYS_OFFSET,
+        *(kernel.as_ptr().add(0x18) as *const u64),
+        &args,
+    );
 
     let mut modes = Vec::new();
     {
diff --git a/src/paging.rs b/src/paging.rs
index 2f1e9d77e5d48a39f3073803b7148ec54fb54776..407433e870ed4a529ec20609896a21705d888e98 100644
--- a/src/paging.rs
+++ b/src/paging.rs
@@ -15,7 +15,7 @@ unsafe fn paging_allocate() -> Option<&'static mut [u64]> {
     }
 }
 
-pub unsafe fn paging_create(kernel_phys: u64) -> Option<u64> {
+pub unsafe fn paging_create(kernel_phys: usize) -> Option<usize> {
     // Create PML4
     let pml4 = paging_allocate()?;
 
@@ -67,12 +67,12 @@ pub unsafe fn paging_create(kernel_phys: u64) -> Option<u64> {
                         pdp_i as u64 * 0x4000_0000 +
                         pd_i as u64 * 0x20_0000 +
                         pt_i as u64 * 0x1000 +
-                        kernel_phys;
+                        kernel_phys as u64;
                     pt[pt_i] = addr | 1 << 1 | 1;
                 }
             }
         }
     }
 
-    Some(pml4.as_ptr() as u64)
+    Some(pml4.as_ptr() as usize)
 }