启用分页时QEMU三重故障

时间:2016-07-02 08:07:20

标签: c x86 kernel qemu osdev

我正在实施我自己的内核而且我被卡住了。我试图将我的内核加载到高半虚拟地址中。我通过标识映射低1M RAM来解决身份地址问题。我创建了一个init部分,它被重新定位到内核的物理地址,以便处理分页初始化。我内核的虚拟偏移量为0xc0000000。这是我的链接器脚本:

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

这是我的切入点。我使用GRUB作为我的引导程序。由于init部分:

,它成功启动并跳转到我的入口点
    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:

这是我的代码填充页面表和内核的页面目录。如您所见,整个代码链接到init部分,以避免重定位问题:

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

我试图指向确切的汇编指令,但这使我的内核工作不正确,我认为这是因为我启用分页时mov cr0, eaxCR3确实包含kernel_page_directory0x3的地址。一旦启用分页,QEMU就会停止响应并且系统会不断重新启动。屏幕正在刷新然后重复打印。任何想法为什么会这样?我怎么解决它?

1 个答案:

答案 0 :(得分:0)

页面目录的地址是否已对齐?每页(框架)的大小为4 KB。我建议为页面目录创建一个结构,如下所示:

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;

因此,您的目录地址必须是4 KB(0x1000)的倍数。 James Molloy's Tutorial可能会对您有所帮助。