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:
parent
a717c0e01d
commit
c6d3aa44b4
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue