巢狀虛擬化

巢狀虛擬化是指 Hyper-V Hypervisor 模擬硬體虛擬化延伸模組。 其他虛擬化軟體可以使用這些模擬延伸模組 (,例如巢狀 Hypervisor) 在 Hyper-V 平臺上執行。

這項功能僅適用于客體分割區。 每個虛擬機器都必須啟用它。 Windows 根分割區不支援巢狀虛擬化。

下列術語是用來定義各種巢狀虛擬化層級:

詞彙 定義
L0 Hypervisor 在實體硬體上執行的 Hyper-V Hypervisor。
L1 根目錄 Windows 根作業系統。
L1 來賓 沒有巢狀 Hypervisor 的 Hyper-V 虛擬機器。
L1 Hypervisor 在 Hyper-V 虛擬機器內執行的巢狀 Hypervisor。
L2 根目錄 根 Windows 作業系統,在 Hyper-V 虛擬機器的內容中執行。
L2 來賓 巢狀虛擬機器,在 Hyper-V 虛擬機器的內容中執行。

相較于裸機,Hypervisor 在 VM 中執行時可能會產生顯著的效能回歸。 L1 Hypervisor 可以使用 L0 Hypervisor 所提供的啟發式介面,優化為在 Hyper-V VM 中執行。

啟發式 VMCS (Intel)

在 Intel 平臺上,虛擬化軟體會使用虛擬機器控制資料結構 (VMCS) 來設定與虛擬化相關的處理器行為。 VMCS 必須使用 VMPTRLD 指令進行作用中,並使用 VMREAD 和 VMWRITE 指示進行修改。 這些指示通常是巢狀虛擬化的重大瓶頸,因為它們必須模擬。

Hypervisor 會公開「啟發式 VMCS」功能,可用來使用客體實體記憶體中的資料結構來控制虛擬化相關的處理器行為。 您可以使用一般記憶體存取指示來修改此資料結構,因此不需要 L1 Hypervisor 執行 VMREAD 或 VMWRITE 或 VMPTRLD 指令。

L1 Hypervisor 可以選擇使用啟發式 VMCS,方法是將 1 寫入 虛擬處理器輔助頁面中的對應欄位。 VP 輔助頁面中的另一個欄位可控制目前啟用的 VMCS。 每個啟發式 VMCS 的大小都只有一頁 (4 KB) ,而且一開始必須零。 不需要執行任何 VMPTRLD 指令,才能啟用已啟用的 VMCS 或目前 VMCS。

在 L1 Hypervisor 執行具有啟發式 VMCS 的 VM 專案之後,VMCS 會被視為在處理器上作用中。 啟發式 VMCS 只能同時在單一處理器上作用中。 L1 Hypervisor 可以執行 VMCLEAR 指令,將已啟用的 VMCS 從作用中轉換為非作用中狀態。 啟用的 VMCS 處於作用中狀態時,任何 VMREAD 或 VMWRITE 指示都不受支援,而且可能會導致非預期的行為。

HV_VMX_ENLIGHTENED_VMCS結構會定義啟發式 VMCS 的配置。 所有非綜合欄位都會對應至 Intel 實體 VMCS 編碼。

清除欄位

L0 Hypervisor 可以選擇快取啟發式 VMCS 的一部分。 啟用的 VMCS 清除欄位可控制從巢狀 VM 專案上的客體記憶體重載啟用 VMCS 的哪些部分。 每次修改啟發式 VMCS 時,L1 Hypervisor 必須清除對應的 VMCS 清除欄位,否則 L0 Hypervisor 可能會使用過時的版本。

清除欄位啟發式是透過啟發式 VMCS 的綜合 「CleanFields」 欄位來控制。 根據預設,會設定所有位,讓 L0 Hypervisor 必須重載每個巢狀 VM 專案的對應 VMCS 欄位。

功能探索

已啟用的 VMCS 介面支援會以 CPUID 分葉0x40000004回報。

啟發式 VMCS 結構會設定版本,以考慮未來的變更。 每個啟發式 VMCS 結構都包含版本欄位,L0 Hypervisor 會報告此欄位。

目前唯一支援的 VMCS 版本是 1。

Hypervisor 實作考慮

對於大部分的 VMCS 欄位,如果 VM 支援 VMCS 欄位,則會支援 VM 的對應啟發式 VMCS 欄位,如透過架構功能探索機制所決定。 CPUID 分葉0x4000000A中會報告例外狀況。

如果架構功能探索機制指出未定義任何啟發式 VMCS 欄位的 VMCS 欄位支援,如果 L1 Hypervisor 選擇使用已啟用的 VMCS,則 L1 Hypervisor 不應該啟用此功能。

Hyper-V L0 Hypervisor 不會指出未定義啟發式 VMCS 欄位或例外狀況的 VMCS 欄位支援。 如果另一個 L0 Hypervisor 需要定義新的啟發式 VMCS 欄位或例外狀況,請連絡 Microsoft。

AMD) (連接 VMCB 欄位

AMD 已在 VMCB 中保留空間供 Hypervisor 使用,以及相關聯的清除位。 保留的位元組位於 VMCB 的控制區段中,位移0x3E0-3FF。 每當 Hypervisor 修改 VMCB) 的「啟發式」區域時,清除位是位 31 (必須失效。

Hyper-V 會利用保留的 VMCB 區域來設定啟發式。 HV_SVM_ENLIGHTENED_VMCB_FIELDS結構會記錄格式。

啟發式 MSR 點陣圖

L0 Hypervisor 會在 Intel 和 AMD 平臺上模擬 「MSR-Bitmap」 控制項,以允許虛擬化軟體控制哪些 MSR 存取會產生攔截。

L1 Hypervisor 可以與 L0 Hypervisor 共同作業,讓 MSR 存取更有效率。 它可以藉由將啟發式 VMCS / VMCB 欄位中的對應欄位設定為 1,以啟用啟發式 MSR 點陣圖。 啟用時,L0 Hypervisor 不會監視 MSR 點陣圖是否有變更。 相反地,L1 Hypervisor 必須在變更其中一個 MSR 點陣圖之後,使對應的清除欄位失效。

CPUID 分葉0x4000000A中會報告啟用 MSR 點陣圖的支援。

與即時移轉的相容性

Hyper-V 能夠即時將子分割區從一個主機移轉至另一個主機。 即時移轉通常對子分割而言是透明的。 不過,在巢狀虛擬化的情況下,L1 Hypervisor 可能需要注意移轉。

即時移轉通知

L1 Hypervisor 可以在移轉資料分割時要求收到通知。 這項功能會在 CPUID 中列舉為 「AccessReenlightenmentControls」 許可權。 L0 Hypervisor 會公開綜合 MSR (HV_X64_MSR_REENLIGHTENMENT_CONTROL) ,L1 Hypervisor 可用來設定中斷向量和目標處理器。 L0 Hypervisor 會在每次移轉之後插入具有指定向量的中斷。

#define HV_X64_MSR_REENLIGHTENMENT_CONTROL (0x40000106)

typedef union
{
    UINT64 AsUINT64;
    struct
    {
        UINT64 Vector :8;
        UINT64 RsvdZ1 :8;
        UINT64 Enabled :1;
        UINT64 RsvdZ2 :15;
        UINT64 TargetVp :32;
    };
} HV_REENLIGHTENMENT_CONTROL;

指定的向量必須對應至固定的 APIC 中斷。 TargetVp 會指定虛擬處理器索引。

TSC 模擬

客體分割區可能會在具有不同 TSC 頻率的兩部機器之間即時移轉。 在這些情況下,可能需要重新計算 來自參考 TSC 頁面的 TscScale 值。

L0 Hypervisor 選擇性地在移轉之後模擬所有 TSC 存取,直到 L1 Hypervisor 有機會重新計算 TscScale 值為止。 L1 Hypervisor 可以藉由寫入至HV_X64_MSR_TSC_EMULATION_CONTROL MSR 來加入宣告 TSC 模擬。 如果加入宣告,L0 Hypervisor 會在進行移轉之後模擬 TSC 存取。

L1 Hypervisor 可以使用 HV_X64_MSR_TSC_EMULATION_STATUS MSR 來查詢 TSC 存取是否目前正在模擬。 例如,L1 Hypervisor 可以訂閱 即時移轉通知 ,並在收到移轉中斷之後查詢 TSC 狀態。 它也可以在使用此 MSR 更新 TscScale 值) 之後,關閉 TSC 模擬 (。

#define HV_X64_MSR_TSC_EMULATION_CONTROL (0x40000107)
#define HV_X64_MSR_TSC_EMULATION_STATUS (0x40000108)

typedef union
{
    UINT64 AsUINT64;
    struct
    {
        UINT64 Enabled :1;
        UINT64 RsvdZ :63;
    };
 } HV_TSC_EMULATION_CONTROL;

typedef union
{
    UINT64 AsUINT64;
    struct
    {
        UINT64 InProgress : 1;
        UINT64 RsvdP1 : 63;
    };
} HV_TSC_EMULATION_STATUS;

虛擬 TLB

Hypervisor 所公開的虛擬 TLB 可能會擴充為快取從 L2 GPO 到 GPO 的翻譯。 如同邏輯處理器上的 TLB,虛擬 TLB 是一個不一致的快取,而且來賓可以看到這種不一致。 Hypervisor 會公開作業來管理 TLB。

直接虛擬排清

Hypervisor 會公開 hypercalls (HvCallFlushVirtualAddressSpaceHvCallFlushVirtualAddressSpaceExHvCallFlushVirtualAddressListHvCallFlushVirtualAddressListEx) ,讓作業系統更有效率地管理虛擬 TLB。 L1 Hypervisor 可以選擇允許其來賓使用這些超calls,並將處理它們的責任委派給 L0 Hypervisor。 這需要在 Intel 平臺上使用分割輔助頁面 (和虛擬 VMCS) 。

使用時,虛擬 TLB 會將所有快取的對應加上巢狀內容的識別碼標記 (VMCS 或 VMCB) 建立它們。 為了回應 L2 客體中的直接虛擬排清 hypercall,L0 Hypervisor 會將巢狀內容建立的所有快取對應失效,其中

  • VmId 與呼叫端的 VmId 相同
  • VpId 包含在指定的 ProcessorMask 中,或指定HV_FLUSH_ALL_PROCESSORS

CPUID 分葉0x4000000A會報告直接虛擬排清的支援。

組態

虛擬排清超calls 的直接處理是由下列方式啟用:

  1. 虛擬處理器輔助頁面 的 NestedEnlightenmentsControl.Features.DirectHypercall 欄位設定為 1。
  2. 將啟發式 VMCS 或 VMCB 的 EnlightenmentsControl.NestedFlushVirtualHypercall 欄位設定為 1。

啟用它之前,L1 Hypervisor 必須設定啟用 VMCS / VMCB 的下列其他欄位:

  • VpId:啟發式 VMCS /VMCB 控制之虛擬處理器的識別碼。
  • VmId:啟發式 VMCS /VMCB 所屬的虛擬機器識別碼。
  • PartitionAssistPage:資料分割輔助頁面的客體實體位址。

L1 Hypervisor 也必須透過 CPUID 向其來賓公開下列功能。

  • UseHypercallForLocalFlush
  • UseHypercallForRemoteFlush

資料分割輔助頁面

資料分割輔助頁面是記憶體記憶體的頁面大小對齊區域,L1 Hypervisor 必須先配置零,才能使用直接排清超calls。 其 GPA 必須寫入啟發式 VMCS /VMCB 中的對應欄位。

struct
{
    UINT32 TlbLockCount;
} VM_PARTITION_ASSIST_PAGE;

綜合VM-Exit

如果呼叫端資料分割輔助頁面的 TlbLockCount 不是零,L0 Hypervisor 會在處理直接虛擬排清超call 之後,將具有綜合結束原因的VM-Exit傳遞給 L1 Hypervisor。

在 Intel 平臺上,會傳遞具有結束原因 HV_VMX_SYNTHETIC_EXIT_REASON_TRAP_AFTER_FLUSH 的VM-Exit。 在 AMD 平臺上,會傳遞具有結束代碼 HV_SVM_EXITCODE_ENL 的VM-Exit,而 ExitInfo1 會設定為 HV_SVM_ENL_EXITCODE_TRAP_AFTER_FLUSH

#define HV_VMX_SYNTHETIC_EXIT_REASON_TRAP_AFTER_FLUSH 0x10000031

#define HV_SVM_EXITCODE_ENL 0xF0000000
#define HV_SVM_ENL_EXITCODE_TRAP_AFTER_FLUSH   (1)

第二層位址轉譯

啟用客體分割區的巢狀虛擬化時,磁碟分割所公開的記憶體管理單位 (MMU) 包含支援第二層位址轉譯。 第二層位址轉譯是 L1 Hypervisor 可用來虛擬化實體記憶體的功能。 在使用中時,某些將視為客體實體位址 (GPO) 的位址會視為 L2 客體實體位址 (L2 GPO) ,並周遊一組分頁結構來轉譯為 GPO。

L1 Hypervisor 可以決定使用第二層位址空間的方式和位置。 每個第二層位址空間都是由定義 64 位識別碼值的來賓識別。 在 Intel 平臺上,此值與 EPT 指標相同。 在 AMD 平臺上,值等於 nCR3 VMCB 欄位。

相容性

Hypervisor 公開的第二層位址轉譯功能通常與 VMX 或 SVM 支援相容,以進行位址轉譯。 不過,下列客體可觀察的差異存在:

  • 在內部,Hypervisor 可能會使用將 L2 GPO 轉譯為 SPA 的陰影頁面資料表。 在這類實作中,這些陰影頁面資料表會以大型 TTLB 的形式出現在軟體上。 不過,可能會觀察到數個差異。 首先,陰影頁面資料表可以在兩個虛擬處理器之間共用,而傳統的TLB 是個別處理器結構,而且是獨立的。 此共用可能會可見,因為一個虛擬處理器的頁面存取權可以填滿陰影頁面資料表專案,後續由另一個虛擬處理器使用。
  • 某些 Hypervisor 實作可能會使用客體分頁表的內部寫入保護,從內部資料結構排清 MMU 對應 (,例如陰影頁面資料表) 。 這在架構上對來賓而言不可見,因為 Hypervisor 會以透明方式處理這些資料表的寫入。 不過,其他分割區或裝置對基礎 GPA 頁面執行的寫入可能不會觸發適當的 TLB 排清。
  • 在某些 Hypervisor 實作上,第二層分頁錯誤可能不會使快取的對應失效。

啟發式第二層 TLB 排清

Hypervisor 也支援一組增強功能,讓來賓更有效率地管理第二層 TLB。 這些增強型作業可以與舊版 TLB 管理作業交換使用。

Hypervisor 支援下列超calls 來使TLB 失效:

Hypercall 描述
HvCallFlushGuestPhysicalAddressSpace 使第二層位址空間內的快取 L2 GPA 與 GPA 對應失效。
HvCallFlushGuestPhysicalAddressList 使第二層位址空間的一部分內快取的 SSO/ L2 GPA 與 GPA 對應失效。

在 AMD 平臺上,所有 TLB 專案都會以 ASID (位址空間識別碼) 進行架構標記。 ASID 的失效會導致與 ASID 相關聯的所有 TLB 整個都失效。 巢狀 Hypervisor 可以選擇性地在 HV_SVM_ENLIGHTENED_VMCB_FIELDS中將 EnlightenedNptTlb 設定為 「1」 來加入宣告「覺察型 TLB」。 如果巢狀 Hypervisor 加入宣告啟發式,ASID 失效只會排清衍生自第一層位址轉譯的 TLB 整個 (,也就是虛擬位址空間) 。 若要排清衍生自巢狀頁面資料表的 TLB 專案 () 並強制 L0 Hypervisor 重建陰影頁面資料表,必須使用 HvCallFlushGuestPhysicalAddressSpace 或 HvCallFlushGuestPhysicalAddressList 超calls。

巢狀虛擬化例外狀況

在 Intel 平臺上。 L1 Hypervisor 可以加入宣告分頁錯誤例外狀況類別中的虛擬化例外狀況。 L0 Hypervisor 會在 Hypervisor 巢狀虛擬化功能 CPUID 分葉中公告此列舉的支援。

您可以將 VirtualException 設定為 [虛擬處理器輔助] 頁面中 HV_NESTED_ENLIGHTENMENTS_CONTROL 資料結構中的 「1」 來啟用此啟發式。

巢狀虛擬化 MSR

巢狀 VP 索引暫存器

L1 Hypervisor 會公開 MSR 來報告目前處理器的基礎 VP 索引。

MSR 位址 註冊名稱 描述
0x40001002 HV_X64_MSR_NESTED_VP_INDEX 在巢狀根分割區中,報告目前處理器的基礎 VP 索引。

巢狀 SynIC MSR

在巢狀根分割區中,下列 MSR 允許存取基底 Hypervisor 的對應 SynIC MSR

若要尋找基礎處理器的索引,呼叫端應該先使用HV_X64_MSR_NESTED_VP_INDEX。

MSR 位址 註冊名稱 基礎 MSR
0x40001080 HV_X64_MSR_NESTED_SCONTROL HV_X64_MSR_SCONTROL
0x40001081 HV_X64_MSR_NESTED_SVERSION HV_X64_MSR_SVERSION
0x40001082 HV_X64_MSR_NESTED_SIEFP HV_X64_MSR_SIEFP
0x40001083 HV_X64_MSR_NESTED_SIMP HV_X64_MSR_SIMP
0x40001084 HV_X64_MSR_NESTED_EOM HV_X64_MSR_EOM
0x40001090 HV_X64_MSR_NESTED_SINT0 HV_X64_MSR_SINT0
0x40001091 HV_X64_MSR_NESTED_SINT1 HV_X64_MSR_SINT1
0x40001092 HV_X64_MSR_NESTED_SINT2 HV_X64_MSR_SINT2
0x40001093 HV_X64_MSR_NESTED_SINT3 HV_X64_MSR_SINT3
0x40001094 HV_X64_MSR_NESTED_SINT4 HV_X64_MSR_SINT4
0x40001095 HV_X64_MSR_NESTED_SINT5 HV_X64_MSR_SINT5
0x40001096 HV_X64_MSR_NESTED_SINT6 HV_X64_MSR_SINT6
0x40001097 HV_X64_MSR_NESTED_SINT7 HV_X64_MSR_SINT7
0x40001098 HV_X64_MSR_NESTED_SINT8 HV_X64_MSR_SINT8
0x40001099 HV_X64_MSR_NESTED_SINT9 HV_X64_MSR_SINT9
0x4000109A HV_X64_MSR_NESTED_SINT10 HV_X64_MSR_SINT10
0x4000109B HV_X64_MSR_NESTED_SINT11 HV_X64_MSR_SINT11
0x4000109C HV_X64_MSR_NESTED_SINT12 HV_X64_MSR_SINT12
0x4000109D HV_X64_MSR_NESTED_SINT13 HV_X64_MSR_SINT13
0x4000109E HV_X64_MSR_NESTED_SINT14 HV_X64_MSR_SINT14
0x4000109F HV_X64_MSR_NESTED_SINT15 HV_X64_MSR_SINT15