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

  • 以及其他人。