From: Tom Lendacky <[email protected]>

An SEV-ES guest requires some additional steps to be launched as compared
to an SEV guest:
  - Implement additional VMCB initialization requirements for SEV-ES.
  - Update MSR_VM_HSAVE_PA to include the encryption bit if SME is active.
  - Add additional MSRs to the list of direct access MSRs so that the
    intercepts can be disabled.
  - Measure all vCPUs using the LAUNCH_UPDATE_VMSA SEV command after all
    calls to LAUNCH_UPDATE_DATA have been performed but before the call
    to LAUNCH_MEASURE has been performed.
  - Use VMSAVE to save host information that is not saved on VMRUN but is
    restored on VMEXIT.
  - Modify the VMRUN path to eliminate guest register state restoring and
    saving.

At this point the guest can be run. However, the run sequence is different
for an SEV-ES guest compared to a normal or even an SEV guest. Because the
guest register state is encrypted, it is all saved as part of VMRUN/VMEXIT
and does not require restoring before or saving after a VMRUN instruction.
As a result, all that is required to perform a VMRUN is to save the RBP
and RAX registers, issue the VMRUN and then restore RAX and RBP.

Additionally, certain state is automatically saved and restored with an
SEV-ES VMRUN. As a result certain register states are not required to be
restored upon VMEXIT (e.g. FS, GS, etc.), so only do that if the guest is
not an SEV-ES guest.

Signed-off-by: Tom Lendacky <[email protected]>
---
 arch/x86/kvm/svm/sev.c | 60 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 60 insertions(+)

diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index 50018436863b..eaa669c16345 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -201,6 +201,16 @@ static int sev_guest_init(struct kvm *kvm, struct 
kvm_sev_cmd *argp)
        return ret;
 }
 
+static int sev_es_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp)
+{
+       if (!sev_es)
+               return -ENOTTY;
+
+       to_kvm_svm(kvm)->sev_info.es_active = true;
+
+       return sev_guest_init(kvm, argp);
+}
+
 static int sev_bind_asid(struct kvm *kvm, unsigned int handle, int *error)
 {
        struct sev_data_activate *data;
@@ -501,6 +511,50 @@ static int sev_launch_update_data(struct kvm *kvm, struct 
kvm_sev_cmd *argp)
        return ret;
 }
 
+static int sev_launch_update_vmsa(struct kvm *kvm, struct kvm_sev_cmd *argp)
+{
+       struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
+       struct sev_data_launch_update_vmsa *vmsa;
+       int i, ret;
+
+       if (!sev_es_guest(kvm))
+               return -ENOTTY;
+
+       vmsa = kzalloc(sizeof(*vmsa), GFP_KERNEL);
+       if (!vmsa)
+               return -ENOMEM;
+
+       for (i = 0; i < kvm->created_vcpus; i++) {
+               struct vcpu_svm *svm = to_svm(kvm->vcpus[i]);
+               struct vmcb_save_area *save = get_vmsa(svm);
+
+               /* Set XCR0 before encrypting */
+               save->xcr0 = svm->vcpu.arch.xcr0;
+
+               /*
+                * The LAUNCH_UPDATE_VMSA command will perform in-place
+                * encryption of the VMSA memory content (i.e it will write
+                * the same memory region with the guest's key), so invalidate
+                * it first.
+                */
+               clflush_cache_range(svm->vmsa, PAGE_SIZE);
+
+               vmsa->handle = sev->handle;
+               vmsa->address = __sme_pa(svm->vmsa);
+               vmsa->len = PAGE_SIZE;
+               ret = sev_issue_cmd(kvm, SEV_CMD_LAUNCH_UPDATE_VMSA, vmsa,
+                                   &argp->error);
+               if (ret)
+                       goto e_free;
+
+               svm->vcpu.arch.vmsa_encrypted = true;
+       }
+
+e_free:
+       kfree(vmsa);
+       return ret;
+}
+
 static int sev_launch_measure(struct kvm *kvm, struct kvm_sev_cmd *argp)
 {
        void __user *measure = (void __user *)(uintptr_t)argp->data;
@@ -948,12 +1002,18 @@ int svm_mem_enc_op(struct kvm *kvm, void __user *argp)
        case KVM_SEV_INIT:
                r = sev_guest_init(kvm, &sev_cmd);
                break;
+       case KVM_SEV_ES_INIT:
+               r = sev_es_guest_init(kvm, &sev_cmd);
+               break;
        case KVM_SEV_LAUNCH_START:
                r = sev_launch_start(kvm, &sev_cmd);
                break;
        case KVM_SEV_LAUNCH_UPDATE_DATA:
                r = sev_launch_update_data(kvm, &sev_cmd);
                break;
+       case KVM_SEV_LAUNCH_UPDATE_VMSA:
+               r = sev_launch_update_vmsa(kvm, &sev_cmd);
+               break;
        case KVM_SEV_LAUNCH_MEASURE:
                r = sev_launch_measure(kvm, &sev_cmd);
                break;
-- 
2.28.0

Reply via email to