public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH v5 0/2] KVM: nSVM: Fix #UD on VMMCALL issues.
@ 2026-03-04  0:22 Sean Christopherson
  2026-03-04  0:22 ` [PATCH v5 1/2] KVM: nSVM: Raise #UD if unhandled VMMCALL isn't intercepted by L1 Sean Christopherson
                   ` (2 more replies)
  0 siblings, 3 replies; 10+ messages in thread
From: Sean Christopherson @ 2026-03-04  0:22 UTC (permalink / raw)
  To: Vitaly Kuznetsov, Sean Christopherson, Paolo Bonzini
  Cc: kvm, linux-kernel, Kevin Cheng

The VMMCALL fixes from Kevin's broader "Align SVM with APM defined behaviors"
series.

v5:
 - Separate the VMMCALL fixes from everything else.
 - Rewrite the changelog to make clear this is fixing only the Hyper-V case.
 - Add a patch to always intercept VMMCALL, because letting it #UD natively
   does more harm than good.

v4:
  - https://lore.kernel.org/all/20260228033328.2285047-1-chengkev@google.com
  - Dropped "KVM: SVM: Inject #UD for STGI if EFER.SVME=0 and SVM Lock
    and DEV are not available" as per Sean
  - Added back STGI and CLGI intercept clearing in init_vmcb to maintain
    previous behavior on intel guests. Previously intel guests always
    had STGI and CLGI intercepts cleared if vgif was enabled. In V3,
    because the clearing of the intercepts was moved from init_vmcb() to
    the !guest_cpuid_is_intel_compatible() case in
    svm_recalc_instruction_intercepts(), the CLGI intercept would be
    indefinitely set on intel guests. I added back the clearing to
    init_vmcb() to retain intel guest behavior before this patch.
  - In "Raise #UD if VMMCALL instruction is not intercepted" patch:
      - Exempt Hyper-V L2 TLB flush hypercalls from the #UD injection,
        as L0 intentionally intercepts these VMMCALLs on behalf of L1
	via the direct hypercall enlightenment.
      - Added nested_svm_is_l2_tlb_flush_hcall() which just returns true
        if the hypercall was a Hyper-V L2 TLB flush hypercall.

v3:
  - https://lore.kernel.org/kvm/20260122045755.205203-1-chengkev@google.com/
  - Elaborated on 'Move STGI and CLGI intercept handling' commit message
    as per Sean
  - Fixed bug due to interaction with svm_enable_nmi_window() and 'Move
    STGI and CLGI intercept handling' as pointed out by Yosry. Code
    changes suggested by Sean/Yosry.
  - Removed open-coded nested_svm_check_permissions() in STGI
    interception function as per Yosry

v2:
  - https://lore.kernel.org/all/20260112174535.3132800-1-chengkev@google.com
  - Split up the series into smaller more logical changes as suggested
    by Sean
  - Added patch for injecting #UD for STGI under APM defined conditions
    as suggested by Sean
  - Combined EFER.SVME=0 conditional with intel CPU logic in
    svm_recalc_instruction_intercepts

Kevin Cheng (1):
  KVM: nSVM: Raise #UD if unhandled VMMCALL isn't intercepted by L1

Sean Christopherson (1):
  KVM: nSVM: Always intercept VMMCALL when L2 is active

 arch/x86/kvm/hyperv.h     |  8 --------
 arch/x86/kvm/svm/hyperv.h |  9 ++++++++-
 arch/x86/kvm/svm/nested.c | 11 +----------
 arch/x86/kvm/svm/svm.c    | 19 ++++++++++++++++++-
 4 files changed, 27 insertions(+), 20 deletions(-)


base-commit: 11439c4635edd669ae435eec308f4ab8a0804808
-- 
2.53.0.473.g4a7958ca14-goog


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

* [PATCH v5 1/2] KVM: nSVM: Raise #UD if unhandled VMMCALL isn't intercepted by L1
  2026-03-04  0:22 [PATCH v5 0/2] KVM: nSVM: Fix #UD on VMMCALL issues Sean Christopherson
