也不是英特尔的VT-x也不是 AMD 的AMD-V支持硬件中的完全递归虚拟化 - 其中 CPU 以与虚拟化环境相同的方式保留嵌套虚拟化环境的层次结构call
/ret
pair.
逻辑处理器仅支持两种操作模式:主机模式(在 Intel 术语中称为 VMX 根模式,AMD 中称为虚拟机管理程序)和来宾模式(在 AMD 手册中称为 VMX 非根模式)。
这意味着一个扁平化的层次结构,其中每个虚拟化环境都被 CPU 同等对待 - CPU 不知道虚拟机的层次结构有多深。
尝试在来宾内部使用虚拟化指令本身会将控制权交给监视器(VMM)。
但最近出现了一些对加速常用虚拟指令的支持,使得嵌套虚拟机成为可能。
我将尝试分析实现嵌套虚拟化所面临的问题。
我没有处理整个事情 - 我正在考虑基本情况,只忽略处理硬件虚拟化的所有部分;这一部分本身就和软件虚拟化一样存在问题。
Note
我不是虚拟化技术方面的专家,也没有任何经验——欢迎指正。
这个答案的目的是让读者从概念上相信嵌套虚拟化是可能的,并概述要面临的问题。
VT-x
逻辑处理器通过执行以下命令进入VMX操作vmxon
- 一旦进入该模式,处理器就处于根模式。
Root模式是VMM的模式,它可以启动、恢复和处理VM。
The VMM then set the current VMCS (VM Control Structure) with vmptrld
- the VMCS contains all the metadata necessary to virtualise a guest.
The VMCS is read and written not with direct memory accesses‡ but with vmread
and vmwrite
instructions.
最后,VMM执行vmlaunch
开始执行来宾。
接收虚拟机
现在逻辑处理器正在虚拟化环境中执行。
假设来宾本身就是一个 VMM,我们将其称为非根 VMM - 它需要重复上述步骤。
但英特尔在其手册中明确表示(手册 3 - 第 25.1.2 章):
以下指令会导致VM exits当它们在 VMX 非 root 操作中执行时:
[...]
VMX 引入的指令也是如此,其中包括:
[...],VMLAUNCH
, VMPTRLD
, [...] 和VMXON
vmxon
该指令导致 VM 退出,根 VMM 从最后一条指令后的指令恢复vmlaunch
,可以检查 VMCS 的退出原因并采取适当的操作。
我不是一个经验丰富的 VMM 编写者,所以我不确定根 VMM 必须做什么来模拟这个指令 - 因为执行vmxon
在 VMX root 模式下将会失败并执行vmxoff
随后是一个vmxon
由非根 VMM 提供的 VM 区域似乎是一个安全漏洞(或导致它的原因),我相信根 VMM 所要做的就是记录来宾现在处于“VMX 根模式”。
这里需要引号:此模式仅存在于软件中,当根 VMM 将控制权交还给非根 VMM 时,CPU 将处于非根 VMX 模式。
之后,非根 VMM 将尝试使用vmptrld
设置当前的 VMCS。
vmptrld
将导致 VM 退出并且根 VMM 再次获得控制 - 如果 CPU 不支持VMCS 阴影 https://www-ssl.intel.com/content/dam/www/public/us/en/documents/white-papers/intel-vmcs-shadowing-paper.pdf根 VMM 必须记录非根 VMM 给出的指针现在是当前 VMCS - 如果 CPU 确实支持VMCS 阴影VMM 设置VMCS链接指针现场itsVMCS(用于虚拟化非根 VMM 的 VMCS)为非根 VMM 提供的 VMCS。
VMM 通过某种方式知道哪个虚拟化 VMCS 处于活动状态。
vmread
and vmwrite
由非根 VMM 执行将或不会导致 VM 退出。
如果 VMCS 阴影处于活动状态,CPU 将不会执行 VM 退出,而是会读取由 VMCS 指向的 VMCSVMCS链接指针在活动 VMCS 中(称为影子VMCS).
这将加快嵌套虚拟机的虚拟化速度。
如果 VMCS 阴影未激活,CPU 将 VM 退出,并且根 VMM 必须模拟读/写。
最后,非根 VMM 将启动其 VM - 这是一个嵌套 VM。
vmlaunch
将触发 VM 退出。
根 VMM 必须做一些事情:
- 将其 VMCS 保存在某处。
- 合并当前 VMCS 和非根 VMM VMCS - 例如,由于 VMCS 控制哪些事件导致 VM 退出,因此合并后的事件必须是这两者的并集。
- 将合并后的 VMCS 加载为 CPU 的当前 VMCS
- Do a
vmlaunch
/vmresume
.
梦里面
现在CPU正在执行嵌套VM(VVM - 虚拟VM?)。
当敏感指令或事件导致 VM 退出时会发生什么?
从处理器的角度来看,虚拟化只有两个级别:根VMX模式和非根VMX模式。
由于客户机处于非根 VMX 模式,因此控制权将转移回根 VMX 模式代码 - 即根 VMM。
根VMM 现在必须了解该事件是来自其VM 还是来自其VM 的VM。
这可以通过跟踪使用情况来完成vmlaunch
/vmresume
并检查 VMCS 中的位。
如果 VM Exit 定向到非根 VMM,则根 VMM 必须加载其原始 VMCS,最终在其中设置非根 VMM 的链接,更新非根 VMM VMCS 状态位并执行vmresume
.
如果 VM 出口定向到它,则根 VMM 将像处理任何其他 VM 出口一样处理它。
梦中梦中梦
如果我们想在嵌套虚拟机中创建虚拟机怎么办?
一种虚拟虚拟机 (VVVM)。
有两点需要注意:
- 根 VMM 仍然是每次 VM 退出期间调用的 VMM。
即使 VVVM 是三层深度,它也不是用于虚拟化它的第一个和/或唯一的管理器。
从安全角度来看,根VMM是薄弱环节。
- 硬件并不真正支持任意深度嵌套。
VMM 可能不需要太多的努力就可以从支持 1 级嵌套到 n 级嵌套(同样,我在这里经验不足),但仍然需要如上所述的特殊支持。
这并不像启动虚拟机那么容易,其他一切都将由 CPU 处理。
AMD-v
AMD-v 中没有根模式与非根模式,CPU 开始执行虚拟机vmrun
它采用一个指向 VMCB(VM 控制块)的指针,该 VMCB 与 Intel 的 VMCS 具有相同的用途。
Upon a vmrun
CPU 处于访客模式。
VMCB 已被缓存,但只能通过通常的内存访问来读取。
The vmload
/vmsave
指令显式地将受缓存影响的 VMCB 字段加载到缓存中并从缓存中保存。
该接口比英特尔的接口更简单,但同样强大 - 即使涉及嵌套虚拟化。
假设我们在虚拟机内并且代码执行vmrun
- 因此我们正在虚拟化一个 VMM。
从技术上讲,VMM 可以随时选择vmrun
会或不会触发虚拟机退出。
然而,实际上,AMD-v 目前要求前者始终如此:
以下情况被视为非法状态组合:
[...]
* 这VMRUN
拦截位清除
因此,根 VMM(我将使用与 Intel 案例中相同的术语)将获得控制权并且必须模拟vmrun
(因为硬件仅支持单级虚拟化)。
根VMM可以保存当前VMCB并将其与非根VMM VMCB合并,并继续执行vmrun
就像英特尔的案例一样。
退出时,根 VMM 必须确定退出是定向到它还是定向到非根 VMM,这同样可以通过跟踪vmrun
以及 VMCB 中的控制位。
又做梦了
我们已经在虚拟机内相对容易地设置了虚拟机 - 现在虚拟机退出时会发生什么?
根 VMM 接收退出,如果定向到非根 VMM,则必须恢复其原始 VMCB 并恢复运行(即使用vmrun
及其原始 VMCB)。
AMD-v 支持快速虚拟化vmsave
and vmload
指令通过考虑它们的地址来宾地址,从而受到通常的页面嵌套虚拟化的影响。
再次提起 Inception
与 Intel 的情况一样,只要 VMM 支持该功能,虚拟化就可以再次嵌套。
针对英特尔案例的关键安全警告也适用于 AMD 案例。
‡ Due to its implementation-defined format and the fact the memory area can be used just as a spill area that is not updated in real time