All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v1] x86/nestedsvm: honour L2's EFLAGS.IF when L1 clears V_INTR_MASKING
@ 2026-10-16 10:00 chunjie.zhu
  2026-10-29 17:20 ` Ross Lagerwall
                   ` (3 more replies)
  0 siblings, 4 replies; 15+ messages in thread
From: chunjie.zhu @ 2026-10-16 10:00 UTC (permalink / raw)
  To: xen-devel
  Cc: jbeulich, andrew.cooper3, roger, jason.andryuk, teddy.astie,
	Chunjie Zhu

From: Chunjie Zhu <chunjie.zhu@citrix.com>

L0 always runs L2 with V_INTR_MASKING set, so a physical interrupt exits to
L0 regardless of L2's EFLAGS.IF.  nsvm_intr_blocked() only blocks when L1
requested V_INTR_MASKING=1 and L1's IF was clear.  With V_INTR_MASKING=0
and L2's IF clear, nestedsvm_vcpu_interrupt() would still inject
VMEXIT(INTR) into L1.

Per the APM (vol.2 rev 3.44 "Physical Interrupt Masking in EFLAGS"), with
V_INTR_MASKING=0 L2's IF masks physical interrupts, and a masked interrupt
must stay pending.  Return NSVM_INTR_MASKED for external interrupts in
that case.

Signed-off-by: Chunjie Zhu <chunjie.zhu@citrix.com>
---
 xen/arch/x86/hvm/svm/nestedsvm.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/xen/arch/x86/hvm/svm/nestedsvm.c b/xen/arch/x86/hvm/svm/nestedsvm.c
index faacfe007832..a419b47eb5f6 100644
--- a/xen/arch/x86/hvm/svm/nestedsvm.c
+++ b/xen/arch/x86/hvm/svm/nestedsvm.c
@@ -1514,6 +1514,17 @@ nestedsvm_vcpu_interrupt(struct vcpu *v, const struct hvm_intack intack)
     case hvm_intsrc_pic:
     case hvm_intsrc_lapic:
     case hvm_intsrc_vector:
+        /*
+         * Without V_INTR_MASKING, L2's EFLAGS.IF masks physical interrupts
+         * (APM vol.2 rev 3.44 "Physical Interrupt Masking in EFLAGS").  L0
+         * always runs L2 with V_INTR_MASKING set, so the interrupt would
+         * otherwise exit to L0 and be forwarded to L1 even with L2's IF
+         * clear.  Leave it pending instead.
+         */
+        if ( !vcpu_nestedsvm(v).ns_hostflags.fields.vintrmask &&
+             !(guest_cpu_user_regs()->eflags & X86_EFLAGS_IF) )
+            return NSVM_INTR_MASKED;
+
         exitcode = VMEXIT_INTR;
         exitinfo2 = intack.vector;
         break;
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [PATCH v1] x86/nestedsvm: honour L2's EFLAGS.IF when L1 clears V_INTR_MASKING
  2026-10-16 10:00 [PATCH v1] x86/nestedsvm: honour L2's EFLAGS.IF when L1 clears V_INTR_MASKING chunjie.zhu
@ 2026-10-29 17:20 ` Ross Lagerwall
  2026-11-03 10:11   ` chunjie.zhu
  2026-11-10  8:57 ` [PATCH v2 0/2] " chunjie.zhu
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 15+ messages in thread
From: Ross Lagerwall @ 2026-10-29 17:20 UTC (permalink / raw)
  To: Chunjie Zhu (朱春杰),
	xen-devel@lists.xenproject.org
  Cc: jbeulich@suse.com, Andrew Cooper, roger@xenproject.org,
	jason.andryuk@amd.com, teddy.astie@vates.tech

> From: Xen-devel <xen-devel-bounces@lists.xenproject.org> on behalf of chunjie.zhu@citrix.com <chunjie.zhu@citrix.com>
> Sent: Friday, October 16, 2026 11:00
> To: xen-devel@lists.xenproject.org <xen-devel@lists.xenproject.org>
> Cc: jbeulich@suse.com <jbeulich@suse.com>; Andrew Cooper <andrew.cooper3@citrix.com>; roger@xenproject.org <roger@xenproject.org>; jason.andryuk@amd.com <jason.andryuk@amd.com>; teddy.astie@vates.tech <teddy.astie@vates.tech>; Chunjie Zhu (朱春杰) <chunjie.zhu@citrix.com>
> Subject: [PATCH v1] x86/nestedsvm: honour L2's EFLAGS.IF when L1 clears V_INTR_MASKING
>
>
> From: Chunjie Zhu <chunjie.zhu@citrix.com>
>
> L0 always runs L2 with V_INTR_MASKING set, so a physical interrupt exits to
> L0 regardless of L2's EFLAGS.IF.  nsvm_intr_blocked() only blocks when L1
> requested V_INTR_MASKING=1 and L1's IF was clear.  With V_INTR_MASKING=0
> and L2's IF clear, nestedsvm_vcpu_interrupt() would still inject
> VMEXIT(INTR) into L1.
>
> Per the APM (vol.2 rev 3.44 "Physical Interrupt Masking in EFLAGS"), with
> V_INTR_MASKING=0 L2's IF masks physical interrupts, and a masked interrupt
> must stay pending.  Return NSVM_INTR_MASKED for external interrupts in
> that case.
>
> Signed-off-by: Chunjie Zhu <chunjie.zhu@citrix.com>
> ---
>  xen/arch/x86/hvm/svm/nestedsvm.c | 11 +++++++++++
>  1 file changed, 11 insertions(+)
>
> diff --git a/xen/arch/x86/hvm/svm/nestedsvm.c b/xen/arch/x86/hvm/svm/nestedsvm.c
> index faacfe007832..a419b47eb5f6 100644
> --- a/xen/arch/x86/hvm/svm/nestedsvm.c
> +++ b/xen/arch/x86/hvm/svm/nestedsvm.c
> @@ -1514,6 +1514,17 @@ nestedsvm_vcpu_interrupt(struct vcpu *v, const struct hvm_intack intack)
>      case hvm_intsrc_pic:
>      case hvm_intsrc_lapic:
>      case hvm_intsrc_vector:
> +        /*
> +         * Without V_INTR_MASKING, L2's EFLAGS.IF masks physical interrupts
> +         * (APM vol.2 rev 3.44 "Physical Interrupt Masking in EFLAGS").  L0
> +         * always runs L2 with V_INTR_MASKING set, so the interrupt would
> +         * otherwise exit to L0 and be forwarded to L1 even with L2's IF
> +         * clear.  Leave it pending instead.
> +         */
> +        if ( !vcpu_nestedsvm(v).ns_hostflags.fields.vintrmask &&
> +             !(guest_cpu_user_regs()->eflags & X86_EFLAGS_IF) )
> +            return NSVM_INTR_MASKED;
> +
>          exitcode = VMEXIT_INTR;
>          exitinfo2 = intack.vector;
>          break;
> --
> 2.34.1
>

Would it not make sense to check this in nsvm_intr_blocked() instead?
There is already a similar (inverse) check there so extending that seems
more logical to me.

E.g. something like this (untested):

diff --git a/xen/arch/x86/hvm/svm/nestedsvm.c b/xen/arch/x86/hvm/svm/nestedsvm.c
index b192be0d56b1..8bded8b9a9c2 100644
--- a/xen/arch/x86/hvm/svm/nestedsvm.c
+++ b/xen/arch/x86/hvm/svm/nestedsvm.c
@@ -1213,8 +1213,9 @@ enum hvm_intblk cf_check nsvm_intr_blocked(struct vcpu *v)
     {
         struct vmcb_struct *n2vmcb = nv->nv_n2vmcx;

-        if ( svm->ns_hostflags.fields.vintrmask &&
-             !svm->ns_hostflags.fields.rflagsif )
+        if ( svm->ns_hostflags.fields.vintrmask ?
+             !svm->ns_hostflags.fields.rflagsif :
+             !(guest_cpu_user_regs()->eflags & X86_EFLAGS_IF) )
             return hvm_intblk_rflags_ie;

         /* when l1 guest passes its devices through to the l2 guest

Ross

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [PATCH v1] x86/nestedsvm: honour L2's EFLAGS.IF when L1 clears V_INTR_MASKING
  2026-10-29 17:20 ` Ross Lagerwall
