Authors: Muli Ben-Yehuda et al., IBM Research / IBM Linux Technology Center


1. 背景与动机

  • 传统虚拟化 (single-level): 一个 hypervisor (L0) 管理多个 VM, 每个 VM 运行 OS;
  • 嵌套虚拟化 (nested virtualization): 允许一个 hypervisor (L1) 作为 guest 运行, 并且还能继续运行自己的 VM (L2);
  • 需求来源:
    • 现代操作系统自带虚拟化功能(Windows XP mode in Win7, Linux 内置 KVM);
    • IaaS 场景: 云提供商 (L0) 希望用户能运行自己的 hypervisor (L1), 自己管理 VM (L2);
    • 研究和测试: 嵌套虚拟化方便调试/benchmark hypervisor;
    • 安全性: 可以研究/防御 hypervisor-level rootkit;

IaaS (Infrastructure as a Service)

  • 云计算三层服务之一: IaaS / PaaS / SaaS;
  • IaaS = 提供虚拟化的计算资源(VM, 存储, 网络);
  • 例子:
    • AWS EC2: 创业公司快速租用 VM, 按需扩展;
    • Google Cloud GPU: 科研人员租用 A100 GPU VM;
  • 与论文关系: 云提供商必须支持 nested virtualization, 才能让用户在 VM 内运行自己的 hypervisor;

2. 虚拟化理论基础

Popek–Goldberg (1974)

  • 三大条件: 等价性 (equivalence), 资源控制 (resource control), 效率 (efficiency);
  • 定理: 如果架构中所有 sensitive 指令 都是 privileged 指令, 则该架构是可虚拟化的;
  • 影响:
    • 解释了早期 x86 不可完全虚拟化 \rightarrow VMware 使用二进制翻译绕过;
    • 推动了 Intel/AMD 添加 VT-x / SVM 硬件扩展;

3. 两种嵌套虚拟化模型

1) Multi-level support

  • 硬件直接支持多层 hypervisor;
  • Trap 从 L2 直接交给 L1;
  • 例子: IBM System z;
  • 高效, 但硬件设计复杂; 且一般多层的支持都是有限的, 如只支持两层, 这篇论文是在理论上支持任意层数的嵌套虚拟化, 因此更高层的虚拟化实际上还是依赖于单层虚拟化的软件支持逻辑: compress

2) Single-level support

  • 硬件只有一层 root mode (L0);
  • 所有 trap(无论来自 L1, L2, L3)都掉到 L0;
  • L0 决定是否转发给 L1;
  • 例子: x86 的 Intel VT-x (VMX), AMD SVM;
  • Multiplexing single-level: L0 将唯一的一层虚拟化能力"复用", 对上层 L1/L2 进行模拟;

4. CPU 虚拟化 (VMX)

VMX & L0

  • VMX = Intel VT-x 的指令扩展集;
  • L0 = 唯一运行在 VMX root mode 的 hypervisor;
  • 所有 VMExit/VMEntry 都必须经过 L0;

Trap 流程: Single-level support: L2 trap \rightarrow L0 trap handler \rightarrow L0 转发给 L1;

5. VMCS (Virtual Machine Control Structure)

VMCS 缓存的内容

  1. Guest state: guest 的寄存器 (因为这里要仿真一个cpu), CR3 等;
  2. Host state: VMExit 后 CPU 应该恢复的 hypervisor 状态 (比如 L2 就要缓存 L1和L0 的状态);
  3. Control fields: 哪些事件触发 VMExit (trap entry), 如何注入事件等;

Host state 的作用

  • VMExit 时 CPU 自动加载 host state 从而能继续在 hypervisor 中运行;
  • 确保 hypervisor 能恢复运行;
  • 在 nested virtualization 中:
    • VMCS0$\rightarrow$2 host state = 回到 L0;
    • VMCS1$\rightarrow2hoststate=L1认为自己要恢复的状态,实际上由L0映射到VMCS02 host state = L1 认为自己要恢复的状态, 实际上由 L0 映射到 VMCS0\rightarrow$1 guest state;
      • 也就是如果要从 L2 返回到 L1, 就要首先映射到 L0, 然后 L0 根据这个内容来恢复 L1 的状态;

Control data merging

  • L0 必须合并 VMCS0$\rightarrow1VMCS11 和 VMCS1\rightarrow$2 的控制字段 \rightarrow VMCS0$\rightarrow$2;
  • 例子: L1 想 trap 事件 EA, 但 L0 不关心 \rightarrow L0 必须在 VMCS0$\rightarrow$2 里设置 trap, 否则 L1 永远收不到;

6. VMCS Merging

定义

  • L0 根据 VMCS0$\rightarrow1(运行L1的上下文)VMCS11(运行 L1 的上下文)和 VMCS1\rightarrow2(L1L2的配置),合成VMCS02(L1 给 L2 的配置), 合成 VMCS0\rightarrow$2(硬件实际使用);
  • 这是 L2 \leftrightarrow L1 切换时的关键步骤;

