嵌套 VMX

概述

在 Intel 处理器上,KVM 使用 Intel 的 VMX (虚拟机扩展) 来轻松高效地运行客户操作系统。通常,这些客户机不能自身成为运行其自身客户机的管理程序,因为在 VMX 中,客户机不能使用 VMX 指令。

“嵌套 VMX”功能添加了这项缺失的功能——运行带其自身嵌套客户机的客户机管理程序(使用 VMX)。它通过允许客户机使用 VMX 指令,并利用硬件中可用的单层 VMX 正确高效地模拟这些指令来实现这一点。

我们将在 OSDI 2010 年的论文《海龟项目:嵌套虚拟化的设计与实现》(“The Turtles Project: Design and Implementation of Nested Virtualization”)中更详细地描述嵌套 VMX 功能的理论基础、其实现及其性能特征,该论文可在以下地址获取:

术语

单层虚拟化有两个级别——宿主机 (KVM) 和客户机。在嵌套虚拟化中,我们有三个级别:宿主机 (KVM),我们称之为 L0;客户机管理程序,我们称之为 L1;以及其嵌套客户机,我们称之为 L2。

运行嵌套 VMX

自 Linux 内核 v4.20 起,嵌套 VMX 功能默认启用。对于较旧的 Linux 内核,可以通过向 kvm-intel 模块提供“nested=1”选项来启用它。

用户空间 (qemu) 无需修改。然而,qemu 默认模拟的 CPU 类型 (qemu64) 未列出“VMX”CPU 功能,因此必须通过向 qemu 提供以下选项之一来显式启用它:

  • cpu host(模拟 CPU 具有真实 CPU 的所有功能)

  • cpu qemu64,+vmx(仅将 vmx 功能添加到指定的 CPU 类型)

ABI

嵌套 VMX 旨在为客户机管理程序提供一个标准且(最终)功能齐全的 VMX 实现。因此,其提供的 ABI 的官方规范是 Intel 的 VMX 规范,即其《Intel 64 和 IA-32 架构软件开发人员手册》的第 3B 卷。目前并非所有 VMX 功能都得到完全支持,但目标是最终支持所有功能,从流行管理程序(KVM 等)实际使用的 VMX 功能开始。

作为 VMX 实现,嵌套 VMX 向 L1 呈现一个 VMCS 结构。根据规范要求,除了 revision_id 和 abort 这两个字段外,该结构对其用户是不透明的,用户不应了解或关心其内部结构。相反,该结构通过 VMREAD 和 VMWRITE 指令访问。尽管如此,出于调试目的,KVM 开发人员可能对了解此结构的内部感兴趣;这就是 arch/x86/kvm/vmx.c 中的 struct vmcs12。

名称“vmcs12”指代 L1 为 L2 构建的 VMCS。在代码中,我们还有“vmcs01”,即 L0 为 L1 构建的 VMCS;而“vmcs02”是 L0 为实际运行 L2 而构建的 VMCS——这方面的做法已在上述论文中解释。

为方便起见,我们在此重复 struct vmcs12 的内容。如果此结构的内部发生变化,可能会破坏跨 KVM 版本的实时迁移。如果 struct vmcs12 或其内部 struct shadow_vmcs 发生任何更改,则应更改 VMCS12_REVISION(来自 vmx.c)。

typedef u64 natural_width;
struct __packed vmcs12 {
        /* According to the Intel spec, a VMCS region must start with
         * these two user-visible fields */
        u32 revision_id;
        u32 abort;

        u32 launch_state; /* set to 0 by VMCLEAR, to 1 by VMLAUNCH */
        u32 padding[7]; /* room for future expansion */

