巢狀 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;
};