diff --git a/src/arch/x86_64/gdt.rs b/src/arch/x86_64/gdt.rs
index ca846074fefc917d339e1a6845569b86b389eef2..00c9430a5482e0dde51d50efa032ce7a2e45337b 100644
--- a/src/arch/x86_64/gdt.rs
+++ b/src/arch/x86_64/gdt.rs
@@ -100,16 +100,13 @@ pub unsafe fn set_tss_stack(stack: usize) {
     TSS.rsp[0] = stack as u64;
 }
 
-/// Initialize GDT
-pub unsafe fn init(tcb_offset: usize, stack_offset: usize) {
+// Initialize GDT
+pub unsafe fn init() {
     // Setup the initial GDT with TLS, so we can setup the TLS GDT (a little confusing)
     // This means that each CPU will have its own GDT, but we only need to define it once as a thread local
     INIT_GDTR.limit = (INIT_GDT.len() * mem::size_of::<GdtEntry>() - 1) as u16;
     INIT_GDTR.base = INIT_GDT.as_ptr() as u64;
 
-    // Set the TLS segment to the offset of the Thread Control Block
-    INIT_GDT[GDT_KERNEL_TLS].set_offset(tcb_offset as u32);
-
     // Load the initial GDT, before we have access to thread locals
     dtables::lgdt(&INIT_GDTR);
 
@@ -117,9 +114,21 @@ pub unsafe fn init(tcb_offset: usize, stack_offset: usize) {
     segmentation::load_cs(SegmentSelector::new(GDT_KERNEL_CODE as u16));
     segmentation::load_ds(SegmentSelector::new(GDT_KERNEL_DATA as u16));
     segmentation::load_es(SegmentSelector::new(GDT_KERNEL_DATA as u16));
-    segmentation::load_fs(SegmentSelector::new(GDT_KERNEL_TLS as u16));
+    segmentation::load_fs(SegmentSelector::new(GDT_KERNEL_DATA as u16));
     segmentation::load_gs(SegmentSelector::new(GDT_KERNEL_DATA as u16));
     segmentation::load_ss(SegmentSelector::new(GDT_KERNEL_DATA as u16));
+}
+
+/// Initialize GDT with TLS
+pub unsafe fn init_paging(tcb_offset: usize, stack_offset: usize) {
+    // Set the TLS segment to the offset of the Thread Control Block
+    INIT_GDT[GDT_KERNEL_TLS].set_offset(tcb_offset as u32);
+
+    // Load the initial GDT, before we have access to thread locals
+    dtables::lgdt(&INIT_GDTR);
+
+    // Load the segment descriptors
+    segmentation::load_fs(SegmentSelector::new(GDT_KERNEL_TLS as u16));
 
     // Now that we have access to thread locals, setup the AP's individual GDT
     GDTR.limit = (GDT.len() * mem::size_of::<GdtEntry>() - 1) as u16;
diff --git a/src/arch/x86_64/idt.rs b/src/arch/x86_64/idt.rs
index f9b9859539f77bc550cb8047a934b4466606c507..59ec6c075db1bd27703e599dd262672364a36790 100644
--- a/src/arch/x86_64/idt.rs
+++ b/src/arch/x86_64/idt.rs
@@ -3,6 +3,11 @@ use x86::dtables::{self, DescriptorTablePointer};
 
 use interrupt::*;
 
+pub static mut INIT_IDTR: DescriptorTablePointer = DescriptorTablePointer {
+    limit: 0,
+    base: 0
+};
+
 pub static mut IDTR: DescriptorTablePointer = DescriptorTablePointer {
     limit: 0,
     base: 0
@@ -11,6 +16,10 @@ pub static mut IDTR: DescriptorTablePointer = DescriptorTablePointer {
 pub static mut IDT: [IdtEntry; 256] = [IdtEntry::new(); 256];
 
 pub unsafe fn init() {
+    dtables::lidt(&INIT_IDTR);
+}
+
+pub unsafe fn init_paging() {
     IDTR.limit = (IDT.len() * mem::size_of::<IdtEntry>() - 1) as u16;
     IDTR.base = IDT.as_ptr() as u64;
 
diff --git a/src/arch/x86_64/start.rs b/src/arch/x86_64/start.rs
index afba151a49521a4b4cdced6a749f8d8b2149254a..81b12bb3bff77651943ad50a3c36a227c2851fc6 100644
--- a/src/arch/x86_64/start.rs
+++ b/src/arch/x86_64/start.rs
@@ -72,17 +72,23 @@ pub unsafe extern fn kstart(args_ptr: *const KernelArgs) -> ! {
         println!("Stack: {:X}:{:X}", stack_base, stack_base + stack_size);
         println!("Env: {:X}:{:X}", env_base, env_base + env_size);
 
+        // Set up GDT before paging
+        gdt::init();
+
+        // Set up IDT before paging
+        idt::init();
+
         // Initialize memory management
         memory::init(0, kernel_base + ((kernel_size + 4095)/4096) * 4096);
 
         // Initialize paging
         let (mut active_table, tcb_offset) = paging::init(0, kernel_base, kernel_base + kernel_size, stack_base, stack_base + stack_size);
 
-        // Set up GDT
-        gdt::init(tcb_offset, stack_base + stack_size);
+        // Set up GDT after paging with TLS
+        gdt::init_paging(tcb_offset, stack_base + stack_size);
 
         // Set up IDT
-        idt::init();
+        idt::init_paging();
 
         // Test tdata and tbss
         {
@@ -151,14 +157,20 @@ pub unsafe extern fn kstart_ap(args_ptr: *const KernelArgsAp) -> ! {
         assert_eq!(BSS_TEST_ZERO, 0);
         assert_eq!(DATA_TEST_NONZERO, 0xFFFF_FFFF_FFFF_FFFF);
 
+        // Set up GDT before paging
+        gdt::init();
+
+        // Set up IDT before paging
+        idt::init();
+
         // Initialize paging
         let tcb_offset = paging::init_ap(cpu_id, bsp_table, stack_start, stack_end);
 
-        // Set up GDT for AP
-        gdt::init(tcb_offset, stack_end);
+        // Set up GDT with TLS
+        gdt::init_paging(tcb_offset, stack_end);
 
         // Set up IDT for AP
-        idt::init();
+        idt::init_paging();
 
         // Test tdata and tbss
         {