You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

启用分页导致QEMU重启,如何修复该分页初始化问题?

分页初始化导致QEMU重启的修复方案

问题背景

分页初始化代码中,执行设置CR3/CR0的内联汇编时QEMU触发重启,移除该汇编段后程序可正常运行,核心问题出在分页初始化逻辑或汇编指令的正确性上。相关代码如下:

分页初始化函数

void paging_init(){
    //Enable Paging
    uint32 cr0;
    memset(g_page_directory, 0, sizeof(page_directory_t) * 1024);
    memset(g_page_tables, 0, sizeof(page_table_t) * 1024);
    
    // Set All page directories to have rw and u/s access (only by kernel)
    for(int i = 0; i < 1024; i++){
        g_page_directory[i].user_supervisor = 1;
        g_page_directory[i].read_write = 1;
    }
    
    // fill all the entries in page table to map all 4MB memory
    for (int i = 0; i < 1024; i++) {
        g_page_tables[i].present = 1;
        g_page_tables[i].read_write = 1;
        g_page_tables[i].user_supervisor = 1;
        g_page_tables[i].frame = (i * PAGE_SIZE) >> 12;
    }
    
    // set first page directory to be accessed with frame 0x11a(kernel region address)
    g_page_directory[0].present = 1;
    g_page_directory[0].accessed = 0;
    g_page_directory[0].user_supervisor = 1;
    g_page_directory[0].frame = 0x11a;
    //Register Page Fault Handler
    isr_register_interrupt_handler(14, page_fault_handler);

    // set cr3 point to page directory
    asm volatile("mov %0, %%cr3" :: "r"(g_page_directory));
    
    // set bit in cr0 to enable paging
    asm volatile("mov %%cr0, %0": "=r"(cr0));
    cr0 = cr0 | 0x80000000;
    asm volatile("mov %0, %%cr0" :: "r"(cr0));
}

引发问题的内联汇编段

// set cr3 point to page directory
asm volatile("mov %0, %%cr3" :: "r"(g_page_directory));

// set bit in cr0 to enable paging
asm volatile("mov %%cr0, %0": "=r"(cr0));
cr0 = cr0 | 0x80000000;
asm volatile("mov %0, %%cr0" :: "r"(cr0));

结构体定义

// Page Directory Struct
typedef struct {
    uint32 present :1;
    uint32 read_write :1;
    uint32 user_supervisor :1;
    uint32 write_through :1;
    uint32 cache_disable :1;
    uint32 accessed :1;
    uint32 dirty :1;
    uint32 page_size :1;
    uint32 global :1;
    uint32 available :3;
    uint32 frame :20;
} page_directory_t;

// Page Table
typedef struct {
    uint32 present :1;
    uint32 read_write :1;
    uint32 user_supervisor :1;
    uint32 write_through :1;
    uint32 cache_disable :1;
    uint32 accessed :1;
    uint32 dirty :1;
    uint32 page_size :1;
    uint32 global :1;
    uint32 available :3;
    uint32 frame :20;
} page_table_t;

核心问题分析与修复步骤

1. CR3传入虚拟地址而非物理地址

x86架构中,CR3寄存器必须存储页目录的物理地址,但代码中直接传入g_page_directory的虚拟地址(即使分页未开启,若内核加载到高地址,虚拟/物理地址可能不重合)。

  • 修复:将页目录的物理地址传入CR3。若内核采用身份映射(虚拟地址=物理地址),需确保g_page_directory位于4KB对齐的物理内存区域;若内核有地址偏移,需减去偏移量后传入:
    // 假设内核虚拟地址 = 物理地址 + 0xC0000000
    uint32 phys_page_dir = (uint32)g_page_directory - 0xC0000000;
    asm volatile("mov %0, %%cr3" :: "r"(phys_page_dir));
    

2. 页目录项的Frame地址错误

代码中g_page_directory[0].frame = 0x11a,但frame字段是物理页地址的高20位(即物理地址>>12)。0x11a对应的物理页地址是0x11a << 12 = 0x11A000,需确认该地址确实是页表的物理起始地址且已正确初始化。

  • 修复:直接通过页表的物理地址计算frame值:
    // 同样转换为物理地址后右移12位
    uint32 phys_page_table = (uint32)g_page_tables - 0xC0000000;
    g_page_directory[0].frame = phys_page_table >> 12;
    

3. 缺失内存屏障与流水线刷新

修改CR3/CR0后,CPU可能存在缓存不一致,导致分页开启后指令访问异常。

  • 修复:添加内存屏障指令,并通过跳转刷新CPU流水线:
    // 设置CR3并添加内存屏障
    asm volatile("mov %0, %%cr3\n\t"
                 "mfence" :: "r"(phys_page_dir) : "memory");
    
    // 开启分页并刷新流水线
    asm volatile("mov %%cr0, %0": "=r"(cr0));
    cr0 |= 0x80000000;
    asm volatile("mov %0, %%cr0\n\t"
                 "jmp 1f\n\t"
                 "1:" :: "r"(cr0) : "memory");
    

4. 页目录项的权限与Present位滥用

代码中循环设置所有页目录项的user_supervisor=1和read_write=1,但大部分项未设置present=1,会导致不必要的页错误风险。

  • 修复:仅对已映射的页目录项设置权限与Present位,其余保持默认0:
    // 仅初始化第一个页目录项
    g_page_directory[0].present = 1;
    g_page_directory[0].user_supervisor = 1;
    g_page_directory[0].read_write = 1;
    // 其他项保持memset后的0值
    

5. 页表映射范围覆盖内核区域

代码中仅映射4MB内存,需确保内核加载的全部物理地址都被包含在映射范围内,避免分页开启后内核指令访问未映射区域触发重启。

  • 修复:根据内核实际大小扩展页表映射范围,确保所有内核代码、数据所在的物理页都被正确映射。

内容的提问来源于stack exchange,提问作者xingharvey

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 11:07:23