Commit 8a2683e1 authored by hanliyang's avatar hanliyang
Browse files

KVM: SVM: Add support for rebooting CSV2 guest

hygon inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/I98WBH


CVE: NA

---------------------------

Currently, reboot a CSV2 guest is unsupported because vCPU state is
encrypted and can't be initialized when guest reboots to execute
OVMF code.

In order to support reboot a CSV2 guest, make a backup of the
encrypted VMSA before booting the guest, and restore VMSA from the
backup before rebooting the guest.

Signed-off-by: default avatarhanliyang <hanliyang@hygon.cn>
parent 22a8a353
Loading
Loading
Loading
Loading
+72 −0
Original line number Diff line number Diff line
@@ -855,6 +855,33 @@ static int csv_mem_enc_ioctl(struct kvm *kvm, void __user *argp)
	return r;
}

/* The caller must flush the stale caches about svm->sev_es.vmsa */
void csv2_sync_reset_vmsa(struct vcpu_svm *svm)
{
	if (svm->sev_es.reset_vmsa)
		memcpy(svm->sev_es.reset_vmsa, svm->sev_es.vmsa, PAGE_SIZE);
}

void csv2_free_reset_vmsa(struct vcpu_svm *svm)
{
	if (svm->sev_es.reset_vmsa) {
		__free_page(virt_to_page(svm->sev_es.reset_vmsa));
		svm->sev_es.reset_vmsa = NULL;
	}
}

int csv2_setup_reset_vmsa(struct vcpu_svm *svm)
{
	struct page *reset_vmsa_page = NULL;

	reset_vmsa_page = alloc_page(GFP_KERNEL_ACCOUNT | __GFP_ZERO);
	if (!reset_vmsa_page)
		return -ENOMEM;

	svm->sev_es.reset_vmsa = page_address(reset_vmsa_page);
	return 0;
}

static int csv2_map_ghcb_gpa(struct vcpu_svm *svm, u64 ghcb_gpa)
{
	if (kvm_vcpu_map(&svm->vcpu, ghcb_gpa >> PAGE_SHIFT, &svm->sev_es.ghcb_map)) {
@@ -975,11 +1002,56 @@ bool csv_has_emulated_ghcb_msr(struct kvm *kvm)

static int csv_control_pre_system_reset(struct kvm *kvm)
{
	struct kvm_vcpu *vcpu;
	unsigned long i;
	int ret;

	if (!sev_es_guest(kvm))
		return 0;

	kvm_for_each_vcpu(i, vcpu, kvm) {
		ret = mutex_lock_killable(&vcpu->mutex);
		if (ret)
			return ret;

		vcpu->arch.guest_state_protected = false;

		mutex_unlock(&vcpu->mutex);
	}

	return 0;
}

static int csv_control_post_system_reset(struct kvm *kvm)
{
	struct kvm_vcpu *vcpu;
	unsigned long i;
	int ret;

	if (!sev_es_guest(kvm))
		return 0;

	/* Flush both host and guest caches of VMSA */
	wbinvd_on_all_cpus();

	kvm_for_each_vcpu(i, vcpu, kvm) {
		struct vcpu_svm *svm = to_svm(vcpu);

		ret = mutex_lock_killable(&vcpu->mutex);
		if (ret)
			return ret;

		memcpy(svm->sev_es.vmsa, svm->sev_es.reset_vmsa, PAGE_SIZE);

		/* Flush encrypted vmsa to memory */
		clflush_cache_range(svm->sev_es.vmsa, PAGE_SIZE);

		svm->vcpu.arch.guest_state_protected = true;
		svm->sev_es.received_first_sipi = false;

		mutex_unlock(&vcpu->mutex);
	}

	return 0;
}

+6 −1
Original line number Diff line number Diff line
@@ -62,13 +62,15 @@ void csv_free_trans_mempool(void);
int csv_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info);
int csv_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info);
bool csv_has_emulated_ghcb_msr(struct kvm *kvm);
void csv2_sync_reset_vmsa(struct vcpu_svm *svm);
void csv2_free_reset_vmsa(struct vcpu_svm *svm);
int csv2_setup_reset_vmsa(struct vcpu_svm *svm);

static inline bool csv2_state_unstable(struct vcpu_svm *svm)
{
	return svm->sev_es.receiver_ghcb_map_fail;
}


#else	/* !CONFIG_HYGON_CSV */

static inline void __init csv_init(struct kvm_x86_ops *ops) { }
@@ -82,6 +84,9 @@ static inline
int csv_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) { return 1; }
static inline bool csv_has_emulated_ghcb_msr(struct kvm *kvm) { return false; }
static inline bool csv2_state_unstable(struct vcpu_svm *svm) { return false; }
static inline void csv2_sync_reset_vmsa(struct vcpu_svm *svm) { }
static inline void csv2_free_reset_vmsa(struct vcpu_svm *svm) { }
static inline int csv2_setup_reset_vmsa(struct vcpu_svm *svm) { return 0; }

#endif	/* CONFIG_HYGON_CSV */

+15 −0
Original line number Diff line number Diff line
@@ -667,6 +667,18 @@ static int __sev_launch_update_vmsa(struct kvm *kvm, struct kvm_vcpu *vcpu,
	  return ret;

	vcpu->arch.guest_state_protected = true;

	/*
	 * Backup encrypted vmsa to support rebooting CSV2 guest. The
	 * clflush_cache_range() is necessary to invalidate prefetched
	 * memory area pointed by svm->sev_es.vmsa so that we can read
	 * fresh memory updated by PSP.
	 */
	if (is_x86_vendor_hygon()) {
		clflush_cache_range(svm->sev_es.vmsa, PAGE_SIZE);
		csv2_sync_reset_vmsa(svm);
	}

	return 0;
}

@@ -2428,6 +2440,9 @@ void sev_free_vcpu(struct kvm_vcpu *vcpu)

	if (svm->sev_es.ghcb_sa_free)
		kvfree(svm->sev_es.ghcb_sa);

	if (is_x86_vendor_hygon())
		csv2_free_reset_vmsa(svm);
}

static void dump_ghcb(struct vcpu_svm *svm)
+8 −0
Original line number Diff line number Diff line
@@ -1455,6 +1455,11 @@ static int svm_vcpu_create(struct kvm_vcpu *vcpu)
		if (!vmsa_page)
			goto error_free_vmcb_page;

		if (is_x86_vendor_hygon()) {
			if (csv2_setup_reset_vmsa(svm))
				goto error_free_vmsa_page;
		}

		/*
		 * SEV-ES guests maintain an encrypted version of their FPU
		 * state which is restored and saved on VMRUN and VMEXIT.
@@ -1490,6 +1495,9 @@ static int svm_vcpu_create(struct kvm_vcpu *vcpu)
error_free_vmsa_page:
	if (vmsa_page)
		__free_page(vmsa_page);

	if (is_x86_vendor_hygon())
		csv2_free_reset_vmsa(svm);
error_free_vmcb_page:
	__free_page(vmcb01_page);
out:
+2 −0
Original line number Diff line number Diff line
@@ -206,6 +206,8 @@ struct vcpu_sev_es_state {
#ifdef CONFIG_HYGON_CSV
	/* migrated ghcb mapping state for HYGON CSV2 */
	bool receiver_ghcb_map_fail;
	/* CSV2 reboot vmsa */
	struct vmcb_save_area *reset_vmsa;
#endif
};