        u64 io_bitmap_a;
        u64 io_bitmap_b;
        u64 msr_bitmap;
        u64 vm_exit_msr_store_addr;
        u64 vm_exit_msr_load_addr;
        u64 vm_entry_msr_load_addr;
        u64 tsc_offset;
        u64 virtual_apic_page_addr;
        u64 apic_access_addr;
        u64 ept_pointer;
        u64 guest_physical_address;
        u64 vmcs_link_pointer;
        u64 guest_ia32_debugctl;
        u64 guest_ia32_pat;
        u64 guest_ia32_efer;
        u64 guest_pdptr0;
        u64 guest_pdptr1;
        u64 guest_pdptr2;
        u64 guest_pdptr3;
        u64 host_ia32_pat;
        u64 host_ia32_efer;
        u64 padding64[8]; /* room for future expansion */
        natural_width cr0_guest_host_mask;
        natural_width cr4_guest_host_mask;
        natural_width cr0_read_shadow;
        natural_width cr4_read_shadow;
        natural_width dead_space[4]; /* Last remnants of cr3_target_value[0-3]. */
        natural_width exit_qualification;
        natural_width guest_linear_address;
        natural_width guest_cr0;
        natural_width guest_cr3;
        natural_width guest_cr4;
        natural_width guest_es_base;
        natural_width guest_cs_base;
        natural_width guest_ss_base;
        natural_width guest_ds_base;
        natural_width guest_fs_base;
        natural_width guest_gs_base;
        natural_width guest_ldtr_base;
        natural_width guest_tr_base;
        natural_width guest_gdtr_base;
        natural_width guest_idtr_base;
        natural_width guest_dr7;
        natural_width guest_rsp;
        natural_width guest_rip;
        natural_width guest_rflags;
        natural_width guest_pending_dbg_exceptions;
        natural_width guest_sysenter_esp;
        natural_width guest_sysenter_eip;
        natural_width host_cr0;
        natural_width host_cr3;
        natural_width host_cr4;
        natural_width host_fs_base;
        natural_width host_gs_base;
        natural_width host_tr_base;
        natural_width host_gdtr_base;
        natural_width host_idtr_base;
        natural_width host_ia32_sysenter_esp;
        natural_width host_ia32_sysenter_eip;
        natural_width host_rsp;
        natural_width host_rip;
        natural_width paddingl[8]; /* room for future expansion */
        u32 pin_based_vm_exec_control;
        u32 cpu_based_vm_exec_control;
        u32 exception_bitmap;
        u32 page_fault_error_code_mask;
        u32 page_fault_error_code_match;
        u32 cr3_target_count;
        u32 vm_exit_controls;
        u32 vm_exit_msr_store_count;
        u32 vm_exit_msr_load_count;
        u32 vm_entry_controls;
        u32 vm_entry_msr_load_count;
        u32 vm_entry_intr_info_field;
        u32 vm_entry_exception_error_code;
        u32 vm_entry_instruction_len;
        u32 tpr_threshold;
        u32 secondary_vm_exec_control;
        u32 vm_instruction_error;
        u32 vm_exit_reason;
        u32 vm_exit_intr_info;
        u32 vm_exit_intr_error_code;
        u32 idt_vectoring_info_field;
        u32 idt_vectoring_error_code;
        u32 vm_exit_instruction_len;
        u32 vmx_instruction_info;
        u32 guest_es_limit;
        u32 guest_cs_limit;
        u32 guest_ss_limit;
        u32 guest_ds_limit;
        u32 guest_fs_limit;
        u32 guest_gs_limit;
        u32 guest_ldtr_limit;
        u32 guest_tr_limit;
        u32 guest_gdtr_limit;
        u32 guest_idtr_limit;
        u32 guest_es_ar_bytes;
        u32 guest_cs_ar_bytes;
        u32 guest_ss_ar_bytes;
        u32 guest_ds_ar_bytes;
        u32 guest_fs_ar_bytes;
        u32 guest_gs_ar_bytes;
        u32 guest_ldtr_ar_bytes;
        u32 guest_tr_ar_bytes;
        u32 guest_interruptibility_info;
        u32 guest_activity_state;
        u32 guest_sysenter_cs;
        u32 host_ia32_sysenter_cs;
        u32 padding32[8]; /* room for future expansion */
        u16 virtual_processor_id;
        u16 guest_es_selector;
        u16 guest_cs_selector;
        u16 guest_ss_selector;
        u16 guest_ds_selector;
        u16 guest_fs_selector;
        u16 guest_gs_selector;
        u16 guest_ldtr_selector;
        u16 guest_tr_selector;
        u16 host_es_selector;
        u16 host_cs_selector;
        u16 host_ss_selector;
        u16 host_ds_selector;
        u16 host_fs_selector;
        u16 host_gs_selector;
        u16 host_tr_selector;
};

作者

这些补丁由以下人员编写:
  • Abel Gordon, abelg (at) il.ibm.com

  • Nadav Har’El, nyh (at) il.ibm.com

  • Orit Wasserman, oritw (at) il.ibm.com

  • Ben-Ami Yassor, benami (at) il.ibm.com

  • Muli Ben-Yehuda, muli (at) il.ibm.com

贡献者:
  • Anthony Liguori, aliguori (at) us.ibm.com

  • Mike Day, mdday (at) us.ibm.com

  • Michael Factor, factor (at) il.ibm.com

  • Zvi Dubitzky, dubi (at) il.ibm.com

以及宝贵的审阅意见由以下人员提供:
  • Avi Kivity, avi (at) redhat.com

  • Gleb Natapov, gleb (at) redhat.com

  • Marcelo Tosatti, mtosatti (at) redhat.com

  • Kevin Tian, kevin.tian (at) intel.com

  • 以及其他人。