@ 2026-11-03 10:11   ` chunjie.zhu
  2026-11-03 17:07     ` Ross Lagerwall
  0 siblings, 1 reply; 15+ messages in thread
From: chunjie.zhu @ 2026-11-03 10:11 UTC (permalink / raw)
  To: ross.lagerwall
  Cc: andrew.cooper3, chunjie.zhu, jason.andryuk, jbeulich, roger,
	teddy.astie, xen-devel

On Thu, Oct 29, 2026 at 17:20, Ross Lagerwall wrote:
> Would it not make sense to check this in nsvm_intr_blocked() instead?
> There is already a similar (inverse) check there so extending that seems
> more logical to me.

Thanks for the suggestion, but I think there would be a regression.

nsvm_intr_blocked() has no access to the intack. Per the APM (vol.2,
section 15.21), V_INTR_MASKING controls whether the masking of interrupts
(EFLAGS.IF and TPR) is to be virtualized.  That masking only applies to
maskable external interrupts (PIC/LAPIC/vector).  NMI is not masked by
IF/TPR, and #MC is an exception, so neither is affected.

With the check moved into nsvm_intr_blocked(), an NMI/#MC aimed at L1
would be held pending whenever V_INTR_MASKING=0 and L2's IF is clear,
which would be a regression for L1 hypervisors that intercept NMI/#MC.

So I would prefer to keep the check in the hvm_intsrc_pic/lapic/vector
case of nestedsvm_vcpu_interrupt(), where the source is known.  If you
would rather have it in nsvm_intr_blocked(), I can instead pass the
intack down (changing nhvm_interrupt_blocked()) so that the IF check
is applied to non-NMI/MCE sources only.

Let me know which you prefer.

Regards,
Chunjie


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v1] x86/nestedsvm: honour L2's EFLAGS.IF when L1 clears V_INTR_MASKING
  2026-11-03 10:11   ` chunjie.zhu
@ 2026-11-03 17:07     ` Ross Lagerwall
  0 siblings, 0 replies; 15+ messages in thread
From: Ross Lagerwall @ 2026-11-03 17:07 UTC (permalink / raw)
  To: Chunjie Zhu (朱春杰)
  Cc: Andrew Cooper, jason.andryuk@amd.com, jbeulich@suse.com,
	roger@xenproject.org, teddy.astie@vates.tech,
	xen-devel@lists.xenproject.org

> From: Chunjie Zhu (朱春杰) <chunjie.zhu@citrix.com>
> Sent: Tuesday, November 03, 2026 10:11
> To: Ross Lagerwall <ross.lagerwall@citrix.com>
> Cc: Andrew Cooper <andrew.cooper3@citrix.com>; Chunjie Zhu (朱春杰) <chunjie.zhu@citrix.com>; jason.andryuk@amd.com <jason.andryuk@amd.com>; jbeulich@suse.com <jbeulich@suse.com>; roger@xenproject.org <roger@xenproject.org>; teddy.astie@vates.tech <teddy.astie@vates.tech>; xen-devel@lists.xenproject.org <xen-devel@lists.xenproject.org>
> Subject: Re: [PATCH v1] x86/nestedsvm: honour L2's EFLAGS.IF when L1 clears V_INTR_MASKING
>
>
> On Thu, Oct 29, 2026 at 17:20, Ross Lagerwall wrote:
> > Would it not make sense to check this in nsvm_intr_blocked() instead?
> > There is already a similar (inverse) check there so extending that seems
> > more logical to me.
>
> Thanks for the suggestion, but I think there would be a regression.
>
> nsvm_intr_blocked() has no access to the intack. Per the APM (vol.2,
> section 15.21), V_INTR_MASKING controls whether the masking of interrupts
> (EFLAGS.IF and TPR) is to be virtualized.  That masking only applies to
> maskable external interrupts (PIC/LAPIC/vector).  NMI is not masked by
> IF/TPR, and #MC is an exception, so neither is affected.
>
> With the check moved into nsvm_intr_blocked(), an NMI/#MC aimed at L1
> would be held pending whenever V_INTR_MASKING=0 and L2's IF is clear,
> which would be a regression for L1 hypervisors that intercept NMI/#MC.
>
> So I would prefer to keep the check in the hvm_intsrc_pic/lapic/vector
> case of nestedsvm_vcpu_interrupt(), where the source is known.  If you
> would rather have it in nsvm_intr_blocked(), I can instead pass the
> intack down (changing nhvm_interrupt_blocked()) so that the IF check
> is applied to non-NMI/MCE sources only.

You shouldn't need to pass in more information. nhvm_intr_blocked()
returns a reason for why the interrupt is blocked. It is up to the
caller to decide what to do with it. If the reason is
hvm_intblk_rflags_ie, then it shouldn't block injecting an NMI.
nested_svm_vcpu_interrupt() currently ignores the reason so I think it
is already broken for the more common case where V_INTR_MASKING=1 and
L1's RFLAGS.IF=0.

So I think this wants a fix to nested_svm_vcpu_interrupt() to handle the
reason correctly which fixes the V_INTR_MASKING=1 case, and then a fix
to nsvm_intr_blocked() to handle the V_INTR_MASKING=0 case.

The reason I prefer putting the logic in nsvm_intr_blocked() is that
there are other users of it besides nested_svm_vcpu_interrupt() and the
behaviour should be consistent there too.

Ross

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH v2 0/2] x86/nestedsvm: honour L2's EFLAGS.IF when L1 clears V_INTR_MASKING
  2026-10-16 10:00 [PATCH v1] x86/nestedsvm: honour L2's EFLAGS.IF when L1 clears V_INTR_MASKING chunjie.zhu
  2026-10-29 17:20 ` Ross Lagerwall
@ 2026-11-10  8:57 ` chunjie.zhu
  2026-11-10  8:57 ` [PATCH v2 1/2] x86/hvm: don't let EFLAGS.IF block NMI/#MC due to nested state chunjie.zhu
  2026-11-10  8:57 ` [PATCH v2 2/2] x86/nestedsvm: honour L2's EFLAGS.IF when L1 clears V_INTR_MASKING chunjie.zhu
  3 siblings, 0 replies; 15+ messages in thread
From: chunjie.zhu @ 2026-11-10  8:57 UTC (permalink / raw)
  To: ross.lagerwall
  Cc: andrew.cooper3, chunjie.zhu, jason.andryuk, jbeulich, roger,
	teddy.astie, xen-devel

From: Chunjie Zhu <chunjie.zhu@citrix.com>

With V_INTR_MASKING=0 in L1's VMCB, L2's EFLAGS.IF masks physical
interrupts, but L0 always runs L2 with V_INTR_MASKING set, so an
interrupt would exit to L0 and be forwarded to L1 even with L2's IF
clear.  Patch 2 fixes this in nsvm_intr_blocked().

As Ross pointed out, that check must not hold back NMI/#MC, and the
callers of nhvm_interrupt_blocked() already ignore the blocking reason,
which is wrong for NMI/#MC even with V_INTR_MASKING=1.  Patch 1 fixes
that first.

