[PATCH 4/4] KVM: SVM: Add support to initialize SEV/SNP functionality in KVM
From: Ashish Kalra
Date: Tue Feb 25 2025 - 16:01:33 EST
Move platform initialization of SEV/SNP from CCP driver probe time to
KVM module load time so that KVM can do SEV/SNP platform initialization
explicitly if it actually wants to use SEV/SNP functionality.
Add support for KVM to explicitly call into the CCP driver at load time
to initialize SEV/SNP. If required, this behavior can be altered with KVM
module parameters to not do SEV/SNP platform initialization at module load
time. Additionally, a corresponding SEV/SNP platform shutdown is invoked
during KVM module unload time.
Suggested-by: Sean Christopherson <seanjc@xxxxxxxxxx>
Signed-off-by: Ashish Kalra <ashish.kalra@xxxxxxx>
Co-developed-by: Sean Christopherson <seanjc@xxxxxxxxxx>
Signed-off-by: Sean Christopherson <seanjc@xxxxxxxxxx>
---
arch/x86/kvm/svm/sev.c | 52 +++++++++++++++++++++++++++++++++++++-----
arch/x86/kvm/svm/svm.c | 4 +++-
arch/x86/kvm/svm/svm.h | 4 ++--
3 files changed, 51 insertions(+), 9 deletions(-)
diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index 354f6c32c435..2b8c1d125164 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -402,7 +402,7 @@ static int __sev_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp,
unsigned long vm_type)
{
struct kvm_sev_info *sev = to_kvm_sev_info(kvm);
- struct sev_platform_init_args init_args = {0};
+ struct sev_platform_init_args init_args = { .probe = false };
bool es_active = vm_type != KVM_X86_SEV_VM;
u64 valid_vmsa_features = es_active ? sev_supported_vmsa_features : 0;
int ret;
@@ -442,10 +442,9 @@ static int __sev_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp,
if (ret)
goto e_no_asid;
- init_args.probe = false;
/*
- * Initialize SEV/SEV-ES if neccessary, i.e. if the CCP driver was
- * configured to defer setup.
+ * Initialize SEV/SEV-ES if neccessary, i.e. if setup failued during
+ * module load, or if the CCP driver was configured to defer setup.
* Ignore failure for SNP VMs, as SNP initialization can succeed even
* if SEV/SEV-ES initialization fails (and vice versa). SNP support is
* communicated via an out param, and is checked below.
@@ -456,7 +455,11 @@ static int __sev_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp,
/* This needs to happen after SEV/SNP firmware initialization. */
if (vm_type == KVM_X86_SNP_VM) {
- if (!init_args.snp_initialized) {
+ /*
+ * SNP is initialized during KVM setup, and KVM shouldn't
+ * advertise support for SNP if initialization failed.
+ */
+ if (WARN_ON_ONCE(!init_args.snp_initialized)) {
ret = -EIO;
goto e_free;
}
@@ -2941,9 +2944,10 @@ void __init sev_set_cpu_caps(void)
}
}
-void __init sev_hardware_setup(void)
+int __init sev_hardware_setup(void)
{
unsigned int eax, ebx, ecx, edx, sev_asid_count, sev_es_asid_count;
+ struct sev_platform_init_args init_args = { .probe = true };
bool sev_snp_supported = false;
bool sev_es_supported = false;
bool sev_supported = false;
@@ -3044,6 +3048,38 @@ void __init sev_hardware_setup(void)
sev_snp_supported = sev_snp_enabled && cc_platform_has(CC_ATTR_HOST_SEV_SNP);
out:
+ /*
+ * Initialize SEV+ in firmware and throughout the plaform. If SNP is
+ * supported by hardare, requested by the user, and fails to initialize,
+ * reject setup if kvm-amd.ko is built as a module, i.e. if userspace
+ * can retry and thus decide whether or not SNP is a hard requirement.
+ * If KVM is fully built-in, continue on so that KVM can still run
+ * non-SNP VMs.
+ *
+ * Leave SEV and SEV-ES as-is; they are compatible with runtime setup,
+ * and KVM will retry initialization if/when the first SEV/SEV-ES VM is
+ * created.
+ *
+ * Note, if psp_init_on_probe is disabled, this will initialize SNP+,
+ * but not SEV or SEV-ES. Deferring SEV/SEV-ES initialization allows
+ * userspace to hotload firmware (SNP enabling is compatible, "legacy"
+ * SEV/SEV-ES enabling is not). As above, KVM will do SEV/SEV-ES at VM
+ * creation if necessary.
+ */
+ if (sev_supported) {
+ /*
+ * Ignore the return value, which only communicates SEV/SEV-ES
+ * status. SNP status is provided as an output param.
+ */
+ sev_platform_init(&init_args);
+ if (!init_args.snp_initialized && sev_snp_supported) {
+ if (IS_MODULE(CONFIG_KVM_AMD))
+ return -EIO;
+
+ sev_snp_supported = false;
+ }
+ }
+
if (boot_cpu_has(X86_FEATURE_SEV))
pr_info("SEV %s (ASIDs %u - %u)\n",
sev_supported ? min_sev_asid <= max_sev_asid ? "enabled" :
@@ -3070,6 +3106,8 @@ void __init sev_hardware_setup(void)
sev_supported_vmsa_features = 0;
if (sev_es_debug_swap_enabled)
sev_supported_vmsa_features |= SVM_SEV_FEAT_DEBUG_SWAP;
+
+ return 0;
}
void sev_hardware_unsetup(void)
@@ -3085,6 +3123,8 @@ void sev_hardware_unsetup(void)
misc_cg_set_capacity(MISC_CG_RES_SEV, 0);
misc_cg_set_capacity(MISC_CG_RES_SEV_ES, 0);
+
+ sev_platform_shutdown();
}
int sev_cpu_init(struct svm_cpu_data *sd)
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index 58e377d32627..db48fb8d6257 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -5358,7 +5358,9 @@ static __init int svm_hardware_setup(void)
* Note, SEV setup consumes npt_enabled and enable_mmio_caching (which
* may be modified by svm_adjust_mmio_mask()), as well as nrips.
*/
- sev_hardware_setup();
+ r = sev_hardware_setup();
+ if (r)
+ return r;
svm_hv_hardware_setup();
diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h
index faa5c0dab0ea..fe7bfab96397 100644
--- a/arch/x86/kvm/svm/svm.h
+++ b/arch/x86/kvm/svm/svm.h
@@ -773,7 +773,7 @@ static inline struct page *snp_safe_alloc_page(void)
void sev_free_vcpu(struct kvm_vcpu *vcpu);
void sev_vm_destroy(struct kvm *kvm);
void __init sev_set_cpu_caps(void);
-void __init sev_hardware_setup(void);
+int __init sev_hardware_setup(void);
void sev_hardware_unsetup(void);
int sev_cpu_init(struct svm_cpu_data *sd);
int sev_dev_get_attr(u32 group, u64 attr, u64 *val);
@@ -797,7 +797,7 @@ static inline struct page *snp_safe_alloc_page(void)
static inline void sev_free_vcpu(struct kvm_vcpu *vcpu) {}
static inline void sev_vm_destroy(struct kvm *kvm) {}
static inline void __init sev_set_cpu_caps(void) {}
-static inline void __init sev_hardware_setup(void) {}
+static inline int __init sev_hardware_setup(void) { return 0; }
static inline void sev_hardware_unsetup(void) {}
static inline int sev_cpu_init(struct svm_cpu_data *sd) { return 0; }
static inline int sev_dev_get_attr(u32 group, u64 attr, u64 *val) { return -ENXIO; }
--
2.48.1.711.g2feabab25a-goog
--ZxKkPI+e2NE34Qwk--