@ 2026-03-04  0:22 ` Sean Christopherson
  2026-03-04  1:18   ` Yosry Ahmed
  2026-03-04  8:36   ` Vitaly Kuznetsov
  2026-03-04  0:22 ` [PATCH v5 2/2] KVM: nSVM: Always intercept VMMCALL when L2 is active Sean Christopherson
  2026-03-05 17:08 ` [PATCH v5 0/2] KVM: nSVM: Fix #UD on VMMCALL issues Sean Christopherson
  2 siblings, 2 replies; 10+ messages in thread
From: Sean Christopherson @ 2026-03-04  0:22 UTC (permalink / raw)
  To: Vitaly Kuznetsov, Sean Christopherson, Paolo Bonzini
  Cc: kvm, linux-kernel, Kevin Cheng

From: Kevin Cheng <chengkev@google.com>

Explicitly synthesize a #UD for VMMCALL if L2 is active, L1 does NOT want
to intercept VMMCALL, nested_svm_l2_tlb_flush_enabled() is true, and the
hypercall is something other than one of the supported Hyper-V hypercalls.
When all of the above conditions are met, KVM will intercept VMMCALL but
never forward it to L1, i.e. will let L2 make hypercalls as if it were L1.

The TLFS says a whole lot of nothing about this scenario, so go with the
architectural behavior, which says that VMMCALL #UDs if it's not
intercepted.

Opportunistically do a 2-for-1 stub trade by stub-ifying the new API
instead of the helpers it uses.  The last remaining "single" stub will
soon be dropped as well.

Suggested-by: Sean Christopherson <seanjc@google.com>
Fixes: 3f4a812edf5c ("KVM: nSVM: hyper-v: Enable L2 TLB flush")
Cc: Vitaly Kuznetsov <vkuznets@redhat.com>
Cc: stable@vger.kernel.org
Signed-off-by: Kevin Cheng <chengkev@google.com>
Link: https://patch.msgid.link/20260228033328.2285047-5-chengkev@google.com
[sean: rewrite changelog and comment, tag for stable, remove defunct stubs]
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
 arch/x86/kvm/hyperv.h     |  8 --------
 arch/x86/kvm/svm/hyperv.h | 11 +++++++++++
 arch/x86/kvm/svm/nested.c |  4 +---
 arch/x86/kvm/svm/svm.c    | 19 ++++++++++++++++++-
 4 files changed, 30 insertions(+), 12 deletions(-)

diff --git a/arch/x86/kvm/hyperv.h b/arch/x86/kvm/hyperv.h
index 6ce160ffa678..6301f79fcbae 100644
--- a/arch/x86/kvm/hyperv.h
+++ b/arch/x86/kvm/hyperv.h
@@ -305,14 +305,6 @@ static inline bool kvm_hv_has_stimer_pending(struct kvm_vcpu *vcpu)
 {
 	return false;
 }
-static inline bool kvm_hv_is_tlb_flush_hcall(struct kvm_vcpu *vcpu)
-{
-	return false;
-}
-static inline bool guest_hv_cpuid_has_l2_tlb_flush(struct kvm_vcpu *vcpu)
-{
-	return false;
-}
 static inline int kvm_hv_verify_vp_assist(struct kvm_vcpu *vcpu)
 {
 	return 0;
diff --git a/arch/x86/kvm/svm/hyperv.h b/arch/x86/kvm/svm/hyperv.h
index d3f8bfc05832..9af03970d40c 100644
--- a/arch/x86/kvm/svm/hyperv.h
+++ b/arch/x86/kvm/svm/hyperv.h
@@ -41,6 +41,13 @@ static inline bool nested_svm_l2_tlb_flush_enabled(struct kvm_vcpu *vcpu)
 	return hv_vcpu->vp_assist_page.nested_control.features.directhypercall;
 }
 
+static inline bool nested_svm_is_l2_tlb_flush_hcall(struct kvm_vcpu *vcpu)
+{
+	return guest_hv_cpuid_has_l2_tlb_flush(vcpu) &&
+	       nested_svm_l2_tlb_flush_enabled(vcpu) &&
+	       kvm_hv_is_tlb_flush_hcall(vcpu);
+}
+
 void svm_hv_inject_synthetic_vmexit_post_tlb_flush(struct kvm_vcpu *vcpu);
 #else /* CONFIG_KVM_HYPERV */
 static inline void nested_svm_hv_update_vm_vp_ids(struct kvm_vcpu *vcpu) {}
@@ -48,6 +55,10 @@ static inline bool nested_svm_l2_tlb_flush_enabled(struct kvm_vcpu *vcpu)
 {
 	return false;
 }
+static inline bool nested_svm_is_l2_tlb_flush_hcall(struct kvm_vcpu *vcpu)
+{
+	return false;
+}
 static inline void svm_hv_inject_synthetic_vmexit_post_tlb_flush(struct kvm_vcpu *vcpu) {}
 #endif /* CONFIG_KVM_HYPERV */
 
diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c
index 53ab6ce3cc26..750bf93c5341 100644
--- a/arch/x86/kvm/svm/nested.c
+++ b/arch/x86/kvm/svm/nested.c
@@ -1674,9 +1674,7 @@ int nested_svm_exit_special(struct vcpu_svm *svm)
 	}
 	case SVM_EXIT_VMMCALL:
 		/* Hyper-V L2 TLB flush hypercall is handled by L0 */
-		if (guest_hv_cpuid_has_l2_tlb_flush(vcpu) &&
-		    nested_svm_l2_tlb_flush_enabled(vcpu) &&
-		    kvm_hv_is_tlb_flush_hcall(vcpu))
+		if (nested_svm_is_l2_tlb_flush_hcall(vcpu))
 			return NESTED_EXIT_HOST;
 		break;
 	default:
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index 8f8bc863e214..38a2fad81ad8 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -52,6 +52,7 @@
 #include "svm.h"
 #include "svm_ops.h"
 
+#include "hyperv.h"
 #include "kvm_onhyperv.h"
 #include "svm_onhyperv.h"
 
@@ -3228,6 +3229,22 @@ static int bus_lock_exit(struct kvm_vcpu *vcpu)
 	return 0;
 }
 
+static int vmmcall_interception(struct kvm_vcpu *vcpu)
+{
+	/*
+	 * Inject a #UD if L2 is active and the VMMCALL isn't a Hyper-V TLB
+	 * hypercall, as VMMCALL #UDs if it's not intercepted, and this path is
+	 * reachable if and only if L1 doesn't want to intercept VMMCALL or has
+	 * enabled L0 (KVM) handling of Hyper-V L2 TLB flush hypercalls.
+	 */
+	if (is_guest_mode(vcpu) && !nested_svm_is_l2_tlb_flush_hcall(vcpu)) {
+		kvm_queue_exception(vcpu, UD_VECTOR);
+		return 1;
+	}
+
+	return kvm_emulate_hypercall(vcpu);
+}
+
 static int (*const svm_exit_handlers[])(struct kvm_vcpu *vcpu) = {
 	[SVM_EXIT_READ_CR0]			= cr_interception,
 	[SVM_EXIT_READ_CR3]			= cr_interception,
@@ -3278,7 +3295,7 @@ static int (*const svm_exit_handlers[])(struct kvm_vcpu *vcpu) = {
 	[SVM_EXIT_TASK_SWITCH]			= task_switch_interception,
 	[SVM_EXIT_SHUTDOWN]			= shutdown_interception,
 	[SVM_EXIT_VMRUN]			= vmrun_interception,
-	[SVM_EXIT_VMMCALL]			= kvm_emulate_hypercall,
+	[SVM_EXIT_VMMCALL]			= vmmcall_interception,
 	[SVM_EXIT_VMLOAD]			= vmload_interception,
 	[SVM_EXIT_VMSAVE]			= vmsave_interception,
 	[SVM_EXIT_STGI]				= stgi_interception,
-- 
2.53.0.473.g4a7958ca14-goog


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

* [PATCH v5 2/2] KVM: nSVM: Always intercept VMMCALL when L2 is active
  2026-03-04  0:22 [PATCH v5 0/2] KVM: nSVM: Fix #UD on VMMCALL issues Sean Christopherson
  2026-03-04  0:22 ` [PATCH v5 1/2] KVM: nSVM: Raise #UD if unhandled VMMCALL isn't intercepted by L1 Sean Christopherson