Changes in v2:
 * Move the check into nsvm_intr_blocked() instead of
   nestedsvm_vcpu_interrupt().
 * Add patch 1 to stop EFLAGS.IF from blocking NMI/#MC.
 * Report hvm_intblk_arch from nvmx_intr_blocked() for states that have
   nothing to do with RFLAGS.IF, so patch 1 doesn't affect VMX.

Chunjie Zhu (2):
  x86/hvm: don't let EFLAGS.IF block NMI/#MC due to nested state
  x86/nestedsvm: honour L2's EFLAGS.IF when L1 clears V_INTR_MASKING

 xen/arch/x86/hvm/hvm.c             |  2 +-
 xen/arch/x86/hvm/svm/nestedsvm.c   | 14 +++++++++++---
 xen/arch/x86/hvm/vmx/intr.c        |  6 +++---
 xen/arch/x86/include/asm/hvm/hvm.h | 12 ++++++++++--
 4 files changed, 25 insertions(+), 9 deletions(-)

-- 
2.34.1



^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH v2 1/2] x86/hvm: don't let EFLAGS.IF block NMI/#MC due to nested state
  2026-10-16 10:00 [PATCH v1] x86/nestedsvm: honour L2's EFLAGS.IF when L1 clears V_INTR_MASKING chunjie.zhu
  2026-10-29 17:20 ` Ross Lagerwall
  2026-11-10  8:57 ` [PATCH v2 0/2] " chunjie.zhu
@ 2026-11-10  8:57 ` chunjie.zhu
  2026-11-10 16:58   ` Ross Lagerwall
  2026-11-10 17:36   ` Ross Lagerwall
  2026-11-10  8:57 ` [PATCH v2 2/2] x86/nestedsvm: honour L2's EFLAGS.IF when L1 clears V_INTR_MASKING chunjie.zhu
  3 siblings, 2 replies; 15+ messages in thread
From: chunjie.zhu @ 2026-11-10  8:57 UTC (permalink / raw)
  To: ross.lagerwall
  Cc: andrew.cooper3, chunjie.zhu, jason.andryuk, jbeulich, roger,
	teddy.astie, xen-devel

From: Chunjie Zhu <chunjie.zhu@citrix.com>

nhvm_interrupt_blocked() reports why an interrupt is blocked, but both
callers, nestedsvm_vcpu_interrupt() and hvm_interrupt_blocked(), treat any
reason as blocking every event source.  EFLAGS.IF (hvm_intblk_rflags_ie)
doesn't mask NMI or #MC, so NMI/#MC get held pending needlessly, e.g. when
L1 sets V_INTR_MASKING and L1's IF is clear.

Have nhvm_interrupt_blocked() take the intack and report no block for
NMI/#MC when the reason is hvm_intblk_rflags_ie.

nvmx_intr_blocked() used hvm_intblk_rflags_ie for vmexit/vmentry in
progress and a valid VM_ENTRY_INTR_INFO.  Those block every event source
and have nothing to do with RFLAGS.IF, so report hvm_intblk_arch instead.
This doesn't change behaviour as all callers only compare against
hvm_intblk_none and hvm_intblk_tpr.

Signed-off-by: Chunjie Zhu <chunjie.zhu@citrix.com>
---
v2:
 * New patch.  Handle the blocking reason in callers, as suggested by Ross.
 * Report hvm_intblk_arch from nvmx_intr_blocked() so the NMI/#MC exemption
   doesn't affect VMX.

 xen/arch/x86/hvm/hvm.c             |  2 +-
 xen/arch/x86/hvm/svm/nestedsvm.c   |  2 +-
 xen/arch/x86/hvm/vmx/intr.c        |  6 +++---
 xen/arch/x86/include/asm/hvm/hvm.h | 12 ++++++++++--
 4 files changed, 15 insertions(+), 7 deletions(-)

diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c
index ef9c7cd05777..8fd095a4f8e6 100644
--- a/xen/arch/x86/hvm/hvm.c
+++ b/xen/arch/x86/hvm/hvm.c
@@ -3897,7 +3897,7 @@ enum hvm_intblk hvm_interrupt_blocked(struct vcpu *v, struct hvm_intack intack)
     if ( nestedhvm_enabled(v->domain) ) {
         enum hvm_intblk intr;
 
-        intr = nhvm_interrupt_blocked(v);
+        intr = nhvm_interrupt_blocked(v, intack);
         if ( intr != hvm_intblk_none )
             return intr;
     }
diff --git a/xen/arch/x86/hvm/svm/nestedsvm.c b/xen/arch/x86/hvm/svm/nestedsvm.c
index 66b77c6efe7d..a93681e9bbcd 100644
--- a/xen/arch/x86/hvm/svm/nestedsvm.c
+++ b/xen/arch/x86/hvm/svm/nestedsvm.c
@@ -1496,7 +1496,7 @@ nestedsvm_vcpu_interrupt(struct vcpu *v, const struct hvm_intack intack)
     uint64_t exitinfo2 = 0;
     ASSERT(nestedhvm_vcpu_in_guestmode(v));
 
-    intr = nhvm_interrupt_blocked(v);
+    intr = nhvm_interrupt_blocked(v, intack);
     if ( intr != hvm_intblk_none )
         return NSVM_INTR_MASKED;
 
diff --git a/xen/arch/x86/hvm/vmx/intr.c b/xen/arch/x86/hvm/vmx/intr.c
index 6c220c4c2e49..38be2387739d 100644
--- a/xen/arch/x86/hvm/vmx/intr.c
+++ b/xen/arch/x86/hvm/vmx/intr.c
@@ -142,18 +142,18 @@ enum hvm_intblk cf_check nvmx_intr_blocked(struct vcpu *v)
     {
         if ( nvcpu->nv_vmexit_pending ||
              nvcpu->nv_vmswitch_in_progress )
-            r = hvm_intblk_rflags_ie;
+            r = hvm_intblk_arch;
         else
         {
             unsigned long intr_info;
 
             __vmread(VM_ENTRY_INTR_INFO, &intr_info);
             if ( intr_info & INTR_INFO_VALID_MASK )
-                r = hvm_intblk_rflags_ie;
+                r = hvm_intblk_arch;
         }
     }
     else if ( nvcpu->nv_vmentry_pending )
-        r = hvm_intblk_rflags_ie;
+        r = hvm_intblk_arch;
 
     return r;
 }
diff --git a/xen/arch/x86/include/asm/hvm/hvm.h b/xen/arch/x86/include/asm/hvm/hvm.h
index 02d9c44373ff..3e03a08a4818 100644
--- a/xen/arch/x86/include/asm/hvm/hvm.h
+++ b/xen/arch/x86/include/asm/hvm/hvm.h
@@ -682,9 +682,17 @@ static inline bool nhvm_vmcx_hap_enabled(struct vcpu *v)
 }
 
 /* interrupt */
-static inline enum hvm_intblk nhvm_interrupt_blocked(struct vcpu *v)
+static inline enum hvm_intblk nhvm_interrupt_blocked(
+    struct vcpu *v, const struct hvm_intack intack)
 {
-    return alternative_call(hvm_funcs.nhvm_intr_blocked, v);
+    enum hvm_intblk intr = alternative_call(hvm_funcs.nhvm_intr_blocked, v);
+
+    /* EFLAGS.IF doesn't mask NMI or #MC. */
+    if ( intr == hvm_intblk_rflags_ie &&
+         (intack.source == hvm_intsrc_nmi || intack.source == hvm_intsrc_mce) )
+        return hvm_intblk_none;
+
+    return intr;
 }
 
 static inline int nhvm_hap_walk_L1_p2m(
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH v2 2/2] x86/nestedsvm: honour L2's EFLAGS.IF when L1 clears V_INTR_MASKING
  2026-10-16 10:00 [PATCH v1] x86/nestedsvm: honour L2's EFLAGS.IF when L1 clears V_INTR_MASKING chunjie.zhu
                   ` (2 preceding siblings ...)
  2026-11-10  8:57 ` [PATCH v2 1/2] x86/hvm: don't let EFLAGS.IF block NMI/#MC due to nested state chunjie.zhu
