From 556cce84508cd44393ff31744ff0e15c9fd3debb Mon Sep 17 00:00:00 2001 From: hanliyang Date: Tue, 8 Aug 2023 23:47:22 +0800 Subject: [PATCH 1/3] anolis: KVM: x86: Introduce control_{pre,post}_system_reset ioctl interfaces ANBZ: #6135 In the upcoming patches, we will support for rebooting CSV2 guests. In order to support rebooting CSV2 guest, we will set vcpu->arch.guest_state_protected to false, before VMRUN, so that VMM can initialize vCPU states and VMSA, and then set vcpu->arch.guest_state_protected back to true to bypass unexpected behaviour in KVM. Besides, cache flush is necessary during rebooting a memory encrypted guest. Introduce control_{pre,post}_system_reset ioctl interfaces to support rebooting memory encrypted guests correctly. Signed-off-by: hanliyang --- arch/x86/include/asm/kvm_host.h | 3 +++ arch/x86/kvm/svm/sev.c | 10 ++++++++++ arch/x86/kvm/svm/svm.c | 3 +++ arch/x86/kvm/svm/svm.h | 3 +++ arch/x86/kvm/x86.c | 14 ++++++++++++++ include/uapi/linux/kvm.h | 4 ++++ 6 files changed, 37 insertions(+) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 88c7893731d4..6c138ff2cab1 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -1335,6 +1335,9 @@ struct kvm_x86_ops { int (*complete_emulated_msr)(struct kvm_vcpu *vcpu, int err); void (*vcpu_deliver_sipi_vector)(struct kvm_vcpu *vcpu, u8 vector); + + int (*control_pre_system_reset)(struct kvm *kvm); + int (*control_post_system_reset)(struct kvm *kvm); }; struct kvm_x86_nested_ops { diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index cfd9ede7bfc2..f88d8e7b0550 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -3537,3 +3537,13 @@ int sev_es_ghcb_map(struct vcpu_svm *svm, u64 ghcb_gpa) return 0; } + +int csv_control_pre_system_reset(struct kvm *kvm) +{ + return 0; +} + +int csv_control_post_system_reset(struct kvm *kvm) +{ + return 0; +} diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index 03ef878e1f0f..67e8231bfc78 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -4718,6 +4718,9 @@ static struct kvm_x86_ops svm_x86_ops __initdata = { .complete_emulated_msr = svm_complete_emulated_msr, .vcpu_deliver_sipi_vector = svm_vcpu_deliver_sipi_vector, + + .control_pre_system_reset = csv_control_pre_system_reset, + .control_post_system_reset = csv_control_post_system_reset, }; static struct kvm_x86_init_ops svm_init_ops __initdata = { diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h index 1d847408c5f8..aa86d23a9bf3 100644 --- a/arch/x86/kvm/svm/svm.h +++ b/arch/x86/kvm/svm/svm.h @@ -606,6 +606,9 @@ void sev_vcpu_deliver_sipi_vector(struct kvm_vcpu *vcpu, u8 vector); void sev_es_unmap_ghcb(struct vcpu_svm *svm); int sev_es_ghcb_map(struct vcpu_svm *svm, u64 ghcb_gpa); +int csv_control_pre_system_reset(struct kvm *kvm); +int csv_control_post_system_reset(struct kvm *kvm); + /* vmenter.S */ void __svm_sev_es_vcpu_run(unsigned long vmcb_pa); diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index a25bb5b4d68c..093fd5279aba 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -6001,6 +6001,20 @@ long kvm_arch_vm_ioctl(struct file *filp, case KVM_X86_SET_MSR_FILTER: r = kvm_vm_ioctl_set_msr_filter(kvm, argp); break; + case KVM_CONTROL_PRE_SYSTEM_RESET: + if (boot_cpu_data.x86_vendor == X86_VENDOR_HYGON && + kvm_x86_ops.control_pre_system_reset) + r = kvm_x86_ops.control_pre_system_reset(kvm); + else + r = -ENOTTY; + break; + case KVM_CONTROL_POST_SYSTEM_RESET: + if (boot_cpu_data.x86_vendor == X86_VENDOR_HYGON && + kvm_x86_ops.control_post_system_reset) + r = kvm_x86_ops.control_post_system_reset(kvm); + else + r = -ENOTTY; + break; default: r = -ENOTTY; } diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index fe1a1681490c..696e4480a420 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -1455,6 +1455,10 @@ struct kvm_s390_ucas_mapping { #define KVM_GET_DEVICE_ATTR _IOW(KVMIO, 0xe2, struct kvm_device_attr) #define KVM_HAS_DEVICE_ATTR _IOW(KVMIO, 0xe3, struct kvm_device_attr) +/* ioctls for control vm during system reset */ +#define KVM_CONTROL_PRE_SYSTEM_RESET _IO(KVMIO, 0xe8) +#define KVM_CONTROL_POST_SYSTEM_RESET _IO(KVMIO, 0xe9) + /* * ioctls for vcpu fds */ -- Gitee From 3884acd62d1553ab4e8b6f4e9dd6911845d1b58d Mon Sep 17 00:00:00 2001 From: hanliyang Date: Thu, 15 Apr 2021 07:56:55 -0400 Subject: [PATCH 2/3] anolis: KVM: SVM: Add support for rebooting CSV2 guest ANBZ: #6135 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 --- arch/x86/kvm/svm/sev.c | 56 ++++++++++++++++++++++++++++++++++++++++++ arch/x86/kvm/svm/svm.c | 10 ++++++++ arch/x86/kvm/svm/svm.h | 2 ++ 3 files changed, 68 insertions(+) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index f88d8e7b0550..ea82fe0dbfc1 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -633,6 +633,17 @@ static int __sev_launch_update_vmsa(struct kvm *kvm, struct kvm_vcpu *vcpu, 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->vmsa so that we can read fresh + * memory updated by PSP. + */ + if (boot_cpu_data.x86_vendor == X86_VENDOR_HYGON) { + clflush_cache_range(svm->vmsa, PAGE_SIZE); + memcpy(svm->reset_vmsa, svm->vmsa, PAGE_SIZE); + } + e_free: kfree(vmsa); return ret; @@ -2799,6 +2810,8 @@ void sev_free_vcpu(struct kvm_vcpu *vcpu) if (svm->ghcb_sa_free) kvfree(svm->ghcb_sa); + + __free_page(virt_to_page(svm->reset_vmsa)); } static void dump_ghcb(struct vcpu_svm *svm) @@ -3540,10 +3553,53 @@ int sev_es_ghcb_map(struct vcpu_svm *svm, u64 ghcb_gpa) int csv_control_pre_system_reset(struct kvm *kvm) { + struct kvm_vcpu *vcpu; + int i, 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; } int csv_control_post_system_reset(struct kvm *kvm) { + struct kvm_vcpu *vcpu; + int i, 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->vmsa, svm->reset_vmsa, PAGE_SIZE); + + /* Flush encrypted vmsa to memory */ + clflush_cache_range(svm->vmsa, PAGE_SIZE); + + svm->vcpu.arch.guest_state_protected = true; + svm->received_first_sipi = false; + + mutex_unlock(&vcpu->mutex); + } + return 0; } diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index 67e8231bfc78..53454593079e 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -1322,6 +1322,7 @@ static int svm_create_vcpu(struct kvm_vcpu *vcpu) struct vcpu_svm *svm; struct page *vmcb_page; struct page *vmsa_page = NULL; + struct page *reset_vmsa_page = NULL; int err; BUILD_BUG_ON(offsetof(struct vcpu_svm, vcpu) != 0); @@ -1341,6 +1342,10 @@ static int svm_create_vcpu(struct kvm_vcpu *vcpu) if (!vmsa_page) goto error_free_vmcb_page; + reset_vmsa_page = alloc_page(GFP_KERNEL_ACCOUNT | __GFP_ZERO); + if (!reset_vmsa_page) + 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. @@ -1376,6 +1381,9 @@ static int svm_create_vcpu(struct kvm_vcpu *vcpu) if (vmsa_page) svm->vmsa = page_address(vmsa_page); + if (reset_vmsa_page) + svm->reset_vmsa = page_address(reset_vmsa_page); + svm->asid_generation = 0; init_vmcb(svm); @@ -1391,6 +1399,8 @@ static int svm_create_vcpu(struct kvm_vcpu *vcpu) error_free_vmsa_page: if (vmsa_page) __free_page(vmsa_page); + if (reset_vmsa_page) + __free_page(reset_vmsa_page); error_free_vmcb_page: __free_page(vmcb_page); out: diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h index aa86d23a9bf3..431a08da24f7 100644 --- a/arch/x86/kvm/svm/svm.h +++ b/arch/x86/kvm/svm/svm.h @@ -195,6 +195,8 @@ struct vcpu_svm { /* CSV2 migrated ghcb mapping state support */ bool receiver_ghcb_map_fail; + /* CSV2 reboot vmsa */ + struct vmcb_save_area *reset_vmsa; }; struct svm_cpu_data { -- Gitee From 29452dbec9fe1e7d37df20a261fb07eff46fc40e Mon Sep 17 00:00:00 2001 From: hanliyang Date: Sat, 6 May 2023 16:01:25 +0800 Subject: [PATCH 3/3] anolis: KVM: SVM: Force flush caches before reboot CSV guest ANBZ: #6135 For memory encrypted guest, its pages' encrypt status will changed at runtime. When user reboot the guest, the pages' encrypt status during last boot were ignored. So during the boot flow of reboot, there may be 2 versions of memory data lies in cache as follows: +--------+ | | | | +--------------+ --+ | | | | \ |________| | | \ cacheline for -> |________| <-+ | | \ pa1(c=0) | | \ |______________| \ | | \_ 64 bytes aligned <- pa1 \ | | _ |______________| 4K | | / | | page cacheline for |________| / | | / pa1(c=1) -> |________| <-+ | | / | | | | / | | | | / | | | | / | | +--------------+ --+ | | | | If the older version cache was flushed after that of newer version, and guest read the memory again, then it will get corrupted data and may lead to crash. In this change, for any memory encrypted guest, the cache is forcibly flushed to memory before the next boot flow, which ensures that memory access is up-to-date. Signed-off-by: hanliyang --- arch/x86/kvm/svm/sev.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index ea82fe0dbfc1..51d90b4573a6 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -3577,12 +3577,12 @@ int csv_control_post_system_reset(struct kvm *kvm) struct kvm_vcpu *vcpu; int i, ret; + /* Flush both host and guest caches before next boot flow */ + wbinvd_on_all_cpus(); + 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); -- Gitee