@ 2026-03-04  0:22 ` Sean Christopherson
  2026-03-04  1:15   ` Yosry Ahmed
  2026-03-04  8:36   ` Vitaly Kuznetsov
  2026-03-05 17:08 ` [PATCH v5 0/2] KVM: nSVM: Fix #UD on VMMCALL issues Sean Christopherson
  2 siblings, 2 replies; 10+ messages in thread
From: Sean Christopherson @ 2026-03-04  0:22 UTC (permalink / raw)
  To: Vitaly Kuznetsov, Sean Christopherson, Paolo Bonzini
  Cc: kvm, linux-kernel, Kevin Cheng

Always intercept VMMCALL now that KVM properly synthesizes a #UD as
appropriate, i.e. when L1 doesn't want to intercept VMMCALL, to avoid
putting L2 into an infinite #UD loop if KVM_X86_QUIRK_FIX_HYPERCALL_INSN
is enabled.

By letting L2 execute VMMCALL natively and thus #UD, for all intents and
purposes KVM morphs the VMMCALL intercept into a #UD intercept (KVM always
intercepts #UD).  When the hypercall quirk is enabled, KVM "emulates"
VMMCALL in response to the #UD by trying to fixup the opcode to the "right"
vendor, then restarts the guest, without skipping the VMMCALL.  As a
result, the guest sees an endless stream of #UDs since it's already
executing the correct vendor hypercall instruction, i.e. the emulator
doesn't anticipate that the #UD could be due to lack of interception, as
opposed to a truly undefined opcode.

Fixes: 0d945bd93511 ("KVM: SVM: Don't allow nested guest to VMMCALL into host")
Cc: stable@vger.kernel.org
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
 arch/x86/kvm/svm/hyperv.h | 4 ----
 arch/x86/kvm/svm/nested.c | 7 -------
 2 files changed, 11 deletions(-)

diff --git a/arch/x86/kvm/svm/hyperv.h b/arch/x86/kvm/svm/hyperv.h
index 9af03970d40c..f70d076911a6 100644
--- a/arch/x86/kvm/svm/hyperv.h
+++ b/arch/x86/kvm/svm/hyperv.h
@@ -51,10 +51,6 @@ static inline bool nested_svm_is_l2_tlb_flush_hcall(struct kvm_vcpu *vcpu)
 void svm_hv_inject_synthetic_vmexit_post_tlb_flush(struct kvm_vcpu *vcpu);
 #else /* CONFIG_KVM_HYPERV */
 static inline void nested_svm_hv_update_vm_vp_ids(struct kvm_vcpu *vcpu) {}
-static inline bool nested_svm_l2_tlb_flush_enabled(struct kvm_vcpu *vcpu)
-{
-	return false;
-}
 static inline bool nested_svm_is_l2_tlb_flush_hcall(struct kvm_vcpu *vcpu)
 {
 	return false;
diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c
index 750bf93c5341..2ac28d2c34ca 100644
--- a/arch/x86/kvm/svm/nested.c
+++ b/arch/x86/kvm/svm/nested.c
@@ -156,13 +156,6 @@ void recalc_intercepts(struct vcpu_svm *svm)
 			vmcb_clr_intercept(c, INTERCEPT_VINTR);
 	}
 
-	/*
-	 * We want to see VMMCALLs from a nested guest only when Hyper-V L2 TLB
-	 * flush feature is enabled.
-	 */
-	if (!nested_svm_l2_tlb_flush_enabled(&svm->vcpu))
-		vmcb_clr_intercept(c, INTERCEPT_VMMCALL);
-
 	for (i = 0; i < MAX_INTERCEPT; i++)
 		c->intercepts[i] |= g->intercepts[i];
 
-- 
2.53.0.473.g4a7958ca14-goog


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

* Re: [PATCH v5 2/2] KVM: nSVM: Always intercept VMMCALL when L2 is active
  2026-03-04  0:22 ` [PATCH v5 2/2] KVM: nSVM: Always intercept VMMCALL when L2 is active Sean Christopherson
@ 2026-03-04  1:15   ` Yosry Ahmed
  2026-03-04  1:20     ` Sean Christopherson
  2026-03-04  8:36   ` Vitaly Kuznetsov
  1 sibling, 1 reply; 10+ messages in thread
From: Yosry Ahmed @ 2026-03-04  1:15 UTC (permalink / raw)
  To: Sean Christopherson
  Cc: Vitaly Kuznetsov, Paolo Bonzini, kvm, linux-kernel, Kevin Cheng

On Tue, Mar 03, 2026 at 04:22:23PM -0800, Sean Christopherson wrote:
> Always intercept VMMCALL now that KVM properly synthesizes a #UD as
> appropriate, i.e. when L1 doesn't want to intercept VMMCALL, to avoid
> putting L2 into an infinite #UD loop if KVM_X86_QUIRK_FIX_HYPERCALL_INSN
> is enabled.
> 
> By letting L2 execute VMMCALL natively and thus #UD, for all intents and
> purposes KVM morphs the VMMCALL intercept into a #UD intercept (KVM always
> intercepts #UD).  When the hypercall quirk is enabled, KVM "emulates"
> VMMCALL in response to the #UD by trying to fixup the opcode to the "right"
> vendor, then restarts the guest, without skipping the VMMCALL.  As a
> result, the guest sees an endless stream of #UDs since it's already
> executing the correct vendor hypercall instruction, i.e. the emulator
> doesn't anticipate that the #UD could be due to lack of interception, as
> opposed to a truly undefined opcode.
> 
> Fixes: 0d945bd93511 ("KVM: SVM: Don't allow nested guest to VMMCALL into host")
> Cc: stable@vger.kernel.org
> Signed-off-by: Sean Christopherson <seanjc@google.com>
> ---
>  arch/x86/kvm/svm/hyperv.h | 4 ----
>  arch/x86/kvm/svm/nested.c | 7 -------
>  2 files changed, 11 deletions(-)
> 
> diff --git a/arch/x86/kvm/svm/hyperv.h b/arch/x86/kvm/svm/hyperv.h
> index 9af03970d40c..f70d076911a6 100644
> --- a/arch/x86/kvm/svm/hyperv.h
> +++ b/arch/x86/kvm/svm/hyperv.h
> @@ -51,10 +51,6 @@ static inline bool nested_svm_is_l2_tlb_flush_hcall(struct kvm_vcpu *vcpu)
>  void svm_hv_inject_synthetic_vmexit_post_tlb_flush(struct kvm_vcpu *vcpu);
>  #else /* CONFIG_KVM_HYPERV */
>  static inline void nested_svm_hv_update_vm_vp_ids(struct kvm_vcpu *vcpu) {}
> -static inline bool nested_svm_l2_tlb_flush_enabled(struct kvm_vcpu *vcpu)
> -{
> -	return false;
> -}

Why is this dropped? We still need it for vmmcall_interception under
!CONFIG_KVM_HYPERV, right?

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

* Re: [PATCH v5 1/2] KVM: nSVM: Raise #UD if unhandled VMMCALL isn't intercepted by L1
  2026-03-04  0:22 ` [PATCH v5 1/2] KVM: nSVM: Raise #UD if unhandled VMMCALL isn't intercepted by L1 Sean Christopherson
@ 2026-03-04  1:18   ` Yosry Ahmed
  2026-03-04  8:36   ` Vitaly Kuznetsov
  1 sibling, 0 replies; 10+ messages in thread
From: Yosry Ahmed @ 2026-03-04  1:18 UTC (permalink / raw)
  To: Sean Christopherson
  Cc: Vitaly Kuznetsov, Paolo Bonzini, kvm, linux-kernel, Kevin Cheng

On Tue, Mar 03, 2026 at 04:22:22PM -0800, Sean Christopherson wrote:
> From: Kevin Cheng <chengkev@google.com>
> 
> Explicitly synthesize a #UD for VMMCALL if L2 is active, L1 does NOT want
> to intercept VMMCALL, nested_svm_l2_tlb_flush_enabled() is true, and the
> hypercall is something other than one of the supported Hyper-V hypercalls.
> When all of the above conditions are met, KVM will intercept VMMCALL but
> never forward it to L1, i.e. will let L2 make hypercalls as if it were L1.
> 
> The TLFS says a whole lot of nothing about this scenario, so go with the
> architectural behavior, which says that VMMCALL #UDs if it's not
> intercepted.
> 
> Opportunistically do a 2-for-1 stub trade by stub-ifying the new API
> instead of the helpers it uses.  The last remaining "single" stub will
> soon be dropped as well.
> 
> Suggested-by: Sean Christopherson <seanjc@google.com>
> Fixes: 3f4a812edf5c ("KVM: nSVM: hyper-v: Enable L2 TLB flush")
> Cc: Vitaly Kuznetsov <vkuznets@redhat.com>
> Cc: stable@vger.kernel.org
> Signed-off-by: Kevin Cheng <chengkev@google.com>
> Link: https://patch.msgid.link/20260228033328.2285047-5-chengkev@google.com
> [sean: rewrite changelog and comment, tag for stable, remove defunct stubs]
> Signed-off-by: Sean Christopherson <seanjc@google.com>

Reviewed-by: Yosry Ahmed <yosry@kernel.org>

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

* Re: [PATCH v5 2/2] KVM: nSVM: Always intercept VMMCALL when L2 is active
  2026-03-04  1:15   ` Yosry Ahmed
@ 2026-03-04  1:20     ` Sean Christopherson
  2026-03-04  1:22       ` Yosry Ahmed
  0 siblings, 1 reply; 10+ messages in thread
From: Sean Christopherson @ 2026-03-04  1:20 UTC (permalink / raw)
  To: Yosry Ahmed
  Cc: Vitaly Kuznetsov, Paolo Bonzini, kvm, linux-kernel, Kevin Cheng