@ 2026-11-10  8:57 ` chunjie.zhu
  2026-11-10 17:03   ` Ross Lagerwall
  3 siblings, 1 reply; 15+ messages in thread
From: chunjie.zhu @ 2026-11-10  8:57 UTC (permalink / raw)
  To: ross.lagerwall
  Cc: andrew.cooper3, chunjie.zhu, jason.andryuk, jbeulich, roger,
	teddy.astie, xen-devel

From: Chunjie Zhu <chunjie.zhu@citrix.com>

L0 always runs L2 with V_INTR_MASKING set, so a physical interrupt exits to
L0 regardless of L2's EFLAGS.IF.  nsvm_intr_blocked() only blocks when L1
requested V_INTR_MASKING=1 and L1's IF was clear.  With V_INTR_MASKING=0
and L2's IF clear, nestedsvm_vcpu_interrupt() would still inject
VMEXIT(INTR) into L1.

Per the APM (vol.2 rev 3.44 "Physical Interrupt Masking in EFLAGS"), with
V_INTR_MASKING=0 L2's IF masks physical interrupts, and a masked interrupt
must stay pending.  Return NSVM_INTR_MASKED for external interrupts in
that case.

Signed-off-by: Chunjie Zhu <chunjie.zhu@citrix.com>
---
v2:
 * Move the check into nsvm_intr_blocked(), as suggested by Ross.

 xen/arch/x86/hvm/svm/nestedsvm.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/xen/arch/x86/hvm/svm/nestedsvm.c b/xen/arch/x86/hvm/svm/nestedsvm.c
