嵌套 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;
};