是否可以在 VM 中使用 VMX CPU 指令?

Is it possible to use VMX CPU instructions inside VM?

VM 来宾内部的进程是否可能使用 VMX(AMD-V、VT-x)CPU 指令,然后由外部 VMM 处理而不是直接在 CPU?

编辑:假设外部VM使用VMX本身来管理它的虚拟客户机(即运行在Ring -1)。

如果可能,是否有任何支持 emulating/intercepting VMX 调用(VMware、Parallels、KVM 等)的 VMM 实现?

在阅读了很多关于虚拟化的内容后,我在 virtualbox 上偶然发现了这个 ticket

正是针对此功能的功能请求,通过阅读评论,VMware Workstation 似乎已经实现了它,因此它确实可以正常工作。

Intel 的 VT-x 和 AMD 的 AMD-V 都不支持硬件中的完全递归虚拟化——其中 CPU 以 call/ret 对的相同方式保持嵌套虚拟化环境的层次结构。

一个逻辑处理器只支持两种操作模式:主机模式(Intel术语中称为VMX root模式,AMD的hypervisor)和来宾模式(AMD的手册中这样称呼,而VMX非root模式在英特尔的)。
这意味着一个扁平的层次结构,其中每个虚拟化环境都被 CPU 相同对待 - CPU 不知道 VM 的层次结构有多深。

尝试在来宾内部使用虚拟化指令本身会将控制权交给监视器 (VMM)。
但是最近出现了一些对加速常用虚拟指令的支持,使嵌套 VM 成为可能。

我将尝试分析实现嵌套虚拟化所面临的问题。
我并没有处理整个事情——我正在考虑基本情况,只遗漏了处理硬件虚拟化的所有部分;这部分本身与软件的虚拟化一样存在问题。

备注
本人不是虚拟化技术专家,完全没有这方面的经验,欢迎指正。
这个答案的目的是让 reader 从概念上相信嵌套虚拟化是可能的,并概述要面对的问题。

VT-x

逻辑处理器通过执行 vmxon 进入 VMX 操作 - 一旦进入该模式,处理器就处于根模式。
Root模式是VMM的模式,可以启动、恢复和处理VM。

然后 VMM 将当前 VMCS(VM 控制结构)设置为 vmptrld - VMCS 包含虚拟化来宾所需的所有元数据。
VMCS 的读取和写入不是使用直接内存访问,而是使用vmreadvmwrite 指令。

最后VMM执行vmlaunch开始执行guest。

正在接收 VM

现在逻辑处理器正在虚拟化环境中执行。
假设来宾本身是一个 VMM,我们称其为非根 VMM - 它需要重复上述步骤。

但英特尔在其手册中很清楚(手册 3 - 第 25.1.2 章):

The following instructions cause VM exits when they are executed in VMX non-root operation:
[...]
This is also true of instructions introduced with VMX, which include:
[...], VMLAUNCH, VMPTRLD, [...] and VMXON

vmxon 该指令导致 VM 退出,根 VMM 从其最后一个 vmlaunch 后的指令恢复,可以检查 VMCS 退出的原因并采取适当的措施。
我不是经验丰富的 VMM 编写者,所以我不确定根 VMM 必须做什么才能完全模拟此指令 - 因为在 VMX 根模式下执行 vmxon 会失败,然后执行 vmxoff vmxon 与非根 VMM 给出的 VM 区域似乎是一个安全漏洞(或导致它)我相信根 VMM 所要做的就是记录来宾现在在 "VMX root mode".
引号在这里是必要的:当根 VMM 将控制权交还给非根 VMM 时,此模式仅存在于软件中 CPU 将处于非根 VMX 模式。

之后,非根 VMM 将尝试使用 vmptrld 设置当前 VMCS。
vmptrld 将导致 VM 退出并且根 VMM 再次控制 - 如果 CPU 不支持 VMCS shadowing 根 VMM 有记录非根 VMM 给出的指针现在是当前 VMCS - 如果 CPU 确实支持 VMCS 阴影 ,则 VMM 设置 VMCS link its VMCS(用于虚拟化非根VMM的那个)的指针字段指向非根VMM给出的VMCS。
VMM 以某种方式知道哪个虚拟化 VMCS 处于活动状态。