index a93681e9bbcd..e3b2d20ffce2 100644
--- a/xen/arch/x86/hvm/svm/nestedsvm.c
+++ b/xen/arch/x86/hvm/svm/nestedsvm.c
@@ -1222,8 +1222,16 @@ enum hvm_intblk cf_check nsvm_intr_blocked(struct vcpu *v)
     {
         struct vmcb_struct *n2vmcb = nv->nv_n2vmcx;
 
-        if ( svm->ns_hostflags.fields.vintrmask &&
-             !svm->ns_hostflags.fields.rflagsif )
+        /*
+         * With V_INTR_MASKING, L1's EFLAGS.IF masks physical interrupts.
+         * Without it, L2's EFLAGS.IF does (APM vol.2 rev 3.44 "Physical
+         * Interrupt Masking in EFLAGS"); L0 always runs L2 with
+         * V_INTR_MASKING set, so the interrupt would otherwise exit to L0
+         * and be forwarded to L1 even with L2's IF clear.
+         */
+        if ( svm->ns_hostflags.fields.vintrmask
+             ? !svm->ns_hostflags.fields.rflagsif
+             : !(guest_cpu_user_regs()->eflags & X86_EFLAGS_IF) )
             return hvm_intblk_rflags_ie;
 
         /* when l1 guest passes its devices through to the l2 guest
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [PATCH v2 1/2] x86/hvm: don't let EFLAGS.IF block NMI/#MC due to nested state
  2026-11-10  8:57 ` [PATCH v2 1/2] x86/hvm: don't let EFLAGS.IF block NMI/#MC due to nested state chunjie.zhu
@ 2026-11-10 16:58   ` Ross Lagerwall
  2026-11-10 17:36   ` Ross Lagerwall
  1 sibling, 0 replies; 15+ messages in thread
From: Ross Lagerwall @ 2026-11-10 16:58 UTC (permalink / raw)
  To: chunjie.zhu
  Cc: andrew.cooper3, jason.andryuk, jbeulich, roger, teddy.astie,
	xen-devel

On 11/10/26 8:57 AM, chunjie.zhu@citrix.com wrote:
> From: Chunjie Zhu <chunjie.zhu@citrix.com>
> 
> nhvm_interrupt_blocked() reports why an interrupt is blocked, but both
> callers, nestedsvm_vcpu_interrupt() and hvm_interrupt_blocked(), treat any
> reason as blocking every event source.  EFLAGS.IF (hvm_intblk_rflags_ie)
> doesn't mask NMI or #MC, so NMI/#MC get held pending needlessly, e.g. when
> L1 sets V_INTR_MASKING and L1's IF is clear.
> 
> Have nhvm_interrupt_blocked() take the intack and report no block for
> NMI/#MC when the reason is hvm_intblk_rflags_ie.
> 
> nvmx_intr_blocked() used hvm_intblk_rflags_ie for vmexit/vmentry in
> progress and a valid VM_ENTRY_INTR_INFO.  Those block every event source
> and have nothing to do with RFLAGS.IF, so report hvm_intblk_arch instead.
> This doesn't change behaviour as all callers only compare against
> hvm_intblk_none and hvm_intblk_tpr.
> 
> Signed-off-by: Chunjie Zhu <chunjie.zhu@citrix.com>
> ---
> v2:
>   * New patch.  Handle the blocking reason in callers, as suggested by Ross.
>   * Report hvm_intblk_arch from nvmx_intr_blocked() so the NMI/#MC exemption
>     doesn't affect VMX.
> 
>   xen/arch/x86/hvm/hvm.c             |  2 +-
>   xen/arch/x86/hvm/svm/nestedsvm.c   |  2 +-
>   xen/arch/x86/hvm/vmx/intr.c        |  6 +++---
>   xen/arch/x86/include/asm/hvm/hvm.h | 12 ++++++++++--
>   4 files changed, 15 insertions(+), 7 deletions(-)
> 
> diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c
> index ef9c7cd05777..8fd095a4f8e6 100644
> --- a/xen/arch/x86/hvm/hvm.c
> +++ b/xen/arch/x86/hvm/hvm.c
> @@ -3897,7 +3897,7 @@ enum hvm_intblk hvm_interrupt_blocked(struct vcpu *v, struct hvm_intack intack)
>       if ( nestedhvm_enabled(v->domain) ) {
>           enum hvm_intblk intr;
>   
> -        intr = nhvm_interrupt_blocked(v);
> +        intr = nhvm_interrupt_blocked(v, intack);
>           if ( intr != hvm_intblk_none )
>               return intr;
>       }
> diff --git a/xen/arch/x86/hvm/svm/nestedsvm.c b/xen/arch/x86/hvm/svm/nestedsvm.c
> index 66b77c6efe7d..a93681e9bbcd 100644
> --- a/xen/arch/x86/hvm/svm/nestedsvm.c
> +++ b/xen/arch/x86/hvm/svm/nestedsvm.c
> @@ -1496,7 +1496,7 @@ nestedsvm_vcpu_interrupt(struct vcpu *v, const struct hvm_intack intack)
>       uint64_t exitinfo2 = 0;
>       ASSERT(nestedhvm_vcpu_in_guestmode(v));
>   
> -    intr = nhvm_interrupt_blocked(v);
> +    intr = nhvm_interrupt_blocked(v, intack);
>       if ( intr != hvm_intblk_none )
>           return NSVM_INTR_MASKED;
>   
> diff --git a/xen/arch/x86/hvm/vmx/intr.c b/xen/arch/x86/hvm/vmx/intr.c
> index 6c220c4c2e49..38be2387739d 100644
> --- a/xen/arch/x86/hvm/vmx/intr.c
> +++ b/xen/arch/x86/hvm/vmx/intr.c
> @@ -142,18 +142,18 @@ enum hvm_intblk cf_check nvmx_intr_blocked(struct vcpu *v)
>       {
>           if ( nvcpu->nv_vmexit_pending ||
>                nvcpu->nv_vmswitch_in_progress )
> -            r = hvm_intblk_rflags_ie;
> +            r = hvm_intblk_arch;
>           else
>           {
>               unsigned long intr_info;
>   
>               __vmread(VM_ENTRY_INTR_INFO, &intr_info);
>               if ( intr_info & INTR_INFO_VALID_MASK )
> -                r = hvm_intblk_rflags_ie;
> +                r = hvm_intblk_arch;
>           }
>       }
>       else if ( nvcpu->nv_vmentry_pending )
> -        r = hvm_intblk_rflags_ie;
> +        r = hvm_intblk_arch;
>   
>       return r;
>   }
> diff --git a/xen/arch/x86/include/asm/hvm/hvm.h b/xen/arch/x86/include/asm/hvm/hvm.h
> index 02d9c44373ff..3e03a08a4818 100644
> --- a/xen/arch/x86/include/asm/hvm/hvm.h
> +++ b/xen/arch/x86/include/asm/hvm/hvm.h
> @@ -682,9 +682,17 @@ static inline bool nhvm_vmcx_hap_enabled(struct vcpu *v)
>   }
>   
>   /* interrupt */
> -static inline enum hvm_intblk nhvm_interrupt_blocked(struct vcpu *v)
> +static inline enum hvm_intblk nhvm_interrupt_blocked(
> +    struct vcpu *v, const struct hvm_intack intack)
>   {
> -    return alternative_call(hvm_funcs.nhvm_intr_blocked, v);
> +    enum hvm_intblk intr = alternative_call(hvm_funcs.nhvm_intr_blocked, v);
> +
> +    /* EFLAGS.IF doesn't mask NMI or #MC. */
> +    if ( intr == hvm_intblk_rflags_ie &&
> +         (intack.source == hvm_intsrc_nmi || intack.source == hvm_intsrc_mce) )
> +        return hvm_intblk_none;
> +
> +    return intr;
>   }
>   
>   static inline int nhvm_hap_walk_L1_p2m(

nvmx_intr_intercept() circumvents this logic by calling
nvmx_intr_blocked() directly. Perhaps you need to replace it with a call
to nhvm_interrupt_blocked() (like the nested SVM code uses) so the logic
applies there too?

Ross


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v2 2/2] x86/nestedsvm: honour L2's EFLAGS.IF when L1 clears V_INTR_MASKING
  2026-11-10  8:57 ` [PATCH v2 2/2] x86/nestedsvm: honour L2's EFLAGS.IF when L1 clears V_INTR_MASKING chunjie.zhu
@ 2026-11-10 17:03   ` Ross Lagerwall
  2026-11-12 11:35     ` [PATCH v3 0/3] x86/hvm: fix EFLAGS.IF handling of pending events with nested virt chunjie.zhu
  0 siblings, 1 reply; 15+ messages in thread
From: Ross Lagerwall @ 2026-11-10 17:03 UTC (permalink / raw)
  To: chunjie.zhu
  Cc: andrew.cooper3, jason.andryuk, jbeulich, roger, teddy.astie,
	xen-devel

On 11/10/26 8:57 AM, chunjie.zhu@citrix.com wrote:
> From: Chunjie Zhu <chunjie.zhu@citrix.com>
> 
> L0 always runs L2 with V_INTR_MASKING set, so a physical interrupt exits to
> L0 regardless of L2's EFLAGS.IF.  nsvm_intr_blocked() only blocks when L1
> requested V_INTR_MASKING=1 and L1's IF was clear.  With V_INTR_MASKING=0
> and L2's IF clear, nestedsvm_vcpu_interrupt() would still inject
> VMEXIT(INTR) into L1.
> 
> Per the APM (vol.2 rev 3.44 "Physical Interrupt Masking in EFLAGS"), with
> V_INTR_MASKING=0 L2's IF masks physical interrupts, and a masked interrupt
> must stay pending.  Return NSVM_INTR_MASKED for external interrupts in
> that case.
> 
> Signed-off-by: Chunjie Zhu <chunjie.zhu@citrix.com>
> ---
> v2:
>   * Move the check into nsvm_intr_blocked(), as suggested by Ross.
> 
>   xen/arch/x86/hvm/svm/nestedsvm.c | 12 ++++++++++--
>   1 file changed, 10 insertions(+), 2 deletions(-)
> 
> diff --git a/xen/arch/x86/hvm/svm/nestedsvm.c b/xen/arch/x86/hvm/svm/nestedsvm.c
> index a93681e9bbcd..e3b2d20ffce2 100644
> --- a/xen/arch/x86/hvm/svm/nestedsvm.c
> +++ b/xen/arch/x86/hvm/svm/nestedsvm.c
> @@ -1222,8 +1222,16 @@ enum hvm_intblk cf_check nsvm_intr_blocked(struct vcpu *v)
>       {
>           struct vmcb_struct *n2vmcb = nv->nv_n2vmcx;
>   
> -        if ( svm->ns_hostflags.fields.vintrmask &&
> -             !svm->ns_hostflags.fields.rflagsif )
> +        /*
> +         * With V_INTR_MASKING, L1's EFLAGS.IF masks physical interrupts.
> +         * Without it, L2's EFLAGS.IF does (APM vol.2 rev 3.44 "Physical
> +         * Interrupt Masking in EFLAGS"); L0 always runs L2 with
> +         * V_INTR_MASKING set, so the interrupt would otherwise exit to L0
> +         * and be forwarded to L1 even with L2's IF clear.
> +         */
> +        if ( svm->ns_hostflags.fields.vintrmask
> +             ? !svm->ns_hostflags.fields.rflagsif
> +             : !(guest_cpu_user_regs()->eflags & X86_EFLAGS_IF) )
>               return hvm_intblk_rflags_ie;
>   
>           /* when l1 guest passes its devices through to the l2 guest

This only works if the function is called with v == current. I think it
is, but it should probably have an assert to confirm this (like
hvm_interrupt_blocked()).

Also note that this series needs rebasing on top of the latest staging
since it doesn't apply cleanly.

Ross


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v2 1/2] x86/hvm: don't let EFLAGS.IF block NMI/#MC due to nested state
  2026-11-10  8:57 ` [PATCH v2 1/2] x86/hvm: don't let EFLAGS.IF block NMI/#MC due to nested state chunjie.zhu
  2026-11-10 16:58   ` Ross Lagerwall
@ 2026-11-10 17:36   ` Ross Lagerwall
  1 sibling, 0 replies; 15+ messages in thread
From: Ross Lagerwall @ 2026-11-10 17:36 UTC (permalink / raw)
  To: chunjie.zhu
  Cc: andrew.cooper3, jason.andryuk, jbeulich, roger, teddy.astie,
	xen-devel

On 11/10/26 8:57 AM, chunjie.zhu@citrix.com wrote:
> From: Chunjie Zhu <chunjie.zhu@citrix.com>
> 
> nhvm_interrupt_blocked() reports why an interrupt is blocked, but both
> callers, nestedsvm_vcpu_interrupt() and hvm_interrupt_blocked(), treat any
> reason as blocking every event source.  EFLAGS.IF (hvm_intblk_rflags_ie)
> doesn't mask NMI or #MC, so NMI/#MC get held pending needlessly, e.g. when
> L1 sets V_INTR_MASKING and L1's IF is clear.
> 
> Have nhvm_interrupt_blocked() take the intack and report no block for
> NMI/#MC when the reason is hvm_intblk_rflags_ie.
> 
> nvmx_intr_blocked() used hvm_intblk_rflags_ie for vmexit/vmentry in
> progress and a valid VM_ENTRY_INTR_INFO.  Those block every event source
> and have nothing to do with RFLAGS.IF, so report hvm_intblk_arch instead.
> This doesn't change behaviour as all callers only compare against
> hvm_intblk_none and hvm_intblk_tpr.
> 
> Signed-off-by: Chunjie Zhu <chunjie.zhu@citrix.com>
> ---
> v2:
>   * New patch.  Handle the blocking reason in callers, as suggested by Ross.
>   * Report hvm_intblk_arch from nvmx_intr_blocked() so the NMI/#MC exemption
>     doesn't affect VMX.
> 
>   xen/arch/x86/hvm/hvm.c             |  2 +-
>   xen/arch/x86/hvm/svm/nestedsvm.c   |  2 +-
>   xen/arch/x86/hvm/vmx/intr.c        |  6 +++---
>   xen/arch/x86/include/asm/hvm/hvm.h | 12 ++++++++++--
>   4 files changed, 15 insertions(+), 7 deletions(-)
> 
> diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c
> index ef9c7cd05777..8fd095a4f8e6 100644
> --- a/xen/arch/x86/hvm/hvm.c
> +++ b/xen/arch/x86/hvm/hvm.c
> @@ -3897,7 +3897,7 @@ enum hvm_intblk hvm_interrupt_blocked(struct vcpu *v, struct hvm_intack intack)
>       if ( nestedhvm_enabled(v->domain) ) {
>           enum hvm_intblk intr;
>   
> -        intr = nhvm_interrupt_blocked(v);
> +        intr = nhvm_interrupt_blocked(v, intack);
>           if ( intr != hvm_intblk_none )
>               return intr;
>       }
> diff --git a/xen/arch/x86/hvm/svm/nestedsvm.c b/xen/arch/x86/hvm/svm/nestedsvm.c
> index 66b77c6efe7d..a93681e9bbcd 100644
> --- a/xen/arch/x86/hvm/svm/nestedsvm.c
> +++ b/xen/arch/x86/hvm/svm/nestedsvm.c
> @@ -1496,7 +1496,7 @@ nestedsvm_vcpu_interrupt(struct vcpu *v, const struct hvm_intack intack)
>       uint64_t exitinfo2 = 0;
>       ASSERT(nestedhvm_vcpu_in_guestmode(v));
>   
> -    intr = nhvm_interrupt_blocked(v);
> +    intr = nhvm_interrupt_blocked(v, intack);
>       if ( intr != hvm_intblk_none )
>           return NSVM_INTR_MASKED;
>   
> diff --git a/xen/arch/x86/hvm/vmx/intr.c b/xen/arch/x86/hvm/vmx/intr.c
> index 6c220c4c2e49..38be2387739d 100644
> --- a/xen/arch/x86/hvm/vmx/intr.c
> +++ b/xen/arch/x86/hvm/vmx/intr.c
> @@ -142,18 +142,18 @@ enum hvm_intblk cf_check nvmx_intr_blocked(struct vcpu *v)
>       {
>           if ( nvcpu->nv_vmexit_pending ||
>                nvcpu->nv_vmswitch_in_progress )
> -            r = hvm_intblk_rflags_ie;
> +            r = hvm_intblk_arch;
>           else
>           {
>               unsigned long intr_info;
>   
>               __vmread(VM_ENTRY_INTR_INFO, &intr_info);
>               if ( intr_info & INTR_INFO_VALID_MASK )
> -                r = hvm_intblk_rflags_ie;
> +                r = hvm_intblk_arch;
>           }
>       }
>       else if ( nvcpu->nv_vmentry_pending )
> -        r = hvm_intblk_rflags_ie;
> +        r = hvm_intblk_arch;
>   
>       return r;
>   }
> diff --git a/xen/arch/x86/include/asm/hvm/hvm.h b/xen/arch/x86/include/asm/hvm/hvm.h
> index 02d9c44373ff..3e03a08a4818 100644
> --- a/xen/arch/x86/include/asm/hvm/hvm.h
> +++ b/xen/arch/x86/include/asm/hvm/hvm.h
> @@ -682,9 +682,17 @@ static inline bool nhvm_vmcx_hap_enabled(struct vcpu *v)
>   }
>   
>   /* interrupt */
> -static inline enum hvm_intblk nhvm_interrupt_blocked(struct vcpu *v)
> +static inline enum hvm_intblk nhvm_interrupt_blocked(
> +    struct vcpu *v, const struct hvm_intack intack)
>   {
> -    return alternative_call(hvm_funcs.nhvm_intr_blocked, v);
> +    enum hvm_intblk intr = alternative_call(hvm_funcs.nhvm_intr_blocked, v);
> +
> +    /* EFLAGS.IF doesn't mask NMI or #MC. */
> +    if ( intr == hvm_intblk_rflags_ie &&
> +         (intack.source == hvm_intsrc_nmi || intack.source == hvm_intsrc_mce) )
> +        return hvm_intblk_none;
> +
> +    return intr;
>   }

