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

[PATCH v1 3/5] x86/nestedsvm: update vmexit without pending or deferred flag


  • To: xen-devel@xxxxxxxxxxxxxxxxxxxx
  • From: chunjie.zhu@xxxxxxxxxx
  • Date: Wed, 23 Sep 2026 17:20:43 +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=8IU4BHlwvEUQATEKce1EuKskCgG/uibOwmDxKxzfdCE=; b=c1VydIVejzKK4rMGX9qxg0YJ5goLqJs5hZwDXwJn6Oo2O/NaOclQDsnSCJdnkeDXvd+2iFsO9zCPmNicLHJblUT0mCs1Z3WjATsskYCgVVXor/+Dnqlou5COJTEMLRMc9VjtTWmpVgJhsFSd03ESP55Ec72wkD7pPGMYT6DNoaAMU8j3bXpJOh7M+Jb3Q4yac2Eq7bMURHxr6LT1meiJhaoL3FUUo+3VqByBeKxwRiC+Y/0sh97uicTs3ClZJh5fPHops8xLvFaKvCnIFGUZO8W1EYPShJ+l+SltcHtVA0qO7zjmewAfg3B7wz1V4TBQht1JHjFlwLCH5Jc29pSndg==
  • Arc-seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=Uv/kP8+5dKvRiUe0mtYsU6E5nv3BHkRBS14/SEmSl+O4KN9aR9oJg4moTlDa8SMUSUbTRLsH55u8mHm6gznYnXUhF5+A2+wZLKegFzH2/1BTvr5RQ8pVFERd+3lLV06HltrLBVQVPoSRSL6qndVlQ5ADCSuVXy/X+1zB57L1TDXNqmhjCfSDTiWvMpu53/KPFk1nTXORtFC1L8SeRCwq9So0ISDXMOPhntA95QcIanYWlEugWhIe9NBhaSH1cF3ojswq1Ab4+/9VkBdOsIgRaiUiCqdMZj9kzJQOQhNBeCJA5POfBhKrwScZXZRPMepoTWUNCp42ZXw5l/PeBprpsQ==
  • 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: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=citrix.com;
  • Cc: jbeulich@xxxxxxxx, andrew.cooper3@xxxxxxxxxx, roger@xxxxxxxxxxxxxx, jason.andryuk@xxxxxxx, teddy.astie@xxxxxxxxxx, Chunjie Zhu <chunjie.zhu@xxxxxxxxxx>
  • Delivery-date: Wed, 23 Sep 2026 09:21:33 +0000
  • List-id: Xen developer discussion <xen-devel.lists.xenproject.org>

From: Chunjie Zhu <chunjie.zhu@xxxxxxxxxx>

Signed-off-by: Chunjie Zhu <chunjie.zhu@xxxxxxxxxx>
---
 xen/arch/x86/hvm/svm/intr.c              |   4 +-
 xen/arch/x86/hvm/svm/nestedhvm.h         |   5 +-
 xen/arch/x86/hvm/svm/nestedsvm.c         | 168 ++++++++---------------
 xen/arch/x86/hvm/svm/svm.c               |  10 +-
 xen/arch/x86/include/asm/hvm/svm-types.h |   7 -
 5 files changed, 72 insertions(+), 122 deletions(-)

diff --git a/xen/arch/x86/hvm/svm/intr.c b/xen/arch/x86/hvm/svm/intr.c
index 7f6cafb6db78..f9b59a0d6a46 100644
--- a/xen/arch/x86/hvm/svm/intr.c
+++ b/xen/arch/x86/hvm/svm/intr.c
@@ -55,7 +55,7 @@ static void svm_inject_nmi(struct vcpu *v)
         vmcb, general1_intercepts | GENERAL1_INTERCEPT_IRET);
 }
 
-static void svm_inject_intr(struct vcpu *v, int vector)
+void svm_inject_intr(struct vcpu *v, int vector)
 {
     struct vmcb_struct *vmcb = v->arch.hvm.svm.vmcb;
     intinfo_t event;
@@ -69,7 +69,7 @@ static void svm_inject_intr(struct vcpu *v, int vector)
     vmcb->event_inj = event;
 }
 