On Wed, Mar 04, 2026, Yosry Ahmed wrote:
> On Tue, Mar 03, 2026 at 04:22:23PM -0800, Sean Christopherson wrote:
> > Always intercept VMMCALL now that KVM properly synthesizes a #UD as
> > appropriate, i.e. when L1 doesn't want to intercept VMMCALL, to avoid
> > putting L2 into an infinite #UD loop if KVM_X86_QUIRK_FIX_HYPERCALL_INSN
> > is enabled.
> > 
> > By letting L2 execute VMMCALL natively and thus #UD, for all intents and
> > purposes KVM morphs the VMMCALL intercept into a #UD intercept (KVM always
> > intercepts #UD).  When the hypercall quirk is enabled, KVM "emulates"
> > VMMCALL in response to the #UD by trying to fixup the opcode to the "right"
> > vendor, then restarts the guest, without skipping the VMMCALL.  As a
> > result, the guest sees an endless stream of #UDs since it's already
> > executing the correct vendor hypercall instruction, i.e. the emulator
> > doesn't anticipate that the #UD could be due to lack of interception, as
> > opposed to a truly undefined opcode.
> > 
> > Fixes: 0d945bd93511 ("KVM: SVM: Don't allow nested guest to VMMCALL into host")
> > Cc: stable@vger.kernel.org
> > Signed-off-by: Sean Christopherson <seanjc@google.com>
> > ---
> >  arch/x86/kvm/svm/hyperv.h | 4 ----
> >  arch/x86/kvm/svm/nested.c | 7 -------
> >  2 files changed, 11 deletions(-)
> > 
> > diff --git a/arch/x86/kvm/svm/hyperv.h b/arch/x86/kvm/svm/hyperv.h
> > index 9af03970d40c..f70d076911a6 100644
> > --- a/arch/x86/kvm/svm/hyperv.h
> > +++ b/arch/x86/kvm/svm/hyperv.h
> > @@ -51,10 +51,6 @@ static inline bool nested_svm_is_l2_tlb_flush_hcall(struct kvm_vcpu *vcpu)
> >  void svm_hv_inject_synthetic_vmexit_post_tlb_flush(struct kvm_vcpu *vcpu);
> >  #else /* CONFIG_KVM_HYPERV */
> >  static inline void nested_svm_hv_update_vm_vp_ids(struct kvm_vcpu *vcpu) {}
> > -static inline bool nested_svm_l2_tlb_flush_enabled(struct kvm_vcpu *vcpu)
> > -{
> > -	return false;
> > -}
> 
> Why is this dropped? We still need it for vmmcall_interception under
> !CONFIG_KVM_HYPERV, right?

Nope, because vmmcall_interception() uses nested_svm_is_l2_tlb_flush_hcall(), and
the previous patch created a stub for that one.  I.e. only the non-stub
CONFIG_KVM_HYPERV=y version references nested_svm_l2_tlb_flush_enabled().

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

* Re: [PATCH v5 2/2] KVM: nSVM: Always intercept VMMCALL when L2 is active
  2026-03-04  1:20     ` Sean Christopherson
@ 2026-03-04  1:22       ` Yosry Ahmed
  0 siblings, 0 replies; 10+ messages in thread
From: Yosry Ahmed @ 2026-03-04  1:22 UTC (permalink / raw)
  To: Sean Christopherson
  Cc: Vitaly Kuznetsov, Paolo Bonzini, kvm, linux-kernel, Kevin Cheng

On Tue, Mar 3, 2026 at 5:20 PM Sean Christopherson <seanjc@google.com> wrote:
>
> On Wed, Mar 04, 2026, Yosry Ahmed wrote:
> > On Tue, Mar 03, 2026 at 04:22:23PM -0800, Sean Christopherson wrote:
> > > Always intercept VMMCALL now that KVM properly synthesizes a #UD as
> > > appropriate, i.e. when L1 doesn't want to intercept VMMCALL, to avoid
> > > putting L2 into an infinite #UD loop if KVM_X86_QUIRK_FIX_HYPERCALL_INSN
> > > is enabled.
> > >
> > > By letting L2 execute VMMCALL natively and thus #UD, for all intents and
> > > purposes KVM morphs the VMMCALL intercept into a #UD intercept (KVM always
> > > intercepts #UD).  When the hypercall quirk is enabled, KVM "emulates"
> > > VMMCALL in response to the #UD by trying to fixup the opcode to the "right"
> > > vendor, then restarts the guest, without skipping the VMMCALL.  As a
> > > result, the guest sees an endless stream of #UDs since it's already
> > > executing the correct vendor hypercall instruction, i.e. the emulator
> > > doesn't anticipate that the #UD could be due to lack of interception, as
> > > opposed to a truly undefined opcode.
> > >
> > > Fixes: 0d945bd93511 ("KVM: SVM: Don't allow nested guest to VMMCALL into host")
> > > Cc: stable@vger.kernel.org
> > > Signed-off-by: Sean Christopherson <seanjc@google.com>
> > > ---
> > >  arch/x86/kvm/svm/hyperv.h | 4 ----
> > >  arch/x86/kvm/svm/nested.c | 7 -------
> > >  2 files changed, 11 deletions(-)
> > >
> > > diff --git a/arch/x86/kvm/svm/hyperv.h b/arch/x86/kvm/svm/hyperv.h
> > > index 9af03970d40c..f70d076911a6 100644
> > > --- a/arch/x86/kvm/svm/hyperv.h
> > > +++ b/arch/x86/kvm/svm/hyperv.h
> > > @@ -51,10 +51,6 @@ static inline bool nested_svm_is_l2_tlb_flush_hcall(struct kvm_vcpu *vcpu)
> > >  void svm_hv_inject_synthetic_vmexit_post_tlb_flush(struct kvm_vcpu *vcpu);
> > >  #else /* CONFIG_KVM_HYPERV */
> > >  static inline void nested_svm_hv_update_vm_vp_ids(struct kvm_vcpu *vcpu) {}
> > > -static inline bool nested_svm_l2_tlb_flush_enabled(struct kvm_vcpu *vcpu)
> > > -{
> > > -   return false;
> > > -}
> >
> > Why is this dropped? We still need it for vmmcall_interception under
> > !CONFIG_KVM_HYPERV, right?
>
> Nope, because vmmcall_interception() uses nested_svm_is_l2_tlb_flush_hcall(), and
> the previous patch created a stub for that one.  I.e. only the non-stub
> CONFIG_KVM_HYPERV=y version references nested_svm_l2_tlb_flush_enabled().