Not specifically for this patch, but I note that hvm_interrupt_blocked()
similarly checks the interrupt flag but only ignores it for NMIs, not MCEs.
That should probably be fixed.

Ross


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH v3 0/3] x86/hvm: fix EFLAGS.IF handling of pending events with nested virt
  2026-11-10 17:03   ` Ross Lagerwall
@ 2026-11-12 11:35     ` chunjie.zhu
  2026-11-12 11:35       ` [PATCH v3 1/3] x86/hvm: don't let EFLAGS.IF block NMI/#MC due to nested state chunjie.zhu
                         ` (2 more replies)
  0 siblings, 3 replies; 15+ messages in thread
From: chunjie.zhu @ 2026-11-12 11:35 UTC (permalink / raw)
  To: ross.lagerwall
  Cc: andrew.cooper3, chunjie.zhu, jason.andryuk, jbeulich, roger,
	teddy.astie, xen-devel

From: Chunjie Zhu <chunjie.zhu@citrix.com>

Patches 1 and 3 stop EFLAGS.IF from holding off NMI/#MC when nested state
is involved (and in hvm_interrupt_blocked() for #MC).  Patch 2 makes
nested SVM honour L2's EFLAGS.IF when L1 clears V_INTR_MASKING.

v3:
 * Address Ross's review comments on v2, see individual patches.
 * New patch 3/3, suggested by Ross.
 * Rebased on staging.


Chunjie Zhu (3):
  x86/hvm: don't let EFLAGS.IF block NMI/#MC due to nested state
  x86/nestedsvm: honour L2's EFLAGS.IF when L1 clears V_INTR_MASKING
  x86/hvm: don't let EFLAGS.IF block #MC in hvm_interrupt_blocked

 xen/arch/x86/hvm/hvm.c             |  4 +++-
 xen/arch/x86/hvm/svm/nestedsvm.c   | 14 ++++++++++++--
 xen/arch/x86/hvm/vmx/intr.c        |  8 ++++----
 xen/arch/x86/include/asm/hvm/hvm.h | 12 ++++++++++--
 4 files changed, 29 insertions(+), 9 deletions(-)

-- 
2.34.1



^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH v3 1/3] x86/hvm: don't let EFLAGS.IF block NMI/#MC due to nested state
  2026-11-12 11:35     ` [PATCH v3 0/3] x86/hvm: fix EFLAGS.IF handling of pending events with nested virt chunjie.zhu
@ 2026-11-12 11:35       ` chunjie.zhu
  2026-11-12 11:35       ` [PATCH v3 2/3] x86/nestedsvm: honour L2's EFLAGS.IF when L1 clears V_INTR_MASKING chunjie.zhu
  2026-11-12 11:35       ` [PATCH v3 3/3] x86/hvm: don't let EFLAGS.IF block #MC in hvm_interrupt_blocked chunjie.zhu
  2 siblings, 0 replies; 15+ messages in thread
From: chunjie.zhu @ 2026-11-12 11:35 UTC (permalink / raw)
  To: ross.lagerwall
  Cc: andrew.cooper3, chunjie.zhu, jason.andryuk, jbeulich, roger,
	teddy.astie, xen-devel

