上周做了一个 64 位 Windows 的培训。其中一部分是讲 WOW64 。用户态的 32 位代码在即将进入内核态之前会从 x86-64 的 compatibility mode 切换到 long mode(刚刚从内核态返回之后会进行相反的切换)。因为切换是在用户态完成的(通过从 32 位代码段直接 jmp 到 64 位代码段),所以不可能修改 cr3(首级页表的首地址)。
培训的时候没有深究这个问题,后来发觉理解得很模糊。32 位保护模式的 MMU 内存映射是两级,每级页表是 1024 项,每项 32 位。64 位 long mode 的 MMU 映射是四级,每级页表 512 项,每项 64 位。如此如何不修改 cr3 就能做 64 位和 32 位的切换?是在进入内核之后又修改了一次 cr3 ?还是 compatibility mode 可以直接使用 64 位的四级页表?
这次我发现 Google 还是有局限性的。两天里 Google 了十几次也没找到答案,可能是做内核的人觉得这个问题太简单不值一提吧。最后还是老老实实的查了 Intel 的 Architectures Software Developer’s Manual, System Programming Guide :(9.8.5.3 64-bit Mode and Compatibility Mode Operation)
In compatibility mode, the following system-level mechanisms continue to operate using the IA-32e-mode architectural semantics:
- Linear-to-physical address translation uses the 64-bit mode extended page-translation mechanism.
- Interrupts and exceptions are handled using the 64-bit mode mechanisms.
- System calls (calls through call gates and SYSENTER/SYSEXIT) are handled using the IA-32e mode mechanisms.
这里的 64-bit 模式也就是 AMD 术语里的 long mode ,而 IA-32e mode 是指 CPU 处于 long mode 或者 compatibility 之中任何一种模式。
所以,compatibility mode 的执行环境有很大一部分是借用 long mode 的模式,因此第一次进入 compatibility mode 之前必须按照 long mode 进行必要的设置(这个设置是在纯 32 位保护模式下关闭 paging 的时候完成的,所以首次进入 compatibility mode 的四级页表只能在低 4G 内存,因为这时只能操作 cr3 的低 32 位)。因为第一次进入 IA-32e 模式一定是在 32 位代码段里进行的,所以 CPU 总是会首先进入 compatibility mode 。
发表评论