在英特尔® 64 和 IA-32 架构软件开发人员手册第 3A 卷 9.3 序列化指令中
当执行启用或禁用分页的指令时(即更改控制寄存器中的PG标志) CR0),该指令应该后跟跳转指令。跳转指令的目标指令是 使用 PG 标志的新设置(即启用或禁用分页)来获取,但跳转指令 其本身是使用之前的设置获取的。 Pentium 4、Intel Xeon 和 P6 系列处理器不需要 移动到寄存器 CR0 之后的跳转操作(因为在 Pentium 4 中使用 MOV 指令, Intel Xeon 或 P6 系列处理器写入 CR0 是完全序列化)。
“序列化指令”将在运行之前“序列化指令执行流”以避免重新排序。
问:
mov
和一个
CR0
寄存器操作数)?是否意味着刷新页表或其他?
完全序列化”意味着什么,因此它不需要遵循“序列化指令”的跳转?
如果没有跳转,在
mov cr0, reg
是非序列化的旧 CPU 上,执行后续指令可以 使用使用旧的 CS:EIP 解释的代码获取结果。 (即使在新的 CPU 上,对于像
if (new_code_written_by_another_core) jmp new_code
这样的情况,实际上也需要序列化指令 - 由于 LoadLoad 和 StoreStore 排序,释放/获取语义在 x86 上免费发生,但是代码获取与数据加载是分开的,并且可以获取您要跳转到的内存位置中的旧值。也许这有助于理解完全序列化的意义。)
立即跳转似乎是一种合理的防御性编码实践(在 P6 / P4 之前的 CPU 上),可以确保在页表错误时立即出现页面错误,而不会在预取缓冲区到达之后的某个时刻出现混乱。从虚拟 CS:EIP 获取的指令。
英特尔手册对于此类内容的推荐代码序列往往比较保守。例如从实模式更改为长模式的序列涉及暂时进入保护模式,因此人们不愿意假设未来的 CPU 将以相同的方式工作,并采用直接从实模式进入的较短序列。
https://wiki.osdev.org/Setting_Up_Long_Mode
在这种情况下,我怀疑这只是最简单和最简单的沟通方式,如果你的页表错误,你会在获取跳转目标时出现页面错误,而不是mov cr0, eax
之后的指令,除非你'重新安装较新的 CPU。因为软件错误确实会发生,并且很难理解它们,尤其是在过去的糟糕日子里,当时更常见的是仅在真实硬件上进行测试,而不是在模拟器中进行测试,在模拟器中您可以单步执行并让它告诉您为什么即使您正在切换模式并且没有使用任何崩溃报告双故障处理程序设置 IVT 或 IDT,系统也会崩溃。
如果您继续获取 PG=0 的指令,则意味着代码不会经过翻译,IP 将继续“扁平”(不翻译)。
在jmp/序列化指令之后,指令将转到新路径并使用“新”翻译作为从中获取的正确代码。