From: Chunjie Zhu <chunjie.zhu@citrix.com>

nhvm_interrupt_blocked() reports why an interrupt is blocked, but both
callers, nestedsvm_vcpu_interrupt() and hvm_interrupt_blocked(), treat any
reason as blocking every event source.  EFLAGS.IF (hvm_intblk_rflags_ie)
doesn't mask NMI or #MC, so NMI/#MC get held pending needlessly, e.g. when
L1 sets V_INTR_MASKING and L1's IF is clear.

Have nhvm_interrupt_blocked() take the intack and report no block for
NMI/#MC when the reason is hvm_intblk_rflags_ie.

nvmx_intr_blocked() used hvm_intblk_rflags_ie for vmexit/vmentry in
progress and a valid VM_ENTRY_INTR_INFO.  Those block every event source
and have nothing to do with RFLAGS.IF, so report hvm_intblk_arch instead.
This doesn't change behaviour as all callers only compare against
hvm_intblk_none and hvm_intblk_tpr.

Signed-off-by: Chunjie Zhu <chunjie.zhu@citrix.com>
---
v3:
 * Use nhvm_interrupt_blocked() in nvmx_intr_intercept() instead of calling
   nvmx_intr_blocked() directly, so the NMI/#MC exemption applies to VMX
   too, as suggested by Ross.
 * Rebased on staging.

 xen/arch/x86/hvm/hvm.c             |  2 +-
 xen/arch/x86/hvm/svm/nestedsvm.c   |  2 +-
 xen/arch/x86/hvm/vmx/intr.c        |  8 ++++----
 xen/arch/x86/include/asm/hvm/hvm.h | 12 ++++++++++--
 4 files changed, 16 insertions(+), 8 deletions(-)

diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c
index ef9c7cd05777..8fd095a4f8e6 100644
--- a/xen/arch/x86/hvm/hvm.c
+++ b/xen/arch/x86/hvm/hvm.c
@@ -3897,7 +3897,7 @@ enum hvm_intblk hvm_interrupt_blocked(struct vcpu *v, struct hvm_intack intack)
     if ( nestedhvm_enabled(v->domain) ) {
         enum hvm_intblk intr;
 
-        intr = nhvm_interrupt_blocked(v);
+        intr = nhvm_interrupt_blocked(v, intack);
         if ( intr != hvm_intblk_none )
             return intr;
     }
diff --git a/xen/arch/x86/hvm/svm/nestedsvm.c b/xen/arch/x86/hvm/svm/nestedsvm.c
index d970869cfd96..40c7717c5572 100644
--- a/xen/arch/x86/hvm/svm/nestedsvm.c
+++ b/xen/arch/x86/hvm/svm/nestedsvm.c
@@ -1417,7 +1417,7 @@ nestedsvm_vcpu_interrupt(struct vcpu *v, const struct hvm_intack intack)
     uint64_t exitinfo2 = 0;
     ASSERT(nestedhvm_vcpu_in_guestmode(v));
 
-    intr = nhvm_interrupt_blocked(v);
+    intr = nhvm_interrupt_blocked(v, intack);
     if ( intr != hvm_intblk_none )
         return NSVM_INTR_MASKED;
 
diff --git a/xen/arch/x86/hvm/vmx/intr.c b/xen/arch/x86/hvm/vmx/intr.c
index 6c220c4c2e49..a6099420fda5 100644
--- a/xen/arch/x86/hvm/vmx/intr.c
+++ b/xen/arch/x86/hvm/vmx/intr.c
@@ -142,18 +142,18 @@ enum hvm_intblk cf_check nvmx_intr_blocked(struct vcpu *v)
     {
         if ( nvcpu->nv_vmexit_pending ||
              nvcpu->nv_vmswitch_in_progress )
-            r = hvm_intblk_rflags_ie;
+            r = hvm_intblk_arch;
         else
         {
             unsigned long intr_info;
 
             __vmread(VM_ENTRY_INTR_INFO, &intr_info);
             if ( intr_info & INTR_INFO_VALID_MASK )
-                r = hvm_intblk_rflags_ie;
+                r = hvm_intblk_arch;
         }
     }
     else if ( nvcpu->nv_vmentry_pending )
-        r = hvm_intblk_rflags_ie;
+        r = hvm_intblk_arch;
 
     return r;
 }
@@ -167,7 +167,7 @@ static int nvmx_intr_intercept(struct vcpu *v, struct hvm_intack intack)
          hvm_interrupt_blocked(v, intack) == hvm_intblk_tpr )
         return 1;
 
-    if ( nvmx_intr_blocked(v) != hvm_intblk_none )
+    if ( nhvm_interrupt_blocked(v, intack) != hvm_intblk_none )
     {
         vmx_enable_intr_window(v, intack);
         return 1;
diff --git a/xen/arch/x86/include/asm/hvm/hvm.h b/xen/arch/x86/include/asm/hvm/hvm.h
index 9bf2064925fa..b10937e229cf 100644
--- a/xen/arch/x86/include/asm/hvm/hvm.h
+++ b/xen/arch/x86/include/asm/hvm/hvm.h
@@ -678,9 +678,17 @@ static inline bool nhvm_vmcx_hap_enabled(struct vcpu *v)
 }
 
 /* interrupt */
-static inline enum hvm_intblk nhvm_interrupt_blocked(struct vcpu *v)
+static inline enum hvm_intblk nhvm_interrupt_blocked(
+    struct vcpu *v, const struct hvm_intack intack)
 {
-    return alternative_call(hvm_funcs.nhvm_intr_blocked, v);
+    enum hvm_intblk intr = alternative_call(hvm_funcs.nhvm_intr_blocked, v);
+
+    /* EFLAGS.IF doesn't mask NMI or #MC. */
+    if ( intr == hvm_intblk_rflags_ie &&
+         (intack.source == hvm_intsrc_nmi || intack.source == hvm_intsrc_mce) )
+        return hvm_intblk_none;
+
+    return intr;
 }
 
 static inline int nhvm_hap_walk_L1_p2m(
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH v3 2/3] x86/nestedsvm: honour L2's EFLAGS.IF when L1 clears V_INTR_MASKING
  2026-11-12 11:35     ` [PATCH v3 0/3] x86/hvm: fix EFLAGS.IF handling of pending events with nested virt chunjie.zhu
  2026-11-12 11:35       ` [PATCH v3 1/3] x86/hvm: don't let EFLAGS.IF block NMI/#MC due to nested state chunjie.zhu
@ 2026-11-12 11:35       ` chunjie.zhu
  2026-11-12 11:35       ` [PATCH v3 3/3] x86/hvm: don't let EFLAGS.IF block #MC in hvm_interrupt_blocked chunjie.zhu
  2 siblings, 0 replies; 15+ messages in thread
From: chunjie.zhu @ 2026-11-12 11:35 UTC (permalink / raw)
  To: ross.lagerwall
  Cc: andrew.cooper3, chunjie.zhu, jason.andryuk, jbeulich, roger,
	teddy.astie, xen-devel

From: Chunjie Zhu <chunjie.zhu@citrix.com>

L0 always runs L2 with V_INTR_MASKING set, so a physical interrupt exits to
L0 regardless of L2's EFLAGS.IF.  nsvm_intr_blocked() only blocks when L1
requested V_INTR_MASKING=1 and L1's IF was clear.  With V_INTR_MASKING=0
and L2's IF clear, nestedsvm_vcpu_interrupt() would still inject
VMEXIT(INTR) into L1.

Per the APM (vol.2 rev 3.44 "Physical Interrupt Masking in EFLAGS"), with
V_INTR_MASKING=0 L2's IF masks physical interrupts, and a masked interrupt
must stay pending.  Return NSVM_INTR_MASKED for external interrupts in
that case.

