KVM: SEV: sync FPU and AVX state at LAUNCH_UPDATE_VMSA time
authorPaolo Bonzini <pbonzini@redhat.com>
Thu, 4 Apr 2024 12:13:21 +0000 (08:13 -0400)
committerPaolo Bonzini <pbonzini@redhat.com>
Thu, 11 Apr 2024 17:08:25 +0000 (13:08 -0400)
SEV-ES allows passing custom contents for x87, SSE and AVX state into the VMSA.
Allow userspace to do that with the usual KVM_SET_XSAVE API and only mark
FPU contents as confidential after it has been copied and encrypted into
the VMSA.

Since the XSAVE state for AVX is the first, it does not need the
compacted-state handling of get_xsave_addr().  However, there are other
parts of XSAVE state in the VMSA that currently are not handled, and
the validation logic of get_xsave_addr() is pointless to duplicate
in KVM, so move get_xsave_addr() to public FPU API; it is really just
a facility to operate on XSAVE state and does not expose any internal
details of arch/x86/kernel/fpu.

Acked-by: Dave Hansen <dave.hansen@linux.intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Message-ID: <20240404121327.3107131-12-pbonzini@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
arch/x86/include/asm/fpu/api.h
arch/x86/kernel/fpu/xstate.c
arch/x86/kernel/fpu/xstate.h
arch/x86/kvm/svm/sev.c
arch/x86/kvm/svm/svm.c

index a2be3aefff9f8974131d08d04830b1e2c8582210..f86ad3335529d4e61e8f2578f8ec9ee641e1b762 100644 (file)
@@ -143,6 +143,9 @@ extern void fpstate_clear_xstate_component(struct fpstate *fps, unsigned int xfe
 
 extern u64 xstate_get_guest_group_perm(void);
 
+extern void *get_xsave_addr(struct xregs_state *xsave, int xfeature_nr);
+
+
 /* KVM specific functions */
 extern bool fpu_alloc_guest_fpstate(struct fpu_guest *gfpu);
 extern void fpu_free_guest_fpstate(struct fpu_guest *gfpu);
index 33a214b1a4cec1fc2d1840968668f3a1f2d4ac94..6d32e415b01e1ab2b5364d274530424cf29e232c 100644 (file)
@@ -991,6 +991,7 @@ void *get_xsave_addr(struct xregs_state *xsave, int xfeature_nr)
 
        return __raw_xsave_addr(xsave, xfeature_nr);
 }
+EXPORT_SYMBOL_GPL(get_xsave_addr);
 
 #ifdef CONFIG_ARCH_HAS_PKEYS
 