vmreadvmwrite 由非根 VMM 执行会或不会导致 VM 退出。
如果 VMCS 阴影处于活动状态,CPU 将不会执行 VM 退出,而是读取活动 VMCS 中 VMCS link 指针 指向的 VMCS(称为影子 VMCS).
这将加速嵌套 VM 的虚拟化。
如果 VMCS 阴影未激活,CPU VM 将退出并且根 VMM 必须模拟 read/write.

最后,非根 VMM 将启动其 VM - 这是一个嵌套 VM。
vmlaunch 将触发 VM 退出。
根 VMM 必须做几件事:

  • 将其 VMCS 保存在某处。
  • 合并当前 VMCS 和非根 VMM VMCS - 因为 VMCS 控制,例如,什么事件导致 VM 退出,合并的必须是这方面的两者的联合。
  • 加载合并的 VMCS 作为 CPU 的当前
  • 做一个vmlaunch/vmresume

梦里

现在 CPU 正在执行嵌套 VM(VVM - 虚拟 VM?)。
当敏感指令或事件导致 VM 退出时会发生什么?

从处理器的角度来看,只有两个级别的虚拟化:根VMX模式和非根VMX模式。
由于来宾处于非根 VMX 模式,因此控制权将转移回根 VMX 模式代码 - 即根 VMM。

根 VMM 现在必须了解该事件是来自其 VM 还是来自其 VM 的 VM。
这可以通过跟踪 vmlaunch/vmresume 的使用并检查 VMCS 中的位来完成。

如果 VM 出口指向非根 VMM,根 VMM 必须加载其原始 VMCS,最终在其中设置 link 非根 VMM,更新非根 VMM VMCS状态位并执行 vmresume.
如果 VM Exit 指向它,则根 VMM 将像处理任何其他 VM Exit 一样处理它。

梦中梦中梦

如果我们想在嵌套的 VM 中创建一个 VM 怎么办? 一种虚拟虚拟机 (VVVM)。

有两点需要注意:

  1. 根 VMM 仍然是每次 VM 退出时调用的那个。
    即使 VVVM 是三层深,它也不是非根非根 VMM,第一个 and/or 唯一用于虚拟化它的管理器。
    从安全的角度来看,根 VMM 是弱 link.
  2. 硬件并不真正支持任意深度嵌套。
    VMM 可能不需要太多的努力就可以从支持 1 级嵌套到 n 级嵌套(同样我在这里没有经验丰富)但是仍然需要如上所述的特殊支持。
    它不像启动虚拟机那么容易,其他一切都将由 CPU.
  3. 处理

AMD-v

在 AMD-v 中没有 root 与非 root 模式,CPU 开始执行一个带有 vmrun 的 VM,该 VM 接受一个指向服务于与 Intel 的 VMCS 的目的相同。
vmrun CPU 处于访客模式。

VMCB 已缓存,但只能通过常规内存访问读取。
vmload/vmsave 指令显式加载和保存缓存中的 VMCB 字段。

此接口比 Intel 的接口更简单,但功能同样强大 - 即使在嵌套虚拟化方面也是如此。

假设我们在 VM 中并且代码执行 vmrun - 因此我们正在虚拟化 VMM。

从技术上讲,VMM 可以选择何时 vmrun 触发或不触发 VM 退出。
然而,实际上,AMD-v 目前要求前者始终如此:

The following conditions are considered illegal state combinations: [...]
* The VMRUN intercept bit is clear

因此根 VMM(我将使用与 Intel 案例中相同的术语)将获得控制权并且必须模拟 vmrun (因为硬件只支持单一级别的虚拟化)。

根 VMM 可以保存当前 VMCB 并将其与非根 VMM VMCB 合并,然后像 Intel 一样继续 vmrun

在退出时,root-VMM 必须确定退出是指向它还是指向非根 VMM,同样这可以通过跟踪 vmrun 和 VMCB 中的控制位来完成。

又做梦了

我们已经相对容易地在 VM 中设置了 VM - 现在 VM 退出时会发生什么?
根 VMM 收到退出,如果指向非根 VMM,则必须恢复其原始 VMCB 并恢复 运行(即使用 vmrun 及其原始 VMCB)。

AMD-v 支持 vmsavevmload 指令的快速虚拟化,通过考虑它们的地址来宾地址,因此受制于通常的页面嵌套虚拟化。

再次启动盗梦空间

与英特尔案例一样,只要 VMM 支持该功能,虚拟化就可以再次嵌套。

针对英特尔案例的严重安全警告也适用于 AMD 案例。


由于其实现定义的格式以及内存区域可以用作不实时更新的溢出区域的事实