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

x86保护模式分页启用后删除恒等映射致系统重启问题排查

问题分析与修复

1. 页表及其条目构造的问题

你的页表构造存在两处关键问题:

  • 内核页表项缺少可写权限:在映射物理1MB到虚拟3GB的循环中,仅给页表项添加了PAGE_STRUCT_ENTRY_PRESENT属性,未添加PAGE_STRUCT_ENTRY_WRITEABLE。x86分页权限遵循最严格匹配原则,即使页目录项(PDE)开了可写,页表项(PTE)未开的话,该页仍处于只读状态,会导致内核运行时无法修改数据触发错误。
  • 页目录访问的地址问题:alloc_page()返回的是物理地址,开启分页后,你通过pdir(物理地址)访问页目录条目时,依赖的是恒等映射。当你删除恒等映射的PDE后,这个物理地址对应的虚拟地址(低地址)不再有映射,访问pdir->entries[...]会直接触发页错误。

2. 内核高半区跳转的问题

你没有完成真正的高半区跳转:
当前的jmp 1f只是同一段内的短跳转,代码仍然运行在低地址虚拟空间(对应物理地址的恒等映射)。当删除恒等映射后,CPU当前执行的指令地址(低虚拟地址)失去映射,立即触发页错误,进入错误处理循环。


修复方案

步骤1:修正页表项权限

给内核映射和恒等映射的页表项添加可写属性:

// 恒等映射循环中补充
ENTRY_ADD_ATTRIBUTE(*entry, PAGE_STRUCT_ENTRY_WRITEABLE);

// 内核映射循环中补充
ENTRY_ADD_ATTRIBUTE(*entry, PAGE_STRUCT_ENTRY_WRITEABLE);

步骤2:完成真正的高半区跳转

将代码执行流切换到高半区的虚拟地址,避免依赖恒等映射:

// 在VMM_init函数内,开启分页后添加
void (*high_half_continue)(pdirectory_t*) = (void (*)(pdirectory_t*))(0xC0000000 + ((uint32_t)&VMM_init_high_half - (uint32_t)&_begin));
high_half_continue(pdir);

// 把删除恒等映射的逻辑拆分到单独函数
void VMM_init_high_half(pdirectory_t *pdir_phys) {
    // 将页目录的物理地址转换为高半区虚拟地址
    pdirectory_t *pdir_virt = (pdirectory_t*)(0xC0000000 + (uint32_t)pdir_phys);
    // 删除恒等映射的PDE
    pdir_virt->entries[PAGE_DIR_INDEX(0x0)] = 0;
    // 刷新TLB生效
    switch_pd(pdir_virt);
}

通过计算内核代码在高半区的虚拟地址偏移,将后续执行逻辑转移到高半区,此时访问页目录使用虚拟地址,不再依赖恒等映射。

步骤3:映射页目录自身到高半区

在页目录中添加自身的映射,方便后续直接通过虚拟地址访问页目录:

// 创建页目录后添加
pd_entry_t *self_entry = &pdir->entries[PAGE_DIR_INDEX(0xFFFFF000)];
ENTRY_ADD_ATTRIBUTE(*self_entry, PAGE_STRUCT_ENTRY_PRESENT);
ENTRY_ADD_ATTRIBUTE(*self_entry, PAGE_STRUCT_ENTRY_WRITEABLE);
ENTRY_SET_FRAME(*self_entry, pdir);

之后可直接通过0xFFFFF000这个虚拟地址访问页目录,无需手动转换物理/虚拟地址。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 00:23:18