嵌套 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 内核,可以通过将“nested=1”选项提供给 kvm-intel 模块来启用它。

不需要修改用户空间 (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

  • 以及其他人。