Hypervisor(二):VMCS region
😄
1 VMCS region
在 VMX 架构下,至少需要实现一个被称为 VMXON region,以及一个被称为 VMCS region 的物理区域。
- VMXON 区域对应于 VMM,VMM 使用 VMXON 区域对一些数据进行记录和维护。
- 每个 VMCS(Virtual Machine Control Structure,虚拟机控制结构)区域对应着一个 VM。VMM 使用 VMCS 区域来配置 VM 的运行环境,以及控制 VM 的运行。
- 在进入 VMX operation 模式前,必须先为 VMM 准备一份 VMXON 区域,同样在进入 VM 前也必须准备相应的 VMCS 区域,并配置 VM 的运行环境。
1.1 VMCS 概述
VMM 通过虚拟机控制结构 VMCS 来设置和保存 VMX root、VMX non-root 模式切换中涉及的数据、寄存器值等。VMCS 具有以下几点特征:
- 一个逻辑处理器对应 VMM 中的一块 VMCS(该区域称为 VMCS region),而不是一个 VM 对应着一个 VMCS。如果一个 VM 使用了几个逻辑处理器 vCPU,则就应该有几个对应的 VMCS 结构。可以使用 MSR
IA32_VMX_BASIC
来确定 VMCS 区域的大小。 - 每个逻辑处理器中都有一个 VMCS 指针(64 bits)指向 VMCS 块的物理地址,应与 4KB 边界对齐。使用
VMPTRST
和VMPTRLD
读取和写入 VMCS 指针的值。如果IA32_VMX_BASIC[48] = 1
,则这些指针不得设置63:32
范围内的任何位,见附录 A.1。 - VMM 使用
VMREAD
、VMWRITE
和VMCLEAR
指令配置 VMCS。
1.2 VMCS 的状态
在 VMX root、VMX non-root 模式切换中,从 VMX root 切换到 VMX non-root 模式被称为 VM-entry;从 VMX non-root 切换到 VMX root 模式被称为 VM-exit。
注意⚠️:VM-exit 和 VM-entry 不会改变 VMCS 的状态。
VMCS 的三类属性状态:
- active — inactive,活动的—非活动的。
- current — not current,当前的—非当前的。在一个逻辑处理器上一个时刻只能有一个 VMCS 处于 current(current-VMCS)。
- launched — clear,已启动—干净。
启动 VMCS region 区域顺序:
- VMCS region 区域初始化之后要先试用
VMCLEAR
使该 VMCS 处于 inactive、not current、clear。 - 使用
VMPTRLD
使 VMCS 变成 active、current。 - 使用
VMLAUNCH
使 VMCS 变成 launched。
制约关系:
- 使用
VMLAUNCH
指令前,必须先使用VMCLEAR
使 VMCS region 处于 clear 状态(此时current-VMCS pointer == 0xFFFFFFFFFFFFFFFF
),然后使用VMPTRLD
将 VMCS 变成 active、current,最后使用VMLAUNCH
指令。 - 使用
VMRESUME
进入 VM 前,该 VM 必须已经使用VMLAUNCH
启动过了。 VMREAD
,VMWRITE
,VMLAUNCH
,VMRESUME
及VMPTRST
指令的操作都是实施在 current-VMCS 之上的。
注意:
- 假设先使用
VMPTRLD
使得 VMCS-A 变成 active、current,current-VMCS point 是 VMCS-A 的。 - 然后又使用
VMPTRLD
使得 VMCS-B 变成 active、current,current-VMCS point 是 VMCS-B 的。那么此时 VMCS-A 的状态是 active(活动状态不变)、not current。 - 在软件中没有任何方法来读取某个 VMCS 处在哪一个状态,CPU 维护这些状态。
VMCLEAR
、VMPTRLD
指令的操作数都是物理地址。
下图描述了 VMCS 的状态变化,在 Intel 手册中使用 “The VMCS” 表示 “Current VMCS”。
1.3 申请 VMCS region
申请 VMCS region 和申请 VMXON region 类似。
- 通过 IA32_VMX_BASIC[44:32] 得到 VMCS 所需空间大小(0x1000,4096 bytes)。
- 通过 IA32_VMX_BASIC[53:50] 得到 VMCS 所需内存类型,如 write-back。
- 调用 MmAllocateContiguousMemorySpecifyCache 函数申请连续指定大小和类型的物理内存。
- 判断返回地址是否在边界对齐 4KB(bit 11:0 为 0)。
- 将 IA32_VMX_BASIC[31:0] 的 VMCS revision identifier 写入 VMCS 前 4 字节的 [bit30:0]。
- 将 VMCS 的 bit 31 = 0(不是 shadow VMCS)。
- 使用
VMCLEAR
使 VMCS region 处于 clear 状态。 - 使用
VMPTRLD
使 VMCS 变成 active、current。
1 | // |
1.4 VMCS 结构
偏移 | 内容 |
---|---|
0 | Bits 30:0 使用 VMCS revision identifier 来填充。来自于 IA32_VMX_BASIC[30:0] 。Bit 31 指示当前 VMCS 是否是 shadow VMCS。0 表示普通的 VMCS,1 表示 shadow VMCS(只有在)VM-execution control fields 的 VMCS shadowing == 1 时该位才有效。 |
4 | 当进行 VM-exit 操作遇到一个错误时,会产生 VMX-abort,然后处理器会将一个非零的值填充到当前的 VMX-abort indicator。通常导致 VMX Abort 事件的原因包括保存客户虚拟机 MSR 奇存器失败、当前 VMCS 区域损坏、加载 Hypervisor 的 MSR 寄存器失败等。 |
8 | VMCS 数据区包含 6 个区域,每个区域都有若干字段。用于 VMM root operation 和 VMM non-root operation 转换时提供/保存信息。 1. Guest-state area:VM-exit 时,处理器的当前状态保存在该区域。VM-entry 时从该区域加载处理器的状态值。 2. Host-state area:VM-exit 时从该区域加载处理器的状态值。这个区域记录了所有有关 Hypervisor 的状态信息 3. VM-execution control fields:重点关注该区域。控制着处理器在 Guest 的行为,设置 VM-exit 的条件。 4. VM-exit control fields:该区域定义了在 VM-exit 事件发生后硬件立即要做的事情。 5. VM-entry control fields:该区域定义了在 VM-entry 事件发生后硬件立即要做的事情。 6. VM-exit information fields:记录引起 VM-exit 事件的原因及接收相关的明细信息。在许多处理器中,该区域都是只读的。如果 MSR IA32_VMX_MISC[bit29] = 1 则表示该区域可使用 VMWRITE 写。 |
注意:由于目前 VMCS 中部分区域需要是 write-back 内存类型,目前申请 VMCS 的内存类型都是 write-back,但是未来可能需要不同的内存类型(Intel 卷3 24.2)。
参考资料:
- 处理器虚拟化技术(邓志)
- New Blue Pill 深入理解硬件虚拟机
- 加密与解密第 4 版 Chapter 10
- Intel® 64 and IA-32 Architectures Software Developer’s Manual Volume 3 (3A, 3B, 3C & 3D): System Programming Guide Chapter 23
1.5 VMCS 字段 ID
VMCS 分为 6 个区域,每个区域里面都有很多字段,每个字段由一个 ID 值来表示(该 ID 值为 32 bits)。每个字段一般有以下几种宽度:
- 16 bits(2 bytes)
- 32 bits(4 bytes)
- 64 bits(8 bytes)
- natural-width(在 32 位系统为 32 bits,在 64 位系统为 64 bits)
对于 VMCS 字段的读写,只能使用 VMREAD
、VMWRITE
,不能使用 MOV
。
ID 字段格式:
每个字段对应一个 ID 值,参看 HyperPlatform—VmcsField。
如 GuestEsSelector = 0x00000800
,GuestEsSelector
表示 ES 段寄存器的选择子,其对应的 ID 值为 0x00000800
。后续对该 16 bits 大小的字段读写就可以使用 VMREAD/VMWRITE
指令了。
1.6 Control 字段保留值
控制区:VM-execution control fields、VM-exit control fields、VM-entry control fields 中有许多值都必须使用保留值,保留值取 0/1
,需要在 IA32_VMX_BASIC[55](0x480)
的影响下进行取值。
影响的 VMCS字段 | bit 55 = 0 | bit 55 = 1 |
---|---|---|
Pin-Based VM-Execution Controls | IA32_VMX_PINBASED_CTLS(0x481) | IA32_VMX_TRUE_PINBASED_CTLS(0x48D) |
primary processor-based VM-execution control | IA32_VMX_PROCBASED_CTLS(0x482) | IA32_VMX_TRUE_PROCBASED_CTLS(0x48E) |
secondary processor-based VM-execution control | IA32_VMX_PROCBASED_CTLS2(0x48B) | IA32_VMX_PROCBASED_CTLS2(0x48B) |
tertiary VM-execution controls | IA32_VMX_PROCBASED_CTLS2(0x492) | IA32_VMX_PROCBASED_CTLS2(0x492) |
VM-exit contorl | IA32_VMX_EXIT_CTLS(0x483) | IA32_VMX_TRUE_EXIT_CTLS(0x48F) |
VM-entry control | IA32_VMX_ENTRY_CTLS(0x484) | IA32_VMX_TRUE_ENTRY_CTLS(0x490) |
上表中的 10 个 MSR 寄存器都是 64 bits,需要将其分为高/低 32 bits,如下图。
用法:
- 当 MSR 高 32 bits [63:32] 中的位为 0 时,对应的控制字段相应的位必须为 0。
- 当 MSR 低 32 bits [31:0] 中的位为 1 时,对应的控制字段相应的位允许为 1。
1 | ULONG AdjustControls(ULONG Ctl/*预设的初始值*/, ULONG Msr) |
备注:由于上面的控制字段都是 32 bits 的,所以将 MSR 的高 32 位类似于低 32 位一样使用。
举例说明:
- 假设将 Pin-Based VM-Execution Controls 初始值设置为 0。
- 此时
IA32_VMX_BASIC[55] = 1
。 IA32_VMX_TRUE_PINBASED_CTLS
高 32 bits 为0x0000003F
,低 32 bits 为0x00000016
。- 计算
0 & 0x0000003F == 0
,0 | 0x00000016 == 0x00000016
。 - 则 bit-1,bit-2,bit-4 必须为 1,其余位都可以为 0。
VM-function control字段,另说,P153。
2 Guest-state area
Guest-state area 包含寄存器类和非寄存器类字段。VM-exit 时,处理器的当前状态保存在该区域,VM-entry 时从该区域加载处理器的状态值。
2.1 寄存器类字段
- 控制寄存器:CR0、CR3、CR4。
- 调试寄存器:DR7。
- 堆栈、指令寄存器:RSP、RIP、RFLSGS。
- 段寄存器:CS、SS、DS、ES、FS、GS、TR。
- 描述符寄存器:GDTR、LDTR、IDTR。
- MSR 寄存器。
说明:对于 MSR 寄存器,只有在 VM-exit control、VM-entry control 区域中对应的寄存器字段设置为 1
时, Guest-state area 区域中的寄存器字段才有效。
2.2 非寄存器类字段
activity state 字段(32 bits,表示虚拟处理的状态)
该字段指示在 VM-entry 和 VM-exit 时,虚拟处理器的当前活动状态。包括 active(活动)与 inactive(非活动)两大类状态。目前 VMX 架构支持的状态如表 3-18 所示。
- active:逻辑处理器可以正常执行 Guest 的指令。
- inactive:逻辑处理器不可以正常执行 Guest 的指令,需要 VMM 进行处理才可以执行 Guest 的指令。
可以查看
IA32_VMX_MISC(0x485)[8:6]
,可以看到以上所有状态都支持。1
2
3
4
5
6
7
8
9kd> rdmsr 0x485
msr[485] = 00000000`5004c1e7
kd> .formats 0x00000000`5004c1e7
Evaluate expression:
Hex: 00000000`5004c1e7
Decimal: 1342489063
Octal: 0000000000012001140747
Binary: 00000000 00000000 00000000 00000000 01010000 00000100 11000001 11100111Interruptibility state 字段(32 bits)
该字段指示当前 Guest 中有没有被阻塞的中断,x86/64 体系中一共有 5 种中断阻塞状态。如下图:
cli
指令禁止中断发生,使rflags.IF = 0
,关中断,屏蔽可屏蔽中断(NMI 不可屏蔽)。sti
指令允许中断发生,使rflags.IF = 1
,开中断,响应中断。
x86 架构允许某些事件在一段时间内被阻止的功能。该字段的每一个位指示了当前有哪种状态被阻塞。
bit 中断被阻塞 描述 0 blocking by STI 指示当前有 STI 阻塞状态。 在
rflags.IF = 0
(不响应可屏蔽的中断)情况下,执行sti
指令以响应可屏蔽中断时,在sti
指令的下一条指令执行完毕前发生的外部中断,会被阻塞起来,当下一条指令执行完毕后,阻塞状态会被自动解除。这个时候才开始 delivery 刚才被阻塞的外部中断。
如果在执行sti
指令前eflags.IF = 1
,那么,这条sti
指令的下一条指令不会存阻塞的状态。
举例如下:sti;
mov [eax], ebx;
如上代码,假设执行sti
指令前rflags.IF = 0
,那么,执行sti
指令后,下一条mov
指令执行完毕前如果发生外部中断将被阻塞(某些处理器也可能会阻塞 NMI)。下一条mov
指令执行完毕后这个阻塞状态将被自动解除,如果此时有被阻塞的外部中断,将会开始进行 delivery。
但是,如果此时执行的mov [eax], ebx
指令产生了 #PF 异常(页面错误),且 exception bitmap 字段的 bit 14 为 1 时将直接导致 VM-exit 发生。VM-exit 时会设置 interruptibility state—blocking by STI = 1
,表示 VM-exit 时在 Guest 中存在blocking by STI
。
Intel 设计blocking by STI
阻塞状态的初衷是用在下面的场合:
sti;
ret;
当sti
指令最后是一条ret
指令时(假设之前rflags.IF = 0
),blocking by STI
阻塞状态的存在避免了在返回前产生中断(阻塞期间不 delivery 可屏蔽的中断)。1 blocking by MOV-SS 指示当前有 MOV-SS 阻塞状态。
使用MOV
或者POP
指令更新 SS 寄存器时,下一条指令执行完毕前会产生了blocking by MOV-SS
阻塞状态。这个阻塞状态将阻塞外部中断、NMI 及 #DB 异常。
在更新 SS 栈段时,需要保证栈指针(典型为内核中的栈)能够得到更新而不被中断打断(外部中断,NMI 或者 #DB 断点异常),避免在中断服务例程中使用了不适当的栈指针。如果软件使用LSS
指令来更新 SS 寄存器和 RSP 栈指针,则不存在blocking by MOV-SS
阻塞状态。因此,Intel 推荐使用LSS
指令代替MOV
或POP
来更新 SS 寄存器。
如:MOV SS,AX; MOV RSP, KernelStack;
或POP SS; MOV [RAX], RBX;
注意:在一个指令流里,不可能同时出现blocking by STI
与blocking by MOV-SS
两种阻塞状态。因此,这两个位不能同时为 1 值。
注意:如果在 VM-entry 时存在blocking by MOV-SS
阻塞状态,那么这个阻塞状态会因为注入事件的delivery 而被解除。blocking by SMI 指示当前有 SMI 阻塞状态。
处理器切换到 SMM 模式执行后,新的 SMI 请求会被阻塞,直到执行RSM
指令退出 SMM 模式后blocking by SMT
阻塞状态被解除。
由于这个原因,VM-entry control 字段enter to SMM = 1
时,表示 VM-entry 将进入 SMM 模式,此时 interruptibility state 字段的blocking by SMI
位必须为 1。反 之,VM-entry 进入非 SMM 模式时,interruptibility state 字段的blocking by SMI
位必须为 0 值。3 blocking by NMI 指示当前有 NMI 阻塞状态。
当处理器响应 NMI 请求时,NMI 服务例程得到 delivery 后将阻塞另一个 NMI 请求,直到执行iret
指令后解除阻塞SMI 请求也是这样。因此,blocking by NMI
阻塞状态的存在依赖于 NMI 是否得到 delivery。(See Section 6.7.1, “Handling Multiple NMls” in the Inte/® 64 and IA-32 Architectures Software Developer’s Manual, Volume 3A and Section 31.8, “NMI Handling While in SMM.”)是否存在
blocking by NMI
阻塞状态的区分方法(主要看 NMI 是否得到 delivery):
1、当 NMI 得到 delivery 执行时,即使在 delivery 期间发生某些错误而产生 VM-exit(在 NMI handler 过程,VM-exit 完成前),处理器就会存在 blocking by NMI 阻塞状态。
2、 当 NMI 由于NMI exiting = 1
而没有得到 delivery 直接 VM-exit 时,处理器在 VM-exit 完成前并不存在 blocking by NMI 阻塞状态。关于
iret
指令对blocking by NMI
阻塞状态的解除:
如果 NMI 或 virtual-NMI 已经被 delivery,此时再产生 NMI 或 virtual-NMI 时将会被阻塞。在执行iret
指令后,阻塞状态将会被解除。不管执行iret
指令会不会产生异常,都不影响阻塞状态的解除(处理器虚拟化技术 P235)。关于 VM-exit interruption information 字段
NMI unblocking
(bit 12)的说明:
只要iret
指令执行,不管执行过程中是否会发生异常,blocking by NMI
或blocking by virtual-NMI
阻塞状态都会被解除。但是并不是状态被解除就会将NMI unblocking
位置 1。
只有在执行iret
指令产生的异常直接导致 VM-exit 时,VM-exit interruption information 字段或 VM-exit qualification field 字段中的NMI unblocking due to IRET
位才会置为 1 值,指示 NMI 或 virtual-NMI 阻塞状态已经被解除。间接导致的 VM-exit 不会将该位置 1。
在以下的几种情形中,NMI unblocking(bit 12)
是未定义的值:
1、在Pin-Based VM-Execution Control(32 bits)
字段的exiting NMI = 1
且virtual NMIs = 0
时,当产生一个 NMI 时将会直接 VM-exit,不会存在阻塞状态。所以iret
指令并不会对blocking by NMI
阻塞状态的解除产生任何影响。
2、当一个 NMI 在delivery 期间,执行一个iret
指令,但是这个iret
指令产生一个异常,并且这个异常并不会直接引发 VM-exit,而是在这个异常的 delivery 期间引发的另一个异常导致的 VM-exit。虽然此时的blocking by NMI
阻塞状态已经被解除,但是NMI unblocking(bit 12)
位并不会置 1。
举例:
(1)当NMI exiting
为 1 时,一个 NMI 请求直接产生 VM-exit,NMI 并没有得到 delivery。处理器在 VM-exit 过程中就不存在blocking by NMI
阻塞状态。此时, interruptibility state 字段的blocking by NMI
位为 0 值。
(2)当 VMM 注入一个 NMI 事件(或 virtual-NMI 事件)时,NMI 通过 guest-IDT 进行 deliver 执行。此时,处理器也存在blocking by NMI
(或blocking by virtual NMI
)阻塞状态。
(3)当 pin-based VM-execution control 字段的NMI exiting
与virtual-NMIs
位同时为 1 时,产生 virtual-NMI 概念,blocking by NMI
此时被视为blocking by virtual NMI
位。当注入一个 virtual-NMI 事件 deliver 执行后,处理器就存在blocking by virtual-NMI
阻塞状态。当注入的 virutal-NMl 事件由于NMIl-window exiting
位为 1 而 产生VM-exit 时,并不存在blocking by virtual-NMI
阻塞状态。
(4)在 NMI 或 virtual-NMI 服务例程中,执行iret
指令后,这个 NMI 或 virtual-NMI 阻塞状态将解除。如果执行iret
指令而发生某些错误,并不影响它解除 NMI 或 virtual NMI 的阻塞状态阻塞状态。执行iret
指令发生一个异常而直接导致 VM-exit 时,在 VM-exit interruption information 字段中的NMI unblocking
位(bit 12) 将为 1 值,指示 NMI 或 virtual-NMI 阻塞状态已经被解除(见 3.10.2.1节)。
说明:
如果 NMI 直接引发 VM-exit,NMI 不被 deliver 执行,也就不会解除阻塞状态。
但是,如果iret
指令产生了一个异常,但这个异常并不直接引发 VM-exit,而后来由于异常 delivery 期间导致 VM-exit (间接引发),此时blocking by NMI
阻塞状态已经被解除。4 Enclave interruption 逻辑处理器处于 enclave 模式时发生 VM exit,则设置为 1。
同时会设置 VM-exit information fields—Basic VM-Exit Information(基本信息类字段)的 Exit reasonbit 27 = 1
(表示与 enclave 模式相关的 VM-exit)。31:5 Reserved 设置为 0。 pending debug exception 字段。
x86 支持延迟发送一些调试异常,如某些情况下的单步调试。 pending debug exceptions 字段用来记录和设置 guest 存在未处理(阻塞)而 pending(悬挂)的 #DB异常。属于 natural-width 类型字段,在 64 位架构处理器上是 64 位,否则为 32 位。
pending debug exception 字段只支持记录 trap 类型 #DB 异常(由硬件断点或者 I/O 断 点触发的 #DB 异常,以及由单步调试而触发的 #DB 异常),对于 fault 类型的 #DB 异常不能记录。
该字段的格式基本和 DR6 寄存器格式很相似:
当遇到一个硬件断点(指令执行、数据读写)或 single-step(单步调试)时,处理器将组织一个 #DB 异常,并提交处理器执行。
在 x86/x64 体系上的 #DB(调试)异常使用 1 号中断向量,分为 fault 和 trap 两类,共有 6 个触发方式。
- fault 类型的 #DB 有 2 类触发方式:
- 由执行断点触发。
- 由访问调试寄存器触发(DR7.GD = 1 时)。当断点对应的
DR7.R/Wx = 00
时,使用执行断点,在断点寄存器 DR0-DR3 里设置断点的地址。当DR7.GD = 1
时,使用 DR 寄存器访问触发方式。在后续的指令中访问任何一个 DR 寄存器将产生 #DB 异常。
- trap 类型 #DB 异常有 4 类触发方式:
- 读写硬件断点(根据 DR7 的
R/Wx
位来区分)。 - 单步调试断点,设置
Rflags.TF = 1
时产生。单步调试分为指令单步调试及分支单步调试两种(单步调试在将eflags.TF
置为 1 后,下一条指令执行完毕后触发,分支单步调试则在目标指令执行前触) - I/O 断点,I/O 断点是指令访问的 I/O 端口地址。在
CR4.DE = 1
时,DR7 的R/Wx = 10
,产生的就是 I/O 读写断点。(见 卷 3A—Chapter 17 Debug, Branch Profile, TSC, and Intel® Resource Director Technology. (Intel® RDT) Features—17.2 Debug Registers。) - 在任务切换时,任务切换的 #DB 异常在执行完任务切换后,处理器检测到 TSS.T 为 1 时触发。(在 VMX 架构下,并不支持在 non-root operation 环境中使用任务切换机制。一旦发生任务切换,将产生 VM-exit 行为。因此,在 pending debug exception 字段中并不支持记录任务切换时产生的 #DB 异常。)
- 读写硬件断点(根据 DR7 的
fault 和 trap 两类异常触发的位置不同,fault 类型如 DR 奇存器访问的 #DB 异常,在执行
MOV-DR
指令前触发,异常处理后还要继续执行该指令;trap 类型的异常,在执行完每一条指令后触发,异常处理后执行下一条指令。上面提到的
blocking by MOV-SS
阻塞状态,会阻塞外部中断、NMI 及 #DB 异常。这里主要对阻塞的 #DB 异常进行说明。- 在
pending debug exception
字段中只能记录:(数据读写的硬件断点、 I/0 断点、单步调试)产生的 #DB 异常。。不会记录指令指令而导致的硬件断点,这是因为blocking by MOV-SS
阻塞状态不会阻塞因为指令执行硬件断点触发的 #DE 异常。 blocking by MOV-SS
阻塞状态并不能阻塞由 DR 寄存器访问而产生的 #DB 异常。当DR7.GD = 1
时,遇到 MOV-DR 指令将直接提交 #DB 异常执行(参考硬件断点),或者产生 VM-exit 行为。
总结就是:对于指令执行的硬件断点的 #DB 异常和 DR 寄存器访问的 #DB 异常,它们不会被悬挂(pending)。
在 VM-exit 时存在 #DB 异常的 pending 状态,有如下 2 种可能。
- 在 VM-exit 之前,一条指令产生 trap 类型异常。
- 在 VM-exit 前,已经存在由于
blocking by Mov-SS
阻塞状态而被 pending 的 #DB 异常。
VM-exit information fields—exit qualification 字段记录的 debug 异常信息与 pending debug exception 字段记录的 debug 异常信息是不同的。它们的用途也不同:
- exit qualification 字段记录的是由 #DB 异常引发 VM-exit 时的 debug 异常信息;
- pending debug exception 字段记录的是由其他原因导致 VM-exit,而 #DB 异常未处理而被 pending(悬挂)的信息。
- fault 类型的 #DB 有 2 类触发方式:
其余字段。
字段 描述 VMCS link pointer (1)如果 VM-execution control 的 VMCS shadowing = 1
,此时该区域表示 VMCS region 的物理地址,可以通过该指针读写 VMCS 的字段。
(2)如果 VM-execution control 的VMCS shadowing = 0
,此时该区域应该设置为0xFFFFFFFFFFFFFFFF
。VMX-preemption timer value 保存定时器的计数值。
(1)如果 VM-execution control 的active VMX-preemption timer = 1
,则该区域有效。表示一个定时器。
(2)每 2^TSC 增加时,该值递减 1,递减为 0 时将会产生 VMX exit。
填充该字段时,该字段的值来自于IA32_VMX_MISC[4:0]
。PDPTEs 仅在启用 EPT 的 PAE 分页模式下可用。
该区域包含 4 个 64 bits 的值:PDPTE0,PDPTE1,PDPTE2,PDPTE3。
(1)只有在支持 EPT,即 VM-execution control 的enable EPT = 1
时,且 Guest OS 在 PAE 分页模式下,需要填充这 4 个字段,以便在 VM-entry 时将这些字段的值加载到内部的 PDPTE 寄存器中、此时,CR3 寄存器将被忽略。
(2)VM-execution control 的enable EPT = 0
时,如果 Guest OS 使用 PAE 分页,CR3 寄存器指向的 4 个 PDPTEs 会被加载到内部的 PDPTE 寄存器,此时忽略 VMCS 中的 PDPTEs 字段。
注意:只有 Guest OS 在 32 位系统的 PAE 分页模式下,且启用 EPT 时才有用,IA-32e 模式不使用。Guest interrupt status 只有在 VM-execution control 的 virtual-interrupt delivery = 1
时,该字段表示虚拟 local APIC,处理器使用该字段来维护虚拟中断的状态。PML index 只有在 VM-execution control 的 enable PML = 1
时,该字段为一个 index 索引值,用在 EPT page-modification log。
2.3 字段填充
3 Host-state area
每一次 VM-exit 时,处理器的状态从 Host-state area 加载。
3.1 寄存器类字段
Host-state area 区域中只有寄存器的的字段,没有其他字段:
- 控制寄存器:CR0、CR3、CR4。
- 堆栈、指令寄存器:RSP、RIP。
- 段寄存器:CS、SS、DS、ES、FS、GS、TR。
- 描述符寄存器:GDTR、LDTR、IDTR。
- MSR 寄存器。
说明:
- RIP 应该填充为 VMM 中的一个管理例程,使得每次 VM-exit 时都进入到该函数。
- 对于 MSR 寄存器,只有在 VM-exit control 区域中对应的寄存器字段设置为
1
时, Host-state area 区域中的寄存器字段才有效。
3.2 字段填充
4 VM-execution control fields
VM-execution 控制类字段主要控制处理器在 VMX non-root operation 模式下的行为能力:
- 典型地可以控制某些条件引发 VM-exit 事件;
- 也控制着 VMX 的某些虚拟化功能的开启,例如 APIC 的虚拟化及 EPT 机制。
将 VM-execution control fields 分为控制域和数据域,控制域决定着对应的数据域中是否有数值。
- 控制域:
- Pin-Based VM-Execution Control(32 bits),设置异步事件来导致 VM-exit 的条件。
- Processor-Based VM-Execution Control
- primary processor-based VM-execution controls(32 bits),设置同步事件来导致 VM-exit 的条件。
- secondary processor-based VM-execution controls(32 bits),设置
Enable
的开关。 - tertiary VM-execution controls(64 bits),设置 HLAT 分页信息。
- 数据域:剩下的其余字段。
4.1 字段解释
将 VM-execution control fields 分为控制域和数据域,控制域决定着对应的数据域中是否有数值。
- 控制域:
- Pin-Based VM-Execution Control(32 bits),设置异步事件来导致 VM-exit 的条件。
- Processor-Based VM-Execution Control
- primary processor-based VM-execution controls(32 bits),设置同步事件来导致 VM-exit 的条件。
- secondary processor-based VM-execution controls(32 bits),设置
Enable
的开关。 - tertiary VM-execution controls(64 bits),设置 HLAT 分页信息。
- 数据域:剩下的其余字段。
Pin-Based VM-Execution Control(32 bits)
Processor-Based VM-Execution Control
Processor-Based VM-Execution Control 包含以下三个字段:
- primary processor-based VM-execution controls(32 bits),设置同步事件来导致 VM-exit 的条件。
- secondary processor-based VM-execution controls(32 bits),设置
Enable
的开关。 - tertiary VM-execution controls(64 bits),设置 HLAT 分页信息。
以下数据域。
控制字段 解释 Exception Bitmap Exception Bitmap 是一个 32 bits 的字段,每一位对应一个中断号(例如 bit-14 对应 #PF 页面异常)。
在 VMX non-root operation 中,如果发生异常,处理器会检查 Exception Bitmap 中 bit-INT Number 位是否为 1,如果为 1 则会产生 VM-exit,为 0 则会通过 Guest-IDT 执行对应的 ISR。
备注:如果需要进行 IDT Hook,则必须要注意对应的位。只有 32 bits,也就只能 Hook 前 32 个中断号对应的中断。I/O-Bitmap Addresses 包含两个 64KB 大小的物理地址(I/O bitmaps A、I/O bitmaps B。每个地位为 64bits)。
当 primary processor-based VM-execution controls 中的 use I/O bitmaps = 1 时,使用该区域来存放 I/O bitmaps 的地址,该地址指向相应的 64 KB(注意不是像 Exception Bitmap 直接存放数据)。
I/O bitmap 中的每个位对应一个 I/O 地址,当位为 1 时,访问相应的位会产生 VM-exit。Time-Stamp Counter offset and Multiplier 当 primary processor-based VM-execution controls 中的 use TSC offseting = 1 时,当前字段提供一个 64 bits 的偏移值。
启用后,使用 RDTSC、RDTSCP、RDMSR 等指令读取 TSC 时,返回的值为 TSC+TSC offset。
该位启用的条件是:
(1)use I/O bitmaps = 1
(2)使用 RDTSC 指令时,RDTSC exiting = 0
(3)使用 RDTSCP 指令时,enable RDTSCP = 1
(4)使用 RDMSR 指令时,MSR read bitmap 相应的位为 0。Guest/Host Masks and Read Shadows for CR0 and CR4 用来保护 CR0、CR4 寄存器。共包含 4 个 natural-width 的字段。
(1)CR0 奇存器的 guest/host mask 与 read shadow 字段。
(4)CR4 奇存器的 guest/host mask 与 read shadow 字段。
使用规则:
guest/host mask 中为 1 的位,表示属于 Host OS 所有,Guest OS 中不能将其修改。也就是说 Guest 从 read shadow 字段读出来的值,对应于 guest/host mask 为 1 的位,不可以进行修改。否则将会导致 VM-exit。
图片引自Day 3: The VMCS, Component Encoding, And Multiprocessor Initialization。CR3-Target Controls 包括 1 个 CR3-target count 字段(32 bits)与 4 个 CR3-target value 字段(natural-width,从 CR3 target value 0 到 CR3-target value 3 字段)。
软件可以查询IA32_VMX_MISC[24:16]
的值来获得支持 CR3-target value 的数量。
在CR3-load exiting = 1
时,CR3-target count 与 CR3-target value 将决定写 CR3 寄存器是否会产生 VM-exit。以 CR3-target count 字段值作为 N(N ≤ 4
):
(1)当向 CR3 写入的值等于这 N 个 CR3-target 值的其中一个时,不会产生 VM-exit。
(2)当向 CR3 写入的值不等于 N 个 CR3-target 值中的任何一个时,则会产生 VM-exit。Controls for APIC Virtualization 当 use TPR shadow = 1
时,当前字段有效,需要提供一个物理地址作为 4K 的 virtual-APIC page 页面。
用来虚拟化 local APIC,包括以下字段:
(1)APIC-access address (64 bits)
(2)Virtual-APIC address (64 bits)
(3)TPR threshold (32 bits)
(4)EOI-exit bitmap (4 fields; 64 bits each)
(5)Posted-interrupt notification vector (16 bits)
(6)Posted-interrupt descriptor address (64 bits)MSR-Bitmap Address 当 primary processor-based VM-execution controls(32 bits)中 use MSR bitmaps = 1
时,当 MSR bitmap 的某位为 1 时,访问该位所对应的 MSR 将产生 VM-exit。
当前字段提供一个 64 bits 的物理地址,指向一个 4 个 1KB 大小的 MSR bitmap 区域。每一个位决定对应的 MSR 地址是否可读(RDMSR),或者是否可写(WRMSR)。
(1)Read bitmap for low MSRs,表示可读的 MSR 地址范围为00000000H-00001FFFH
。
(2)Read bitmap for high MSRs,表示可读的 MSR 地址范围为C0000000H-C0001FFFH
。
(3)Write bitmap for low MSRs,表示可写的 MSR 地址范围为00000000H-00001FFFH
。
(4)Write bitmap for high MSRs,表示可写的 MSR 地址范围为C0000000H-C0001FFFH
。Executive-VMCS Pointer 这个字段用于 SMM dual-monitor treatment(SMM 双重监控处理)机制下,当发生 SMM VM-exit 时,这个字段用来保存 executive-monitor 的 VMCS pointer。 Extended-Page-Table Pointer (EPTP) 在 enable EPT = 1
时,表示开启 EPT,指向 EPT PML4 table(类似于普通分页 CR3 指向 PLM4 页表)。Virtual-Processor Identifier (VPID) 在 enable VPID = 1
时,该字段表示虚拟机标示符,16 bits。
(1)这个 VPID 用来标识虚拟处理器的 cache 域。
(2)处理器会在 cache 中维护 VPID 对应的一份 cache。
(3)如果每次 VM-entry 使用不同的 VPID 值,这样就为 VM 指定多个虚拟处理器 cache 域。
(4)另外,VMX 架构引入了 INVVPID 指令来刷新由 VPID 对应的 cache 信息。Controls for PAUSE-Loop Exiting 功能的引进主要是解决在一个长时间的 spin lock(自旋锁)等待循环里浪费处理器的时间。在这个自旋锁等待循环里处理器可以产生 VM-exit, 从而去执行其他的任务。 VM-Function Controls 64 bits。用于管理 VMX non-operation 中 VMFUNC
指令的使用。在 secondary processor-based VM-execution controls 中Enable VM functions = 1
时该字段才有效。
VM-functions control 字段每个位对应一个 VM 功能号,最大功能号为 63。VMFUNC
指令执行时,VM 功能号提供在 EAX 奇存器中,当提供的功能号大于63 时,将产生 #UD 异常。
在 VMX non-root operation 内执行VMFUNC
指令时,如果使用的功能号在 VM functions control 字段相应位为 0,将产生 VM-exit。
软件应该检查IA32_VMX_VMFUNC
寄存器,确定支持哪个 VM 功能号。IA32_VMX_VMFUNC
寄存器在enable VM functions = 1
允许被置位下有效。VMCS Shadowing Bitmap Addresses 在 VM-execution control 的 VMCS shadowing = 1
时当前字段才有效。该字段包含两个 64 bits 的物理地址:VMREAD bitmap address、VMWRITE bitmap address。每个 bitmap 都是 4KB 大小,ENCLS-Exiting Bitmap 如果 secondary processor-based VM-execution controls 中 Enable ENCLS exiting = 1
时,如果 bitmap 为 1 的位对应的 EAX 中对应的位也为 1,则会产生 VM-exit。ENCLV-Exiting Bitmap 如果 secondary processor-based VM-execution controls 中 Enable ENCLV exiting = 1
时,如果 bitmap 为 1 的位对应的 EAX 中对应的位也为 1,则会产生 VM-exit。PCONFIG-Exiting Bitmap 如果 secondary processor-based VM-execution controls 中 Enable PCONFIG = 1
时,如果 bitmap 为 1 的位对应的 EAX 中对应的位也为 1,则会产生 VM-exit。Control Field for Page-Modification Logging secondary processor-based VM-execution controls 中 Enable PML = 1
时,该字段才有效。它是页面修改日志的 4 KB 对齐地址。Controls for Virtualization Exceptions secondary processor-based VM-execution controls 中 Enable EPT-violation #VE = 1
时,该字段才有效。包含以下两个信息:
(1)该字段表示虚拟化异常信息地址(64 bits)。当逻辑进程遇到虚拟化异常时,将虚拟化异常信息保存在虚拟化异常信息地址中(Virtualization-exception information address)。
Virtualization-exception information address 是 Virtualization-exception information area 的地址。
(2)EPTP index。当 EPT violation 导致虚拟化异常时,处理器将此字段的值写入虚拟化异常信息区域。 EPTP-Switching VM 功能更新这个字段。XSS-Exiting Bitmap secondary processor-based VM-execution controls 中 Enable XSAVES/XRSTORS = 1
时,该字段才有效。这两个指令的执行可能会参考该位图。Sub-Page-Permission-Table Pointer (SPPTP) 如果启用了 EPT 的子页面写权限特性,则可以以 128 字节的粒度确定 EPT 写权限(参见第 28.3.4 节)。 这些权限是使用内存中的子页面权限结构的层次结构来确定的。 Fields Related to Hypervisor-Managed Linear-Address Translation tertiary VM-execution controls 中 Enable HLAT = 1
表示启用启用 HLAT 分页。当前字段表示分页转译相关信息。
4.2 字段填充
5 VM-entry control fields
该区域中的字段控制着 VM-entry 时处理器对 Guest state area 区域操作的行为。在 VM-entry 时处理器会检查这些字段,如果检查不通过,则会产生 VMfailValid,在 VM-exit information fields 的 VM-instruction error field 中保存错误号。
包含三个区域:
- VM-entry Controls(32bits)
- VM-entry Controls for MSRs(32bits)
- VM-entry Controls for Event Injection
- VM-entry interruption-information field(32 bits)
- VM-entry exception error code(32 bits)
- VM-entry instruction length(32 bits)
进行 VM-entry 操作时处理器会执行严格的检查,可以分为以下几个阶段。
- 第一阶段:对
VMLAUNCH
或VMRESUME
指令的执行进行基本检查。 - 第二阶段:对当前 VMCS 内的 VM-execution、VM-exit 以及 VM-entry 控制区域和 host-state 区域进行检查。
- 第三阶段:对当前 VMCS 内的 guest-state 区域进行检查,并加载 MSR。
- 在所有的检查都通过后,处理器从 guest-state 区域里加载处理器状态和执行环境信息。如果设置需要加载 MSR,则接着从 VM-entry MSR-load MSR 列表区域里加载 MSR。
- VM-entry 操作附加动作会清由执行
MONITOR
指令而产生的地址监控,这个措施可以防止 guest 软件检测到自己处于虚拟机内。 - 在成功完成 VM-entry 后,如果注入了一个向量事件,则通过 guest-IDT 立即 deliver 这个向量事件执行。如果存在 pending debug exception 事件,则在注入事件完成后 deliver 一个
#DB
异常执行。
在整个 VM-entry 操作流程里,如果 VM-entry 失败可能产生以下三种结果:
VMLAUNCH
和VMRESUME
指令产生异常(仅能产生#UD
或#GP
异常),从而执行相应的异常服务例程(如执行这两个指令的权限不够时会产生#GP
异常)。《处理器虚拟化技术 P304》VMLAUNCH
和VMRESUME
指令产生VMfailInvalid
或VMfailValid
类型失败,处理器接着执行下一条指令(Hypervisor 中)。VMLAUNCH
和VMRESUME
指令的执行由于检查 guest-state 区域不通过,或者在加载 MSR 阶段失败而产生 VM-exit,从而转入 host-RIP 的入口点执行(进入VM-exit 处理函数)。
更多具体的字段检查,详看《处理器虚拟化技术 P300》。
5.1 字段解释
类似于上面的 VM-execution control fields 字段一样,只要是 Control Field 都有控制域(前半部分)和数据域(后半部分)。
bit 31
是有效位,为 1 时指示VM-entry interuption information
字段有效,为 0 时无无效。bits 10:8
设置事件类型,包括7 个事件类型,如表 3-10 所示。bits 7:0
设置中断或异常的向量号(IDT),当事件类型是NMI 时,向量号必须为2。事件类 型为 other 时,向量号必须为0值。VM-entry interruption information
字段bit 11 == 1
时,指示有错误码需要提交。在注入事件 delivery 时,错误码会被压入栈中。这个位只有在注入硬件异常事件时才能被置 1,否则会产生 VMfailValid 失败。能产生错误码的硬件异常是:#DF,#TS,#NP,#SS,#GP,#PF 及 #AC 这 7 类。注入其余的硬件异常不能将此位置 1。
说明:事件注入机制目的是向 VM 中注入一个事件,使得每次 VM-entry 之后立即得到执行。而至于注入的事件会不会产生 VM-exit,需要根据注入事件(中断)类型查 VM-execution Control 等控制区域是否开启了对该类中断的监控,如果命中了就会产生 VM-exit(直接向量事件)。如果控制区域没有对注入的事件进行监控,那就不会马上产生 VM-exit,但是如果在中断/异常分发的过程中产生了错误,那就会导致 VM-exit,这种 VM-exit 就叫做间接向量事件导致的 VM-exit。
中断和异常的区别:
中断/异常类型 | 描述 | 备注 |
---|---|---|
硬件中断 | 外部中断,使用 32~255 号中断。 | |
硬件异常 | (1)0~31 号中,除了 #BP 与 #OF 异常以外的所有异常。 (2)能产生 ErrorCode 的硬件异常:双重错误 (#DF)、无效 TSS (#TS)、段不存在 (#NP)、栈段错误 (#SS)、通用保护异常 (#GP)、页错误 (#PF)、对齐检查 (#AC)。 |
产生 ErrorCode 的硬件异常,要向堆栈压入错误码 ErrorCode |
软件中断 | 软件中断指由 INT 指令执行的中断。 |
中断时需要进行段权限检查 CPL(在 delivery 期间) |
软件异常 | 软件异常指由 INT3 与 INTO 指令产生的 #BP 与 #OF 异常。64 位模式下 INTO 指令是无效的。 |
中断时需要进行段权限检查 CPL(在 delivery 期间) |
5.2 向量化事件
允许在 VM-entry 完成后,在执行任何 Guest 指令之前,处理器执行由 VMM 设置的注入的事件。这个事件可以是一个中断或异常,甚至 pending MTF VM-exit 事件,他们被称为向量化事件。而含有向量化事件的 VM-entry 被称为向量化的 VM-entry。
向量化事件通过 VM-entry control fields
区域的以下 3 个字段来进行设置。
- VM-entry interruption-information field(32 bits):设置中断或异常的中断向量号(IDT)、事件类型、事件有效位、错误码的 delivery 标志位。
- VM-entry exception error code(32 bits) :当
VM-entry interruption-information field bit 31 = 1
时,注入的是硬件异常,且包含有 #DF、#TS、#NP、#SS、#GP、#PF、#AC 时才会产生错误码。因此只有注入这几个硬件异常时才需要提供错误码,对于其他异常或事件可以忽略该字段。 - VM-entry instruction length(32 bits):当注入事件属于软件异常、软件中断以及特权级软件中断时,必须在这个字段提供注入的指令长度(指令长度一般在
1~15
之间,为0
时会产生 VMfailValid 失败)。以便注入的事件引发 VM-exit 时能够在 VM-exit instruction length 找到这些指令的长度。
6 VM-exit control fields
这些字段用来控制发生 VM-exit 时的处理器行为,决定如何进行 VM-exit 操作。
即在 VM-exit 时决定着如何保存和填充寄存器的值,以便在 VMM 中使用。
包含以下 3 个字段:
- VM-exit control 字段。
- VM-exit MSR-store count 与 VM-exit MsR-store address 字段。
- VM-exit MSR-load count 与 VM-exit MSR-load address 宇段。
6.1 字段解释
字段 | 描述 |
---|---|
VM-exit control 字段 | 在 VM-exit 时决定着如何保存和填充寄存器的值。如下图。 |
VM-exit MSR-store count VM-exit MSR-store address |
VM-exit 时将相关 MSR 寄存器的值存储在 VM-exit MSR-store address 指向的对应区域中。 |
VM-exit MSR-load count VM-exit MSR-load address |
VM-exit 时从 VM-exit MSR-store address 指向的对应区域中加载值到 MSR 寄存器。 类似于 VM-entry control fields——VM-entry Controls for MSRs——VM-entry MSR-load count(32 bits) 和 VM-entry MSR-load address(64 bits)。 |
6.2 字段填充
7 VM-exit information fields
VMCS 的 VM-exit 信息类字段用来保存发生 VM-exit 事件的原因及明细信息,VMM 利用这些信息来决定如何管理和控制 VM。
VM-exit 信息类字段的 VM-instruction error 字段保存着 VMX 指令执行失败而发生 VMfailValid 的原因值。当VMX 指令发 VMfaillnvalid 失败时,不会产生错误原因值。
VM-exit information fields 包含以下几类信息:
- Basic VM-Exit Information,基本信息类。
- Information for VM Exits Due to Vectored Events,直接向量事件类。
- Information for VM Exits That Occur During Event Delivery,间接向量事件类。
- Information for VM Exits Due to Instruction Execution,指令信息类。
- VM exits due to SMIS。
- VM-Instruction Error Field,指令失败类。
注意:一般 VM-exit information fields 是只读的,当 MSR IA32_VMX_MISC[29](0x485) = 1
表示可写。
7.1 基本信息类字段
基本信息类字段包括:
- Exit reason(32 bits)
- Exit qualification(natural-width)
- Guest-linear address(natural-width)
- Guest-physical address(64 bits)
如果我们需要输出 VM-exit 信息区域所有字段的信息,当根据 exit reason 字段值输出相应的 VM-exit 原因信息时,需要先检查 VM-exit instruction error 字段,确定 VMX 指令是成功的,然后再输出具体的 VM-exit 原因信息。
每类导致 VM-exit 的事件都对应一个原因编号,VMM 根据这个原因编号值来确定由什么事件导致 VM-exit 发生,再结合其余字段进一步提供的明细信息做出相应处理。
以下两个特殊值:
- VM-exit 原因码 0 值是有效的,它表示由异常或 NMI 导致的 VM exit。
- VM-exit instruction error 字段的值为 0 时,表明 VMX 指令执行成功。
Exit reason(32 bits)。32 位的 Exit reason 退出原因格式如下,
bit[15:0]
具体指代原因见《Intel 卷3合集 24.9.1》 和《处理器虚拟化技术 3.10.1.2 VM-ext 原因》。由 VM-entry 失败而导致的 VM-exit 与在 guest 里产生的 VM-exit 的区分:
- exit reason 字段
bits 31 = 1
表示 VM-entry 失败而导致的 VM-exit。 - exit reason 字段
bits 31 = 0
表示在 guest 里产生的 VM-exit。
备注:在每次 VM-exit 时,需要先检查 VM-exit instruction error 字段,确定 VMX 指令是成功的,然后再输出具体的 VM-exit 原因信息。
导致 VM-exit 发生的原因中,主要分无条件 VM-exit 与有条件 VM-exit 两大类。另外在 VMX non-root operation 模式中收到某些事件也会直接产生 VM-exit(例如 INIT, SMI,以及 SIPI 消息)。当 Guest 发生 triple fault 时,也会无条件地产生 VM-exit。
类型 描述 指令类导致 VM-exit 无条件指令类:
(1)所有 VMX 指令(除 VMFUNC 指令外)。
(2)CPUID,GETSEC,INVD,XSETBV 指令。
有条件指令类:
取决于 primary processor-based VM-execution controls、secondary processor-based VM-execution controls 和 tertiary VM-execution controls 中设置的条件。例如当INVLPG exiting
为 1 时,尝试执行INVLPG
指令将导致 VM-exit。
HLT, INVLPG, INVPCID, RDPMC, RDTSC, RDTSCP,RSM,MOV from CR3,MOV to CR3,MOV from CR8,MOV to CR8,CLTS,LMSW,MOV to CRO,MOV to CR4,MOV-DR,IN/OUT,INS/OUTS,RDMSR,WRMSR,MWAIT,MONITOR,PAUSE,LGDT,LIDT,LLDT,LTR,SGDT,SIDT,SLDT,STR,RDRAND,WBINVD 指令。事件类导致 VM-exit (1)primary processor-based VM-execution controls、secondary processor-based VM-execution controls 和 tertiary VM-execution controls 中设置的条件。如当发生异常时,异常向量号在 exception bitmap 对应的位为 1 时将引发 VM-exit。
比如:包括INT3
和INTO
指令引起的软件异常、BOUND
和UD2
指令引起的硬件异常、对于 #PF 异常有些特殊,当 exception bitmap 的 bit 14 为 1,并且PFEC & PFEC_MASK = PFEC_ MATCH
时,#PF 异常会导致 VM-exit。
(2)注入机制中(VM-entry Controls for Event Injection)注入的事件导致的 VM-exit。
(3)三重错误(triple fault)。
(4)VM-entry 失败而导致的 VM-exit。不同于上面三种在 VMX non-root operation 模式下引发的,这种 VM-exit 是在 VMX root operation 模式下引发的,包括:
1、在检查 guest-state 区域的字段时,由于无效的 guest-state 字段而导致 VM-exit。
2、在加载 guest-state 区域的 MSR 时失败而导致 VM-exit。
3、在 VM-entry 期间可能由于遇到 machine-check 事件而失败导致 VM-exit。- exit reason 字段
Exit qualification(natural-width 类型)。在 VM-exit 时,这个字段记录由于某些原因导致 VM-exit 的明细信息,具体内容见《处理器虚拟化技术 3.10.1.3》
7.2 直接与间接向量事件
这两个字段记录的是事件注入机制导致的 VM-exit 详细的信息。
- Information for VM Exits Due to Vectored Events,直接向量事件类。
- Information for VM Exits That Occur During Event Delivery,间接向量事件类。
直接向量事件
所谓的 直接向量事件 是指直接引发 VM-exit 的向量事件(中断或异常没有机会得到 delivery 而直接 VM-exit)。主要包含以下 4 类:
- 硬件异常。由于异常向量号(IDT 号)在 exception bitmap 对应的位为
1
而导致的 VM-exit。 - 软件异常(
#BP, #OF
)。由于异常向量号(IDT 号)在 exception bitmap 对应的位为1
而导致的 VM-exit。 - 外部中断(硬件中断)。发生外部中断请求时,由于
external-interrupt exiting = 1
而直接导致的 VM-exit。 - NMI。发生 NMI 请求时,由于
NMI exiting = 1
而直接导致的 VM-exit。
注意:直接向量事件不包括软件中断和特权级软件中断(但是他们可以属于间接向量事件)。
VM-exit interruption information(32 bits)。
32 bits 的
VM-exit interruption information
字段记录的是向量事件的 delivery 信息,格式如下图。注意:VM-exit interruption information
字段仅支持以上提到的硬件异常、软件异常(#BP, #OF
)、外部中断(硬件中断)、NMI。这是因为 Guest 中不会因为软件中断和特权级软件中断而直接引发 VM-exit(只会在 delivery 的过程中引发)。注意:bit 31 为 1 时,VM-exit interruption information 字段才有效。
能产生错误码的硬件异常是:#DF,#TS,#NP,#SS,#GP,#PF 及 #AC 这 7 类。其他硬件异常、外部中断、NMI 及软件异常并不存在错误码。
注意:只有在异常直接导致 VM-exit 发生时,bit 12 才被置位,表示 IRET 指令执行
- 如果 NMI 直接引发 VM-exit,NMI 不被 deliver 执行,也就不会解除阻塞状态。
- 但是,如果
iret
指令产生了一个异常,但这个异常并不直接引发 VM-exit,而后来由于异常 delivery 期间导致 VM-exit (间接引发),此时blocking by NMI
阻塞状态已经被解除。
VM-exit interruption error code(32 bits)。
如果 VM-exit 由硬件异常引发,当 VM-exit interruption information 字段的 bit 11 为 1 时,VM-exit interruption error code 字段记录异常的错误码。错误码仅存在于前面所述的 7 种硬件异常里,包括:#DF,#TS,#NP,#SS,#GP,#PF 及 #AC 异常。其他类型的事件 这个字段清为 0 值。
间接向量事件
所谓的 间接向量事件 是指:当一个向量事件发生后它不直接产生 VM-exit,在通过 Guest-IDT delivery 期间由于遇到了某些错误而导致 VM-exit。
间接导致 VM-exit 的向量事件可以为:硬件异常、软件异常、软件中断、特权级软件中断、外部中断及 NMI、注入的向量化事件(因此,这个字段的中断类型也可以为软件中断或者特权级软件异常(由事件注入来实现))。
常见的间接向量事件:
- 在向量事件 delivery 期间引发了一个异常,这个异常由于向量号在 exception bitmap 字段对应的位为 1 而导致 VM-exit(包括转变为 #DF 异常)。
- 在向量事件 delivery 期间引发连串异常,最终转变为 triple fault 而导致 VM-exit。
- 向量事件 delivery 期间由于向量号在 Guest-IDT 内对应的描述符为 task-gate 描述符,尝试进行任务切换而导致 VM-exit。
- 向量事件 delivery 期间访问了 APIC-access page 页面(包括线性访问和 guest-physical address 访问)而导致 VM-exit(在启用
virtualize APIC-accesses
功能时)。 - 向量事件在 delivery 期间发生了 EPT violation 或者 EPT misconfiguration 而导致 VM-exit(在启用
enable EPT
功能时)。
IDT-vectoring information 字段(32 bits),只记录间接向量事件的信息,它与 VM-exit interruption information 字段极为相似。
bit 11 为 1 时,表明间接向量事件存在错误码,只有 #DF,#TS,#NP,#SS,#GP,#PF, #AC 7 类硬件异常才会产生错误码。
IDT-vectoring error code 字段(32 bits)。它的作用和 VM-exit interruption error code 字段是一样的,所不同的是记录的向量事件对象不同。
说明:事件注入机制目的是向 VM 中注入一个事件,使得每次 VM-entry 之后立即得到执行。而至于注入的事件会不会产生 VM-exit,需要根据注入事件(中断)类型查 VM-execution Control 等控制区域是否开启了对该类中断的监控,如果命中了就会产生 VM-exit(直接向量事件)。如果控制区域没有对注入的事件进行监控,那就不会马上产生 VM-exit,但是如果在中断/异常分发的过程中产生了错误,那就会导致 VM-exit,这种 VM-exit 就叫做间接向量事件导致的 VM-exit。
7.3 指令信息类
Information for VM Exits Due to Instruction Execution 包含以下两个字段:
VM-exit instruction length(32 bits),记录引起 VM-exit 指令的长度,指令长度在 1 到 15 之间。注意:如果软件异常,软件中断或者特权级软件中断是通过事件注入方式 deliver, 它们间接导致了 VM-exit 发生,那么,VM-exit instruction length 字段记录的指今长度等于 VM-entry instruction length 字段的值。
比如在 VM 中执行一个
CPUID
指令无条件导致 VM-exit,guest 中的CPUID
的执行被 VMM 拦截,VMM 进行了虚拟化后(模拟执行该指令),然后返回一个虚拟化的结果给 guest。那么,VMM 重新进入VM 后,guest 应该从哪里开始执行呢?肯定不是指令本身。 guest 应该从引|起 VM-exit 的指令的下一条指令开始执行。这就是 VM-exit instruction length 字段的作用。所有指令类引起的 VM-exit 在 VM 中都是 fault 类型,也就是说:guest-state 区域的 guest-RIP 字段是指向引起 VM-exit 的指令的地址,VM-exit instruction length 字段保存这条指令的长度,VMM 在重新进入 VM 前,根据 VM-exit instruction length 字段的值来计算 guest 下一条指令的地址,然后设置 geust-RIP 字段的值。新的 geust-RIP = geust-RIP + VM-exit instruction length
。VM-exit instruction information(32 bits)。如果 VM-exit 是由下面的指令引起的:INS,OUTS,LGDT,LIDT,LLDT,LTR,SGDT,SIDT,SLDT,STR,RDRAND,INVPCID,INVEPT,INVVPID,VMCLEAR,VMPTRLD,VMPTRST,VMREAD,VMWRITE 以及 VMXON 指令,此时,VM-exit qualification 字段记录这些指令(除 INS/OUTS 和 RDRAND 指令外)内存操作数的偏移量(参见 3.10.1.4 节),32 位宽的 VM-exit instruction information 字段进一步补充指令的明细信息。只有结合这两个字段才能够完整地分析指令信息,给 VMM 提供管理帮助。
7.4 指令错误类
当执行一条 VMX 指令时,如果发生 VMfailValid 失败,32 位的 VM-instruetion error 字段将记录指令的错误码。注意:如果 VMX 指令执行时产生异常,处理器会执行异常处理。此时,不会存在 VMfailValid 失败。
VMX 指令执行的结果(判断 rflags 的 CF 或 ZF):
- 执行成功:VMsuccess,指令会清所有的 rflags 寄存器标志位,例如 CF 与 ZF 标志。
- 执行失败:
- VMfailInvalid,表示因 current-VMCS 指针无效或 VMCS 内的 ID 值是无效时而失败,失败原因不会被记录。
- VMfailValid,表示遇到某些原因而执行失败,失败原因记录在 VM-exit instruction error 字段。
如果我们需要输出 VM-exit 信息区域所有字段的信息,当根据 exit reason 字段值输出相应的 VM-exit 原因信息时,需要先检查 VM-exit instruction error 字段,确定 VMX 指令是成功的,然后再输出具体的 VM-exit 原因信息。如果 VMX 指令执行失败则会产生 VMfailValid。
下表 2-5,列举了所有可能产生 VMfailValid 失败的原因,每个原因有唯一的指令错误码(VM-instruetion error 的值)。
注意:上表是 如果 VMX 指令执行失败产生 VMfailValid 时对应的 VM-instruetion error。而 VMfaillnvalid:表示因 current-VMCS 指针无效而失败。当 current-VMCS 指针或 VMCS 内的 ID 值是无效时,VMX 指令会置 CF 标志位,指示执行失败。
7.5 VM-exit 分类
导致 VM-exit 发生的原因中,主要分无条件 VM-exit与有条件 VM-exit两大类。另外在 VMX non-root operation 模式中收到某些事件也会直接产生 VM-exit(例如 INIT, SMI,以及 SIPI 消息)。当 guest 发生 triple fault 时,也会无条件地产生 VM-exit。
类型 | 描述 |
---|---|
指令类导致 VM-exit | 无条件指令类: (1)所有 VMX 指令(除 VMFUNC 指令外)。 (2)CPUID,GETSEC,INVD,XSETBV 指令。 有条件指令类: 取决于 primary processor-based VM-execution controls、secondary processor-based VM-execution controls 和 tertiary VM-execution controls 中设置的条件。例如当 INVLPG exiting 为 1 时,尝试执行 INVLPG 指令将导致 VM-exit。HLT, INVLPG, INVPCID, RDPMC, RDTSC, RDTSCP,RSM,MOV from CR3,MOV to CR3,MOV from CR8,MOV to CR8,CLTS,LMSW,MOV to CRO,MOV to CR4,MOV-DR,IN/OUT,INS/OUTS,RDMSR,WRMSR,MWAIT,MONITOR,PAUSE,LGDT,LIDT,LLDT,LTR,SGDT,SIDT,SLDT,STR,RDRAND,WBINVD 指令。 |
事件类导致 VM-exit | (1)primary processor-based VM-execution controls、secondary processor-based VM-execution controls 和 tertiary VM-execution controls 中设置的条件。如当发生异常时,异常向量号在 exception bitmap 对应的位为 1 时将引发 VM-exit。 比如:包括 INT3 和 INTO 指令引起的软件异常、BOUND 和 UD2 指令引起的硬件异常、对于 #PF 异常有些特殊,当 exception bitmap 的 bit 14 为 1,并且 PFEC & PFEC_MASK = PFEC_ MATCH 时,#PF 异常会导致 VM-exit。(2)注入机制中(VM-entry Controls for Event Injection)注入的事件导致的 VM-exit。 (3)三重错误(triple fault)。 (4)VM-entry 失败而导致的 VM-exit。不同于上面三种在 VMX non-root operation 模式下引发的,这种 VM-exit 是在 VMX root operation 模式下引发的,包括: 1、在检查 guest-state 区域的字段时,由于无效的 guest-state 字段而导致 VM-exit。 2、在加载 guest-state 区域的 MSR 时失败而导致 VM-exit。 3、在 VM-entry 期间可能由于遇到 machine-check 事件而失败导致 VM-exit。 |