点亮什么与为什么
002 我们把机器拉进了 32 位保护模式,打了个 P 到 debugcon 就停了。可 Cinux 是个 x86_64 系统,真正要跑的是 64 位。这一章,我们就在那张 32 位 PM 的地基上,搭一套临时分页、按 Intel 规定的固定顺序拨开几个开关,再用一句远跳跨进 64 位长模式——跨过去之后,debugcon 会再吐一个 L。
这一章我们要点亮什么
在 pm_entry 打完 'P' 之后,我们不再 hlt,而是接着干两件事,把 CPU 从 32 位 PM 推进 64 位长模式:
pm_entry (32 位 PM)
└─▶ setup_page_tables() # 在 0x1000/0x2000/0x3000 搭临时恒等映射
└─▶ enter_long_mode()
├─ CR3 = 0x1000 # 装载 PML4 基址
├─ CR4 |= PAE # 开物理地址扩展
├─ EFER |= LME # 开"长模式使能"
├─ lgdt gdt64_ptr # 换一张带 64 位段的 GDT
├─ CR0 |= PG # 开分页——这一拍长模式才真正生效
└─ ljmp $0x18, $long_mode_entry # 远跳到 64 位代码段
└─▶ long_mode_entry (.code64)
├─ 数据段 = 0x20、rsp = 0x90000
├─ outb 'L', $0xE9 # debugcon 打 'L'
└─ hlt 循环完成后,build/debug.log 里会依次出现 P(002 进 PM 时打的)和 L(本章进长模式时打的)——连起来就是 PL。看到 L,就证明 Cinux 已经是货真价实的 64 位 CPU 模式了。
为什么现在需要它
你可能觉得 32 位 PM 已经够用了,为什么要费劲进 64 位?因为后面我们要写的是一个真正的 x86_64 内核:它要用 64 位寄存器、要寻址远超 4GB 的内存、要用 syscall/sysret 这套 64 位专属的快速系统调用。这些在 32 位 PM 里统统做不到。
但进长模式有个硬门槛:长模式强制要求分页开启。和 32 位 PM 不同(PM 下分页是可选的),长模式必须建立在"分页已开 + CR4.PAE 已开 + IA-32e 页表已就绪"的基础上。原因在于:32 位 PAE 分页本身是三级结构(PDPT → PD → PT),而 x86-64 长模式使用的 IA-32e paging 沿用了 PAE 风格的 64 位页表项、并在 PDPT 之上再加一级 PML4,这才形成 PML4 → PDPT → PD → PT 的四级页表——CPU 一旦进入长模式,所有地址翻译都得走这套四级结构,没有分页它根本没法翻译地址。
所以这一章的主线其实就是:先搭一套刚好够用的临时分页,再按顺序拨开关。这套分页我们故意做得极简——只恒等映射前 8MB,够 bootloader 自己和接下来要加载的内核跑起来就行。真正的物理内存管理(PMM)和虚拟内存管理(VMM)是 001/002 的事,现在不碰。
外部依据:Intel SDM Vol.3A §4.1(四级分页)、§4.3(2MB 大页)、§4.5(PAE)、§11.8.2(EFER 与 LME)、§9.8(切换到长模式的固定序列)。