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

[PATCH] x86/nestedsvm: Record NextRIP with the deferred #VMEXIT


  • To: xen-devel@xxxxxxxxxxxxxxxxxxxx
  • From: Stephen Cheng <stephen.cheng@xxxxxxxxxx>
  • Date: Thu, 22 Oct 2026 17:06:57 +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=cVJ9QDL7TJ6xYiY9YpmuP2JzHHKsIqxTUFhVhiztq4Q=; b=L3OsvMgSZZBKvSbiVR07VxkKnM9SRUOOx2LgvNPd5VqzKIjUa26Y//OXuSe4RVgGxX5lbZ/J6vgkfkzXt/USprITCogPP+nis4chQ2Ob1WlAjoGJH+WQ/b+F72tmdp5vu7clg/OoXGZOuv6f7CI2rE9Kxd+yWcypEDwy91xepZGXVwE+nlJs89B0HGQS5uNlBQvJaGpBjV67n3HYThR/aUemm1ww0wwbhOsENvZTl6ZY/hFsWRe1yWi2ttJWltOSuSsK1B1CRfxAxHozYRgbKkXpxz9APGWSwbsyuTCfRtRvolooJtKhgwhkSBybonBcg7gygTMWeVbbDbUDRtbqrw==
  • Arc-seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=yCEoVYhJ4o1DmIXLO7OGC6SsKpjneRfw0ybrF7zZURKtdEu0sWq/lp/2HkzJIjC3O+wfYaMvEFBfGTzoo1FeumOpC0MqHlO5yXgJ4wu4Ls2n7d47MCA1q72MWnmI+n3puyIW+Ukn745xmOd1TCLrw3NHJ/6vc851N4LsREohTb0c5cPnKx3myMPsNay2NpQcjLCT8rfd2TLdC5XwU/kSxxGkP2AU+z5XvttV4jiGfvWv4/qZi1Wm6v1U5vcqchSKHkUGgGk+eNYDmFGIknxfHRuuoOflPngM8Cp8BT6Jek0gebv3zZQfCJ74gd8OwHOQkiR+5MCJU1a1/ldx0k3JHg==
  • 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: Ross Lagerwall <ross.lagerwall@xxxxxxxxxx>, Stephen Cheng <stephen.cheng@xxxxxxxxxx>, Jan Beulich <jbeulich@xxxxxxxx>, Andrew Cooper <andrew.cooper3@xxxxxxxxxx>, Roger Pau Monné <roger@xxxxxxxxxxxxxx>, Jason Andryuk <jason.andryuk@xxxxxxx>, Teddy Astie <teddy.astie@xxxxxxxxxx>, Chunjie Zhu <chunjie.zhu@xxxxxxxxxx>
  • Delivery-date: Thu, 22 Oct 2026 09:07:20 +0000
  • List-id: Xen developer discussion <xen-devel.lists.xenproject.org>

nsvm_vmcb_prepare4vmexit() copies NextRIP from the hardware #VMEXIT,
but L0 also delivers #VMEXITs it creates itself, for which that value
belongs to a different exit.  Such a #VMEXIT saves no nRIP (AMD64 APM
vol 2, publication 24593 rev 3.44, section 15.7.1).  For example:

 1. L1 intercepts #GP but not MSR accesses.
 2. L2 executes RDMSR of an MSR which Xen doesn't implement.
 3. Hardware exits to L0 with VMEXIT_MSR and NextRIP after the RDMSR.
 4. L0 handles the RDMSR itself and injects #GP into L2.
 5. L1 intercepts #GP, so L0 delivers VMEXIT_EXCP_GP to L1.
 6. L1 sees NextRIP after the RDMSR, instead of 0.

Record NextRIP in ns_vmexit alongside the exitcode and exitinfo, and
have each source of a #VMEXIT supply it: the hardware value for a
reflected #VMEXIT, the next RIP for an INT3/INTO which L0 emulated, and
zero for everything else, including a failed VMRUN.

Fixes: 9a779e4fc161 ("Implement SVM specific part for Nested Virtualization")
Signed-off-by: Stephen Cheng <stephen.cheng@xxxxxxxxxx>
Assisted-by: Claude:claude-opus-5-5
---
Found with a nested-SVM XTF test, not yet upstream.

This is the #VMEXIT counterpart of commit 9d6b6e4f7394 ("x86/svm: Sync
nextrip during virtual VMRUN").

BOUND's #BR is raised by the emulator as a plain hardware exception
without an instruction length, so it still reports 0 rather than the
next RIP.

Chunjie's "x86/nestedsvm: rework nested VMRUN/VMEXIT handling" series
also changes nestedsvm_vmexit_defer() and nsvm_vcpu_vmexit_inject();
under it NextRIP would become one more value passed alongside
exitinfo1/2.  I'm happy to rebase on top of it, or the other way round,
whichever merge order is preferred.

 xen/arch/x86/hvm/svm/nestedhvm.h         |  3 ++-
 xen/arch/x86/hvm/svm/nestedsvm.c         | 32 ++++++++++++++++++++----
 xen/arch/x86/hvm/svm/svm.c               |  8 +++---
 xen/arch/x86/include/asm/hvm/svm-types.h |  1 +
 4 files changed, 35 insertions(+), 9 deletions(-)

diff --git a/xen/arch/x86/hvm/svm/nestedhvm.h b/xen/arch/x86/hvm/svm/nestedhvm.h
index 9bb04a0434..963390e8a9 100644
--- a/xen/arch/x86/hvm/svm/nestedhvm.h
+++ b/xen/arch/x86/hvm/svm/nestedhvm.h
@@ -28,7 +28,8 @@
 
 int nestedsvm_vmcb_map(struct vcpu *v, uint64_t vmcbaddr);
 void nestedsvm_vmexit_defer(struct vcpu *v,
-    uint64_t exitcode, uint64_t exitinfo1, uint64_t exitinfo2);
+    uint64_t exitcode, uint64_t exitinfo1, uint64_t exitinfo2,
+    uint64_t nextrip);
 enum nestedhvm_vmexits
 nestedsvm_vmexit_n2n1(struct vcpu *v, struct cpu_user_regs *regs);
 enum nestedhvm_vmexits
diff --git a/xen/arch/x86/hvm/svm/nestedsvm.c b/xen/arch/x86/hvm/svm/nestedsvm.c
index 2b998e26bc..241912e1ad 100644
--- a/xen/arch/x86/hvm/svm/nestedsvm.c
+++ b/xen/arch/x86/hvm/svm/nestedsvm.c
@@ -154,6 +154,7 @@ int cf_check nsvm_vcpu_reset(struct vcpu *v)
     svm->ns_hostflags.bytes = 0;
     svm->ns_vmexit.exitinfo1 = 0;
     svm->ns_vmexit.exitinfo2 = 0;
+    svm->ns_vmexit.nextrip = 0;
 
     svm->ns_iomap = NULL;
 
@@ -696,6 +697,7 @@ nsvm_vcpu_vmrun(struct vcpu *v, struct cpu_user_regs *regs)
     if ( inst_len == 0 )
     {
         svm->ns_vmexit.exitcode = VMEXIT_SHUTDOWN;
+        svm->ns_vmexit.nextrip = 0;
         return -1;
     }
 
@@ -717,6 +719,7 @@ nsvm_vcpu_vmrun(struct vcpu *v, struct cpu_user_regs *regs)
     case NSVM_ERROR_VVMCB:
         gdprintk(XENLOG_ERR, "inject VMEXIT(INVALID)\n");
         svm->ns_vmexit.exitcode = VMEXIT_INVALID;
+        svm->ns_vmexit.nextrip = 0;
         return -1;
     case NSVM_ERROR_VMENTRY:
     default:
@@ -782,6 +785,7 @@ nsvm_vcpu_vmexit_inject(struct vcpu *v, struct 
cpu_user_regs *regs,
     }
 
     ns_vmcb->exitcode = exitcode;
+    ns_vmcb->nextrip = svm->ns_vmexit.nextrip;
     ns_vmcb->event_inj.raw = 0;
     return 0;
 }
@@ -789,10 +793,26 @@ nsvm_vcpu_vmexit_inject(struct vcpu *v, struct 
cpu_user_regs *regs,
 int cf_check nsvm_vcpu_vmexit_event(
     struct vcpu *v, const struct x86_event *event)
 {
+    const struct vmcb_struct *vmcb = v->arch.hvm.svm.vmcb;
+    uint64_t nextrip = 0;
+
     ASSERT(vcpu_nestedhvm(v).nv_vvmcx != NULL);
 
+    /*
+     * An exception intercept saves NextRIP only for INT3, INTO and BOUND
+     * (AMD APM vol 2 rev 3.44, 15.7.1).  The emulator raises BOUND's #BR as
+     * a plain hardware exception, so only INT3/INTO can be recognised here.
+     * As in svm_inject_event(), truncate it outside of 64bit mode.
+     */
+    if ( event->type == X86_ET_SW_EXC )
+    {
+        nextrip = guest_cpu_user_regs()->rip + event->insn_len;
+        if ( !((vmcb_get_efer(vmcb) & EFER_LMA) && vmcb->cs.l) )
+            nextrip = (uint32_t)nextrip;
+    }
+
     nestedsvm_vmexit_defer(v, VMEXIT_EXCEPTION_DE + event->vector,
-                           event->error_code, event->data);
+                           event->error_code, event->data, nextrip);
     return NESTEDHVM_VMEXIT_DONE;
 }
 
@@ -1063,8 +1083,7 @@ nsvm_vmcb_prepare4vmexit(struct vcpu *v, struct 
cpu_user_regs *regs)
 
     /* LBR virtualization - keep lbr control as is */
 
-    /* NextRIP */
-    ns_vmcb->nextrip = n2vmcb->nextrip;
+    /* NextRIP is set by nsvm_vcpu_vmexit_inject(). */
 
     /* Decode Assist */
     ns_vmcb->guest_ins_len = n2vmcb->guest_ins_len;
