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_BASIC MSR (0x480) 的 bits 44:32 读取(当前为 4KB,未来可能变)
  • 地址:必须 4KB 对齐(低 12 位为 0),且不能超出处理器物理地址宽度
  • 分配后必须把 VMCS Revision ID 写入前 4 字节(bits 30:0,bit 31 清零),从 IA32_VMX_BASIC MSR (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_FIXED1
  • IA32_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 模式(古老的地址线回绕兼容特性)。现代系统不用关心。

posted @ 2026-03-11 10:08  BitWarden  阅读(4)  评论(0)    收藏  举报