* [PATCH 0/7] KVM: x86: EFER validity fixes and cleanups
@ 2026-06-30 23:47 Yosry Ahmed
2026-06-30 23:47 ` [PATCH 1/7] KVM: x86: Check EFER validity on KVM_SET_SREGS* Yosry Ahmed
` (6 more replies)
0 siblings, 7 replies; 8+ messages in thread
From: Yosry Ahmed @ 2026-06-30 23:47 UTC (permalink / raw)
To: Sean Christopherson; +Cc: Paolo Bonzini, kvm, linux-kernel, Yosry Ahmed
A couple of bug fixes for EFER validity checks, namely not doing any on
KVM_SET_SREGS*, and incorrectly preserving EFER.SVME and EFER.LMSLE
state after module unload+load.
The rest of the series reworks EFER supported bits and moves them into
kvm_caps, ensuring they are always reinitialized on vendor module init.
The final patch adds test cases to set_sregs that catch the bug fixed by
patch 1.
Yosry Ahmed (7):
KVM: x86: Check EFER validity on KVM_SET_SREGS*
KVM: SVM: Disallow EFER.SVME and EFER.LSMLE if nested is disabled
KVM: x86: Disallow EFER.LME and EFER.LMA if long mode is not supported
KVM: x86: Add a per-vendor callback to setup EFER caps
KVM: x86: Reverse the polarity of efer_reserved_bits
KVM: x86: Move supported EFER bits to kvm_caps
KVM: selftests: Extend set_sregs test to cover EFER
arch/x86/include/asm/kvm-x86-ops.h | 1 +
arch/x86/include/asm/kvm_host.h | 3 +
arch/x86/kvm/msrs.c | 21 +----
arch/x86/kvm/msrs.h | 1 +
arch/x86/kvm/regs.c | 3 +-
arch/x86/kvm/svm/svm.c | 14 +++-
arch/x86/kvm/x86.c | 18 ++--
.../selftests/kvm/include/x86/processor.h | 2 +
.../selftests/kvm/x86/set_sregs_test.c | 83 ++++++++++++++-----
9 files changed, 96 insertions(+), 50 deletions(-)
base-commit: 50406d35f5635e1cc523e61409d57e851b5f5df8
--
2.55.0.rc0.799.gd6f94ed593-goog
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 1/7] KVM: x86: Check EFER validity on KVM_SET_SREGS*
2026-06-30 23:47 [PATCH 0/7] KVM: x86: EFER validity fixes and cleanups Yosry Ahmed
@ 2026-06-30 23:47 ` Yosry Ahmed
2026-06-30 23:47 ` [PATCH 2/7] KVM: SVM: Disallow EFER.SVME and EFER.LSMLE if nested is disabled Yosry Ahmed
` (5 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Yosry Ahmed @ 2026-06-30 23:47 UTC (permalink / raw)
To: Sean Christopherson; +Cc: Paolo Bonzini, kvm, linux-kernel, Yosry Ahmed, stable
When handling userspace SREGS writes, check the validity of EFER (i.e.
allowed bits) before writing the new value of EFER through the
per-vendor set_efer callbacks. This prevents userspace from writing
bogus values (e.g. EFER.SVME=1 with nested=0).
Note: on KVM_SET_MSRS, KVM only checks EFER validity in terms of KVM
caps, not guest caps, so it is possible to set EFER bits that are
supported by KVM but not by the guest CPUID. Potentially allowing
userspace to set msrs before CPUID.
However, for KVM_SET_SREGS*, check the validity of the set bits against
both KVM and guest caps. This is consistent with other validity checks
(e.g. for CR4) that check validity against guest caps, which already
imposes the need to set CPUID before SREGS.
Cc: stable@vger.kernel.org
Signed-off-by: Yosry Ahmed <yosry@kernel.org>
---
arch/x86/kvm/regs.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/arch/x86/kvm/regs.c b/arch/x86/kvm/regs.c
index d2caf5a67dba4..94c4e4e41868f 100644
--- a/arch/x86/kvm/regs.c
+++ b/arch/x86/kvm/regs.c
@@ -563,7 +563,8 @@ static bool kvm_is_valid_sregs(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs)
}
return kvm_is_valid_cr4(vcpu, sregs->cr4) &&
- kvm_is_valid_cr0(vcpu, sregs->cr0);
+ kvm_is_valid_cr0(vcpu, sregs->cr0) &&
+ kvm_valid_efer(vcpu, sregs->efer);
}
static int __set_sregs_common(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs,
--
2.55.0.rc0.799.gd6f94ed593-goog
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 2/7] KVM: SVM: Disallow EFER.SVME and EFER.LSMLE if nested is disabled
2026-06-30 23:47 [PATCH 0/7] KVM: x86: EFER validity fixes and cleanups Yosry Ahmed
2026-06-30 23:47 ` [PATCH 1/7] KVM: x86: Check EFER validity on KVM_SET_SREGS* Yosry Ahmed
@ 2026-06-30 23:47 ` Yosry Ahmed
2026-06-30 23:47 ` [PATCH 3/7] KVM: x86: Disallow EFER.LME and EFER.LMA if long mode is not supported Yosry Ahmed
` (4 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Yosry Ahmed @ 2026-06-30 23:47 UTC (permalink / raw)
To: Sean Christopherson; +Cc: Paolo Bonzini, kvm, linux-kernel, Yosry Ahmed, stable
Explicitly disallow setting EFER.SVME and EFER.LSMLE if nested
virtualization is disabled on SVM, to prevent the bits remaining allowed
if kvm_amd is loaded with nested=1 and then reloaded with nested=0.
This is a minimal fix for the benefit of stable backports, which will be
followed by a more systematic fix (moving efer_reserved_bits to
kvm_caps).
Cc: stable@vger.kernel.org
Signed-off-by: Yosry Ahmed <yosry@kernel.org>
---
arch/x86/kvm/msrs.c | 8 +++++++-
arch/x86/kvm/msrs.h | 1 +
arch/x86/kvm/svm/svm.c | 3 +++
3 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/arch/x86/kvm/msrs.c b/arch/x86/kvm/msrs.c
index c230b18d87e38..45170df0ce40b 100644
--- a/arch/x86/kvm/msrs.c
+++ b/arch/x86/kvm/msrs.c
@@ -660,10 +660,16 @@ static int set_efer(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
void kvm_enable_efer_bits(u64 mask)
{
- efer_reserved_bits &= ~mask;
+ efer_reserved_bits &= ~mask;
}
EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_enable_efer_bits);
+void kvm_disable_efer_bits(u64 mask)
+{
+ efer_reserved_bits |= mask;
+}
+EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_disable_efer_bits);
+
bool kvm_msr_allowed(struct kvm_vcpu *vcpu, u32 index, u32 type)
{
struct kvm_x86_msr_filter *msr_filter;
diff --git a/arch/x86/kvm/msrs.h b/arch/x86/kvm/msrs.h
index b698983e37fb6..89f10447cdddf 100644
--- a/arch/x86/kvm/msrs.h
+++ b/arch/x86/kvm/msrs.h
@@ -59,6 +59,7 @@ int kvm_get_reg_list(struct kvm_vcpu *vcpu,
struct kvm_reg_list __user *user_list);
void kvm_enable_efer_bits(u64);
+void kvm_disable_efer_bits(u64);
bool kvm_valid_efer(struct kvm_vcpu *vcpu, u64 efer);
int kvm_emulate_msr_read(struct kvm_vcpu *vcpu, u32 index, u64 *data);
int kvm_emulate_msr_write(struct kvm_vcpu *vcpu, u32 index, u64 data);
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index ef69a51ab27f9..1d51500238462 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -5645,6 +5645,9 @@ static __init int svm_hardware_setup(void)
r = nested_svm_init_msrpm_merge_offsets();
if (r)
return r;
+ } else {
+ kvm_disable_efer_bits(EFER_SVME);
+ kvm_disable_efer_bits(EFER_LMSLE);
}
/*
--
2.55.0.rc0.799.gd6f94ed593-goog
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 3/7] KVM: x86: Disallow EFER.LME and EFER.LMA if long mode is not supported
2026-06-30 23:47 [PATCH 0/7] KVM: x86: EFER validity fixes and cleanups Yosry Ahmed
2026-06-30 23:47 ` [PATCH 1/7] KVM: x86: Check EFER validity on KVM_SET_SREGS* Yosry Ahmed
2026-06-30 23:47 ` [PATCH 2/7] KVM: SVM: Disallow EFER.SVME and EFER.LSMLE if nested is disabled Yosry Ahmed
@ 2026-06-30 23:47 ` Yosry Ahmed
2026-06-30 23:47 ` [PATCH 4/7] KVM: x86: Add a per-vendor callback to setup EFER caps Yosry Ahmed
` (3 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Yosry Ahmed @ 2026-06-30 23:47 UTC (permalink / raw)
To: Sean Christopherson; +Cc: Paolo Bonzini, kvm, linux-kernel, Yosry Ahmed
Remove EFER.LME and EFER.LMA from EFER reserved bits only if long mode
is actually supported. KVM does check long-mode support before allowing
the bits for guest writes and userspace writes through KVM_SET_SREGS*
(in __kvm_valid_efer()), but userspace writes through KVM_SET_MSRS only
check reserved bits.
A nice side effect (and the true motivation) is getting rid of
the #ifdeffery when initializing efer_reserved_bits.
Signed-off-by: Yosry Ahmed <yosry@kernel.org>
---
arch/x86/kvm/msrs.c | 10 +---------
arch/x86/kvm/x86.c | 3 +++
2 files changed, 4 insertions(+), 9 deletions(-)
diff --git a/arch/x86/kvm/msrs.c b/arch/x86/kvm/msrs.c
index 45170df0ce40b..17d4c813a9e8a 100644
--- a/arch/x86/kvm/msrs.c
+++ b/arch/x86/kvm/msrs.c
@@ -19,16 +19,8 @@ bool __read_mostly report_ignored_msrs = true;
module_param(report_ignored_msrs, bool, 0644);
EXPORT_SYMBOL_FOR_KVM_INTERNAL(report_ignored_msrs);
-/* EFER defaults:
- * - enable syscall per default because its emulated by KVM
- * - enable LME and LMA per default on 64 bit KVM
- */
-#ifdef CONFIG_X86_64
-static
-u64 __read_mostly efer_reserved_bits = ~((u64)(EFER_SCE | EFER_LME | EFER_LMA));
-#else
+/* Enable syscall by default because its emulated by KVM */
static u64 __read_mostly efer_reserved_bits = ~((u64)EFER_SCE);
-#endif
#define MAX_IO_MSRS 256
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 8dbc0fa302a8b..a0b2c40d93c21 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -6892,6 +6892,9 @@ EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_setup_xss_caps);
static void kvm_setup_efer_caps(void)
{
+ if (kvm_cpu_cap_has(X86_FEATURE_LM))
+ kvm_enable_efer_bits(EFER_LME | EFER_LMA);
+
if (kvm_cpu_cap_has(X86_FEATURE_NX))
kvm_enable_efer_bits(EFER_NX);
--
2.55.0.rc0.799.gd6f94ed593-goog
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 4/7] KVM: x86: Add a per-vendor callback to setup EFER caps
2026-06-30 23:47 [PATCH 0/7] KVM: x86: EFER validity fixes and cleanups Yosry Ahmed
` (2 preceding siblings ...)
2026-06-30 23:47 ` [PATCH 3/7] KVM: x86: Disallow EFER.LME and EFER.LMA if long mode is not supported Yosry Ahmed
@ 2026-06-30 23:47 ` Yosry Ahmed
2026-06-30 23:47 ` [PATCH 5/7] KVM: x86: Reverse the polarity of efer_reserved_bits Yosry Ahmed
` (2 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Yosry Ahmed @ 2026-06-30 23:47 UTC (permalink / raw)
To: Sean Christopherson; +Cc: Paolo Bonzini, kvm, linux-kernel, Yosry Ahmed
Move handling EFER.SVME and EFER.LMSLE from hardware setup to a new
optional per-vendor callback invoked from kvm_setup_efer_caps(). This
centralizes allowed EFER bits handling to kvm_setup_efer_caps(),
facilitating following changes to move efer_reserved_bits into kvm_caps.
Move the call to kvm_setup_efer_caps() after per-vendor ops are
initialized.
No functional change intended.
Signed-off-by: Yosry Ahmed <yosry@kernel.org>
---
arch/x86/include/asm/kvm-x86-ops.h | 1 +
arch/x86/include/asm/kvm_host.h | 1 +
arch/x86/kvm/svm/svm.c | 20 +++++++++++++-------
arch/x86/kvm/x86.c | 6 ++++--
4 files changed, 19 insertions(+), 9 deletions(-)
diff --git a/arch/x86/include/asm/kvm-x86-ops.h b/arch/x86/include/asm/kvm-x86-ops.h
index 83dc5086138b3..d587242dcab56 100644
--- a/arch/x86/include/asm/kvm-x86-ops.h
+++ b/arch/x86/include/asm/kvm-x86-ops.h
@@ -45,6 +45,7 @@ KVM_X86_OP_OPTIONAL(post_set_cr3)
KVM_X86_OP(is_valid_cr4)
KVM_X86_OP(set_cr4)
KVM_X86_OP(set_efer)
+KVM_X86_OP_OPTIONAL(setup_efer_caps)
KVM_X86_OP(get_idt)
KVM_X86_OP(set_idt)
KVM_X86_OP(get_gdt)
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index d8700eb848b45..0efd93f961df4 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -1730,6 +1730,7 @@ struct kvm_x86_ops {
bool (*is_valid_cr4)(struct kvm_vcpu *vcpu, unsigned long cr4);
void (*set_cr4)(struct kvm_vcpu *vcpu, unsigned long cr4);
int (*set_efer)(struct kvm_vcpu *vcpu, u64 efer);
+ void (*setup_efer_caps)(void);
void (*get_idt)(struct kvm_vcpu *vcpu, struct desc_ptr *dt);
void (*set_idt)(struct kvm_vcpu *vcpu, struct desc_ptr *dt);
void (*get_gdt)(struct kvm_vcpu *vcpu, struct desc_ptr *dt);
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index 1d51500238462..747f1e31a3622 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -274,6 +274,18 @@ int svm_set_efer(struct kvm_vcpu *vcpu, u64 efer)
return 0;
}
+static void svm_setup_efer_caps(void)
+{
+ if (nested) {
+ kvm_enable_efer_bits(EFER_SVME);
+ if (!boot_cpu_has(X86_FEATURE_EFER_LMSLE_MBZ))
+ kvm_enable_efer_bits(EFER_LMSLE);
+ } else {
+ kvm_disable_efer_bits(EFER_SVME);
+ kvm_disable_efer_bits(EFER_LMSLE);
+ }
+}
+
static u32 svm_get_interrupt_shadow(struct kvm_vcpu *vcpu)
{
struct vcpu_svm *svm = to_svm(vcpu);
@@ -5364,6 +5376,7 @@ struct kvm_x86_ops svm_x86_ops __initdata = {
.is_valid_cr4 = svm_is_valid_cr4,
.set_cr4 = svm_set_cr4,
.set_efer = svm_set_efer,
+ .setup_efer_caps = svm_setup_efer_caps,
.get_idt = svm_get_idt,
.set_idt = svm_set_idt,
.get_gdt = svm_get_gdt,
@@ -5638,16 +5651,9 @@ static __init int svm_hardware_setup(void)
if (nested) {
pr_info("Nested Virtualization enabled\n");
- kvm_enable_efer_bits(EFER_SVME);
- if (!boot_cpu_has(X86_FEATURE_EFER_LMSLE_MBZ))
- kvm_enable_efer_bits(EFER_LMSLE);
-
r = nested_svm_init_msrpm_merge_offsets();
if (r)
return r;
- } else {
- kvm_disable_efer_bits(EFER_SVME);
- kvm_disable_efer_bits(EFER_LMSLE);
}
/*
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index a0b2c40d93c21..a297a77469b38 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -6903,6 +6903,8 @@ static void kvm_setup_efer_caps(void)
if (kvm_cpu_cap_has(X86_FEATURE_AUTOIBRS))
kvm_enable_efer_bits(EFER_AUTOIBRS);
+
+ kvm_x86_call(setup_efer_caps)();
}
static inline void kvm_ops_update(struct kvm_x86_init_ops *ops)
@@ -7041,13 +7043,13 @@ int kvm_x86_vendor_init(struct kvm_x86_init_ops *ops)
if (r != 0)
goto out_mmu_exit;
- kvm_setup_efer_caps();
-
enable_device_posted_irqs &= enable_apicv &&
irq_remapping_cap(IRQ_POSTING_CAP);
kvm_ops_update(ops);
+ kvm_setup_efer_caps();
+
for_each_online_cpu(cpu) {
smp_call_function_single(cpu, kvm_x86_check_cpu_compat, &r, 1);
if (r < 0)
--
2.55.0.rc0.799.gd6f94ed593-goog
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 5/7] KVM: x86: Reverse the polarity of efer_reserved_bits
2026-06-30 23:47 [PATCH 0/7] KVM: x86: EFER validity fixes and cleanups Yosry Ahmed
` (3 preceding siblings ...)
2026-06-30 23:47 ` [PATCH 4/7] KVM: x86: Add a per-vendor callback to setup EFER caps Yosry Ahmed
@ 2026-06-30 23:47 ` Yosry Ahmed
2026-06-30 23:47 ` [PATCH 6/7] KVM: x86: Move supported EFER bits to kvm_caps Yosry Ahmed
2026-06-30 23:47 ` [PATCH 7/7] KVM: selftests: Extend set_sregs test to cover EFER Yosry Ahmed
6 siblings, 0 replies; 8+ messages in thread
From: Yosry Ahmed @ 2026-06-30 23:47 UTC (permalink / raw)
To: Sean Christopherson; +Cc: Paolo Bonzini, kvm, linux-kernel, Yosry Ahmed
In preparation for moving efer_reserved_bits into kvm_caps, reverse its
polarity and make it efer_supported_bits, to be more consistent with
other fields in kvm_caps.
No functional change intended.
Signed-off-by: Yosry Ahmed <yosry@kernel.org>
---
arch/x86/kvm/msrs.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/arch/x86/kvm/msrs.c b/arch/x86/kvm/msrs.c
index 17d4c813a9e8a..b9a16a27f6c23 100644
--- a/arch/x86/kvm/msrs.c
+++ b/arch/x86/kvm/msrs.c
@@ -20,7 +20,7 @@ module_param(report_ignored_msrs, bool, 0644);
EXPORT_SYMBOL_FOR_KVM_INTERNAL(report_ignored_msrs);
/* Enable syscall by default because its emulated by KVM */
-static u64 __read_mostly efer_reserved_bits = ~((u64)EFER_SCE);
+static u64 __read_mostly efer_supported_bits = ((u64)EFER_SCE);
#define MAX_IO_MSRS 256
@@ -606,7 +606,7 @@ static bool __kvm_valid_efer(struct kvm_vcpu *vcpu, u64 efer)
}
bool kvm_valid_efer(struct kvm_vcpu *vcpu, u64 efer)
{
- if (efer & efer_reserved_bits)
+ if (!(efer & efer_supported_bits))
return false;
return __kvm_valid_efer(vcpu, efer);
@@ -619,7 +619,7 @@ static int set_efer(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
u64 efer = msr_info->data;
int r;
- if (efer & efer_reserved_bits)
+ if (!(efer & efer_supported_bits))
return 1;
if (!msr_info->host_initiated) {
@@ -652,13 +652,13 @@ static int set_efer(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
void kvm_enable_efer_bits(u64 mask)
{
- efer_reserved_bits &= ~mask;
+ efer_supported_bits |= mask;
}
EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_enable_efer_bits);
void kvm_disable_efer_bits(u64 mask)
{
- efer_reserved_bits |= mask;
+ efer_supported_bits &= ~mask;
}
EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_disable_efer_bits);
--
2.55.0.rc0.799.gd6f94ed593-goog
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 6/7] KVM: x86: Move supported EFER bits to kvm_caps
2026-06-30 23:47 [PATCH 0/7] KVM: x86: EFER validity fixes and cleanups Yosry Ahmed
` (4 preceding siblings ...)
2026-06-30 23:47 ` [PATCH 5/7] KVM: x86: Reverse the polarity of efer_reserved_bits Yosry Ahmed
@ 2026-06-30 23:47 ` Yosry Ahmed
2026-06-30 23:47 ` [PATCH 7/7] KVM: selftests: Extend set_sregs test to cover EFER Yosry Ahmed
6 siblings, 0 replies; 8+ messages in thread
From: Yosry Ahmed @ 2026-06-30 23:47 UTC (permalink / raw)
To: Sean Christopherson; +Cc: Paolo Bonzini, kvm, linux-kernel, Yosry Ahmed
Supported EFER bits naturally fits into kvm_caps because it needs to be
recomputed during vendor init (e.g. to account for EFER.SVME being
allowed/disallowed based on nested being enabled/disabled).
Move efer_supported_bits into kvm_caps as supported_efer_bits (for
naming consistency), and reinitialize it at the beginning of
kvm_setup_efer_caps(), removing the need to clear unsupported bits in
vendor code (e.g. EFER.SVME).
As the bitmask is now globally visible as part of kvm_caps, there's
little use for helpers to enable/disable specific bits, so drop them and
open-code updates to kvm_caps.supported_efer_bits.
No functional change intended.
Suggested-by: Sean Christopherson <seanjc@google.com>
Signed-off-by: Yosry Ahmed <yosry@kernel.org>
---
arch/x86/include/asm/kvm_host.h | 2 ++
arch/x86/kvm/msrs.c | 19 ++-----------------
arch/x86/kvm/svm/svm.c | 7 ++-----
arch/x86/kvm/x86.c | 11 +++++++----
4 files changed, 13 insertions(+), 26 deletions(-)
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 0efd93f961df4..40be39fea39b9 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -329,6 +329,8 @@ struct kvm_caps {
u64 supported_xss;
u64 supported_perf_cap;
+ u64 supported_efer_bits;
+
u64 supported_quirks;
u64 inapplicable_quirks;
};
diff --git a/arch/x86/kvm/msrs.c b/arch/x86/kvm/msrs.c
index b9a16a27f6c23..a39c73088f1fb 100644
--- a/arch/x86/kvm/msrs.c
+++ b/arch/x86/kvm/msrs.c
@@ -19,9 +19,6 @@ bool __read_mostly report_ignored_msrs = true;
module_param(report_ignored_msrs, bool, 0644);
EXPORT_SYMBOL_FOR_KVM_INTERNAL(report_ignored_msrs);
-/* Enable syscall by default because its emulated by KVM */
-static u64 __read_mostly efer_supported_bits = ((u64)EFER_SCE);
-
#define MAX_IO_MSRS 256
struct msr_bitmap_range {
@@ -606,7 +603,7 @@ static bool __kvm_valid_efer(struct kvm_vcpu *vcpu, u64 efer)
}
bool kvm_valid_efer(struct kvm_vcpu *vcpu, u64 efer)
{
- if (!(efer & efer_supported_bits))
+ if (!(efer & kvm_caps.supported_efer_bits))
return false;
return __kvm_valid_efer(vcpu, efer);
@@ -619,7 +616,7 @@ static int set_efer(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
u64 efer = msr_info->data;
int r;
- if (!(efer & efer_supported_bits))
+ if (!(efer & kvm_caps.supported_efer_bits))
return 1;
if (!msr_info->host_initiated) {
@@ -650,18 +647,6 @@ static int set_efer(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
return 0;
}
-void kvm_enable_efer_bits(u64 mask)
-{
- efer_supported_bits |= mask;
-}
-EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_enable_efer_bits);
-
-void kvm_disable_efer_bits(u64 mask)
-{
- efer_supported_bits &= ~mask;
-}
-EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_disable_efer_bits);
-
bool kvm_msr_allowed(struct kvm_vcpu *vcpu, u32 index, u32 type)
{
struct kvm_x86_msr_filter *msr_filter;
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index 747f1e31a3622..e755f43f4376e 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -277,12 +277,9 @@ int svm_set_efer(struct kvm_vcpu *vcpu, u64 efer)
static void svm_setup_efer_caps(void)
{
if (nested) {
- kvm_enable_efer_bits(EFER_SVME);
+ kvm_caps.supported_efer_bits |= EFER_SVME;
if (!boot_cpu_has(X86_FEATURE_EFER_LMSLE_MBZ))
- kvm_enable_efer_bits(EFER_LMSLE);
- } else {
- kvm_disable_efer_bits(EFER_SVME);
- kvm_disable_efer_bits(EFER_LMSLE);
+ kvm_caps.supported_efer_bits |= EFER_LMSLE;
}
}
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index a297a77469b38..5cedaa8409f8d 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -6892,17 +6892,20 @@ EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_setup_xss_caps);
static void kvm_setup_efer_caps(void)
{
+ /* Enable syscall by default because its emulated by KVM */
+ kvm_caps.supported_efer_bits = (u64)EFER_SCE;
+
if (kvm_cpu_cap_has(X86_FEATURE_LM))
- kvm_enable_efer_bits(EFER_LME | EFER_LMA);
+ kvm_caps.supported_efer_bits |= (EFER_LME | EFER_LMA);
if (kvm_cpu_cap_has(X86_FEATURE_NX))
- kvm_enable_efer_bits(EFER_NX);
+ kvm_caps.supported_efer_bits |= EFER_NX;
if (kvm_cpu_cap_has(X86_FEATURE_FXSR_OPT))
- kvm_enable_efer_bits(EFER_FFXSR);
+ kvm_caps.supported_efer_bits |= EFER_FFXSR;
if (kvm_cpu_cap_has(X86_FEATURE_AUTOIBRS))
- kvm_enable_efer_bits(EFER_AUTOIBRS);
+ kvm_caps.supported_efer_bits |= EFER_AUTOIBRS;
kvm_x86_call(setup_efer_caps)();
}
--
2.55.0.rc0.799.gd6f94ed593-goog
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 7/7] KVM: selftests: Extend set_sregs test to cover EFER
2026-06-30 23:47 [PATCH 0/7] KVM: x86: EFER validity fixes and cleanups Yosry Ahmed
` (5 preceding siblings ...)
2026-06-30 23:47 ` [PATCH 6/7] KVM: x86: Move supported EFER bits to kvm_caps Yosry Ahmed
@ 2026-06-30 23:47 ` Yosry Ahmed
6 siblings, 0 replies; 8+ messages in thread
From: Yosry Ahmed @ 2026-06-30 23:47 UTC (permalink / raw)
To: Sean Christopherson; +Cc: Paolo Bonzini, kvm, linux-kernel, Yosry Ahmed
Extend the set_sregs test to cover various bits in EFER. Update
TEST_INVALID_CR_BIT() to operate on EFER as well as CRx (and rename it
accordingly). Add test cases to check that EFER bits are disallowed
without the relevant CPUID enablement.
Assisted-by: Gemini:unknown-version
Signed-off-by: Yosry Ahmed <yosry@kernel.org>
---
.../selftests/kvm/include/x86/processor.h | 2 +
.../selftests/kvm/x86/set_sregs_test.c | 83 ++++++++++++++-----
2 files changed, 64 insertions(+), 21 deletions(-)
diff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/testing/selftests/kvm/include/x86/processor.h
index 7d3a27bc0d842..b161174ece453 100644
--- a/tools/testing/selftests/kvm/include/x86/processor.h
+++ b/tools/testing/selftests/kvm/include/x86/processor.h
@@ -208,6 +208,7 @@ struct kvm_x86_cpu_feature {
#define X86_FEATURE_PERFCTR_NB KVM_X86_CPU_FEATURE(0x80000001, 0, ECX, 24)
#define X86_FEATURE_PERFCTR_LLC KVM_X86_CPU_FEATURE(0x80000001, 0, ECX, 28)
#define X86_FEATURE_NX KVM_X86_CPU_FEATURE(0x80000001, 0, EDX, 20)
+#define X86_FEATURE_FXSR_OPT KVM_X86_CPU_FEATURE(0x80000001, 0, EDX, 25)
#define X86_FEATURE_GBPAGES KVM_X86_CPU_FEATURE(0x80000001, 0, EDX, 26)
#define X86_FEATURE_RDTSCP KVM_X86_CPU_FEATURE(0x80000001, 0, EDX, 27)
#define X86_FEATURE_LM KVM_X86_CPU_FEATURE(0x80000001, 0, EDX, 29)
@@ -226,6 +227,7 @@ struct kvm_x86_cpu_feature {
#define X86_FEATURE_SEV KVM_X86_CPU_FEATURE(0x8000001F, 0, EAX, 1)
#define X86_FEATURE_SEV_ES KVM_X86_CPU_FEATURE(0x8000001F, 0, EAX, 3)
#define X86_FEATURE_SEV_SNP KVM_X86_CPU_FEATURE(0x8000001F, 0, EAX, 4)
+#define X86_FEATURE_AUTOIBRS KVM_X86_CPU_FEATURE(0x80000021, 0, EAX, 8)
#define X86_FEATURE_GP_ON_USER_CPUID KVM_X86_CPU_FEATURE(0x80000021, 0, EAX, 17)
#define X86_FEATURE_PERFMON_V2 KVM_X86_CPU_FEATURE(0x80000022, 0, EAX, 0)
#define X86_FEATURE_LBR_PMC_FREEZE KVM_X86_CPU_FEATURE(0x80000022, 0, EAX, 2)
diff --git a/tools/testing/selftests/kvm/x86/set_sregs_test.c b/tools/testing/selftests/kvm/x86/set_sregs_test.c
index 8e654cc9ab168..562afab378d11 100644
--- a/tools/testing/selftests/kvm/x86/set_sregs_test.c
+++ b/tools/testing/selftests/kvm/x86/set_sregs_test.c
@@ -21,20 +21,20 @@
#include "kvm_util.h"
#include "processor.h"
-#define TEST_INVALID_CR_BIT(vcpu, cr, orig, bit) \
+#define TEST_INVALID_SREG_BIT(vcpu, reg, orig, bit) \
do { \
struct kvm_sregs new; \
int rc; \
\
/* Skip the sub-test, the feature/bit is supported. */ \
- if (orig.cr & bit) \
+ if (orig.reg & bit) \
break; \
\
- memcpy(&new, &orig, sizeof(sregs)); \
- new.cr |= bit; \
+ memcpy(&new, &orig, sizeof(new)); \
+ new.reg |= bit; \
\
rc = _vcpu_sregs_set(vcpu, &new); \
- TEST_ASSERT(rc, "KVM allowed invalid " #cr " bit (0x%lx)", bit); \
+ TEST_ASSERT(rc, "KVM allowed invalid " #reg " bit (0x%llx)", (unsigned long long)bit); \
\
/* Sanity check that KVM didn't change anything. */ \
vcpu_sregs_get(vcpu, &new); \
@@ -46,6 +46,8 @@ do { \
X86_CR4_MCE | X86_CR4_PGE | X86_CR4_PCE | \
X86_CR4_OSFXSR | X86_CR4_OSXMMEXCPT)
+#define KVM_ALWAYS_ALLOWED_EFER EFER_SCE
+
static u64 calc_supported_cr4_feature_bits(void)
{
u64 cr4 = KVM_ALWAYS_ALLOWED_CR4;
@@ -74,6 +76,24 @@ static u64 calc_supported_cr4_feature_bits(void)
return cr4;
}
+static u64 calc_supported_efer_feature_bits(void)
+{
+ u64 efer = KVM_ALWAYS_ALLOWED_EFER;
+
+ if (kvm_cpu_has(X86_FEATURE_LM))
+ efer |= (EFER_LME | EFER_LMA);
+ if (kvm_cpu_has(X86_FEATURE_NX))
+ efer |= EFER_NX;
+ if (kvm_cpu_has(X86_FEATURE_SVM))
+ efer |= EFER_SVME;
+ if (kvm_cpu_has(X86_FEATURE_FXSR_OPT))
+ efer |= EFER_FFXSR;
+ if (kvm_cpu_has(X86_FEATURE_AUTOIBRS))
+ efer |= EFER_AUTOIBRS;
+
+ return efer;
+}
+
static void test_cr_bits(struct kvm_vcpu *vcpu, u64 cr4)
{
struct kvm_sregs sregs;
@@ -96,26 +116,45 @@ static void test_cr_bits(struct kvm_vcpu *vcpu, u64 cr4)
(sregs.cr4 & X86_CR4_PKE) ? "set" : "clear");
vcpu_sregs_get(vcpu, &sregs);
- TEST_ASSERT(sregs.cr4 == cr4, "sregs.CR4 (0x%llx) != CR4 (0x%lx)",
- sregs.cr4, cr4);
-
- TEST_INVALID_CR_BIT(vcpu, cr4, sregs, X86_CR4_UMIP);
- TEST_INVALID_CR_BIT(vcpu, cr4, sregs, X86_CR4_LA57);
- TEST_INVALID_CR_BIT(vcpu, cr4, sregs, X86_CR4_VMXE);
- TEST_INVALID_CR_BIT(vcpu, cr4, sregs, X86_CR4_SMXE);
- TEST_INVALID_CR_BIT(vcpu, cr4, sregs, X86_CR4_FSGSBASE);
- TEST_INVALID_CR_BIT(vcpu, cr4, sregs, X86_CR4_PCIDE);
- TEST_INVALID_CR_BIT(vcpu, cr4, sregs, X86_CR4_OSXSAVE);
- TEST_INVALID_CR_BIT(vcpu, cr4, sregs, X86_CR4_SMEP);
- TEST_INVALID_CR_BIT(vcpu, cr4, sregs, X86_CR4_SMAP);
- TEST_INVALID_CR_BIT(vcpu, cr4, sregs, X86_CR4_PKE);
+ TEST_ASSERT_EQ(sregs.cr4, cr4);
+
+ TEST_INVALID_SREG_BIT(vcpu, cr4, sregs, X86_CR4_UMIP);
+ TEST_INVALID_SREG_BIT(vcpu, cr4, sregs, X86_CR4_LA57);
+ TEST_INVALID_SREG_BIT(vcpu, cr4, sregs, X86_CR4_VMXE);
+ TEST_INVALID_SREG_BIT(vcpu, cr4, sregs, X86_CR4_SMXE);
+ TEST_INVALID_SREG_BIT(vcpu, cr4, sregs, X86_CR4_FSGSBASE);
+ TEST_INVALID_SREG_BIT(vcpu, cr4, sregs, X86_CR4_PCIDE);
+ TEST_INVALID_SREG_BIT(vcpu, cr4, sregs, X86_CR4_OSXSAVE);
+ TEST_INVALID_SREG_BIT(vcpu, cr4, sregs, X86_CR4_SMEP);
+ TEST_INVALID_SREG_BIT(vcpu, cr4, sregs, X86_CR4_SMAP);
+ TEST_INVALID_SREG_BIT(vcpu, cr4, sregs, X86_CR4_PKE);
for (i = 32; i < 64; i++)
- TEST_INVALID_CR_BIT(vcpu, cr0, sregs, BIT(i));
+ TEST_INVALID_SREG_BIT(vcpu, cr0, sregs, BIT(i));
/* NW without CD is illegal, as is PG without PE. */
- TEST_INVALID_CR_BIT(vcpu, cr0, sregs, X86_CR0_NW);
- TEST_INVALID_CR_BIT(vcpu, cr0, sregs, X86_CR0_PG);
+ TEST_INVALID_SREG_BIT(vcpu, cr0, sregs, X86_CR0_NW);
+ TEST_INVALID_SREG_BIT(vcpu, cr0, sregs, X86_CR0_PG);
+}
+
+static void test_efer_bits(struct kvm_vcpu *vcpu, u64 efer)
+{
+ struct kvm_sregs sregs;
+ int rc;
+
+ vcpu_sregs_get(vcpu, &sregs);
+ sregs.efer |= efer;
+ rc = _vcpu_sregs_set(vcpu, &sregs);
+ TEST_ASSERT(!rc, "Failed to set supported EFER bits (0x%llx)", sregs.efer);
+
+ vcpu_sregs_get(vcpu, &sregs);
+ TEST_ASSERT_EQ(sregs.efer, efer);
+
+ TEST_INVALID_SREG_BIT(vcpu, efer, sregs, EFER_LME);
+ TEST_INVALID_SREG_BIT(vcpu, efer, sregs, EFER_NX);
+ TEST_INVALID_SREG_BIT(vcpu, efer, sregs, EFER_SVME);
+ TEST_INVALID_SREG_BIT(vcpu, efer, sregs, EFER_FFXSR);
+ TEST_INVALID_SREG_BIT(vcpu, efer, sregs, EFER_AUTOIBRS);
}
int main(int argc, char *argv[])
@@ -132,6 +171,7 @@ int main(int argc, char *argv[])
*/
vm = vm_create_barebones();
vcpu = __vm_vcpu_add(vm, 0);
+ test_efer_bits(vcpu, KVM_ALWAYS_ALLOWED_EFER);
test_cr_bits(vcpu, KVM_ALWAYS_ALLOWED_CR4);
kvm_vm_free(vm);
@@ -151,6 +191,7 @@ int main(int argc, char *argv[])
sregs.apic_base);
test_cr_bits(vcpu, calc_supported_cr4_feature_bits());
+ test_efer_bits(vcpu, calc_supported_efer_feature_bits());
kvm_vm_free(vm);
--
2.55.0.rc0.799.gd6f94ed593-goog
^ permalink raw reply related [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-06-30 23:47 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-30 23:47 [PATCH 0/7] KVM: x86: EFER validity fixes and cleanups Yosry Ahmed
2026-06-30 23:47 ` [PATCH 1/7] KVM: x86: Check EFER validity on KVM_SET_SREGS* Yosry Ahmed
2026-06-30 23:47 ` [PATCH 2/7] KVM: SVM: Disallow EFER.SVME and EFER.LSMLE if nested is disabled Yosry Ahmed
2026-06-30 23:47 ` [PATCH 3/7] KVM: x86: Disallow EFER.LME and EFER.LMA if long mode is not supported Yosry Ahmed
2026-06-30 23:47 ` [PATCH 4/7] KVM: x86: Add a per-vendor callback to setup EFER caps Yosry Ahmed
2026-06-30 23:47 ` [PATCH 5/7] KVM: x86: Reverse the polarity of efer_reserved_bits Yosry Ahmed
2026-06-30 23:47 ` [PATCH 6/7] KVM: x86: Move supported EFER bits to kvm_caps Yosry Ahmed
2026-06-30 23:47 ` [PATCH 7/7] KVM: selftests: Extend set_sregs test to cover EFER Yosry Ahmed
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox