Windows Intel X86 VT 学习 - 初识VT
VT概念
┌─────────────────────────────────────────┐
│ VMX Operation │
│ │
│ ┌──────────────┐ ┌──────────────────┐ │
│ │ VMX Root │ │ VMX Non-Root │ │
│ │ (VMM 运行) │ │ (Guest 运行) │ │
│ │ │ │ │ │
│ │ Ring 0~3 │ │ Ring 0~3 │ │
│ └──────┬───────┘ └────────┬─────────┘ │
│ │ VM Entry │ │
│ │ ──────────────> │ │
│ │ │ │
│ │ VM Exit │ │
│ │ <────────────── │ │
│ │ │ │
└─────────┴───────────────────┴───────────┘
VMX Operation
任何VT环境下,必须要首先开启这个工作状态才能进去VT。需要注意但是CPU得工作模式不会修改
Root vs Non-Root
- VMX Root Operation — VMM(你的 HV 代码)跑在这里
- VMX Non-Root Operation — Guest(被虚拟化的 OS)跑在这里
- Non-Root 不可感知:没有任何软件可见的 bit 表示当前处于 Non-Root 模式,Guest 无法通过读寄存器发现自己在虚拟机里
生命周期
① VMXON
│
▼
[VMX Root] ← 你的 VMM 在这,配置 VMCS
│
│ ② VMLAUNCH (第一次进 Guest)
▼
[VMX Non-Root] ← Guest 开始跑
│
│ ③ Guest 碰到敏感操作 → VM Exit
▼
[VMX Root] ← VMM 的 Exit Handler 处理
│
│ ④ VMRESUME (再次进 Guest)
▼
[VMX Non-Root] ← Guest 继续跑
│
│ ... ③④ 不断循环 ...
│
│ ⑤ VMM 决定关闭
▼
[VMX Root] → VMXOFF → 退出 VMX Operation
基于上面说的,说白了是Root和Non Root 切换
进入 VMX 的完整链路
CPUID 检测 VMX 支持 (ECX bit 5)
↓
检查/设置 IA32_FEATURE_CONTROL MSR (0x3A)
↓
设置 CR4.VMXE = 1
↓
分配 VMXON Region (4KB 对齐) + 写入 Revision ID
↓
执行 VMXON
CPUID 检测
CPUID 指令,用来检测 CPU 是否支持 VMX。执行 CPUID(EAX=1),检查 ECX 的 bit 5,为 1 则支持。
IA32_FEATURE_CONTROL MSR
这个实际上就是一个物理锁,BIOS 关闭后,这个标志位能体现。
- Bit 0(Lock):一旦置 1,这个 MSR 就锁死了,重启前改不了
- Bit 2(Enable VMXON outside SMX):允许在非 SMX(TXT)环境下使用 VMXON
IA32_FEATURE_CONTROL (MSR 0x3A)
Bit 63 Bit 0
┌──────────────────────────────────────────┬───┬───┐
│ Reserved │ 2 │ 0 │
└──────────────────────────────────────────┴───┴───┘
│ │
│ └─ Lock bit
└───── Enable VMXON outside SMX
| Lock (Bit 0) | Enable (Bit 2) | 含义 | 你能做什么 |
|---|---|---|---|
| 0 | 0 | 没锁,也没开 | 自己写入 0x5(Lock + Enable),然后 VMXON |
| 0 | 1 | 没锁,开了(理论上不太常见) | 补上 Lock bit,然后 VMXON |
| 1 | 1 | 锁了,开了 | 直接 VMXON,最正常的情况 |
| 1 | 0 | 锁了,没开 | 完蛋,代码里改不了,去 BIOS 开 |
CR4.VMXE
开启VT的前置条件 CR4.VMXE = 1,需要开启寄存器的控制位(内核直接用 API 拿 CR4 寄存器标志位)
VMXON
进入VT的指令:VMXON 的作用是让 CPU 进入 VMX 模式。
执行 VMXON 之前,需要先分配 VMXON Region:
- 大小:从
IA32_VMX_BASICMSR (0x480) 的 bits 44:32 读取(当前为 4KB,未来可能变) - 地址:必须 4KB 对齐(低 12 位为 0),且不能超出处理器物理地址宽度
- 分配后必须把 VMCS Revision ID 写入前 4 字节(bits 30:0,bit 31 清零),从
IA32_VMX_BASICMSR (0x480) 的 bits 30:0 读取 - Revision ID 是 CPU 硬件写死的,不能自己定义。CPU 执行 VMXON 时会校验这个值,不匹配则失败(VMfailInvalid)
- 目的:VMXON Region / VMCS 的内部布局是微架构相关的,不同代 CPU 布局可能不同,Revision ID 用于版本校验 (目前没有公开资料逆向分析这个里面的结构体的)
- 每个逻辑处理器各一份,VMXON 到 VMXOFF 之间不要访问或修改
- VMXON 的操作数是 VMXON Region 的 物理地址
VMXON Region 内存布局(4KB)
偏移 0x00: [bit 31: 必须为0] [bit 30:0: VMCS revision ID]
偏移 0x04: 不需要初始化,CPU 自己用
...
偏移 0xFFF:
VMXON (pm64)
VMCS(Virtual Machine Control Structure)— 概念引入(24.5)
VMX 的核心数据结构,控制 VM Entry / VM Exit 的行为。Chapter 24 只做概念引入,详细内容见 Chapter 25 笔记。
- 粒度:一个虚拟 CPU 对应一个独立的 VMCS,不是一个 VM 一个
- VMCS pointer 是 CPU 内部状态(类似 CR3),每个逻辑处理器各自维护一个
- 不能用 MOV 直接读写 VMCS 内存(内部布局 implementation-specific,且 CPU 可能有内部缓存),必须通过 VMREAD/VMWRITE
- 通过 VMPTRLD 加载、VMCLEAR 初始化、VMREAD/VMWRITE 读写字段
VMXON Region vs VMCS 的区别
| 结构 | 粒度 | 存什么 |
|---|---|---|
| VMXON Region | per-逻辑处理器 | VMX 模式本身的内部状态(不公开) |
| VMCS | per-虚拟 CPU | Guest 的寄存器快照 + 控制策略 |
示例:4 个逻辑处理器各跑 1 个 Guest → 4 个 VMXON Region + 4 个 VMCS
示例:4 个逻辑处理器跑 2 个 VM 各 4 个 vCPU → 4 个 VMXON Region + 8 个 VMCS
CR0/CR4 固定位
进入 VMX 后,CR0 和 CR4 的某些 bit 被 CPU 钉死,改不了:
- CR0.PE = 1(必须保护模式)
- CR0.PG = 1(必须开分页)
- CR0.NE = 1
- CR4.VMXE = 1
具体哪些 bit 被固定,由以下 MSR 决定:
IA32_VMX_CR0_FIXED0/IA32_VMX_CR0_FIXED1IA32_VMX_CR4_FIXED0/IA32_VMX_CR4_FIXED1
这意味着 Guest 默认只能运行在分页保护模式下。
代码中设置方式:CR0 = (CR0 | FIXED0) & FIXED1,一行搞定。
违反固定位 → VMXON 失败(进入时)或 #GP(运行时用 MOV CR 去改)
例外:后来的 CPU 支持 unrestricted guest VM-execution control,启用后 Non-Root 下 CR0.PE 和 CR0.PG 可以为 0,允许 Guest 跑实模式(用于虚拟化 OS 启动阶段)。
VMX 操作的其他限制
INIT 信号不对称
INIT 信号到达
├─ CPU 在 Root → blocked(直接丢弃,保护 VMM 不被重置)
└─ CPU 在 Non-Root → VM Exit(让 VMM 决定怎么处理)
INIT 是什么
INIT 是一个 CPU 硬件信号(通过 APIC 发送),效果是把目标 CPU 核心重置到接近刚上电的状态(寄存器回到初始值,但内存不清)。
谁会触发 INIT
主要场景:多核启动。开机时只有 BSP(Bootstrap Processor)在跑,要唤醒其他 AP(Application Processor)时用 INIT-SIPI-SIPI 序列:
BSP 通过 APIC ICR 发给目标 AP:
① INIT ← 把 AP 重置到已知状态
② SIPI ← 告诉 AP "从这个地址开始执行"
③ SIPI ← 再发一次(规范要求两次,防丢失)
理论上任何 Ring 0 代码都可以通过写 APIC ICR 给其他核心发 INIT,但正常运行的 OS 中几乎不会有人发。
为什么 VMX 里要特殊处理
Root 下屏蔽是因为 INIT 会重置 CPU 状态,如果 VMM 被重置,整个虚拟化环境崩溃。
Non-Root 下触发 VM Exit,由 VMM 代为处理(如虚拟化 OS 启动时 Guest 的 BSP 给 AP 发 INIT-SIPI-SIPI)。
A20M 阻断
VMX 中不允许 A20M 模式(古老的地址线回绕兼容特性)。现代系统不用关心。

浙公网安备 33010602011771号