[PATCH 4/4] KVM: SVM: Add support to initialize SEV/SNP functionality in KVM

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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--




[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux