* 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* 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 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 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 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* [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