【问题标题】:Enabling Paging in x86 Qemu在 x86 Qemu 中启用分页
【发布时间】:2016-11-20 15:17:19
【问题描述】:

我正在尝试在 Qemu 上启用 x86 架构上的分页。
但是这段代码会导致三重错误。

此代码段在保护模式下执行。
代码:https://github.com/mridulv/simpleOperatingSystem

这是内核入口文件

[bits 32]

[extern main]
[extern main2]
[extern page_table]

call main

lea ECX, [page_table - 0xC0000000]
mov CR3, ECX

mov ECX, CR0
or ECX, 0x80000000
mov CR0, ECX

;lea ECX, [StartInHigherHalf]
;jmp ECX

;StartInHigherHalf:
;   call main2

jmp $

这是创建页面目录表的内核文件。

unsigned int page_table[1024] __attribute__((aligned(4096)));;

void set_page_tables() {
    unsigned int pages_entry[1024 * 1024] __attribute__((aligned(4096)));

    unsigned int KERNEL_VIRTUAL_OFFSET = 0xC0000000;
    unsigned int KERNEL_FIRST_VIRTUAL_ADDRESS = 0xC0000000 >> 12;

    int numPageTables = 4;
    int numPagesInPageTable = 1024;
    int numPageTableEntriesInPageDirectory = 1024;
    int totalPages = numPageTables * numPagesInPageTable;

    unsigned int index = 0;
    unsigned int *pages_ptr = (unsigned int *)(pages_entry - KERNEL_VIRTUAL_OFFSET);
    unsigned int *page_table_ptr = (unsigned int *)(pages_entry - KERNEL_VIRTUAL_OFFSET);

    unsigned int positionAndFlags = 7;

    while (index < totalPages) {
        pages_ptr[index] = positionAndFlags;
        index = index + 1;
        positionAndFlags += 4096;
    }

    positionAndFlags = 7;
    index = KERNEL_FIRST_VIRTUAL_ADDRESS;
    unsigned int totalPagesLeft = KERNEL_FIRST_VIRTUAL_ADDRESS + totalPages;
    while (index < totalPagesLeft) {
        pages_ptr[index] = positionAndFlags;
        index = index + 1;
        positionAndFlags += 4096;
    }

    positionAndFlags = (unsigned int)&pages_ptr[0];
    positionAndFlags = positionAndFlags | 7;
    index = 0;
    while (index < numPageTableEntriesInPageDirectory) {
        page_table_ptr[index] = positionAndFlags;
        index = index + 1;
        positionAndFlags += 4096;
    }
}

void main() {
    char* video_memory = (char*) 0xb8000;
    *video_memory = 'X';
    set_page_tables();
}

void main2() {
    char* video_memory = (char*) 0xb8004;
    *video_memory = 'Y';
    // __asm__ 
    // (
    //  "leal (page_table, ), %ecx\n\t" // 0xC0000000 = KERNEL_VIRTUAL_BASE
    //  "movl %cr3, %ecx"
    // );
}

链接器.ld

SECTIONS {
    . = 0xC0100000;

    .text : AT(ADDR(.text) - 0xC0000000) {
        *(.text);
    }

    . = ALIGN(0x1000);
    .bss : AT(ADDR(.bss) - 0xC0000000) {
        *(.text);
    }

    . = ALIGN(0x1000);
    .data : AT(ADDR(.data) - 0xC0000000) {
        *(.text);
    }
}

【问题讨论】:

  • 一些显而易见的事情。我可以告诉您,您没有启用A20 line。在boot.asm 中,您保存DL(启动驱动器),然后立即放置数据(PROTECTED_MODE_STRBEGIN_STR)。 CPU 将尝试执行数据。将您的数据放在所有代码之后(尝试将它们移动到引导签名之前)。另一个大问题是您将内核加载到内存中的 0x1000。您的链接器脚本使用 0xC0100000 的基础。这假设您的内核在 0x100000 加载,但您的内核在 0x1000。所以在你的情况下你可能需要0xC0001000
  • 我什至还没有看到其他内容。但是,在尝试启用分页的代码之前,您已经遇到了很多问题。
  • 我还建议将lea ECX, [page_table - 0xC0000000] 更改为mov ECX, page_table - 0xC0000000
  • 由于您的代码最初在内存地址 0x1000 上运行,为了在启用分页后执行 0x1000 到 0x1FFF 区域中的代码,您需要标识映射页面。这意味着将物理地址为 0x1000 的页面映射到 0x1000 的虚拟内存地址。不这样做会使启用分页后的代码失败
  • @MichaelPetch 我在 4kb (0x1000) 处对齐不同的部分,这与在 0x100000 处加载不同

标签: c x86 paging qemu


【解决方案1】:

我建议您在代码顶部添加部分声明,以使事情清楚 -

section .text

你的代码中的错误是在执行之前

call main

您的代码不会(根本)通过填写 ESP 寄存器来设置堆栈。

要做到这一点,你的代码应该是这样的

  1. 像这样创建一个常量 'STACK_SIZE' -

    STACK_SIZE equ <valueOfSizeOfStack>
    
  2. 为堆栈创建空间

    section .bss
    kernelStack: RESB <valueOfSizeOfStack>
    

在“valueOfSizeOfStack”字段中,您可以为堆栈添加 4096 (4-KB) 或任何倍数(不要太大,它将计入您的二进制大小)。

【讨论】:

  • 在 GitHub 上搜索 CircuitKernel 以获取我正在创建的完整内核示例。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-12-09
  • 1970-01-01
  • 2013-05-02
  • 1970-01-01
  • 2020-12-08
  • 1970-01-01
  • 2014-11-27
相关资源
最近更新 更多