[PATCH 2/3] KVM: SVM: Add host support for Enhanced SMT Protection
From: Pratik R. Sampat
Date: Mon Sep 14 2026 - 14:05:23 EST
Enhanced SMT Protection (ESMTP) protects an SEV-SNP guest from SMT side
channels by requiring that, while a vCPU is in guest mode, its SMT sibling
thread is either idle in host mode or executing a vCPU the guest has
declared to be a legal sibling. A legal sibling is another ESMTP vCPU of
the same guest that carries the same VCPU_SIBLING_MASK and agrees on
VCPU_ID outside that mask.
Hardware enforces this at VMRUN, which no longer enters guest mode right
away but stalls until the condition holds. Three new exits report that it
could not be met, all of them non-fatal:
ESMTP_ILLSIB: a sibling thread is running a vCPU that is not a legal
sibling of this one.
ESMTP_TIMEOUT: the stall exceeded the timeout programmed in the VMCB. The
timer bounds how long VMRUN waits for the core to settle. A module
parameter exposes an interface to program this timer.
ESMTP_RETRY: If DBREQ is detected, VMRUN exits. ESMTP_RETRY indicates an
internal event and the hypervisor should execute a VMRUN.
Refer to the AMD64 APM Vol 2, section "AMD64 Enhanced SMT Protection".
Signed-off-by: Pratik R. Sampat <prsampat@xxxxxxx>
---
arch/x86/include/asm/cpufeatures.h | 1 +
arch/x86/include/asm/msr-index.h | 1 +
arch/x86/include/asm/svm.h | 12 ++++++--
arch/x86/include/uapi/asm/svm.h | 9 +++++-
arch/x86/kernel/cpu/scattered.c | 1 +
arch/x86/kvm/svm/sev.c | 48 +++++++++++++++++++++++++++++-
arch/x86/kvm/svm/svm.c | 7 +++++
7 files changed, 75 insertions(+), 4 deletions(-)
diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h
index f70ee74b5f92..4cf477e95c58 100644
--- a/arch/x86/include/asm/cpufeatures.h
+++ b/arch/x86/include/asm/cpufeatures.h
@@ -529,6 +529,7 @@
* and purposes if CLEAR_CPU_BUF_VM is set).
*/
#define X86_FEATURE_X2AVIC_EXT (21*32+20) /* AMD SVM x2AVIC support for 4k vCPUs */
+#define X86_FEATURE_AMD_ESMTP (21*32+21) /* AMD Enhanced SMT Protection */
/*
* BUG word(s)
diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
index 3a8e51a0c9e8..fbcb3313e946 100644
--- a/arch/x86/include/asm/msr-index.h
+++ b/arch/x86/include/asm/msr-index.h
@@ -761,6 +761,7 @@
#define MSR_AMD64_SEG_RMP_ENABLED_BIT 0
#define MSR_AMD64_SEG_RMP_ENABLED BIT_ULL(MSR_AMD64_SEG_RMP_ENABLED_BIT)
#define MSR_AMD64_RMP_SEGMENT_SHIFT(x) (((x) & GENMASK_ULL(13, 8)) >> 8)
+#define MSR_AMD64_IDLE_WAKEUP_ICR 0xc0010137
#define MSR_SVSM_CAA 0xc001f000
diff --git a/arch/x86/include/asm/svm.h b/arch/x86/include/asm/svm.h
index aa63431ba92c..323ab7089302 100644
--- a/arch/x86/include/asm/svm.h
+++ b/arch/x86/include/asm/svm.h
@@ -165,7 +165,8 @@ struct __attribute__ ((__packed__)) vmcb_control_area {
u8 reserved_9[22];
u64 allowed_sev_features; /* Offset 0x138 */
u64 guest_sev_features; /* Offset 0x140 */
- u8 reserved_10[664];
+ u64 esmtp_timeout; /* Offset 0x148 */
+ u8 reserved_10[656];
/*
* Offset 0x3e0, 32 bytes reserved
* for use by hypervisor/software.
@@ -310,6 +311,7 @@ static_assert((X2AVIC_4K_MAX_PHYSICAL_ID & AVIC_PHYSICAL_MAX_INDEX_MASK) == X2AV
#define SVM_SEV_FEAT_ALTERNATE_INJECTION BIT(4)
#define SVM_SEV_FEAT_DEBUG_SWAP BIT(5)
#define SVM_SEV_FEAT_SECURE_TSC BIT(9)
+#define SVM_SEV_FEAT_ESMTP BIT(17)
#define VMCB_ALLOWED_SEV_FEATURES_VALID BIT_ULL(63)
@@ -482,6 +484,11 @@ struct sev_es_save_area {
u8 fpreg_x87[80];
u8 fpreg_xmm[256];
u8 fpreg_ymm[256];
+ u8 reserved_0x670[560];
+
+ /* Enhanced SMT Protection */
+ u32 vcpu_id;
+ u32 vcpu_sibling_mask;
} __packed;
struct ghcb_save_area {
@@ -554,7 +561,7 @@ struct vmcb {
#define EXPECTED_VMCB_SAVE_AREA_SIZE 744
#define EXPECTED_GHCB_SAVE_AREA_SIZE 1032
-#define EXPECTED_SEV_ES_SAVE_AREA_SIZE 1648
+#define EXPECTED_SEV_ES_SAVE_AREA_SIZE 2216
#define EXPECTED_VMCB_CONTROL_AREA_SIZE 1024
#define EXPECTED_GHCB_SIZE PAGE_SIZE
@@ -589,6 +596,7 @@ static inline void __unused_size_checks(void)
BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x320);
BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x380);
BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x3f0);
+ BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x670);
BUILD_BUG_RESERVED_OFFSET(ghcb_save_area, 0x0);
BUILD_BUG_RESERVED_OFFSET(ghcb_save_area, 0xcc);
diff --git a/arch/x86/include/uapi/asm/svm.h b/arch/x86/include/uapi/asm/svm.h
index 010a45c9f614..e59baf4b3c3f 100644
--- a/arch/x86/include/uapi/asm/svm.h
+++ b/arch/x86/include/uapi/asm/svm.h
@@ -135,6 +135,10 @@
#define SVM_EXIT_SW 0xf0000000ull
#define SVM_EXIT_ERR -1ull
+/* Enhanced SMT Protection VM exits taken during VMRUN sibling sync */
+#define SVM_EXIT_ESMTP_ILLSIB -5ull /* Illegal ESMTP sibling */
+#define SVM_EXIT_ESMTP_TIMEOUT -6ull /* ESMTP_TIMEOUT expired */
+#define SVM_EXIT_ESMTP_RETRY -7ull /* Internal event; retry VMRUN */
#define SVM_EXIT_REASONS \
{ SVM_EXIT_READ_CR0, "read_cr0" }, \
@@ -246,7 +250,10 @@
{ SVM_VMGEXIT_EXT_GUEST_REQUEST, "vmgexit_ext_guest_request" }, \
{ SVM_VMGEXIT_AP_CREATION, "vmgexit_ap_creation" }, \
{ SVM_VMGEXIT_HV_FEATURES, "vmgexit_hypervisor_feature" }, \
- { SVM_EXIT_ERR, "invalid_guest_state" }
+ { SVM_EXIT_ERR, "invalid_guest_state" }, \
+ { SVM_EXIT_ESMTP_ILLSIB, "esmtp_illsib" }, \
+ { SVM_EXIT_ESMTP_TIMEOUT, "esmtp_timeout" }, \
+ { SVM_EXIT_ESMTP_RETRY, "esmtp_retry" }
#endif /* _UAPI__SVM_H */
diff --git a/arch/x86/kernel/cpu/scattered.c b/arch/x86/kernel/cpu/scattered.c
index 8665a6474806..16620fa0e290 100644
--- a/arch/x86/kernel/cpu/scattered.c
+++ b/arch/x86/kernel/cpu/scattered.c
@@ -67,6 +67,7 @@ static const struct cpuid_bit cpuid_bits[] = {
{ X86_FEATURE_PERFMON_V2, CPUID_EAX, 0, 0x80000022, 0 },
{ X86_FEATURE_AMD_LBR_V2, CPUID_EAX, 1, 0x80000022, 0 },
{ X86_FEATURE_AMD_LBR_PMC_FREEZE, CPUID_EAX, 2, 0x80000022, 0 },
+ { X86_FEATURE_AMD_ESMTP, CPUID_EDX, 1, 0x80000025, 0 },
{ X86_FEATURE_AMD_HTR_CORES, CPUID_EAX, 30, 0x80000026, 0 },
{ 0, 0, 0, 0, 0 }
};
diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index 5705723f1f41..edb99af4d6a6 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -67,6 +67,11 @@ module_param_named(sev_snp, sev_snp_enabled, bool, 0444);
static unsigned int __ro_after_init nr_ciphertext_hiding_asids;
module_param_named(ciphertext_hiding_asids, nr_ciphertext_hiding_asids, uint, 0444);
+static unsigned long esmtp_timeout_ns;
+module_param(esmtp_timeout_ns, ulong, 0644);
+MODULE_PARM_DESC(esmtp_timeout_ns,
+ "ESMTP VMRUN sibling-wait timeout in nanoseconds (0 disables the timer)");
+
#define AP_RESET_HOLD_NONE 0
#define AP_RESET_HOLD_NAE_EVENT 1
#define AP_RESET_HOLD_MSR_PROTO 2
@@ -506,7 +511,7 @@ static int __sev_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp,
return -EINVAL;
if (!snp_active)
- valid_vmsa_features &= ~SVM_SEV_FEAT_SECURE_TSC;
+ valid_vmsa_features &= ~(SVM_SEV_FEAT_SECURE_TSC | SVM_SEV_FEAT_ESMTP);
if (data->vmsa_features & ~valid_vmsa_features)
return -EINVAL;
@@ -1021,6 +1026,15 @@ static int sev_es_sync_vmsa(struct vcpu_svm *svm)
save->sev_features = sev->vmsa_features;
+ if (sev->vmsa_features & SVM_SEV_FEAT_ESMTP) {
+ save->vcpu_id = vcpu->vcpu_id;
+ /*
+ * All vCPUs of a guest are put into one group and can be run
+ * co-resident on the sibling thread of the same core.
+ */
+ save->vcpu_sibling_mask = U32_MAX;
+ }
+
/*
* Skip FPU and AVX setup with KVM_SEV_ES_INIT to avoid
* breaking older measurements.
@@ -3083,6 +3097,30 @@ static const char * __init sev_str_feature_state(bool is_supported, bool is_usab
return is_supported ? is_usable ? "enabled" : "unusable" : "disabled";
}
+static bool sev_esmtp_setup_wakeup_icr(void)
+{
+ unsigned int cpu, sibling;
+ bool wrmsr_success = true;
+
+ for_each_online_cpu(cpu) {
+ for_each_cpu(sibling, topology_sibling_cpumask(cpu)) {
+ u64 icr;
+ int ret;
+
+ if (sibling == cpu)
+ continue;
+
+ icr = (u64)per_cpu(x86_cpu_to_apicid, sibling) << 32;
+ icr |= LOCAL_TIMER_VECTOR & 0xff;
+ ret = wrmsrq_safe_on_cpu(cpu, MSR_AMD64_IDLE_WAKEUP_ICR, icr);
+ if (ret)
+ wrmsr_success = false;
+ }
+ }
+
+ return wrmsr_success;
+}
+
void __init sev_hardware_setup(void)
{
unsigned int eax, ebx, ecx, edx, sev_asid_count, sev_es_asid_count;
@@ -3259,6 +3297,10 @@ void __init sev_hardware_setup(void)
if (sev_snp_enabled && tsc_khz && cpu_feature_enabled(X86_FEATURE_SNP_SECURE_TSC))
sev_supported_vmsa_features |= SVM_SEV_FEAT_SECURE_TSC;
+
+ if (sev_snp_enabled && cpu_feature_enabled(X86_FEATURE_AMD_ESMTP) &&
+ sev_esmtp_setup_wakeup_icr())
+ sev_supported_vmsa_features |= SVM_SEV_FEAT_ESMTP;
}
void sev_hardware_unsetup(void)
@@ -4792,6 +4834,10 @@ static void sev_es_init_vmcb(struct vcpu_svm *svm, bool init_event)
svm->vmcb->control.allowed_sev_features = sev->vmsa_features |
VMCB_ALLOWED_SEV_FEATURES_VALID;
+ if (sev->vmsa_features & SVM_SEV_FEAT_ESMTP)
+ svm->vmcb->control.esmtp_timeout = mul_u64_u32_div(esmtp_timeout_ns,
+ tsc_khz, 1000000);
+
/* Can't intercept CR register access, HV can't modify CR registers */
svm_clr_intercept(svm, INTERCEPT_CR0_READ);
svm_clr_intercept(svm, INTERCEPT_CR4_READ);
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index 5d15c706e43b..a1d77ce041b7 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -3743,6 +3743,13 @@ static int svm_handle_exit(struct kvm_vcpu *vcpu, fastpath_t exit_fastpath)
return 0;
}
+ if (svm->vmcb->control.exit_code == SVM_EXIT_ESMTP_RETRY ||
+ svm->vmcb->control.exit_code == SVM_EXIT_ESMTP_ILLSIB ||
+ svm->vmcb->control.exit_code == SVM_EXIT_ESMTP_TIMEOUT) {
+ cond_resched();
+ return 1;
+ }
+
if (exit_fastpath != EXIT_FASTPATH_NONE)
return 1;
--
2.43.0