@@ -1219,7 +1238,8 @@ enum hvm_intblk cf_check nsvm_intr_blocked(struct vcpu *v)
 /* VMEXIT emulation */
 void
 nestedsvm_vmexit_defer(struct vcpu *v,
-    uint64_t exitcode, uint64_t exitinfo1, uint64_t exitinfo2)
+    uint64_t exitcode, uint64_t exitinfo1, uint64_t exitinfo2,
+    uint64_t nextrip)
 {
     struct nestedsvm *svm = &vcpu_nestedsvm(v);
     struct vmcb_struct *vmcb = v->arch.hvm.svm.vmcb;
@@ -1232,6 +1252,7 @@ nestedsvm_vmexit_defer(struct vcpu *v,
     svm->ns_vmexit.exitcode = exitcode;
     svm->ns_vmexit.exitinfo1 = exitinfo1;
     svm->ns_vmexit.exitinfo2 = exitinfo2;
+    svm->ns_vmexit.nextrip = nextrip;
     vcpu_nestedhvm(v).nv_vmexit_pending = 1;
 }
 
@@ -1359,6 +1380,7 @@ nestedsvm_vcpu_vmexit(struct vcpu *v, struct 
cpu_user_regs *regs,
 
         case NESTEDHVM_VMEXIT_ERROR:
             exitcode = VMEXIT_INVALID;
+            vcpu_nestedsvm(v).ns_vmexit.nextrip = 0;
             break;
         default:
             ASSERT(!nestedhvm_vcpu_in_guestmode(v));
@@ -1478,7 +1500,7 @@ nestedsvm_vcpu_interrupt(struct vcpu *v, const struct 
hvm_intack intack)
                                      guest_cpu_user_regs(), exitcode);
     if ( ret )
     {
-        nestedsvm_vmexit_defer(v, exitcode, intack.source, exitinfo2);
+        nestedsvm_vmexit_defer(v, exitcode, intack.source, exitinfo2, 0);
         return NSVM_INTR_FORCEVMEXIT;
     }
 
diff --git a/xen/arch/x86/hvm/svm/svm.c b/xen/arch/x86/hvm/svm/svm.c
index 71e48351f2..cf9e3ef1ab 100644
--- a/xen/arch/x86/hvm/svm/svm.c
+++ b/xen/arch/x86/hvm/svm/svm.c
@@ -1618,7 +1618,7 @@ static void svm_do_nested_pgfault(struct vcpu *v,
     case -1:
         ASSERT(nestedhvm_enabled(v->domain) && nestedhvm_vcpu_in_guestmode(v));
         /* inject #VMEXIT(NPF) into guest. */
-        nestedsvm_vmexit_defer(v, VMEXIT_NPF, pfec, gpa);
+        nestedsvm_vmexit_defer(v, VMEXIT_NPF, pfec, gpa, 0);
         return;
     }
 
@@ -2556,7 +2556,7 @@ void asmlinkage svm_vmexit_handler(void)
         enum nestedhvm_vmexits nsret;
         struct nestedvcpu *nv = &vcpu_nestedhvm(v);
         struct vmcb_struct *ns_vmcb = nv->nv_vvmcx;
-        uint64_t exitinfo1, exitinfo2;
+        uint64_t exitinfo1, exitinfo2, nextrip;
 
         paging_update_nestedmode(v);
 
@@ -2581,6 +2581,7 @@ void asmlinkage svm_vmexit_handler(void)
              */
             exitinfo1 = vmcb->exitinfo1;
             exitinfo2 = vmcb->exitinfo2;
+            nextrip = vmcb->nextrip;
             nv->nv_vmswitch_in_progress = 1;
             nsret = nestedsvm_vmexit_n2n1(v, regs);
             nv->nv_vmswitch_in_progress = 0;
@@ -2588,7 +2589,8 @@ void asmlinkage svm_vmexit_handler(void)
             {
             case NESTEDHVM_VMEXIT_DONE:
                 /* defer VMEXIT injection */
-                nestedsvm_vmexit_defer(v, exit_reason, exitinfo1, exitinfo2);
+                nestedsvm_vmexit_defer(v, exit_reason, exitinfo1, exitinfo2,
+                                       nextrip);
                 goto out;
             case NESTEDHVM_VMEXIT_FATALERROR:
                 gdprintk(XENLOG_ERR, "unexpected nestedsvm_vmexit() error\n");
diff --git a/xen/arch/x86/include/asm/hvm/svm-types.h 
b/xen/arch/x86/include/asm/hvm/svm-types.h
index beab9a3af2..fafbd646cc 100644
--- a/xen/arch/x86/include/asm/hvm/svm-types.h
+++ b/xen/arch/x86/include/asm/hvm/svm-types.h
@@ -74,6 +74,7 @@ struct nestedsvm {
         uint64_t exitcode;  /* native exitcode to inject into l1 guest */
         uint64_t exitinfo1; /* additional information to the exitcode */
         uint64_t exitinfo2; /* additional information to the exitcode */
+        uint64_t nextrip;   /* NextRIP to report to the l1 guest */
     } ns_vmexit;
 
     union {
-- 
2.49.0




 


Rackspace

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