Oh I thought we were removing the stub for
nested_svm_is_l2_tlb_flush_hcall(), these names are too similar :P

Sorry for the noise.

Feel free to add:

Reviewed-by: Yosry Ahmed <yosry@kernel.org>

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

* Re: [PATCH v5 1/2] KVM: nSVM: Raise #UD if unhandled VMMCALL isn't intercepted by L1
  2026-03-04  0:22 ` [PATCH v5 1/2] KVM: nSVM: Raise #UD if unhandled VMMCALL isn't intercepted by L1 Sean Christopherson
  2026-03-04  1:18   ` Yosry Ahmed
@ 2026-03-04  8:36   ` Vitaly Kuznetsov
  1 sibling, 0 replies; 10+ messages in thread
From: Vitaly Kuznetsov @ 2026-03-04  8:36 UTC (permalink / raw)
  To: Sean Christopherson, Sean Christopherson, Paolo Bonzini
  Cc: kvm, linux-kernel, Kevin Cheng

Sean Christopherson <seanjc@google.com> writes:

> From: Kevin Cheng <chengkev@google.com>
>
> Explicitly synthesize a #UD for VMMCALL if L2 is active, L1 does NOT want
> to intercept VMMCALL, nested_svm_l2_tlb_flush_enabled() is true, and the
> hypercall is something other than one of the supported Hyper-V hypercalls.
> When all of the above conditions are met, KVM will intercept VMMCALL but
> never forward it to L1, i.e. will let L2 make hypercalls as if it were L1.
>
> The TLFS says a whole lot of nothing about this scenario, so go with the
> architectural behavior, which says that VMMCALL #UDs if it's not
> intercepted.
>
> Opportunistically do a 2-for-1 stub trade by stub-ifying the new API
> instead of the helpers it uses.  The last remaining "single" stub will
> soon be dropped as well.
>
> Suggested-by: Sean Christopherson <seanjc@google.com>
> Fixes: 3f4a812edf5c ("KVM: nSVM: hyper-v: Enable L2 TLB flush")
> Cc: Vitaly Kuznetsov <vkuznets@redhat.com>
> Cc: stable@vger.kernel.org
> Signed-off-by: Kevin Cheng <chengkev@google.com>
> Link: https://patch.msgid.link/20260228033328.2285047-5-chengkev@google.com
> [sean: rewrite changelog and comment, tag for stable, remove defunct stubs]
> Signed-off-by: Sean Christopherson <seanjc@google.com>
> ---
>  arch/x86/kvm/hyperv.h     |  8 --------
>  arch/x86/kvm/svm/hyperv.h | 11 +++++++++++
>  arch/x86/kvm/svm/nested.c |  4 +---
>  arch/x86/kvm/svm/svm.c    | 19 ++++++++++++++++++-
>  4 files changed, 30 insertions(+), 12 deletions(-)
>
> diff --git a/arch/x86/kvm/hyperv.h b/arch/x86/kvm/hyperv.h
> index 6ce160ffa678..6301f79fcbae 100644
> --- a/arch/x86/kvm/hyperv.h
> +++ b/arch/x86/kvm/hyperv.h
> @@ -305,14 +305,6 @@ static inline bool kvm_hv_has_stimer_pending(struct kvm_vcpu *vcpu)
>  {
>  	return false;
>  }
> -static inline bool kvm_hv_is_tlb_flush_hcall(struct kvm_vcpu *vcpu)
> -{
> -	return false;
> -}
> -static inline bool guest_hv_cpuid_has_l2_tlb_flush(struct kvm_vcpu *vcpu)
> -{
> -	return false;
> -}
>  static inline int kvm_hv_verify_vp_assist(struct kvm_vcpu *vcpu)
>  {
>  	return 0;
> diff --git a/arch/x86/kvm/svm/hyperv.h b/arch/x86/kvm/svm/hyperv.h
> index d3f8bfc05832..9af03970d40c 100644
> --- a/arch/x86/kvm/svm/hyperv.h
> +++ b/arch/x86/kvm/svm/hyperv.h
> @@ -41,6 +41,13 @@ static inline bool nested_svm_l2_tlb_flush_enabled(struct kvm_vcpu *vcpu)
>  	return hv_vcpu->vp_assist_page.nested_control.features.directhypercall;
>  }
>  
> +static inline bool nested_svm_is_l2_tlb_flush_hcall(struct kvm_vcpu *vcpu)
> +{
> +	return guest_hv_cpuid_has_l2_tlb_flush(vcpu) &&
> +	       nested_svm_l2_tlb_flush_enabled(vcpu) &&
> +	       kvm_hv_is_tlb_flush_hcall(vcpu);
> +}
> +
>  void svm_hv_inject_synthetic_vmexit_post_tlb_flush(struct kvm_vcpu *vcpu);
>  #else /* CONFIG_KVM_HYPERV */
>  static inline void nested_svm_hv_update_vm_vp_ids(struct kvm_vcpu *vcpu) {}
> @@ -48,6 +55,10 @@ static inline bool nested_svm_l2_tlb_flush_enabled(struct kvm_vcpu *vcpu)
>  {
>  	return false;
>  }
> +static inline bool nested_svm_is_l2_tlb_flush_hcall(struct kvm_vcpu *vcpu)
> +{
> +	return false;
> +}
>  static inline void svm_hv_inject_synthetic_vmexit_post_tlb_flush(struct kvm_vcpu *vcpu) {}
>  #endif /* CONFIG_KVM_HYPERV */
>  
> diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c
> index 53ab6ce3cc26..750bf93c5341 100644
> --- a/arch/x86/kvm/svm/nested.c
> +++ b/arch/x86/kvm/svm/nested.c
> @@ -1674,9 +1674,7 @@ int nested_svm_exit_special(struct vcpu_svm *svm)
>  	}
>  	case SVM_EXIT_VMMCALL:
>  		/* Hyper-V L2 TLB flush hypercall is handled by L0 */
> -		if (guest_hv_cpuid_has_l2_tlb_flush(vcpu) &&
> -		    nested_svm_l2_tlb_flush_enabled(vcpu) &&
> -		    kvm_hv_is_tlb_flush_hcall(vcpu))
> +		if (nested_svm_is_l2_tlb_flush_hcall(vcpu))
>  			return NESTED_EXIT_HOST;
>  		break;
>  	default:
> diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
> index 8f8bc863e214..38a2fad81ad8 100644
> --- a/arch/x86/kvm/svm/svm.c
> +++ b/arch/x86/kvm/svm/svm.c
> @@ -52,6 +52,7 @@
>  #include "svm.h"
>  #include "svm_ops.h"
>  
> +#include "hyperv.h"
>  #include "kvm_onhyperv.h"
>  #include "svm_onhyperv.h"
>  
> @@ -3228,6 +3229,22 @@ static int bus_lock_exit(struct kvm_vcpu *vcpu)
>  	return 0;
>  }
>  
> +static int vmmcall_interception(struct kvm_vcpu *vcpu)
> +{
> +	/*
> +	 * Inject a #UD if L2 is active and the VMMCALL isn't a Hyper-V TLB
> +	 * hypercall, as VMMCALL #UDs if it's not intercepted, and this path is
> +	 * reachable if and only if L1 doesn't want to intercept VMMCALL or has
> +	 * enabled L0 (KVM) handling of Hyper-V L2 TLB flush hypercalls.
> +	 */
> +	if (is_guest_mode(vcpu) && !nested_svm_is_l2_tlb_flush_hcall(vcpu)) {
> +		kvm_queue_exception(vcpu, UD_VECTOR);
> +		return 1;
> +	}
> +
> +	return kvm_emulate_hypercall(vcpu);
> +}
> +
>  static int (*const svm_exit_handlers[])(struct kvm_vcpu *vcpu) = {
>  	[SVM_EXIT_READ_CR0]			= cr_interception,
>  	[SVM_EXIT_READ_CR3]			= cr_interception,
> @@ -3278,7 +3295,7 @@ static int (*const svm_exit_handlers[])(struct kvm_vcpu *vcpu) = {
>  	[SVM_EXIT_TASK_SWITCH]			= task_switch_interception,
>  	[SVM_EXIT_SHUTDOWN]			= shutdown_interception,
>  	[SVM_EXIT_VMRUN]			= vmrun_interception,
> -	[SVM_EXIT_VMMCALL]			= kvm_emulate_hypercall,
> +	[SVM_EXIT_VMMCALL]			= vmmcall_interception,
>  	[SVM_EXIT_VMLOAD]			= vmload_interception,
>  	[SVM_EXIT_VMSAVE]			= vmsave_interception,
>  	[SVM_EXIT_STGI]				= stgi_interception,

Reviewed-by: Vitaly Kuznetsov <vkuznets@redhat.com>

-- 
Vitaly


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

* Re: [PATCH v5 2/2] KVM: nSVM: Always intercept VMMCALL when L2 is active
  2026-03-04  0:22 ` [PATCH v5 2/2] KVM: nSVM: Always intercept VMMCALL when L2 is active Sean Christopherson
  2026-03-04  1:15   ` Yosry Ahmed
@ 2026-03-04  8:36   ` Vitaly Kuznetsov
  1 sibling, 0 replies; 10+ messages in thread