index 19ca623ffa2ac77204d9ffa47208d867a5dbb8da..05df04f3962823c17240deaf31eb7ba9b073d6df 100644 (file)
@@ -54,8 +54,6 @@ extern int copy_sigframe_from_user_to_xstate(struct task_struct *tsk, const void
 extern void fpu__init_cpu_xstate(void);
 extern void fpu__init_system_xstate(unsigned int legacy_size);
 
-extern void *get_xsave_addr(struct xregs_state *xsave, int xfeature_nr);
-
 static inline u64 xfeatures_mask_supervisor(void)
 {
        return fpu_kernel_cfg.max_features & XFEATURE_MASK_SUPERVISOR_SUPPORTED;
index 1512bacd74a90b70b37cc29eddbbc936d6363139..3517d6736c93d21fefe4b0200b1d37f613daac95 100644 (file)
@@ -23,6 +23,7 @@
 #include <asm/pkru.h>
 #include <asm/trapnr.h>
 #include <asm/fpu/xcr.h>
+#include <asm/fpu/xstate.h>
 #include <asm/debugreg.h>
 
 #include "mmu.h"
@@ -584,6 +585,10 @@ static int sev_es_sync_vmsa(struct vcpu_svm *svm)
        struct kvm_vcpu *vcpu = &svm->vcpu;
        struct kvm_sev_info *sev = &to_kvm_svm(vcpu->kvm)->sev_info;
        struct sev_es_save_area *save = svm->sev_es.vmsa;
+       struct xregs_state *xsave;
+       const u8 *s;
+       u8 *d;
+       int i;
 
        /* Check some debug related fields before encrypting the VMSA */
        if (svm->vcpu.guest_debug || (svm->vmcb->save.dr7 & ~DR7_FIXED_1))
@@ -626,6 +631,44 @@ static int sev_es_sync_vmsa(struct vcpu_svm *svm)
 
        save->sev_features = sev->vmsa_features;
 
+       /*
+        * Skip FPU and AVX setup with KVM_SEV_ES_INIT to avoid
+        * breaking older measurements.
+        */
+       if (vcpu->kvm->arch.vm_type != KVM_X86_DEFAULT_VM) {
+               xsave = &vcpu->arch.guest_fpu.fpstate->regs.xsave;
+               save->x87_dp = xsave->i387.rdp;
+               save->mxcsr = xsave->i387.mxcsr;
+               save->x87_ftw = xsave->i387.twd;
+               save->x87_fsw = xsave->i387.swd;
+               save->x87_fcw = xsave->i387.cwd;
+               save->x87_fop = xsave->i387.fop;
+               save->x87_ds = 0;
+               save->x87_cs = 0;
+               save->x87_rip = xsave->i387.rip;
+
+               for (i = 0; i < 8; i++) {
+                       /*
+                        * The format of the x87 save area is undocumented and
+                        * definitely not what you would expect.  It consists of
+                        * an 8*8 bytes area with bytes 0-7, and an 8*2 bytes
+                        * area with bytes 8-9 of each register.
+                        */
+                       d = save->fpreg_x87 + i * 8;
+                       s = ((u8 *)xsave->i387.st_space) + i * 16;
+                       memcpy(d, s, 8);
+                       save->fpreg_x87[64 + i * 2] = s[8];
+                       save->fpreg_x87[64 + i * 2 + 1] = s[9];
+               }
+               memcpy(save->fpreg_xmm, xsave->i387.xmm_space, 256);
+
+               s = get_xsave_addr(xsave, XFEATURE_YMM);
+               if (s)
+                       memcpy(save->fpreg_ymm, s, 256);
+               else
+                       memset(save->fpreg_ymm, 0, 256);
+       }
+
        pr_debug("Virtual Machine Save Area (VMSA):\n");
        print_hex_dump_debug("", DUMP_PREFIX_NONE, 16, 1, save, sizeof(*save), false);
 
@@ -664,6 +707,13 @@ static int __sev_launch_update_vmsa(struct kvm *kvm, struct kvm_vcpu *vcpu,
        if (ret)
          return ret;
 
+       /*
+        * SEV-ES guests maintain an encrypted version of their FPU
+        * state which is restored and saved on VMRUN and VMEXIT.
+        * Mark vcpu->arch.guest_fpu->fpstate as scratch so it won't
+        * do xsave/xrstor on it.
+        */
+       fpstate_set_confidential(&vcpu->arch.guest_fpu);
        vcpu->arch.guest_state_protected = true;
        return 0;
 }
index b0038ece55cbccddf6fbdb0a6b41a14f72cb9ae7..0f3b59da0d4a891431b9474c575552a133407026 100644 (file)
@@ -1433,14 +1433,6 @@ static int svm_vcpu_create(struct kvm_vcpu *vcpu)
                vmsa_page = snp_safe_alloc_page(vcpu);
                if (!vmsa_page)
                        goto error_free_vmcb_page;
-
-               /*
-                * SEV-ES guests maintain an encrypted version of their FPU
-                * state which is restored and saved on VMRUN and VMEXIT.
-                * Mark vcpu->arch.guest_fpu->fpstate as scratch so it won't
-                * do xsave/xrstor on it.
-                */
-               fpstate_set_confidential(&vcpu->arch.guest_fpu);
        }
 
        err = avic_init_vcpu(svm);