diff --git a/core/src/arch/x86_64/platform.zig b/core/src/arch/x86_64/platform.zig
index 905358c..fe8b2ee 100644
--- a/core/src/arch/x86_64/platform.zig
+++ b/core/src/arch/x86_64/platform.zig
@@ -1,3 +1,4 @@
+const std = @import("std");
 const gdt = @import("gdt.zig");
 const idt = @import("idt.zig");
 const pic = @import("pic.zig");
@@ -18,6 +19,21 @@ fn enableNX() void {
     );
 }
 
+var stack: [PAGE_SIZE * 8]u8 = std.mem.zeroes([PAGE_SIZE * 8]u8);
+const top: usize = (PAGE_SIZE * 8) - 16;
+
+pub inline fn _start() noreturn {
+    asm volatile (
+        \\ mov %[stack], %rsp
+        \\ addq %[top], %rsp
+        \\ call main
+        :
+        : [stack] "i" (&stack),
+          [top] "i" (top),
+    );
+    unreachable;
+}
+
 // Initialize platform-specific components.
 pub fn platformInit() void {
     gdt.setupGDT();
diff --git a/core/src/arch/x86_64/vmm.zig b/core/src/arch/x86_64/vmm.zig
index 93fb7f1..09e5e17 100644
--- a/core/src/arch/x86_64/vmm.zig
+++ b/core/src/arch/x86_64/vmm.zig
@@ -3,6 +3,7 @@ const easyboot = @cImport(@cInclude("easyboot.h"));
 const mmap = @import("../../mmap.zig");
 const pmm = @import("../../pmm.zig");
 const platform = @import("platform.zig");
+const debug = @import("../debug.zig");
 
 const USER_ADDRESS_RANGE_END = 0x0000_7fff_ffff_ffff;
 pub const PHYSICAL_MAPPING_BASE = 0xffff_8000_0000_0000;
@@ -151,6 +152,11 @@ pub fn getEntry(space: AddressSpace, base: usize, virt_address: u64) ?*PageTable
     return pt_entry;
 }
 
