[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH v2 4/7] x86/nestedvmx: Load L2's EFER on VM entry and restore L1's on VM exit


  • To: xen-devel@xxxxxxxxxxxxxxxxxxxx
  • From: chunjie.zhu@xxxxxxxxxx
  • Date: Thu, 29 Oct 2026 14:26:56 +0800
  • Arc-authentication-results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=citrix.com; dmarc=pass action=none header.from=citrix.com; dkim=pass header.d=citrix.com; arc=none
  • Arc-message-signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=kRYuKRMvwLZ55Mb3+BJf5qlLfaVtGKo4c1WB061e+e4=; b=h9Z7MSPaCGFJCgAeWRszLSLcJe2iAVP2kaXYe4o8967sxcgUuXqaU/jhpB5ccWNNcEpdTX3yjfVdF2QCc2wCbtS4wkO+oaKXqh266kvUpVeKbDEUKXNgaZnRtvbSuLHp3GkYHlEX/elUGy8jGE+ZFrTESavBw7UjtbVB1oz704y/eT+KXHw6nfMa3rL8EgC1Brj/jWc3HnovQBI99tihu8lxvsG/hZUaOUQ+m9J5gigZJO3Eh0n1TCVIHe76KCjRpkuAkPdtk7JXVBSb7F9Q8L8ewwUoF6maQUtc+Q7/LChXkBjGpLhBq1pgDKPpQNVy01QTdcpNhu0NSgIC0hzkQQ==
  • Arc-seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=E0dQoTcor2TTOmUNpIca37xUvUXBkAG6kzMcIGJjX5fpoAG++TvtwFOIzIAfHWU5HE1FmftpwfMaaB70JcVZADdeiiERdgZpyvk2oeGBterGB6YfDrsyGIdQDlrTvcLB2yK6FgnU/Y/1kdPjuvIMTP+HhiBN6uWnCCFTwmdHzBbkeEiymuPcVMTQh1KHrve+wzIXOKWtQ75HX5NKieTxBXtoo/ICXlYZzawomh9vyeVbEMT5NSoluh0EDINWan/7BlOFWjNhjE9WwCkgddb1Ao7sCMJ7LufLbwrYoyTxlsvfHMq5vHLBj42ifcqRiqLPrP8xnf6OkZAz/62aDgQDKg==
  • Authentication-results: eu.smtp.expurgate.cloud; dkim=pass header.s=selector1 header.d=citrix.com header.i="@citrix.com" header.h="From:Date:Subject:Message-Id:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck"
  • Authentication-results: mx.microsoft.com 1; dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=citrix.com;
  • Cc: andrew.cooper3@xxxxxxxxxx, anthony.perard@xxxxxxxxxx, michal.orzel@xxxxxxx, jbeulich@xxxxxxxx, julien@xxxxxxx, roger@xxxxxxxxxxxxxx, sstabellini@xxxxxxxxxx, dpsmith@xxxxxxxxxxxxxxxxxxxx, Stephen.Cheng@xxxxxxxxxx, Chunjie Zhu <chunjie.zhu@xxxxxxxxxx>
  • Delivery-date: Thu, 29 Oct 2026 06:28:10 +0000
  • List-id: Xen developer discussion <xen-devel.lists.xenproject.org>

From: Chunjie Zhu <chunjie.zhu@xxxxxxxxxx>

nvmx_update_entry_control() strips VM_ENTRY_LOAD_GUEST_EFER from the vmcs02
entry controls, and virtual_vmentry() only derives EFER.LMA/LME from
VM_ENTRY_IA32E_MODE.  As a result:

 * While L2 runs, hardware EFER is L0's own value (LME set), and since
   vmx_update_guest_efer() clears the RDMSR intercept when guest_efer matches
   the value it believes is loaded, L2's RDMSR of EFER is passed through and
   returns LME=1 even though L2 never set it.

 * L0's v->arch.hvm.guest_efer has LME cleared for a non-64bit L2, so L2 never
   sets LME in L0's view

L2 Linux AP trampolines read EFER and skip the WRMSR if LME is already set.
L0 therefore never sees LME, and when L2 enables CR0.PG it is treated as
legacy PAE paging.  PDPTEs are loaded from what is really a PML4, and VM
entry fails:

  vmentry failure (reason 0x80000021): Invalid guest state (2)
  PDPTE0 = 0x0000000023604063  EFER(VMCS) = 0

