Skip to content

好,这一轮踩坑其实已经是一套非常典型、非常有价值的 x86_64 启动事故链了。我帮你整理成一份可以直接写进简历 / 面试讲解 / 技术博客的“完整踩坑复盘”


🧠 Cinux Long Mode 启动踩坑复盘(高价值总结)

🧩 一、问题现象

在实现 Bootloader Stage2 → Long Mode 切换过程中:

  • 启动流程执行到开启分页(CR0.PG)时
  • 直接 CPU 崩溃(triple fault)
  • GDB 显示:
text
cr0 = 0x11          → 未开启分页
cr4 = 0x20          → PAE 已开启
efer = 0x1000       → 仅 SVME

👉 但一旦写入:

asm
mov %eax, %cr0

💥 立即崩溃


🔍 二、排查过程(关键推理链)

🧪 Step 1:确认页表结构

页表内容:

text
PML4 → PDPT → PD
PD:
  0x0000000000000083
  0x0000000000200083
  0x0000000000400083

👉 判断:

✔ 使用 2MB 大页(PS=1) ✔ Identity Mapping(连续映射) ✔ 结构本身是正确的


🧪 Step 2:CR4 检查

text
CR4 = 0x20 → PAE = 1

✔ 正确 ✔ 满足 long mode 前提之一


🧪 Step 3:致命线索(关键)

text
efer = 0x1000 → 仅 SVME

👉 分析:

  • 没有 LME(Long Mode Enable)
  • CPU 仍处于“非 long mode”状态

💥 三、核心错误(致命 bug)

❌ 错误点:LME 位写错

asm
.set EFER_LME, 0x1000   // ❌ 错误

✅ 正确应该是:

asm
.set EFER_LME, 0x100    // ✔ bit 8

🔥 影响:

写法实际效果
0x100开启 LME ✔
0x1000设置 SVME ❌

👉 导致:

💥 CPU 从未进入 long mode 💥 却尝试开启分页 → 直接 triple fault


🧠 四、关键认知误区

❗ 误区 1:只要 PAE + CR0.PG 就是 long mode

✔ 错误 👉 还必须:

text
EFER.LME = 1

❗ 误区 2:页表正确就能启动

✔ 错误 👉 long mode 是“状态机”,不是只看页表


❗ 误区 3:CR0.PG 可以随便开

✔ 错误 👉 必须满足:

  • PAE = 1
  • LME = 1
  • 页表合法

🚨 五、正确的 Long Mode 启动顺序(核心经验)

这是这次踩坑的最重要结论


✅ 标准顺序(必须严格遵守)

asm
1️⃣ 开 PAE
   CR4.PAE = 1

2️⃣ 开 Long Mode Enable
   EFER.LME = 1   ← ❗最关键

3️⃣ 设置页表
   CR3 = PML4

4️⃣ 开分页
   CR0.PG = 1

5️⃣ 远跳进入 64-bit
   ljmp selector, offset

🔥 六、如何快速验证(经验总结)

✅ 关键寄存器检查

plaintext
info registers cr0
info registers cr4
info registers efer

✅ 正确状态应该是:

text
CR0 = 0x80000011   (PE + PG)
CR4 = 0x20         (PAE)
EFER = 0x1100      (LME + SVME)

❗ 一旦不满足:

👉 直接:

💥 triple fault / 启动失败


🧪 七、最有效 Debug 技巧(这次你已经用上了)

1️⃣ 看 CR2

plaintext
info registers cr2

👉 判断 page fault 地址


2️⃣ 直接 dump 页表

plaintext
x /8gx 0x1000
x /8gx 0x2000
x /8gx 0x3000

👉 判断:

  • 结构是否正确
  • entry 是否错位

3️⃣ 单步观察 CR0 写入

plaintext
break *addr
stepi

👉 精确定位 crash 点


🧠 八、这次踩坑的本质总结

💥 问题不在“页表”,而在“CPU状态机切换错误”


核心问题链:

text
❌ 没开 LME
❌ 直接开 PG
❌ CPU 进入非法状态
❌ triple fault

🎯 九、面试级总结(可以直接说)

👉 你可以这样总结这次经历:


在实现 x86_64 long mode 切换时,我遇到了在开启分页(CR0.PG)时 CPU 直接 triple fault 的问题。

经过逐步排查寄存器状态(CR0/CR4/EFER),发现 CR4.PAE 已开启,但 EFER.LME 未正确设置。

进一步定位发现 EFER_LME 位定义错误(误写为 0x1000,而正确应为 0x100)。

该问题导致 CPU 未进入 long mode,却错误开启分页,从而触发非法状态异常。

最终修正 LME 位并严格按照: PAE → LME → CR3 → PG 的顺序初始化,成功进入 long mode。

这次问题让我深入理解了:

  • x86_64 启动的状态机模型
  • CR0/CR4/EFER 的协同关系
  • 页表不仅是结构问题,更是状态依赖问题

🚀 十、这次经验的价值

这次踩坑本质上你已经掌握:

✔ x86 启动全链路 ✔ 页表三级结构 ✔ long mode 切换机制 ✔ CPU 控制寄存器协同关系 ✔ GDB 硬核调试能力

035_multi_terminal-40-g5d72b8b · 5d72b8b · 2026-06-26