【问题标题】:How do (x86) virtual machines generally handle flags?(x86) 虚拟机通常如何处理标志?
【发布时间】:2016-10-27 19:11:34
【问题描述】:

对于一个业余项目,我正在尝试编写一个半可编程 x86 虚拟机。

我了解格式,所以大部分设计都比较简单,但是在执行带有操作数的指令后,标志经常会发生变化。检查每个潜在位的效率非常低,所以我正在考虑将标志寄存器弹出到 VM 中,对它进行与运算,然后设置 VM 的标志寄存器。但是,这仍然是很多的开销。

这有点自以为是,但我有什么遗漏吗?

【问题讨论】:

  • 不清楚你在做什么。
  • 嗯,通常管理程序不会模拟指令 pee 步骤,而是让来宾代码(大部分)未经修改地运行,当硬件检测到特权指令时会陷入管理程序内核中。当然在实践中它比这要复杂得多,但关键是通常你只需要在进入管理程序时才需要担心标志寄存器的状态,而不是在每条指令之后。
  • @Jester 我认为他正在构建一个模拟器。
  • 我不明白你在做什么。如果这是一个虚拟机,你通常会让代码执行并做它的事情,直到你得到一个 vmcall,并且基本上不关心标志。如果这是一个“幼稚”的模拟器,并且您正在用高级语言重新实现 x86 指令集,那么您可以很聪明,并且在大多数情况下避免计算标志(通过将其延迟到以后或通过提前检查哪些标志可能是相关)。
  • 如果您只是在模拟指令(这意味着您从未在 CPU 上本地运行指令,无论是否使用 VT-X),请编辑您的问题以表明您正在构建一个 模拟器.

标签: assembly x86 virtual-machine virtualization


【解决方案1】:

如果您希望模拟器按原样模拟处理器,那么是的,您需要完全模拟标志。

这意味着清除需要清除的位(使用 AND),设置需要设置的位(使用 OR),并在需要时复制/计算位(即 Z 标志需要测试结果是否为零,携带需要知道你是否有溢出等)

没有办法解决。

这就像解码R/M mod 字节。您无法绕过加载该字节,检查模式以确定这是寄存器还是内存访问,并相应地应用它们......

实际上,这意味着您的模拟器将“慢得多”(除非您使用 3Ghz 现代处理器来模拟旧的 10Mhz 处理器,否则当您有时间执行 300 个指令周期时……所以您应该很好。)

如果您有兴趣,我写了一个6502 emulator 并使用 Apple 2 ROM 对其进行了测试。我必须添加睡眠才能让它以 100Mhz 或更高的速度运行......(该处理器最初运行的是 1Mhz......)

【讨论】:

  • 有办法;它被称为惰性评估:将足够的信息保存到计算标志,但不要这样做。 x86 写入标志的频率高于读取标志的频率。
  • @PeterCordes 是的,我想您可以将最后一个结果保存在一个变量中,如果您需要评估 Z、C、V 以及其他一些标志,也可以使用它。它可能比每次评估标志。但在某些情况下可能会很复杂,例如在 ADCROL 指令之间计算 C 时的冲突
【解决方案2】:

您似乎在询问模拟 x86,而不是虚拟化它。由于modern x86 hardware supports virtualization,CPU 在本地运行客户代码,并且只为一些特权指令捕获到虚拟机管理程序,这就是术语“虚拟化”通常的含义。


懒惰的标志评估是典型的。无需实际计算所有标志,只需保存设置标志的最后一条指令中的操作数。然后,如果确实有东西读取了标志,请找出标志值需要是什么。

这意味着您实际上不必在每次写入(几乎每条指令)时计算 PF 和 AF,只需每次读取它们(主要是 PUSHF 或中断,几乎没有任何代码读取 PF(除了对于 FP 分支,它表示 NaN))。在纯 C 中,在每条整数指令之后计算 PF 是昂贵的,因为它需要对结果的低 8 位进行 popcount。 (而且我认为 C 编译器通常无法识别该模式并使用 setp 自己,更不用说使用 pushflahf 来存储多个标志,如果编译 x86 模拟器以在 x86 主机上运行。他们但是,当针对具有该功能的主机 CPU(例如 -march=nehalem)时,有时会识别人口计数模式并发出 popcnt 指令。

BOCHS 使用此技术,并在此简短 pdf 的 Lazy Flags 部分中详细描述了实现:How Bochs Works Under the Hood 2nd edition。他们保存结果,以便他们可以导出 ZF、SF 和 PF,以及 CF 和 OF 的高 2 位以及 AF 的第 3 位的进位。有了这个,他们永远不需要重播指令来计算其标志结果。

由于某些指令没有写入所有标志(即部分标志更新),并且可能来自像 BSF 这样的指令根据输入而不是输出设置 ZF,因此会带来额外的复杂性。


进一步阅读

This paper on emulators.com 提供了很多关于如何有效地保存足够的状态来重建标志的细节。它有一个“用于 CPU 仿真的 2.1 Lazy Arithmetic Flags”。

其中一位作者是 Darek Mihocka(长期仿真器作者,现在显然在英特尔工作)。他写了很多关于使非 JIT 仿真器快速运行的有趣的东西,以及一般的 CPU 性能的东西,其中大部分都发布在他的网站上,http://www.emulators.com/。例如。 this article 关于在模拟器的解释器循环中避免分支预测错误,该循环调度到实现每个操作码的函数是非常有趣的。 Darek 也是我之前链接的那篇关于 BOCHS 内部的文章的合著者。

懒惰标志评估的谷歌点击也可能是相关的:https://silviocesare.wordpress.com/2009/03/08/lazy-eflags-evaluation-and-other-emulator-optimisations/

上次模拟类似 x86 的标志出现了,我的惰性标志答案中的 discussion in comments 有一些有趣的东西:例如@Raymond Chen 建议链接到 Mihocka & Troeger 论文,@amdn 指出 JIT 动态翻译可以产生比解释更快的仿真。

【讨论】:

    猜你喜欢
    • 2019-08-13
    • 1970-01-01
    • 2013-03-05
    • 1970-01-01
    • 2012-11-27
    • 1970-01-01
    • 1970-01-01
    • 2013-08-11
    • 2023-03-27
    相关资源
    最近更新 更多