-static void svm_enable_intr_window(struct vcpu *v, struct hvm_intack intack)
+void svm_enable_intr_window(struct vcpu *v, struct hvm_intack intack)
 {
     struct vmcb_struct *vmcb = v->arch.hvm.svm.vmcb;
     uint32_t general1_intercepts = vmcb_get_general1_intercepts(vmcb);
diff --git a/xen/arch/x86/hvm/svm/nestedhvm.h b/xen/arch/x86/hvm/svm/nestedhvm.h
index e22ef259c171..5876a948f39a 100644
--- a/xen/arch/x86/hvm/svm/nestedhvm.h
+++ b/xen/arch/x86/hvm/svm/nestedhvm.h
@@ -27,7 +27,8 @@
     (!!((v)->arch.hvm.guest_efer & EFER_SVME))
 
 int nestedsvm_vmcb_map(struct vcpu *v, uint64_t vmcbaddr);
-void nestedsvm_vmexit_defer(struct vcpu *v,
+enum nestedhvm_vmexits
+nestedsvm_vcpu_vmexit(struct vcpu *v, struct cpu_user_regs *regs,
     uint64_t exitcode, uint64_t exitinfo1, uint64_t exitinfo2);
 enum nestedhvm_vmexits
 nestedsvm_vmexit_n2n1(struct vcpu *v, struct cpu_user_regs *regs);
@@ -65,6 +66,8 @@ int cf_check nsvm_hap_walk_L1_p2m(
 #define NSVM_INTR_MASKED         0
 
 int nestedsvm_vcpu_interrupt(struct vcpu *v, const struct hvm_intack intack);
+void svm_enable_intr_window(struct vcpu *v, struct hvm_intack intack);
+void svm_inject_intr(struct vcpu *v, int vector);
 
 #endif /* __X86_HVM_SVM_NESTEDHVM_PRIV_H__ */
 
diff --git a/xen/arch/x86/hvm/svm/nestedsvm.c b/xen/arch/x86/hvm/svm/nestedsvm.c
index edb3f444756d..9e5e804a5b00 100644
--- a/xen/arch/x86/hvm/svm/nestedsvm.c
+++ b/xen/arch/x86/hvm/svm/nestedsvm.c
@@ -152,8 +152,6 @@ int cf_check nsvm_vcpu_reset(struct vcpu *v)
     svm->ns_vmcb_hostcr3 = 0;
     svm->ns_asid = 0;
     svm->ns_hostflags.bytes = 0;
-    svm->ns_vmexit.exitinfo1 = 0;
-    svm->ns_vmexit.exitinfo2 = 0;
 
     svm->ns_iomap = NULL;
 
@@ -712,14 +710,10 @@ nsvm_vcpu_vmrun(struct vcpu *v, struct cpu_user_regs 
*regs)
     int ret;
     unsigned int inst_len;
     struct nestedvcpu *nv = &vcpu_nestedhvm(v);
-    struct nestedsvm *svm = &vcpu_nestedsvm(v);
 
     inst_len = svm_get_insn_len(v, INSTR_VMRUN);
     if ( inst_len == 0 )
-    {
-        svm->ns_vmexit.exitcode = VMEXIT_SHUTDOWN;
         return -1;
-    }
 
     nv->nv_vmswitch_in_progress = 1;
     ASSERT(nv->nv_vvmcx != NULL);
@@ -738,15 +732,10 @@ nsvm_vcpu_vmrun(struct vcpu *v, struct cpu_user_regs 
*regs)
         break;
     case NSVM_ERROR_VVMCB:
         gdprintk(XENLOG_ERR, "inject VMEXIT(INVALID)\n");
-        svm->ns_vmexit.exitcode = VMEXIT_INVALID;
         return -1;
     case NSVM_ERROR_VMENTRY:
     default:
-        gdprintk(XENLOG_ERR,
-            "nsvm_vcpu_vmentry failed, injecting #UD\n");
-        hvm_inject_hw_exception(X86_EXC_UD, X86_EVENT_NO_EC);
-        /* Must happen after hvm_inject_hw_exception or it doesn't work right. 
*/
-        nv->nv_vmswitch_in_progress = 0;
+        gdprintk(XENLOG_ERR, "nsvm_vcpu_vmentry failed\n");
         return 1;
     }
 
@@ -760,47 +749,55 @@ nsvm_vcpu_vmrun(struct vcpu *v, struct cpu_user_regs 
*regs)
 
 static int
 nsvm_vcpu_vmexit_inject(struct vcpu *v, struct cpu_user_regs *regs,
-    uint64_t exitcode)
+    uint64_t exitcode, uint64_t exitinfo1, uint64_t exitinfo2)
 {
     struct nestedvcpu *nv = &vcpu_nestedhvm(v);
-    struct nestedsvm *svm = &vcpu_nestedsvm(v);
     struct vmcb_struct *ns_vmcb;
     struct vmcb_struct *vmcb = v->arch.hvm.svm.vmcb;
 
-    if ( vmcb->_vintr.fields.vgif_enable )
+    ASSERT(vmcb->_vintr.fields.vgif_enable);
         vmcb->_vintr.fields.vgif = 0;
-    else
-        svm->ns_gif = 0;
 
     ns_vmcb = nv->nv_vvmcx;
 
-    if ( nv->nv_vmexit_pending )
+    switch ( exitcode )
+    {
+    case VMEXIT_INTR:
     {
-        switch ( exitcode )
+        struct hvm_intack intack = {
+            .source = exitinfo1,
+            .vector = exitinfo2
+        };
+
+        /* See the comment in svm_intr_assist() for why this is necessary */
+        if ( unlikely(vmcb->event_inj.v) ||
+             hvm_interrupt_blocked(v, intack) )
         {
-        case VMEXIT_INTR:
-            if ( unlikely(ns_vmcb->event_inj.v) && nv->nv_vmentry_pending &&
-                 hvm_event_needs_reinjection(ns_vmcb->event_inj.type,
-                                             ns_vmcb->event_inj.vector) )
-                ns_vmcb->exit_int_info = ns_vmcb->event_inj;
-            break;
-        case VMEXIT_EXCEPTION_PF:
-            ns_vmcb->_cr2 = ns_vmcb->ei.exc.cr2;
-            fallthrough;
-        case VMEXIT_NPF:
-            ns_vmcb->exitinfo2 = svm->ns_vmexit.exitinfo2;
-            fallthrough;
-        case VMEXIT_EXCEPTION_NP:
-        case VMEXIT_EXCEPTION_SS:
-        case VMEXIT_EXCEPTION_GP:
-        case VMEXIT_EXCEPTION_15:
-        case VMEXIT_EXCEPTION_MF:
-        case VMEXIT_EXCEPTION_AC:
-            ns_vmcb->exitinfo1 = svm->ns_vmexit.exitinfo1;
-            break;
-        default:
+            svm_enable_intr_window(v, intack);
             break;
         }
+
+        svm_inject_intr(v, intack.vector);
+        pt_intr_post(v, intack);
+        break;
+    }
+
+    case VMEXIT_EXCEPTION_PF:
+        ns_vmcb->_cr2 = exitinfo2;
+        fallthrough;
+    case VMEXIT_NPF:
+        ns_vmcb->exitinfo2 = exitinfo2;
+        fallthrough;
+    case VMEXIT_EXCEPTION_NP:
+    case VMEXIT_EXCEPTION_SS:
+    case VMEXIT_EXCEPTION_GP:
+    case VMEXIT_EXCEPTION_15:
+    case VMEXIT_EXCEPTION_MF:
+    case VMEXIT_EXCEPTION_AC:
+        ns_vmcb->exitinfo1 = exitinfo1;
+        break;
+    default:
+        break;
     }
 
     ns_vmcb->exitcode = exitcode;
@@ -811,10 +808,16 @@ 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)
 {
+    enum nestedhvm_vmexits ret;
+
     ASSERT(vcpu_nestedhvm(v).nv_vvmcx != NULL);
 
-    nestedsvm_vmexit_defer(v, VMEXIT_EXCEPTION_DE + event->vector,
-                           event->error_code, event->data);
+    ret = nestedsvm_vcpu_vmexit(v, guest_cpu_user_regs(),
+                                VMEXIT_EXCEPTION_DE + event->vector,
+                                event->error_code, event->data);
+    if ( ret == NESTEDHVM_VMEXIT_FATALERROR )
+        domain_crash(v->domain);
+
     return NESTEDHVM_VMEXIT_DONE;
 }
 
@@ -1219,7 +1222,7 @@ enum hvm_intblk cf_check nsvm_intr_blocked(struct vcpu *v)
         if ( v->io.req.state != STATE_IOREQ_NONE )
             return hvm_intblk_shadow;
 
-        if ( !nv->nv_vmexit_pending && n2vmcb->exit_int_info.v )
+        if ( n2vmcb->exit_int_info.v )
         {
             /* Give the l2 guest a chance to finish the delivery of
              * the last injected interrupt or exception before we
@@ -1228,42 +1231,15 @@ enum hvm_intblk cf_check nsvm_intr_blocked(struct vcpu 
*v)
             return hvm_intblk_shadow;
         }
     }
-
-    if ( nv->nv_vmexit_pending )
-        /* hvm_inject_hw_exception() must have run before.
-         * exceptions have higher priority than interrupts.
-         */
-        return hvm_intblk_rflags_ie;
-
     return hvm_intblk_none;
 }
 
-/* VMEXIT emulation */
-void
-nestedsvm_vmexit_defer(struct vcpu *v,
-    uint64_t exitcode, uint64_t exitinfo1, uint64_t exitinfo2)
-{
-    struct nestedsvm *svm = &vcpu_nestedsvm(v);
-    struct vmcb_struct *vmcb = v->arch.hvm.svm.vmcb;
-
-    if ( vmcb->_vintr.fields.vgif_enable )
-        vmcb->_vintr.fields.vgif = 0;
-    else
-        svm->ns_gif = 0;
-
-    svm->ns_vmexit.exitcode = exitcode;
-    svm->ns_vmexit.exitinfo1 = exitinfo1;
-    svm->ns_vmexit.exitinfo2 = exitinfo2;
-    vcpu_nestedhvm(v).nv_vmexit_pending = 1;
-}
-
 enum nestedhvm_vmexits
 nestedsvm_check_intercepts(struct vcpu *v, struct cpu_user_regs *regs,
     uint64_t exitcode)
 {
     bool is_intercepted;
 
-    ASSERT(vcpu_nestedhvm(v).nv_vmexit_pending == 0);
     is_intercepted = nsvm_vmcb_guest_intercepts_exitcode(v, regs, exitcode);
 
     /* 
@@ -1352,11 +1328,12 @@ nestedsvm_vmexit_n2n1(struct vcpu *v, struct 
cpu_user_regs *regs)
 /* The exitcode is in native SVM/VMX format. The forced exitcode
  * is in generic format.
  */
-static enum nestedhvm_vmexits
+enum nestedhvm_vmexits
 nestedsvm_vcpu_vmexit(struct vcpu *v, struct cpu_user_regs *regs,
-    uint64_t exitcode)
+    uint64_t exitcode, uint64_t exitinfo1, uint64_t exitinfo2)
 {
     int rc;
+    enum nestedhvm_vmexits ret = NESTEDHVM_VMEXIT_DONE;
     struct nestedvcpu *nv = &vcpu_nestedhvm(v);
 
     nv->nv_vmswitch_in_progress = 1;
@@ -1368,14 +1345,12 @@ nestedsvm_vcpu_vmexit(struct vcpu *v, struct 
cpu_user_regs *regs,
      */
     if ( nestedhvm_vcpu_in_guestmode(v) )
     {
-        enum nestedhvm_vmexits ret;
-
         ret = nestedsvm_vmexit_n2n1(v, regs);
         switch ( ret )
         {
         case NESTEDHVM_VMEXIT_FATALERROR:
             gdprintk(XENLOG_ERR, "VMEXIT: fatal error\n");
-            return ret;
+            goto out;
         case NESTEDHVM_VMEXIT_HOST:
             BUG();
 
@@ -1395,46 +1370,18 @@ nestedsvm_vcpu_vmexit(struct vcpu *v, struct 
cpu_user_regs *regs,
     /* Prepare for running the l1 guest. Make the actual
      * modifications to the virtual VMCB/VMCS.
      */
-    rc = nsvm_vcpu_vmexit_inject(v, regs, exitcode);
+    rc = nsvm_vcpu_vmexit_inject(v, regs, exitcode, exitinfo1, exitinfo2);
 
     /* If l1 guest uses shadow paging, update the paging mode. */
     if ( !nestedhvm_paging_mode_hap(v) )
         paging_update_paging_modes(v);
 
-    nv->nv_vmswitch_in_progress = 0;
-
     if ( rc )
-        return NESTEDHVM_VMEXIT_FATALERROR;
-
-    return NESTEDHVM_VMEXIT_DONE;
-}
-
-/* VCPU switch */
-void asmlinkage nsvm_vcpu_switch(void)
-{
-    struct cpu_user_regs *regs = guest_cpu_user_regs();
-    struct vcpu *v = current;
-    struct nestedvcpu *nv;
-    struct nestedsvm *svm;
-
-    if ( !nestedhvm_enabled(v->domain) )
-        return;
-
-    nv = &vcpu_nestedhvm(v);
-    svm = &vcpu_nestedsvm(v);
-    ASSERT(v->arch.hvm.svm.vmcb != NULL);
-    ASSERT(nv->nv_n1vmcx != NULL);
-    ASSERT(nv->nv_n2vmcx != NULL);
-    ASSERT(nv->nv_n1vmcx_pa != INVALID_PADDR);
-    ASSERT(nv->nv_n2vmcx_pa != INVALID_PADDR);
+        ret = NESTEDHVM_VMEXIT_FATALERROR;
 
-    if ( nv->nv_vmexit_pending )
-    {
-    vmexit:
-        nestedsvm_vcpu_vmexit(v, regs, svm->ns_vmexit.exitcode);
-        nv->nv_vmexit_pending = 0;
-        return;
-    }
+out:
+    nv->nv_vmswitch_in_progress = 0;
+    return ret;
 }
 
 /* Interrupts, Virtual GIF */
@@ -1477,7 +1424,10 @@ 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);
+        ret = nestedsvm_vcpu_vmexit(v, guest_cpu_user_regs(), exitcode,
+                                    intack.source, exitinfo2);
+        if ( ret == NESTEDHVM_VMEXIT_FATALERROR )
+            domain_crash(v->domain);
         return NSVM_INTR_FORCEVMEXIT;
     }
 
diff --git a/xen/arch/x86/hvm/svm/svm.c b/xen/arch/x86/hvm/svm/svm.c
index fc447d24d67e..a8d258aa3980 100644
--- a/xen/arch/x86/hvm/svm/svm.c
+++ b/xen/arch/x86/hvm/svm/svm.c
@@ -1619,7 +1619,9 @@ 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);
+        if ( nestedsvm_vcpu_vmexit(v, regs, VMEXIT_NPF, pfec, gpa) ==
+             NESTEDHVM_VMEXIT_FATALERROR )
+            domain_crash(v->domain);
         return;
     }
 
@@ -2595,8 +2597,10 @@ void asmlinkage svm_vmexit_handler(void)
             switch ( nsret )
             {
             case NESTEDHVM_VMEXIT_DONE:
-                /* defer VMEXIT injection */
-                nestedsvm_vmexit_defer(v, exit_reason, exitinfo1, exitinfo2);
+                nsret = nestedsvm_vcpu_vmexit(v, regs, exit_reason,
+                                              exitinfo1, exitinfo2);
+                if ( nsret == NESTEDHVM_VMEXIT_FATALERROR )
+                    domain_crash(v->domain);
                 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 beab9a3af203..256bf5b91402 100644
--- a/xen/arch/x86/include/asm/hvm/svm-types.h
+++ b/xen/arch/x86/include/asm/hvm/svm-types.h
@@ -69,13 +69,6 @@ struct nestedsvm {
     bool ns_gif;
     bool ns_hap_enabled;
 
-    /* Only meaningful when vmexit_pending flag is set */
-    struct {
-        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 */
-    } ns_vmexit;
-
     union {
         uint32_t bytes;
         struct {
-- 
2.34.1




 


Rackspace

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