From: Vitaly Kuznetsov @ 2026-03-04  8:36 UTC (permalink / raw)
  To: Sean Christopherson, Sean Christopherson, Paolo Bonzini
  Cc: kvm, linux-kernel, Kevin Cheng

Sean Christopherson <seanjc@google.com> writes:

> Always intercept VMMCALL now that KVM properly synthesizes a #UD as
> appropriate, i.e. when L1 doesn't want to intercept VMMCALL, to avoid
> putting L2 into an infinite #UD loop if KVM_X86_QUIRK_FIX_HYPERCALL_INSN
> is enabled.
>
> By letting L2 execute VMMCALL natively and thus #UD, for all intents and
> purposes KVM morphs the VMMCALL intercept into a #UD intercept (KVM always
> intercepts #UD).  When the hypercall quirk is enabled, KVM "emulates"
> VMMCALL in response to the #UD by trying to fixup the opcode to the "right"
> vendor, then restarts the guest, without skipping the VMMCALL.  As a
> result, the guest sees an endless stream of #UDs since it's already
> executing the correct vendor hypercall instruction, i.e. the emulator
> doesn't anticipate that the #UD could be due to lack of interception, as
> opposed to a truly undefined opcode.
>
> Fixes: 0d945bd93511 ("KVM: SVM: Don't allow nested guest to VMMCALL into host")
> Cc: stable@vger.kernel.org
> Signed-off-by: Sean Christopherson <seanjc@google.com>
> ---
>  arch/x86/kvm/svm/hyperv.h | 4 ----
>  arch/x86/kvm/svm/nested.c | 7 -------
>  2 files changed, 11 deletions(-)
>
> diff --git a/arch/x86/kvm/svm/hyperv.h b/arch/x86/kvm/svm/hyperv.h
> index 9af03970d40c..f70d076911a6 100644
> --- a/arch/x86/kvm/svm/hyperv.h
> +++ b/arch/x86/kvm/svm/hyperv.h
> @@ -51,10 +51,6 @@ static inline bool nested_svm_is_l2_tlb_flush_hcall(struct kvm_vcpu *vcpu)
>  void svm_hv_inject_synthetic_vmexit_post_tlb_flush(struct kvm_vcpu *vcpu);
>  #else /* CONFIG_KVM_HYPERV */
>  static inline void nested_svm_hv_update_vm_vp_ids(struct kvm_vcpu *vcpu) {}
> -static inline bool nested_svm_l2_tlb_flush_enabled(struct kvm_vcpu *vcpu)
> -{
> -	return false;
> -}
>  static inline bool nested_svm_is_l2_tlb_flush_hcall(struct kvm_vcpu *vcpu)
>  {
>  	return false;
> diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c
> index 750bf93c5341..2ac28d2c34ca 100644
> --- a/arch/x86/kvm/svm/nested.c
> +++ b/arch/x86/kvm/svm/nested.c
> @@ -156,13 +156,6 @@ void recalc_intercepts(struct vcpu_svm *svm)
>  			vmcb_clr_intercept(c, INTERCEPT_VINTR);
>  	}
>  
> -	/*
> -	 * We want to see VMMCALLs from a nested guest only when Hyper-V L2 TLB
> -	 * flush feature is enabled.
> -	 */
> -	if (!nested_svm_l2_tlb_flush_enabled(&svm->vcpu))
> -		vmcb_clr_intercept(c, INTERCEPT_VMMCALL);
> -
>  	for (i = 0; i < MAX_INTERCEPT; i++)
>  		c->intercepts[i] |= g->intercepts[i];