+pub fn getAddress(space: AddressSpace, base: usize, virt_address: u64) ?usize {
+    const entry = getEntry(space, base, virt_address) orelse return null;
+    return entry.getAddress();
+}
+
 pub fn copyToUser(space: AddressSpace, base: usize, user: usize, kernel: [*]const u8, size: usize) !void {
     const remainder: usize = @rem(user, platform.PAGE_SIZE);
     const user_page = user - remainder;
@@ -277,22 +283,20 @@ fn setUpKernelPageDirectory(allocator: *pmm.FrameAllocator, tag: *easyboot.multi
     return table;
 }
 
-pub fn setUpInitialUserPageDirectory(allocator: *pmm.FrameAllocator, tag: *easyboot.multiboot_tag_mmap_t, user_table: *PageTable) !usize {
+pub fn setUpInitialUserPageDirectory(allocator: *pmm.FrameAllocator, tag: *easyboot.multiboot_tag_mmap_t, space: AddressSpace) !usize {
     const kernel_space = AddressSpace.create(readPageTable(), PHYSICAL_MAPPING_BASE);
     const kernel_table = kernel_space.table;
 
     const physical_address_space_size = mmap.getAddressSpaceSize(tag) orelse return error.InvalidMemoryMap;
 
-    user_table.* = std.mem.zeroes(PageTable);
+    space.table.* = std.mem.zeroes(PageTable);
 
     const directory_upper_half: *[256]PageTableEntry = kernel_table.entries[256..];
-    const user_directory_upper_half: *[256]PageTableEntry = user_table.entries[256..];
+    const user_directory_upper_half: *[256]PageTableEntry = space.table.entries[256..];
     @memcpy(user_directory_upper_half, directory_upper_half);
 
     const user_physical_address_base = (USER_ADDRESS_RANGE_END + 1) - physical_address_space_size;
 
-    const space = AddressSpace.create(.{ .address = @intFromPtr(user_table) }, 0);
-
     try mapPhysicalMemory(allocator, tag, space, PHYSICAL_MAPPING_BASE, user_physical_address_base, @intFromEnum(Flags.ReadWrite) | @intFromEnum(Flags.NoExecute) | @intFromEnum(Flags.User));
 
     return user_physical_address_base;
diff --git a/core/src/main.zig b/core/src/main.zig
index 0dbe38f..f33b101 100644
--- a/core/src/main.zig
+++ b/core/src/main.zig
@@ -13,51 +13,11 @@ const elf = @import("elf.zig");
 
 const MultibootInfo = [*c]u8;
 
-fn adjustAddressToPageBoundary(address: *usize, size: *usize) void {
-    const diff = address.* % platform.PAGE_SIZE;
-
-    address.* -= diff;
-    size.* += diff;
+export fn _start(_: u32, _: MultibootInfo) callconv(.C) noreturn {
+    platform._start();
 }
 
-fn reserveMultibootMemory(allocator: *pmm.FrameAllocator, info: MultibootInfo) !void {
-    const info_tag: *easyboot.multiboot_info_t = @alignCast(@ptrCast(info));
-
-    var address: usize = @intFromPtr(info);
-    var size: usize = info_tag.total_size;
-    adjustAddressToPageBoundary(&address, &size);
-
-    debug.print("Locking multiboot memory at {x}, {d} bytes\n", .{ address, size });
-
-    try pmm.lockFrames(allocator, address, try std.math.divCeil(usize, size, platform.PAGE_SIZE));
-
-    const Context = struct {
-        allocator: *pmm.FrameAllocator,
-    };
-
-    var ctx = Context{ .allocator = allocator };
-
-    multiboot.findMultibootTags(easyboot.multiboot_tag_module_t, @ptrCast(info), struct {
-        fn reserveMemory(mod: *easyboot.multiboot_tag_module_t, context: *const Context) !void {
-            var mod_address: usize = mod.mod_start;
-            var mod_size: usize = mod.mod_end - mod.mod_start;
-            adjustAddressToPageBoundary(&mod_address, &mod_size);
-
-            debug.print("Locking memory for module {s} at address {x}, {d} bytes\n", .{ mod.string(), mod_address, mod_size });
-
-            try pmm.lockFrames(context.allocator, mod_address, try std.math.divCeil(usize, mod_size, platform.PAGE_SIZE));
-        }
-
-        fn handler(mod: *easyboot.multiboot_tag_module_t, context: *const anyopaque) void {
-            reserveMemory(mod, @alignCast(@ptrCast(context))) catch |err| {
-                debug.print("Error while reserving multiboot memory {s}: {}\n", .{ mod.string(), err });
-                while (true) {}
-            };
-        }
-    }.handler, &ctx);
-}
-
-export fn _start(magic: u32, info: MultibootInfo) callconv(.C) noreturn {
+export fn main(magic: u32, info: MultibootInfo) callconv(.C) noreturn {
     interrupts.disableInterrupts();
 
     if (magic != easyboot.MULTIBOOT2_BOOTLOADER_MAGIC) {
@@ -80,7 +40,7 @@ export fn _start(magic: u32, info: MultibootInfo) callconv(.C) noreturn {
         while (true) {}
     };
 
-    reserveMultibootMemory(&allocator, info) catch |err| {
+    pmm.reserveMultibootMemory(&allocator, info) catch |err| {
         debug.print("Error while reserving multiboot memory: {}\n", .{err});
         while (true) {}
     };
@@ -108,7 +68,7 @@ export fn _start(magic: u32, info: MultibootInfo) callconv(.C) noreturn {
             const frame = try pmm.allocFrame(context.allocator);
             const space = vmm.AddressSpace.create(frame, vmm.PHYSICAL_MAPPING_BASE);
 
-            const base = try vmm.setUpInitialUserPageDirectory(context.allocator, context.mmap, space.table);
+            const base = try vmm.setUpInitialUserPageDirectory(context.allocator, context.mmap, space);
 
             const module = try thread.createThreadControlBlock(context.allocator);
 
diff --git a/core/src/pmm.zig b/core/src/pmm.zig
index e133272..9b7f1c6 100644
--- a/core/src/pmm.zig
+++ b/core/src/pmm.zig
@@ -5,6 +5,8 @@ const vmm = @import("arch/vmm.zig");
 const mmap = @import("mmap.zig");
 const bmap = @import("lib/bitmap.zig");
 const locking = @import("lib/spinlock.zig");
+const debug = @import("arch/debug.zig");
+const multiboot = @import("multiboot.zig");
 
 const FrameAllocatorError = error{
     InvalidMemoryMap,
@@ -104,9 +106,74 @@ pub fn initializeFrameAllocator(tag: *easyboot.multiboot_tag_mmap_t) !FrameAlloc
     // Avoid causing trouble.
     try lockFrame(&allocator, 0);
 
+    try reserveKernelMemory(&allocator);
+
     return allocator;
 }
 
+fn adjustAddressToPageBoundary(address: *usize, size: *usize) void {
+    const diff = address.* % platform.PAGE_SIZE;
+
+    address.* -= diff;
+    size.* += diff;
+}
+
+extern const kernel_start: [*]u8;
+extern const kernel_end: [*]u8;
+
+fn reserveKernelMemory(allocator: *FrameAllocator) !void {
+    debug.print("Kernel begins at {*} and ends at {*}\n", .{ &kernel_start, &kernel_end });
+
+    const start: usize = @intFromPtr(&kernel_start);
+    const end: usize = @intFromPtr(&kernel_end);
+    const pages = try std.math.divCeil(usize, end - start, platform.PAGE_SIZE);
+
+    const page_table = vmm.readPageTable();
+    const space = vmm.AddressSpace.create(page_table, 0);
+
+    var i: usize = 0;
+    while (i < pages) : (i += 1) {
+        try lockFrame(allocator, vmm.getAddress(space, 0, start + (i * platform.PAGE_SIZE)).?);
+    }
+}
+
+pub fn reserveMultibootMemory(allocator: *FrameAllocator, info: [*c]u8) !void {
+    const info_tag: *easyboot.multiboot_info_t = @alignCast(@ptrCast(info));
+
+    var address: usize = @intFromPtr(info);
+    var size: usize = info_tag.total_size;
+    adjustAddressToPageBoundary(&address, &size);
+
+    debug.print("Locking multiboot memory at {x}, {d} bytes\n", .{ address, size });
+
+    try lockFrames(allocator, address, try std.math.divCeil(usize, size, platform.PAGE_SIZE));
+
+    const Context = struct {
+        allocator: *FrameAllocator,
+    };
+
+    var ctx = Context{ .allocator = allocator };
+
+    multiboot.findMultibootTags(easyboot.multiboot_tag_module_t, @ptrCast(info), struct {
+        fn reserveMemory(mod: *easyboot.multiboot_tag_module_t, context: *const Context) !void {
+            var mod_address: usize = mod.mod_start;
+            var mod_size: usize = mod.mod_end - mod.mod_start;
+            adjustAddressToPageBoundary(&mod_address, &mod_size);
+
+            debug.print("Locking memory for module {s} at address {x}, {d} bytes\n", .{ mod.string(), mod_address, mod_size });
+
+            try lockFrames(context.allocator, mod_address, try std.math.divCeil(usize, mod_size, platform.PAGE_SIZE));
+        }
+
+        fn handler(mod: *easyboot.multiboot_tag_module_t, context: *const anyopaque) void {
+            reserveMemory(mod, @alignCast(@ptrCast(context))) catch |err| {
+                debug.print("Error while reserving multiboot memory {s}: {}\n", .{ mod.string(), err });
+                while (true) {}
+            };
+        }
+    }.handler, &ctx);
+}
+
 var lock: locking.SpinLock = .{};
 var global_allocator: *FrameAllocator = undefined;