Signed-off-by: Chunjie Zhu <chunjie.zhu@citrix.com>
---
v3:
 * Add ASSERT(v == current), as suggested by Ross.
 * Rebased on staging.

 xen/arch/x86/hvm/svm/nestedsvm.c | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/xen/arch/x86/hvm/svm/nestedsvm.c b/xen/arch/x86/hvm/svm/nestedsvm.c
index 40c7717c5572..706aed8ed026 100644
--- a/xen/arch/x86/hvm/svm/nestedsvm.c
+++ b/xen/arch/x86/hvm/svm/nestedsvm.c
@@ -1150,6 +1150,7 @@ enum hvm_intblk cf_check nsvm_intr_blocked(struct vcpu *v)
     struct nestedsvm *svm = &vcpu_nestedsvm(v);
     struct nestedvcpu *nv = &vcpu_nestedhvm(v);
 
+    ASSERT(v == current);
     ASSERT(nestedhvm_enabled(v->domain));
 
     if ( !nestedsvm_gif_isset(v) )
@@ -1159,7 +1160,16 @@ enum hvm_intblk cf_check nsvm_intr_blocked(struct vcpu *v)
     {
         struct vmcb_struct *n2vmcb = nv->nv_n2vmcx;
 
-        if ( svm->ns_hostflags.vintrmask && !svm->ns_hostflags.rflagsif )
+        /*
+         * With V_INTR_MASKING, L1's EFLAGS.IF masks physical interrupts.
+         * Without it, L2's EFLAGS.IF does (APM vol.2 rev 3.44 "Physical
+         * Interrupt Masking in EFLAGS"); L0 always runs L2 with
+         * V_INTR_MASKING set, so the interrupt would otherwise exit to L0
+         * and be forwarded to L1 even with L2's IF clear.
+         */
+        if ( svm->ns_hostflags.vintrmask
+             ? !svm->ns_hostflags.rflagsif
+             : !(guest_cpu_user_regs()->eflags & X86_EFLAGS_IF) )
             return hvm_intblk_rflags_ie;
 
         /* when l1 guest passes its devices through to the l2 guest
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH v3 3/3] x86/hvm: don't let EFLAGS.IF block #MC in hvm_interrupt_blocked
  2026-11-12 11:35     ` [PATCH v3 0/3] x86/hvm: fix EFLAGS.IF handling of pending events with nested virt chunjie.zhu
  2026-11-12 11:35       ` [PATCH v3 1/3] x86/hvm: don't let EFLAGS.IF block NMI/#MC due to nested state chunjie.zhu
  2026-11-12 11:35       ` [PATCH v3 2/3] x86/nestedsvm: honour L2's EFLAGS.IF when L1 clears V_INTR_MASKING chunjie.zhu
@ 2026-11-12 11:35       ` chunjie.zhu
  2026-11-12 14:15         ` Jan Beulich
  2 siblings, 1 reply; 15+ messages in thread
From: chunjie.zhu @ 2026-11-12 11:35 UTC (permalink / raw)
  To: ross.lagerwall
  Cc: andrew.cooper3, chunjie.zhu, jason.andryuk, jbeulich, roger,
	teddy.astie, xen-devel

From: Chunjie Zhu <chunjie.zhu@citrix.com>

EFLAGS.IF doesn't mask NMI or #MC, but hvm_interrupt_blocked() only
exempted NMI from the EFLAGS.IF check, so a pending #MC was held off
while the guest had interrupts disabled.

Exempt hvm_intsrc_mce as well.

Signed-off-by: Chunjie Zhu <chunjie.zhu@citrix.com>
---
v3:
 * New patch.  hvm_interrupt_blocked() only exempted NMI from the EFLAGS.IF
   check, not #MC, as noted by Ross.

 xen/arch/x86/hvm/hvm.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c
index 8fd095a4f8e6..710647626427 100644
--- a/xen/arch/x86/hvm/hvm.c
+++ b/xen/arch/x86/hvm/hvm.c
@@ -3902,7 +3902,9 @@ enum hvm_intblk hvm_interrupt_blocked(struct vcpu *v, struct hvm_intack intack)
             return intr;
     }
 
+    /* EFLAGS.IF doesn't mask NMI or #MC. */
     if ( (intack.source != hvm_intsrc_nmi) &&
+         (intack.source != hvm_intsrc_mce) &&
          !(guest_cpu_user_regs()->eflags & X86_EFLAGS_IF) )
         return hvm_intblk_rflags_ie;
 
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [PATCH v3 3/3] x86/hvm: don't let EFLAGS.IF block #MC in hvm_interrupt_blocked
  2026-11-12 11:35       ` [PATCH v3 3/3] x86/hvm: don't let EFLAGS.IF block #MC in hvm_interrupt_blocked chunjie.zhu
@ 2026-11-12 14:15         ` Jan Beulich
  0 siblings, 0 replies; 15+ messages in thread
From: Jan Beulich @ 2026-11-12 14:15 UTC (permalink / raw)
  To: chunjie.zhu
  Cc: andrew.cooper3, jason.andryuk, roger, teddy.astie, xen-devel,
	ross.lagerwall

On 12.11.2026 12:35, chunjie.zhu@citrix.com wrote:
> From: Chunjie Zhu <chunjie.zhu@citrix.com>
> 
> EFLAGS.IF doesn't mask NMI or #MC, but hvm_interrupt_blocked() only
> exempted NMI from the EFLAGS.IF check, so a pending #MC was held off
> while the guest had interrupts disabled.
> 
> Exempt hvm_intsrc_mce as well.
> 
> Signed-off-by: Chunjie Zhu <chunjie.zhu@citrix.com>
> ---
> v3:
>  * New patch.  hvm_interrupt_blocked() only exempted NMI from the EFLAGS.IF
>    check, not #MC, as noted by Ross.

I.e. Reported-by: wants adding? (Happy to do so while committing.)

Reviewed-by: Jan Beulich <jbeulich@suse.com>

Jan


^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2026-11-12 14:16 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-16 10:00 [PATCH v1] x86/nestedsvm: honour L2's EFLAGS.IF when L1 clears V_INTR_MASKING chunjie.zhu
2026-10-29 17:20 ` Ross Lagerwall
2026-11-03 10:11   ` chunjie.zhu
2026-11-03 17:07     ` Ross Lagerwall
2026-11-10  8:57 ` [PATCH v2 0/2] " chunjie.zhu
2026-11-10  8:57 ` [PATCH v2 1/2] x86/hvm: don't let EFLAGS.IF block NMI/#MC due to nested state chunjie.zhu
2026-11-10 16:58   ` Ross Lagerwall
2026-11-10 17:36   ` Ross Lagerwall
2026-11-10  8:57 ` [PATCH v2 2/2] x86/nestedsvm: honour L2's EFLAGS.IF when L1 clears V_INTR_MASKING chunjie.zhu
2026-11-10 17:03   ` Ross Lagerwall
2026-11-12 11:35     ` [PATCH v3 0/3] x86/hvm: fix EFLAGS.IF handling of pending events with nested virt chunjie.zhu
2026-11-12 11:35       ` [PATCH v3 1/3] x86/hvm: don't let EFLAGS.IF block NMI/#MC due to nested state chunjie.zhu
2026-11-12 11:35       ` [PATCH v3 2/3] x86/nestedsvm: honour L2's EFLAGS.IF when L1 clears V_INTR_MASKING chunjie.zhu
2026-11-12 11:35       ` [PATCH v3 3/3] x86/hvm: don't let EFLAGS.IF block #MC in hvm_interrupt_blocked chunjie.zhu
2026-11-12 14:15         ` Jan Beulich

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.