[PATCH v2 4/6] KVM: x86: Recompute EFER.LMA instead of inheriting the current value on EFER writes

From: Sean Christopherson

Date: Tue Oct 27 2026 - 18:50:09 EST


Recompute EFER.LMA on bare writes to EFER (via WRMSR or KVM_SET_MSRS)
instead of carrying forward the current EFER.LMA. While it _should_ be
impossible for EFER.LMA to end up in an inconsistent state on such a write,
KVM has had a rash of exactly those bugs, mostly because SVM is quite lax
when it comes to consistenty checks, which in turn bleeds into KVM's nested
SVM implementation. Retaining the existing EFER is also rather confusing,
as understanding why keeping EFER.LMA is correct requires a lot of a priori
knowledge of both x86-64 and KVM.

Keep the core concept of the existing logic as a sanity check: WARN if the
computed EFER.LMA would be toggled, because as described above, that
_should_ be impossible. Don't return an error if the sanity check fails,
e.g. don't inject a #GP, as setting EFER.LMA based on the current state
provides architecturally consistency.

Signed-off-by: Sean Christopherson <seanjc@xxxxxxxxxx>
---
arch/x86/kvm/msrs.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/arch/x86/kvm/msrs.c b/arch/x86/kvm/msrs.c
index c3cb1575fe66..c55402970a34 100644
--- a/arch/x86/kvm/msrs.c
+++ b/arch/x86/kvm/msrs.c
@@ -638,8 +638,17 @@ static int set_efer(struct kvm_vcpu *vcpu, u64 efer)
(vcpu->arch.efer & EFER_SVME) && !(efer & EFER_SVME))
return 1;

- efer &= ~EFER_LMA;
- efer |= vcpu->arch.efer & EFER_LMA;
+ if ((efer & EFER_LME) && is_paging(vcpu) && is_pae(vcpu))
+ efer |= EFER_LMA;
+ else
+ efer &= ~EFER_LMA;
+
+ /*
+ * Toggling EFER.LMA on a bare write to EFER should be impossible, as
+ * it means KVM allowed architecturally invalid state at some point in
+ * the past, i.e. allowed EFER.LMA=1 && (!CR0.PG || !CR4.PAE).
+ */
+ WARN_ON_ONCE((efer & EFER_LMA) != (vcpu->arch.efer & EFER_LMA));

r = kvm_x86_call(set_efer)(vcpu, efer);
if (r) {
--
2.56.0.307.g5fe7bd680c-goog