which crashes the L1 domain ("Unhandled nested vmexit: reason 2147483681").

Fix this by:

 * Forcing VM_ENTRY_LOAD_GUEST_EFER in vmcs02 (when the CPU supports it), so
   hardware EFER, and hence passthrough RDMSR, matches guest_efer.

 * In virtual_vmentry(), taking guest_efer from vmcs12 GUEST_EFER when L1 set
   VM_ENTRY_LOAD_GUEST_EFER, preserving LME for an unpaged L2, with LMA still
   following IA32E_MODE.  Then push it to hardware with
   hvm_update_guest_efer().

 * In virtual_vmexit(), restoring guest_efer from vmcs01 GUEST_EFER so that
   L2's SCE/NXE/LME bits don't leak into L1.

Signed-off-by: Chunjie Zhu <chunjie.zhu@xxxxxxxxxx>
Assisted-by: Claude:claude-sonnet-5
---
 xen/arch/x86/hvm/vmx/vvmx.c | 30 ++++++++++++++++++++++++++++--
 1 file changed, 28 insertions(+), 2 deletions(-)

diff --git a/xen/arch/x86/hvm/vmx/vvmx.c b/xen/arch/x86/hvm/vmx/vvmx.c
index 0ec9eb6be450..e37f59bea7ce 100644
--- a/xen/arch/x86/hvm/vmx/vvmx.c
+++ b/xen/arch/x86/hvm/vmx/vvmx.c
@@ -695,6 +695,12 @@ static void nvmx_update_entry_control(struct vcpu *v)
     shadow_cntrl &= ~(VM_ENTRY_LOAD_GUEST_PAT
                       | VM_ENTRY_LOAD_GUEST_EFER
                       | VM_ENTRY_LOAD_PERF_GLOBAL_CTRL);
+    /*
+     * Always load L2's EFER from v->arch.hvm.guest_efer, so hardware (and
+     * passthrough RDMSR) agrees with it rather than showing L0's own EFER.
+     */
+    if ( cpu_has_vmx_efer )
+        shadow_cntrl |= VM_ENTRY_LOAD_GUEST_EFER;
     __vmwrite(VM_ENTRY_CONTROLS, shadow_cntrl);
 }
 
@@ -1220,13 +1226,24 @@ static void virtual_vmentry(struct cpu_user_regs *regs)
     lm_l1 = hvm_long_mode_active(v);
     lm_l2 = !!(get_vvmcs(v, VM_ENTRY_CONTROLS) & VM_ENTRY_IA32E_MODE);
 
-    if ( lm_l2 )
-        v->arch.hvm.guest_efer |= EFER_LMA | EFER_LME;
+    if ( get_vvmcs(v, VM_ENTRY_CONTROLS) & VM_ENTRY_LOAD_GUEST_EFER )
+    {
+        /*
+         * L2 may have EFER.LME set while still unpaged (LMA clear), e.g. an
+         * AP trampoline which skips the WRMSR because it reads LME already
+         * set.  Keep it, or enabling paging later is treated as legacy PAE.
+         */
+        v->arch.hvm.guest_efer = get_vvmcs(v, GUEST_EFER) & ~EFER_LMA;
+    }
     else
         v->arch.hvm.guest_efer &= ~(EFER_LMA | EFER_LME);
 
+    if ( lm_l2 )
+        v->arch.hvm.guest_efer |= EFER_LMA | EFER_LME;
+
     load_shadow_control(v);
     load_shadow_guest_state(v);
+    hvm_update_guest_efer(v);
 
     if ( lm_l1 != lm_l2 )
         paging_update_paging_modes(v);
@@ -1452,6 +1469,15 @@ static void virtual_vmexit(struct cpu_user_regs *regs)
     lm_l2 = hvm_long_mode_active(v);
     lm_l1 = !!(get_vvmcs(v, VM_EXIT_CONTROLS) & VM_EXIT_IA32E_MODE);
 
+    /* Don't leak L2's EFER (SCE, NXE, LME) into L1. */
+    if ( cpu_has_vmx_efer )
+    {
+        unsigned long efer;
+
+        __vmread(GUEST_EFER, &efer);
+        v->arch.hvm.guest_efer = efer;
+    }
+
     if ( lm_l1 )
         v->arch.hvm.guest_efer |= EFER_LMA | EFER_LME;
     else
-- 
2.34.1




 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.