x86/nestedsvm: honour L2's EFLAGS.IF when L1 clears V_INTR_MASKING
PATCH 1/1 x86/nestedsvm: honour L2's EFLAGS.IF when L1 clears V_INTR_MASKING
From: Chunjie Zhu <[email protected]>
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.
--- xen/arch/x86/hvm/svm/nestedsvm.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
xen/arch/x86/hvm/svm/nestedsvm.c +11 -0
@@ -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 ;
Signature · 1 line
2.34.1
Quoted 41 lines
From: Xen-devel <[email protected]> on behalf of [email protected] <[email protected]>
Sent: Friday, October 16, 2026 11:00
To: [email protected] <[email protected]>
Cc: [email protected] <[email protected]>; Andrew Cooper <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; Chunjie Zhu (朱春杰) <[email protected]>
Subject: [PATCH v1] x86/nestedsvm: honour L2's EFLAGS.IF when L1 clearsV_INTR_MASKINGFrom: Chunjie Zhu <[email protected]>
L0 always runs L2 with
V_INTR_MASKINGset, 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_MASKEDfor external interrupts in that case.Signed-off-by Chunjie Zhu <[email protected]>---
xen/arch/x86/hvm/svm/nestedsvm.c | 11 +++++++++++1 file changed, 11 insertions(+)xen/arch/x86/hvm/svm/nestedsvm.c +0
-0@@ -1514,6 +1514,17 @@ nestedsvm_vcpu_interrupt(struct vcpu *v, const struct hvm_intack intack)case
hvm_intsrc_pic:
casehvm_intsrc_lapic:
casehvm_intsrc_vector:
+ /*
+ * WithoutV_INTR_MASKING, L2's EFLAGS.IF masks physical interrupts + * (APM vol.2 rev 3.44 "Physical Interrupt Masking in EFLAGS"). L0 + * always runs L2 withV_INTR_MASKINGset, 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 &&
Quoted 9 lines
+ !(guest_cpu_user_regs()->eflags &
X86_EFLAGS_IF) ) + returnNSVM_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):
xen/arch/x86/hvm/svm/nestedsvm.c +3 -2
@@ -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
Quoted 3 lines · Ross Lagerwall
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
Quoted 24 lines
From: Chunjie Zhu (朱春杰) <[email protected]>
Sent: Tuesday, November 03, 2026 10:11
To: Ross Lagerwall <[email protected]>
Cc: Andrew Cooper <[email protected]>; Chunjie Zhu (朱春杰) <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>
Subject: Re: [PATCH v1] x86/nestedsvm: honour L2's EFLAGS.IF when L1 clearsV_INTR_MASKINGQuoted 3 lines · Ross Lagerwall
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_MASKINGcontrols 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.
Quoted 5 lines
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 innsvm_intr_blocked(), I can instead pass the intack down (changingnhvm_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
v2 cover x86/nestedsvm: honour L2's EFLAGS.IF when L1 clears V_INTR_MASKING
In reply to [email protected]From: Chunjie Zhu <[email protected]>
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(-)
Signature · 1 line
2.34.1
PATCH v2 1/2 x86/hvm: don't let EFLAGS.IF block NMI/#MC due to nested state
In reply to [email protected]From: Chunjie Zhu <[email protected]>
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.
---
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(-)
xen/arch/x86/hvm/hvm.c +1 -1
@@ -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; }
xen/arch/x86/hvm/svm/nestedsvm.c +1 -1
@@ -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 ;
xen/arch/x86/hvm/vmx/intr.c +3 -3
@@ -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; }
xen/arch/x86/include/asm/hvm/hvm.h +10 -2
@@ -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(
Signature · 1 line
2.34.1
Re: [PATCH v2 1/2] x86/hvm: don't let EFLAGS.IF block NMI/#MC due to nested state
Quoted 96 lines · On 11/10/26 8:57 AM, [email protected] wrote:
From: Chunjie Zhu <[email protected]>
nhvm_interrupt_blocked()reports why an interrupt is blocked, but both callers,nestedsvm_vcpu_interrupt()andhvm_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 setsV_INTR_MASKINGand L1's IF is clear.Have
nhvm_interrupt_blocked()take the intack and report no block for NMI/#MC when the reason ishvm_intblk_rflags_ie.
nvmx_intr_blocked()usedhvm_intblk_rflags_iefor vmexit/vmentry in progress and a validVM_ENTRY_INTR_INFO. Those block every event source and have nothing to do with RFLAGS.IF, so reporthvm_intblk_archinstead. This doesn't change behaviour as all callers only compare againsthvm_intblk_noneandhvm_intblk_tpr.Signed-off-by Chunjie Zhu <[email protected]>---
v2:
* New patch. Handle the blocking reason in callers, as suggested by Ross.
* Reporthvm_intblk_archfromnvmx_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(-)xen/arch/x86/hvm/hvm.c +1
-1@@ -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; }xen/arch/x86/hvm/svm/nestedsvm.c +1
-1@@ -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 ;xen/arch/x86/hvm/vmx/intr.c +3
-3@@ -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; }xen/arch/x86/include/asm/hvm/hvm.h +3
-2@@ -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);
xen/arch/x86/include/asm/hvm/hvm.h static inline bool nhvm_vmcx_hap_enabled(struct vcpu *v) · line 698
Quoted 10 lines
xen/arch/x86/include/asm/hvm/hvm.h +7
-0+ +/* 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
Re: [PATCH v2 1/2] x86/hvm: don't let EFLAGS.IF block NMI/#MC due to nested state
In reply to [email protected]Quoted 94 lines · On 11/10/26 8:57 AM, [email protected] wrote:
From: Chunjie Zhu <[email protected]>
nhvm_interrupt_blocked()reports why an interrupt is blocked, but both callers,nestedsvm_vcpu_interrupt()andhvm_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 setsV_INTR_MASKINGand L1's IF is clear.Have
nhvm_interrupt_blocked()take the intack and report no block for NMI/#MC when the reason ishvm_intblk_rflags_ie.
nvmx_intr_blocked()usedhvm_intblk_rflags_iefor vmexit/vmentry in progress and a validVM_ENTRY_INTR_INFO. Those block every event source and have nothing to do with RFLAGS.IF, so reporthvm_intblk_archinstead. This doesn't change behaviour as all callers only compare againsthvm_intblk_noneandhvm_intblk_tpr.Signed-off-by Chunjie Zhu <[email protected]>---
v2:
* New patch. Handle the blocking reason in callers, as suggested by Ross.
* Reporthvm_intblk_archfromnvmx_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(-)xen/arch/x86/hvm/hvm.c +1
-1@@ -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; }xen/arch/x86/hvm/svm/nestedsvm.c +1
-1@@ -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 ;xen/arch/x86/hvm/vmx/intr.c +3
-3@@ -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; }xen/arch/x86/include/asm/hvm/hvm.h +2
-1@@ -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) {
xen/arch/x86/include/asm/hvm/hvm.h static inline bool nhvm_vmcx_hap_enabled(struct vcpu *v) · line 696
Quoted 10 lines
xen/arch/x86/include/asm/hvm/hvm.h +8
-1-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
PATCH v2 2/2 x86/nestedsvm: honour L2's EFLAGS.IF when L1 clears V_INTR_MASKING
In reply to [email protected]From: Chunjie Zhu <[email protected]>
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.
---
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(-)
xen/arch/x86/hvm/svm/nestedsvm.c +10 -2
@@ -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
Signature · 1 line
2.34.1
Quoted 34 lines · On 11/10/26 8:57 AM, [email protected] wrote:
From: Chunjie Zhu <[email protected]>
L0 always runs L2 with
V_INTR_MASKINGset, 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_MASKEDfor external interrupts in that case.Signed-off-by Chunjie Zhu <[email protected]>---
v2:
* Move the check intonsvm_intr_blocked(), as suggested by Ross.xen/arch/x86/hvm/svm/nestedsvm.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-)xen/arch/x86/hvm/svm/nestedsvm.c +3
-2@@ -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
xen/arch/x86/hvm/svm/nestedsvm.c enum hvm_intblk cf_check nsvm_intr_blocked(struct vcpu *v) · line 1237
Quoted 10 lines
xen/arch/x86/hvm/svm/nestedsvm.c +7
-0+* 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