Introduce Enhanced SMT Protection for SEV-SNP
Subsystems (5)
v4 cover Introduce Enhanced SMT Protection for SEV-SNP
Enhanced SMT Protection (ESMTP) allows an SEV-SNP VM to require that, while one of its vCPUs is in guest mode, every SMT sibling thread on that physical core is either idle in host mode or running a vCPU the guest itself has declared a legal sibling. This mitigates the side-channel risk of sharing core resources with untrusted host threads or with another guest.
Unlike core scheduling, where co-residency is a host kernel policy expressed with cookies, ESMTP is enforced by hardware. The sibling mask lives in the VMSA. The host is not trusted to run arbitrary kernel, userspace, or interrupt-handling work on a sibling thread while an ESMTP vCPU is active on that core.
Both KVM and the guest fully set the VCPU_SIBLING_MASK, which places every vCPU of the guest in one group so that any two of them may be co-resident. Combined with the ASID check, the sibling of a vCPU in guest mode is then always either another vCPU of that same guest or a thread idle in host mode.
Usage
-----
Requires patched OVMF [1] and QEMU [2] builds (unchanged from v2). Launch an SEV-SNP guest with ESMTP enabled on the sev-snp-guest object:
-object sev-snp-guest,id=sev0,cbitpos=51,reduced-phys-bits=1,esmtp=on
ESMTP is opt-in because it carries a performance cost as VMRUN stalls until the sibling runs work from a trusted vCPU or is in host idle.
The guest reports the feature in dmesg among the SNP feature names:
# dmesg | grep -i SEV ... SEV-SNP ... ESMTProt ...
Empirical test
--------------
* Identify siblings via:
cat /sys/devices/system/cpu/cpuX/topology/thread_siblings_list
* Pin the guest on the siblings
* Spawn a load in the guest e.g. via stress-ng --cpu 2 for SMT=2. Expect the guest utilization % for both sibling CPUs to be at 100% as the vCPU siblings are deemed trusted
* Spawn a load in the host latched onto one of the siblings. E.g. taskset -C X stress-ng --cpu 1
* Expect CPU X's utilization to be shared between host and guest %. Also expect drop in CPU utilization on the thread Sibling CPU as when the host task runs the guest will be forced idle.
Patches based on kvm/next
v4:
* Fix conflict of reserved bits introduced by PML support in vmcb_control_area
Response to Sashiko AI:
The EVENTINJ field is cleared unconditionally. so after a VMRUN, even if the VALID bit wasn't set, the field would be cleared. so I don't think we can hit the scenario that Sashiko is referring to.
v3: https://lore.kernel.org/kvm/[email protected]
* Collected Reviewed-by tags - Tom
* Reword esmtp_timeout_ns description - Tom
* Use sev_status to determine SNP support for guest as well - Tom
* Convert esmtp_timeout_ns to u32 instead of u64 to avoid a #DE error if the division extends over 64 bits. - Sashiko
Response to Sashiko AI
* Possibility of a stale EVENTINJ to clobber a valid EXITINTINFO here in svm_complete_interrupts():
- Today we make check only on a non-zero EVENTINJ rather than a VALID one since that check on exitintinfo is made on it anyways later within the function.
- AFAIU, it is not possible to have leftover events from L1 injected into L2 wihout the valid bit set.
- Only an L1 that does so deliberately can possibly trigger this and even then the impact is limited to that L1's L2.
- I could add an additional control->event_inj & SVM_EXITINTINFO_VALID check if is really a case that can be reached in reality but keeping it off for now.
* Not syncing msr-index changes since several bits apart from ESMTP are also changed and perf tooling maintainers generally sync that. However, if needed I drop in a patch that syncs the MSRs too.
v2: https://lore.kernel.org/kvm/[email protected]/
* Move idle wakeup ICR programming to svm_enable_virtualization_cpu() instead of sev_hardware_setup() - Sashiko
* Remove cond_sched() in ESMTP exit paths - Sashiko
* Add a VMSA builing of ESMTP fields for hyperv - Sashiko
* Add ESMTP to SNP_FEATURES_IMPL and SNP_FEATURES_IMPL_REQ - Local Claude Sashiko instance
* Cover hotplug CPUs / SMT changes by programming the ICR for all CPUs
- local Claude Sashiko instance
* Add esmtp timeout documentation to kernel paramters
* Collected Acked-by tag - Boris
v1: https://lore.kernel.org/kvm/[email protected]
[1]: https://github.com/tianocore/edk2/pull/13128
[2]: https://lore.kernel.org/qemu-devel/[email protected]/
Pratik R. Sampat (4):
KVM: SVM: Re-queue events that were never injected
KVM: SVM: Add host support for Enhanced SMT Protection x86/sev: Add guest support for Enhanced SMT Protection x86/hyperv: Add guest support for Enhanced SMT Protection
.../admin-guide/kernel-parameters.txt | 19 ++++++++ arch/x86/boot/compressed/sev.c | 6 ++- arch/x86/coco/sev/core.c | 13 +++++ arch/x86/hyperv/ivm.c | 11 +++++ arch/x86/include/asm/cpufeatures.h | 1 + arch/x86/include/asm/msr-index.h | 5 +- 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 | 41 ++++++++++++++-- arch/x86/kvm/svm/svm.h | 2 + 12 files changed, 157 insertions(+), 11 deletions(-)
Signature · 1 line
2.43.0
PATCH v4 1/4 KVM: SVM: Re-queue events that were never injected
When injecting an event into the guest via the event_inj field, a non-zero event_inj value on #VMEXIT means that the hardware was not able to inject the event into the guest. This used to only occur for the VMEXIT_INVALID intercept code, which was a fatal error and resulted in the guest being torn down.
Enhanced SMT Protection (ESMTP) invalidates that assumption. When ESMTP is enabled, VMRUN doesn't enter guest mode immediately; it stalls at a synchronization point until every sibling thread is either idle or has executed VMRUN for a legal sibling vCPU. If an ESMTP timeout / illegal sibling exit / interrupt arrives while VMRUN is stalled, VMRUN can now terminate with the corresponding #VMEXIT intercept code, without entering guest mode, and thus without injecting the event.
For example, on a 2-way SMT core running vCPU0 on thread 0 and vCPU1 on thread 1:
Thread 0 (vCPU0) Thread 1 (vCPU1)
---------------- ----------------
Interrupt A injected to the guest in host
VMRUN |
| |
v |
Stall waiting for sibling |
| |
| host INTR arrives on thread 0 |
| while it waits |
| |
v |
#VMEXIT v Interrupt B injected to the guest idle
VMRUN
In this case injecting interrupt B clobbers the last event. Interrupt A is never delivered and lost forever.
Therefore, in preparation for ESMTP support, accommodate for potentially lost interrupts by detecting an undelivered injected event and re-queuing it so as not to lose the event.
svm_cancel_injection() already recovers a staged event this way and becomes redundant, so fold it in.
---
v3..v4: No Change
--- arch/x86/kvm/svm/svm.c | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-)
arch/x86/kvm/svm/svm.c +29 -4
@@ -4459,6 +4459,7 @@ static void svm_complete_interrupts(struct kvm_vcpu *vcpu)struct vcpu_svm *svm =to_svm (vcpu);u8 vector;int type; +struct vmcb_control_area *control = &svm->vmcb->control;u32 exitintinfo = svm->vmcb->control.exit_int_info;bool nmi_l1_to_l2 = svm->nmi_l1_to_l2;bool soft_int_injected = svm->soft_int_injected;
@@ -4466,6 +4467,27 @@ static void svm_complete_interrupts(struct kvm_vcpu *vcpu) svm->nmi_l1_to_l2 = false; svm->soft_int_injected = false; +/* +* Hardware clears EVENTINJ field when it injects an event. +* A non-empty EVENTINJ on #VMEXIT means the vCPU never entered guest +* mode, and thus that the event was never delivered. Migrate the event +* to EXITINTINFO so that it's requeued instead of being dropped. +* +* An undelivered event doesn't imply a fatal VMEXIT_INVALID. With +* Enhanced SMT Protection, VMRUN may exit with an ordinary #VMEXIT +* without having injected that event into the guest. +* +* Clobbering EXITINTINFO is safe precisely because the vCPU never +* entered guest mode. +*/ +if (control->event_inj) { + control->exit_int_info = control->event_inj; + control->exit_int_info_err = control->event_inj_err; + control->event_inj =0 ; + + exitintinfo = control->exit_int_info; + } +/* * If we've made progress since setting awaiting_iret_completion, we've * executed an IRET and can allow NMI injection.
@@ -4529,11 +4551,14 @@ static void svm_complete_interrupts(struct kvm_vcpu *vcpu)static void svm_cancel_injection (struct kvm_vcpu *vcpu) {struct vcpu_svm *svm =to_svm (vcpu); -struct vmcb_control_area *control = &svm->vmcb->control; - control->exit_int_info = control->event_inj; - control->exit_int_info_err = control->event_inj_err; - control->event_inj =0 ; +/* +* EXITINTINFO is stale as it holds the event from the previous #VMEXIT +* (or from the last time the current VMCB was run). Invalidate it so +* that svm_complete_interrupts() requeues if and only if KVM staged an +* event in EVENTINJ. +*/ + svm->vmcb->control.exit_int_info =0 ;svm_complete_interrupts (vcpu); }
Signature · 1 line
2.43.0
[email protected]
AI reviewRe: [Patch v4 1/4] KVM: SVM: Re-queue events that were never injected
Thank you for your contribution! Sashiko AI review found 3 potential issues to consider: - [Medium] KVM: SVM: self-IPI on force_immediate_exit aborts ESMTP VMRUN sync in a loop - [Low] kvm: svm…
Thank you for your contribution! Sashiko AI review found 3 potential issues to consider:
- [Medium] KVM: SVM: self-IPI on force_immediate_exit aborts ESMTP VMRUN sync in a loop
- [Low] kvm: svm: svm_complete_interrupts() clobbers VMCB exit_int_info and event_inj
- [Low] KVM: SVM: inaccurate comment in svm_complete_interrupts() on EVENTINJ clear
--
commit 3134c7d995fb21938bef42d0d4bd1ee3a0a75674
Author: Pratik R. Sampat <[email protected]>
KVM: SVM: Re-queue events that were never injected
This commit updates svm_complete_interrupts() to migrate any remaining event in event_inj to exit_int_info on #VMEXIT so that events left uninjected when VMRUN exits early are re-queued, and simplifies svm_cancel_injection() accordingly.
Quoted 13 lines
arch/x86/kvm/svm/svm.c +1
-0@@ -4463,6 +4463,7 @@ static void svm_complete_interrupts(struct kvm_vcpu *vcpu)struct vcpu_svm *svm =to_svm (vcpu);u8 vector;int type; +struct vmcb_control_area *control = &svm->vmcb->control;u32 exitintinfo = svm->vmcb->control.exit_int_info;bool nmi_l1_to_l2 = svm->nmi_l1_to_l2;bool soft_int_injected = svm->soft_int_injected;
arch/x86/kvm/svm/svm.c static void svm_complete_interrupts(struct kvm_vcpu *vcpu) · line 4478
Quoted 8 lines
arch/x86/kvm/svm/svm.c +5
-0@@ -4470,6 +4471,27 @@ static void svm_complete_interrupts(struct kvm_vcpu *vcpu) svm->nmi_l1_to_l2 = false; svm->soft_int_injected = false; +/* +* Hardware clears EVENTINJ field when it injects an event. +* A non-empty EVENTINJ on #VMEXIT means the vCPU never entered guest +* mode, and thus that the event was never delivered. Migrate the event +* to EXITINTINFO so that it's requeued instead of being dropped.
[Severity: Low]
As reported by Sean Christopherson on v1 [1], is this comment accurate about when hardware clears EVENTINJ? Hardware clears EVENTINJ unconditionally on #VMEXIT when exiting guest mode, whereas pre-entry Enhanced SMT Protection exits terminate VMRUN at the synchronization point before guest mode is entered and leave EVENTINJ intact.
Quoted 5 lines
arch/x86/kvm/svm/svm.c +5
-0+* +* An undelivered event doesn't imply a fatal VMEXIT_INVALID. With +* Enhanced SMT Protection, VMRUN may exit with an ordinary #VMEXIT +* without having injected that event into the guest. +*
arch/x86/kvm/svm/svm.c static void svm_complete_interrupts(struct kvm_vcpu *vcpu) · line 4489
Quoted 10 lines
arch/x86/kvm/svm/svm.c +10
-0+* Clobbering EXITINTINFO is safe precisely because the vCPU never +* entered guest mode. +*/ +if (control->event_inj) { + control->exit_int_info = control->event_inj; + control->exit_int_info_err = control->event_inj_err; + control->event_inj =0 ; + + exitintinfo = control->exit_int_info; + }
[Severity: Medium]
As reported by Sean Christopherson on v1 [2], how does re-queuing control->event_inj interact with KVM_RUN_FORCE_IMMEDIATE_EXIT under Enhanced SMT Protection?
When kvm_check_and_inject_events() stages an event into control->event_inj while a second event is pending, vcpu_enter_guest() sets KVM_RUN_FORCE_IMMEDIATE_EXIT so svm_vcpu_run() sends a physical self-IPI via smp_send_reschedule() before VMRUN. If ESMTP stalls VMRUN at the sibling synchronization point, that pending self-IPI immediately aborts VMRUN with SVM_EXIT_INTR before guest entry. svm_complete_interrupts() then re-queues the first event with injected = true and sets KVM_REQ_EVENT, causing the next vcpu_enter_guest() iteration to hit -EBUSY in kvm_check_and_inject_events() and send another self-IPI before VMRUN, livelocking the vCPU without ever synchronizing with its sibling.
[Severity: Low]
Does modifying control->exit_int_info and control->event_inj in the VMCB here corrupt subsequent diagnostic output in svm_handle_exit()?
svm_vcpu_run() calls svm_complete_interrupts() before vcpu_enter_guest() calls svm_handle_exit(). When VMRUN fails with SVM_EXIT_ERR (for example, due to an invalid control->event_inj), dump_vmcb() in svm_handle_exit() now prints event_inj as 0 and reports the offending event under exit_int_info, and trace_kvm_nested_vmexit() logs the undelivered event_inj as exit_int_info (disagreeing with trace_kvm_exit() called just before svm_complete_interrupts()). Using local variables for exitintinfo and exit_int_info_err while only clearing control->event_inj would leave control->exit_int_info intact.
[1] https://lore.kernel.org/r/[email protected]
[2] https://lore.kernel.org/r/[email protected]
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
PATCH v4 2/4 KVM: SVM: Add host support for Enhanced SMT Protection
In reply to Pratik R. SampatEnhanced 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".
---
v3..v4:
* Fix conflict of reserved bits introduced by PML support in vmcb_control_area
--- .../admin-guide/kernel-parameters.txt | 19 ++++++++ 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 | 8 ++++ arch/x86/kvm/svm/svm.h | 2 + 9 files changed, 97 insertions(+), 4 deletions(-)
Documentation/admin-guide/kernel-parameters.txt +19 -0
@@ -3269,6 +3269,25 @@ Kernel parameters max_snp_asid == min_sev_asid-1, will effectively make SEV-ES unusable. + kvm-amd.esmtp_timeout_ns= + [KVM,AMD] Enhanced SMT Protection (ESMTP) VMRUN + timeout, in nanoseconds, for SEV-SNP guests that enable + ESMTP. When an ESMTP vCPU executes VMRUN, hardware + waits for every sibling thread to either be idle or + run a legal sibling vCPU of the same guest before + entering guest mode. If the wait exceeds this timeout, + VMRUN exits with VMEXIT_ESMTP_TIMEOUT and KVM retries + the VMRUN. + + The value is converted to TSC cycles and programmed + into the VMCB when a vCPU is initialized, so changing + it at runtime only affects vCPUs that are created or + reset afterwards. + + Default is 0, which disables the timeout, i.e. VMRUN + waits until the sibling condition is met or a physical + interrupt arrives. + kvm-arm.mode= [KVM,ARM,EARLY] Select one of KVM/arm64's modes of operation.
arch/x86/include/asm/cpufeatures.h +1 -0
@@ -532,6 +532,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)
arch/x86/include/asm/msr-index.h +1 -0
@@ -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
arch/x86/include/asm/svm.h +10 -2
@@ -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[128 ]; +u64 esmtp_timeout;/* Offset 0x148 */ +u8 reserved_10[120 ];u64 pml_addr;/* Offset 0x1c8 */ u16 pml_index;/* Offset 0x1d0 */ u8 reserved_11[526 ];
@@ -314,6 +315,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)
@@ -486,6 +488,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 {
@@ -558,7 +565,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
@@ -593,6 +600,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 );
arch/x86/include/uapi/asm/svm.h +8 -1
@@ -136,6 +136,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" }, \
@@ -248,7 +252,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 */
arch/x86/kernel/cpu/scattered.c +1 -0
@@ -68,6 +68,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 } };
arch/x86/kvm/svm/sev.c +47 -1
84 lines not on this page · raw message
arch/x86/kvm/svm/svm.c +8 -0
@@ -603,6 +603,8 @@ static int svm_enable_virtualization_cpu(void)__svm_write_tsc_multiplier (SVM_TSC_RATIO_DEFAULT ); } +sev_esmtp_enable_wakeup_icr (); +svm_init_os_visible_workarounds ();svm_init_erratum_383 ();
@@ -3853,6 +3855,12 @@ 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 ) { +return 1 ; + } +if (exit_fastpath !=EXIT_FASTPATH_NONE )return 1 ;
arch/x86/kvm/svm/svm.h +2 -0
@@ -1020,6 +1020,7 @@ void __init sev_set_cpu_caps(void);void __init sev_hardware_setup (void );void sev_hardware_unsetup (void );int sev_cpu_init (struct svm_cpu_data *sd); +void sev_esmtp_enable_wakeup_icr (void );int sev_dev_get_attr (u32 group,u64 attr,u64 *val);extern unsigned int max_sev_asid;void sev_handle_rmp_fault (struct kvm_vcpu *vcpu,gpa_t gpa,u64 error_code);
@@ -1048,6 +1049,7 @@ static inline void __init sev_set_cpu_caps(void) {}static inline void __init sev_hardware_setup (void ) {}static inline void sev_hardware_unsetup (void ) {}static inline int sev_cpu_init (struct svm_cpu_data *sd) {return 0 ; } +static inline void sev_esmtp_enable_wakeup_icr (void ) {}static inline int sev_dev_get_attr (u32 group,u64 attr,u64 *val) {return -ENXIO ; }#define max_sev_asid 0static inline void sev_handle_rmp_fault (struct kvm_vcpu *vcpu,gpa_t gpa,u64 error_code) {}
Signature · 1 line
2.43.0
[email protected]
AI reviewRe: [Patch v4 2/4] KVM: SVM: Add host support for Enhanced SMT Protection
Thank you for your contribution! Sashiko AI review found 2 potential issues to consider: - [Medium] KVM: SVM: spurious LAPIC timer interrupts from sev_esmtp_enable_wakeup_icr() - [Low] KVM: SVM…
Thank you for your contribution! Sashiko AI review found 2 potential issues to consider:
- [Medium] KVM: SVM: spurious LAPIC timer interrupts from sev_esmtp_enable_wakeup_icr()
- [Low] KVM: SVM: 32-bit truncation breaks SVM_EXIT_ESMTP_* in trace_kvm_exit()
--
commit 3f8afd82427b162ada6d9e482a251e8b54f02146
Author: Pratik R. Sampat <[email protected]>
KVM: SVM: Add host support for Enhanced SMT Protection
This commit adds KVM host support for AMD Enhanced SMT Protection (ESMTP) for SEV-SNP guests, including feature detection, VMSA and VMCB setup, idle wakeup ICR configuration, and handling of the new ESMTP VMEXITs.
Quoted 16 lines
arch/x86/include/uapi/asm/svm.h +4
-0@@ -136,6 +136,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" }, \
arch/x86/include/uapi/asm/svm.h line 258
Quoted 8 lines
arch/x86/include/uapi/asm/svm.h +4
-1@@ -248,7 +252,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" }
[Severity: Low]
Will trace_kvm_exit() ever match these 64-bit SVM_EXIT_ESMTP_* values in SVM_EXIT_REASONS?
svm_get_exit_info() truncates the 64-bit control->exit_code into a u32 *reason, and TRACE_EVENT_KVM_EXIT stores exit_reason as an unsigned int. When kvm_print_exit_reason() passes that 32-bit field to __print_symbolic_u64(exit_reason, SVM_EXIT_REASONS), it zero-extends to 0x00000000fffffffbULL instead of matching -5ull (0xfffffffffffffffbULL), so ftrace prints raw hex values instead of the symbolic names.
Quoted 4 lines
arch/x86/kvm/svm/sev.c +0
-0
[ ... ]
Quoted 18 lines
arch/x86/kvm/svm/sev.c +14
-0@@ -3312,6 +3329,31 @@ int sev_cpu_init(struct svm_cpu_data *sd)return 0 ; } +void sev_esmtp_enable_wakeup_icr (void ) +{ +unsigned int cpu, sibling, shift; +u32 core; + +if (!(sev_supported_vmsa_features &SVM_SEV_FEAT_ESMTP )) +return ; + + cpu =raw_smp_processor_id (); + shift =topology_get_domain_shift (TOPO_CORE_DOMAIN ); + core =per_cpu (x86_cpu_to_apicid, cpu) >> shift; + +for_each_present_cpu (sibling) { +u32 apicid =per_cpu (x86_cpu_to_apicid, sibling);
arch/x86/kvm/svm/sev.c int sev_cpu_init(struct svm_cpu_data *sd) · line 3355
Quoted 10 lines
arch/x86/kvm/svm/sev.c +10
-0+u64 icr; + +if (sibling == cpu || (apicid >> shift) != core) +continue ; + + icr = (u64 )apicid <<32 ; + icr |=LOCAL_TIMER_VECTOR ; +WARN_ON_ONCE (wrmsrq_safe (MSR_AMD64_IDLE_WAKEUP_ICR , icr)); + } +}
[Severity: Medium]
Does programming MSR_AMD64_IDLE_WAKEUP_ICR with LOCAL_TIMER_VECTOR inject spurious local APIC timer interrupts on the sibling CPU whenever this thread wakes from idle while the sibling is running an ESMTP vCPU?
When the sibling exits guest mode and enables interrupts, LOCAL_TIMER_VECTOR runs sysvec_apic_timer_interrupt() -> local_apic_timer_interrupt(), which unconditionally increments APIC_TIMER IRQ stats and calls evt->event_handler(evt). When high-resolution timers are disabled, tick_handle_periodic() and tick_nohz_lowres_handler() do not check whether dev->next_event has been reached, so every ESMTP wakeup IPI prematurely advances jiffies and charges a full tick of CPU time via update_process_times().
Using RESCHEDULE_VECTOR (which KVM already uses to kick CPUs out of guest mode) would avoid invoking the clockevent handler.
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
PATCH v4 3/4 x86/sev: Add guest support for Enhanced SMT Protection
In reply to Pratik R. SampatEnhanced SMT Protection requires every VMSA to carry the vCPU's identity and the mask of SMT siblings it may co-run with. The guest builds the VMSA itself when bringing up an AP, so populate both fields there, before the page is turned into a VMSA and while it is still writable.
Fully set the sibling mask so that all threads of the same guest trust each other.
---
v3..v4: No Change
--- arch/x86/boot/compressed/sev.c | 6 ++++-- arch/x86/coco/sev/core.c | 13 +++++++++++++ arch/x86/include/asm/msr-index.h | 4 +++- 3 files changed, 20 insertions(+), 3 deletions(-)
arch/x86/boot/compressed/sev.c +4 -2
@@ -188,7 +188,8 @@ bool sev_es_check_ghcb_fault(unsigned long address)MSR_AMD64_SNP_RESERVED_BIT13 | \MSR_AMD64_SNP_RESERVED_BIT15 | \MSR_AMD64_SNP_SECURE_AVIC | \ -MSR_AMD64_SNP_RESERVED_BITS19_22 | \ +MSR_AMD64_SNP_ESMT_PROT | \ +MSR_AMD64_SNP_RESERVED_BITS20_22 | \ MSR_AMD64_SNP_RESERVED_MASK) #ifdef CONFIG_AMD_SECURE_AVIC
@@ -204,7 +205,8 @@ bool sev_es_check_ghcb_fault(unsigned long address)*/ #define SNP_FEATURES_IMPL (MSR_AMD64_SNP_DEBUG_SWAP | \ MSR_AMD64_SNP_SECURE_TSC | \ - SNP_FEATURE_SECURE_AVIC) + SNP_FEATURE_SECURE_AVIC | \ + MSR_AMD64_SNP_ESMT_PROT)u64 snp_get_unsupported_features (u64 status) {
arch/x86/coco/sev/core.c +13 -0
@@ -89,6 +89,7 @@ static const char * const sev_status_feat_names[] = { [MSR_AMD64_SNP_VMSA_REG_PROT_BIT] = "VMSARegProt", [MSR_AMD64_SNP_SMT_PROT_BIT] = "SMTProt", [MSR_AMD64_SNP_SECURE_AVIC_BIT] = "SecureAVIC", + [MSR_AMD64_SNP_ESMT_PROT_BIT] = "ESMTProt", [MSR_AMD64_SNP_IBPB_ON_ENTRY_BIT] = "IBPBOnEntry", };
@@ -849,6 +850,18 @@ static int wakeup_cpu_via_vmgexit(u32 apic_id, unsigned long start_ip, unsigned vmsa->vmpl = snp_vmpl; vmsa->sev_features = sev_status >>2 ; +if (sev_status &MSR_AMD64_SEV_SNP_ENABLED && + (sev_status &MSR_AMD64_SNP_ESMT_PROT )) { + vmsa->vcpu_id = apic_id; +/* +* The mask fully set puts every vCPU in one group, so any two +* of them may be co-resident. A legal ESMTP sibling must +* also match on ASID, so the sibling of a vCPU in guest mode is +* always either another vCPU of this same guest or idle. +*/ + vmsa->vcpu_sibling_mask =U32_MAX ; + } +/* Populate AP's TSC scale/offset to get accurate TSC values. */ if (cc_platform_has (CC_ATTR_GUEST_SNP_SECURE_TSC )) { vmsa->tsc_scale = snp_tsc_scale;
arch/x86/include/asm/msr-index.h +3 -1
@@ -745,7 +745,9 @@#define MSR_AMD64_SNP_SMT_PROT BIT_ULL(MSR_AMD64_SNP_SMT_PROT_BIT)#define MSR_AMD64_SNP_SECURE_AVIC_BIT 18#define MSR_AMD64_SNP_SECURE_AVIC BIT_ULL(MSR_AMD64_SNP_SECURE_AVIC_BIT) -#define MSR_AMD64_SNP_RESERVED_BITS19_22 GENMASK_ULL(22, 19) +#define MSR_AMD64_SNP_ESMT_PROT_BIT 19 +#define MSR_AMD64_SNP_ESMT_PROT BIT_ULL(MSR_AMD64_SNP_ESMT_PROT_BIT) +#define MSR_AMD64_SNP_RESERVED_BITS20_22 GENMASK_ULL(22, 20)#define MSR_AMD64_SNP_IBPB_ON_ENTRY_BIT 23#define MSR_AMD64_SNP_IBPB_ON_ENTRY BIT_ULL(MSR_AMD64_SNP_IBPB_ON_ENTRY_BIT)#define MSR_AMD64_SNP_RESV_BIT 24
Signature · 1 line
2.43.0
PATCH v4 4/4 x86/hyperv: Add guest support for Enhanced SMT Protection
In reply to Pratik R. SampatAdd corresponding hyperv support for Enhanced SMT protection. A fully enlightened SEV-SNP guest on Hyper-V builds the VMSA itself when bringing up an AP, so populate both fields there, before the page is turned into a VMSA and while it is still writable.
Similar to KVM, fully set the sibling mask so that all threads of the same guest trust each other.
---
v3..v4: No Change
--- arch/x86/hyperv/ivm.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
arch/x86/hyperv/ivm.c +11 -0
@@ -347,6 +347,17 @@ int hv_snp_boot_ap(u32 apic_id, unsigned long start_ip, unsigned int cpu) vmsa->vmpl =0 ; vmsa->sev_features = sev_status >>2 ; +if (sev_status &MSR_AMD64_SNP_ESMT_PROT ) { + vmsa->vcpu_id = apic_id; +/* +* The mask fully set puts every vCPU in one group, so any two +* of them may be co-resident. A legal ESMTP sibling must +* also match on ASID, so the sibling of a vCPU in guest mode is +* always either another vCPU of this same guest or idle. +*/ + vmsa->vcpu_sibling_mask =U32_MAX ; + } + ret =snp_set_vmsa (vmsa, true);if (ret) {pr_err ("RMPADJUST(%llx) failed: %llx\n" , (u64 )vmsa, ret);
Signature · 1 line
2.43.0