KVM: SVM: Add support for rebooting CSV2 guest

Upstream: no

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: hanliyang <hanliyang@hygon.cn>
This commit is contained in:
hanliyang 2021-04-15 07:56:55 -04:00
parent a717c0e01d
commit c6d3aa44b4
5 changed files with 103 additions and 1 deletions

View File

@ -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;
}

View File

@ -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 */

View File

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

View File

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

View File

@ -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
};