特点

  • 动态发生: 在 VMEntry/VMExit 切换时临时合并;
  • 不是一次性存起来: 因为 VMCS 内容会频繁变化;

优化

  • Partial copy: 只合并修改过的字段;
  • Batch copy: 一次 memcpy 多个字段, 绕过 vmread/vmwrite(但不符合官方规范, 兼容性不保证);

7. 内存虚拟化 (MMU)

Shadow page tables

  • 早期方案: L0 维护一张影子页表 (GVA\rightarrowHPA);
  • 缺点: 每次 guest 页表修改都要 trap \rightarrow 慢;

硬件二维页表 (EPT/NPT)

  • CPU 直接支持两层转换:

GVA \rightarrow GPA (guest page table)
GPA \rightarrow HPA (EPT/NPT)

  • 优化了:
  • 页表更新 (MOV CR3, INVLPG)
  • Page fault(大多数在 guest 内完成, 不再 VMExit)
  • TLB flush(VPID tag 支持减少开销)

Multi-dimensional paging

  • 在 nested virtualization 下:
  • L1 以为自己有 EPT1$\rightarrow$2;
  • L0 实际把 EPT0$\rightarrow1EPT11 和 EPT1\rightarrow$2 压缩合并 \rightarrow EPT0$\rightarrow$2;
  • 如果有 L3:
  • 理论上有 EPT2$\rightarrow$3;
  • L0 最终把 EPT0$\rightarrow1,EPT11, EPT1\rightarrow2,EPT22, EPT2\rightarrow$3 压缩 \rightarrow EPT0$\rightarrow$3;
  • 最终 CPU 只用一张表, 把 L3 GPA \rightarrow L0 HPA;

性能

  • 如果不用 multi-dimensional paging: 频繁 VMExit \rightarrow three-fold slowdown(性能下降 3 倍);
  • 用了 multi-dimensional paging: 性能接近单层虚拟化;

8. I/O 虚拟化

三种方式

  1. PIO (Port I/O)
  • 专用指令 in/out;
  • 在虚拟化中, 几乎总是 VMExit;
  1. MMIO (Memory-Mapped I/O)
  • 把设备寄存器映射到内存地址;
  • 虚拟化下:
    • 直通设备: 不会 trap;
    • 模拟设备: 会 VMExit;
  1. Device assignment
  • 设备直通 \rightarrow 性能最好, 但迁移难;

Multi-level device assignment

  • L2 也能直通设备;
  • L0 通过 IOMMU 把 L2 GPA 映射到 HPA;
  • 避免多层拷贝, 性能接近裸机;

9. DMA 地址问题

  • 设备 DMA 使用的地址不是 VA, 而是 PA;
  • 普通系统: 驱动通过 dma_map_page() 提供 PA;
  • 有 IOMMU: 设备发 IOVA (device virtual address), IOMMU 翻译成 Host PA;
  • 虚拟化下:
  • guest 提供 GPA;
  • L0/IOMMU 翻译成 HPA;

10. Rootkit 与虚拟化

  • Rootkit = 获取最高权限并隐藏自身的恶意工具集;
  • 分类:
  • 用户态(替换 ps/ls);
  • 内核态(修改 LKM, 系统调用表);
  • 固件/硬件级(BIOS, UEFI);
  • 与虚拟化关系:
  • Hypervisor-level rootkit: 藏在 L0 层, guest OS 完全无法发现;

11. 性能瓶颈与优化

Transitions between L1 and L2

  • 每次切换 \rightarrow L2 VMExit \rightarrow L0 \rightarrow VMCS merging \rightarrow L1 \rightarrow 再回到 L2;
  • 优化方法:
  • 部分复制 VMCS;
  • 批量复制 VMCS;
  • 避免频繁 vmread/vmwrite;

Exit handling in L1

  • L1 用 vmread/vmwrite 访问 VMCS1$\rightarrow$2 \rightarrow 每次都要 VMExit 到 L0 \rightarrow 开销大;
  • 优化: 二进制翻译 vmread/vmwrite \rightarrow 直接读写内存 (DRW, Direct Read/Write);

12. 总结

  • 核心贡献:
  • 在 x86 上实现高效嵌套虚拟化;
  • 提出 multi-dimensional paging (MMU) 和 multi-level device assignment (I/O);
  • 性能结果:
  • 常见 workload 的开销仅比单层虚拟化多 6–8%;
  • 避免 naive 方法的 three-fold slowdown;
  • 意义:
  • 为云计算 (IaaS), 系统安全, 研究提供嵌套虚拟化能力;
  • 推动了 x86 虚拟化架构和 hypervisor 的发展;