QEMU triple faults when enabling paging
<p>I'm implementing my own kernel and I'm stuck. I'm trying to load my kernel into the high half virtual addresses. I've tackled the identity addresses problem by identity mapping the low <code>1M</code> of RAM. I've created an <code>init</code> section that is relocated to the kernel's physical address in order to take care of paging initialisation. My kernel's virtual offset is <code>0xc0000000</code>. This is my linker script:</p>

<pre><code>OUTPUT_FORMAT(elf32-i386)
ENTRY(start)

KERNEL_VIRTUAL_OFFSET = 0xC0000000;

SECTIONS
{
    . = 1M;
    kernel_start = .;

    start_init = .;
   .init ALIGN(4K) :
                          { *(.multiboot);
                            *(.init);
                            *(.tables);
                          }
    end_init = .;

    . += KERNEL_VIRTUAL_OFFSET;

    kernel_high_half_start = .;
   .text ALIGN(4K) :  AT(ADDR(.text) - KERNEL_VIRTUAL_OFFSET)
                         {*(.text) }
   .data ALIGN(4K) :  AT(ADDR(.data) - KERNEL_VIRTUAL_OFFSET)
                         { *(.data) }
   .rodata ALIGN(4K) : AT(ADDR(.rodata) - KERNEL_VIRTUAL_OFFSET)
                         { *(.rodata) }
   .bss  ALIGN(4K) :  AT(ADDR(.bss) - KERNEL_VIRTUAL_OFFSET)
                         { *(.bss)  }
    kernel_high_half_end = .;

    kernel_end = . - KERNEL_VIRTUAL_OFFSET;
}
</code></pre>

<p>Here's my entry point. I'm using GRUB as my bootloader. It successfully boots and jumps into my entry point because of the <code>init</code> section:</p>

<pre><code>    its 32
    section .multiboot
    ;grub bootloader header
            align 4
            dd 0x1BADB002            ;magic
            dd 0x00                  ;flags
            dd - (0x1BADB002 + 0x00) ;checksum. m+f+c should be zero

    ; Declarations
    global start
    extern kmain
    extern paging_init
    extern kernel_page_directory

    section .init

    enable_paging:
      mov eax, kernel_page_directory
      mov cr3, eax
      mov eax, cr0
      or eax, 0x80000000
      mov cr0, eax  ; ***** PAGING ENABLED HERE *****
      ret

    start:
      cli           ;block interrupts
      mov esp, init_stack
      call paging_init
      call enable_paging

      ;mov eax, 0xb8000
      ;mov byte[eax], 'h'
      ;mov byte[eax+1], 0x7

      ; Now high half kernel is mapped to the page directory
      mov esp, stack_space  ;set stack pointer
      push ebx ; grub boot info
      call kmain

    loop:
      hlt           ;halt the CPU
      jmp loop

    resb 4096; 4KB small stack for my init section.
    init_stack:

    section .bss
    resb 8192 ;8KB for stack
    stack_space:
</code></pre>

<p>Here is my code the fills the page table and the page directory of the kernel. As you can see, the whole code is linked into the <code>init</code> section, to avoid relocation problems:</p>

<pre><code>page_table_t kernel_page_directory[PAGE_DIR_SIZE]
__attribute__((aligned(PAGE_SIZE))) __attribute__((section(".tables"))) = {0};

page_pointer_t kernel_page_tables[PAGE_TABLE_SIZE]
__attribute__((aligned(PAGE_SIZE))) __attribute__((section(".tables"))) = {0};

page_pointer_t identity_page_table[PAGE_TABLE_SIZE]
__attribute__((aligned(PAGE_SIZE))) __attribute__((section(".tables"))) = {0};

/* Identity map the low 1M
 * In early boot stage.
 */
static void __attribute__((section(".init"))) map_identity()
{
   //map bios
   unsigned int current_page = 0;
   for(int i = 0; i &lt; BIOS_PAGE_TABLE_ENTRIES; i++, current_page += PAGE_SIZE)
   {
      identity_page_table[i] = (current_page) | 0x3;
   }

   //map init
   current_page = INIT_START;
   for(int i = INIT_START &gt;&gt; 12 &amp; 0x3FF;
       i &lt; ((INIT_START &gt;&gt; 12 &amp; 0x3FF) + (INIT_SIZE / PAGE_SIZE));
       i++, current_page += PAGE_SIZE)
   {
      identity_page_table[i] = (current_page) | 0x3;
   }

   kernel_page_directory[0] = ((unsigned long)(identity_page_table)) | 0x3;
}

/* Map the kernel memory to its page directory,
 * **in early boot stage.
 * We don't need to map the init section, we don't need it anymore.
 */

__attribute__((section(".init"))) static void map_kernel_memory()
{
   //Identity map the init section
   //Start at 1MB i.e. its page aligned.
   unsigned int start_index = 256;
   unsigned long current_page = KERNEL_START;

   for(int i = start_index;
       i &lt; start_index + (KERNEL_SIZE / PAGE_SIZE) + 1;
       i++, current_page += PAGE_SIZE)
   {
       kernel_page_tables[i] = current_page | 0x3;
   }

   kernel_page_directory[KERNEL_DIRECTORY_ENTRY] = ((unsigned long)kernel_page_tables) | 0x3;
}

__attribute__((section(".init"))) void paging_init()
{
   map_identity();

   map_kernel_memory();
}
</code></pre>

<p>I tried to point to the exact assembly instruction, but that makes my kernel work incorrectly and I think it is because of <code>mov cr0, eax</code> when I enable paging. <code>CR3</code> does contain the address of <code>kernel_page_directory</code> or with <code>0x3</code>. As soon as I enable paging, QEMU stops responding and the system reboots constantly. The screen is being flushed and then printed repeatedly. Any ideas why this is happening? How I can fix it?</p>