Reviewed-by: Vitaly Kuznetsov <vkuznets@redhat.com>

-- 
Vitaly


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

* Re: [PATCH v5 0/2] KVM: nSVM: Fix #UD on VMMCALL issues.
  2026-03-04  0:22 [PATCH v5 0/2] KVM: nSVM: Fix #UD on VMMCALL issues Sean Christopherson
  2026-03-04  0:22 ` [PATCH v5 1/2] KVM: nSVM: Raise #UD if unhandled VMMCALL isn't intercepted by L1 Sean Christopherson
  2026-03-04  0:22 ` [PATCH v5 2/2] KVM: nSVM: Always intercept VMMCALL when L2 is active Sean Christopherson
@ 2026-03-05 17:08 ` Sean Christopherson
  2 siblings, 0 replies; 10+ messages in thread
From: Sean Christopherson @ 2026-03-05 17:08 UTC (permalink / raw)
  To: Sean Christopherson, Vitaly Kuznetsov, Paolo Bonzini
  Cc: kvm, linux-kernel, Kevin Cheng

On Tue, 03 Mar 2026 16:22:21 -0800, Sean Christopherson wrote:
> The VMMCALL fixes from Kevin's broader "Align SVM with APM defined behaviors"
> series.
> 
> v5:
>  - Separate the VMMCALL fixes from everything else.
>  - Rewrite the changelog to make clear this is fixing only the Hyper-V case.
>  - Add a patch to always intercept VMMCALL, because letting it #UD natively
>    does more harm than good.
> 
> [...]

Applied to kvm-x86 nested, thanks!

[1/2] KVM: nSVM: Raise #UD if unhandled VMMCALL isn't intercepted by L1
      https://github.com/kvm-x86/linux/commit/c36991c6f8d2
[2/2] KVM: nSVM: Always intercept VMMCALL when L2 is active
      https://github.com/kvm-x86/linux/commit/33d3617a52f9

--
https://github.com/kvm-x86/linux/tree/next

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

end of thread, other threads:[~2026-03-05 17:12 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-04  0:22 [PATCH v5 0/2] KVM: nSVM: Fix #UD on VMMCALL issues Sean Christopherson
2026-03-04  0:22 ` [PATCH v5 1/2] KVM: nSVM: Raise #UD if unhandled VMMCALL isn't intercepted by L1 Sean Christopherson
2026-03-04  1:18   ` Yosry Ahmed
2026-03-04  8:36   ` Vitaly Kuznetsov
2026-03-04  0:22 ` [PATCH v5 2/2] KVM: nSVM: Always intercept VMMCALL when L2 is active Sean Christopherson
2026-03-04  1:15   ` Yosry Ahmed
2026-03-04  1:20     ` Sean Christopherson
2026-03-04  1:22       ` Yosry Ahmed
2026-03-04  8:36   ` Vitaly Kuznetsov
2026-03-05 17:08 ` [PATCH v5 0/2] KVM: nSVM: Fix #UD on VMMCALL issues Sean Christopherson

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox