QEMU triple faults when enabling paging

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 1M of RAM. I've created an init section that is relocated to the kernel's physical address in order to take care of paging initialisation. My kernel's virtual offset is 0xc0000000 . This is my linker script:

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;
}

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

    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:

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 init section, to avoid relocation problems:

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 < 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 >> 12 & 0x3FF;
       i < ((INIT_START >> 12 & 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 < 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();
}

I tried to point to the exact assembly instruction, but that makes my kernel work incorrectly and I think it is because of mov cr0, eax when I enable paging. CR3 does contain the address of kernel_page_directory or with 0x3 . 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?


Is the address of your page directory Page-Aligned? The size of each page (frame) is 4 KB. I suggest to create a struct for page directory like this:

typedef struct page_directory{
    page_table_t *tables[1024];
    size_t tablesPhysical[1024]; // Physical address of page tables
    size_t physicalAddr;         // Physical address of `tablesPhysical'
} page_directory_t;

So, your address of the directory must be the multiple of 4 KB (0x1000). James Molloy's Tutorial may help you.

链接地址: http://www.djcxy.com/p/66804.html

上一篇: 如何在开机过程中uboot打印信息

下一篇: 启用分页时QEMU三重故障