启用分页时QEMU三重故障

我正在执行我自己的内核,并且我卡住了。 我试图将我的内核加载到高半虚拟地址中。 我已经通过标识映射低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_directory的地址或0x3 。 只要启用分页,QEMU会停止响应,并且系统会不断重启。 屏幕正在刷新,然后重复打印。 任何想法为什么发生这种情况? 我如何解决它?


你的页面目录的地址是否与页面对齐? 每个页面(帧)的大小是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的教程可以帮助你。

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

上一篇: QEMU triple faults when enabling paging

下一篇: Stub in Apple's EFI env