FPU context save/restore for SVM in vmm(4), matches a previous diff
authormlarkin <mlarkin@openbsd.org>
Tue, 30 May 2017 17:49:47 +0000 (17:49 +0000)
committermlarkin <mlarkin@openbsd.org>
Tue, 30 May 2017 17:49:47 +0000 (17:49 +0000)
from a few weeks ago that did the same for Intel/VMX.

ok deraadt

sys/arch/amd64/amd64/vmm.c
sys/arch/amd64/amd64/vmm_support.S
sys/arch/amd64/include/specialreg.h
sys/arch/amd64/include/vmmvar.h

index 0daf85e..a8b6066 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: vmm.c,v 1.148 2017/05/30 12:48:01 mlarkin Exp $       */
+/*     $OpenBSD: vmm.c,v 1.149 2017/05/30 17:49:47 mlarkin Exp $       */
 /*
  * Copyright (c) 2014 Mike Larkin <mlarkin@openbsd.org>
  *
@@ -154,7 +154,9 @@ int vmx_get_exit_info(uint64_t *, uint64_t *);
 int vmx_handle_exit(struct vcpu *);
 int svm_handle_exit(struct vcpu *);
 int svm_handle_msr(struct vcpu *);
+int vmm_handle_xsetbv(struct vcpu *, uint64_t *);
 int vmx_handle_xsetbv(struct vcpu *);
+int svm_handle_xsetbv(struct vcpu *);
 int vmm_handle_cpuid(struct vcpu *);
 int vmx_handle_rdmsr(struct vcpu *);
 int vmx_handle_wrmsr(struct vcpu *);
@@ -1717,6 +1719,7 @@ vcpu_reset_regs_svm(struct vcpu *vcpu, struct vcpu_reg_state *vrs)
         * SKINIT instruction (SVM_INTERCEPT_SKINIT)
         * ICEBP instruction (SVM_INTERCEPT_ICEBP)
         * MWAIT instruction (SVM_INTERCEPT_MWAIT_UNCOND)
+        * XSETBV instruction (SVM_INTERCEPT_XSETBV) (if available)
         */
        vmcb->v_intercept1 = SVM_INTERCEPT_INTR | SVM_INTERCEPT_NMI |
            SVM_INTERCEPT_CPUID | SVM_INTERCEPT_HLT | SVM_INTERCEPT_INOUT |
@@ -1727,6 +1730,9 @@ vcpu_reset_regs_svm(struct vcpu *vcpu, struct vcpu_reg_state *vrs)
            SVM_INTERCEPT_CLGI | SVM_INTERCEPT_SKINIT | SVM_INTERCEPT_ICEBP |
            SVM_INTERCEPT_MWAIT_UNCOND;
 
+       if (xsave_mask)
+               vmcb->v_intercept2 |= SVM_INTERCEPT_XSETBV;
+
        /* Setup I/O bitmap */
        memset((uint8_t *)vcpu->vc_svm_ioio_va, 0xFF, 3 * PAGE_SIZE);
        vmcb->v_iopm_pa = (uint64_t)(vcpu->vc_svm_ioio_pa);
@@ -1785,6 +1791,9 @@ vcpu_reset_regs_svm(struct vcpu *vcpu, struct vcpu_reg_state *vrs)
        vmcb->v_efer |= (EFER_LME | EFER_LMA);
        vmcb->v_cr4 |= CR4_PAE;
 
+       /* xcr0 power on default sets bit 0 (x87 state) */
+       vcpu->vc_gueststate.vg_xcr0 = XCR0_X87;
+
 exit:
        return ret;
 }
@@ -4068,6 +4077,10 @@ svm_handle_exit(struct vcpu *vcpu)
                ret = svm_handle_msr(vcpu);
                update_rip = 1;
                break;
+       case SVM_VMEXIT_XSETBV:
+               ret = svm_handle_xsetbv(vcpu);
+               update_rip = 1;
+               break;
        case SVM_VMEXIT_IOIO:
                ret = svm_handle_inout(vcpu);
                update_rip = 1;
@@ -4812,8 +4825,7 @@ vmx_handle_rdmsr(struct vcpu *vcpu)
 /*
  * vmx_handle_xsetbv
  *
- * Handler for xsetbv instructions. We allow the guest VM to set xcr0 values
- * limited to the xsave_mask in use in the host.
+ * VMX-specific part of the xsetbv instruction exit handler
  *
  * Parameters:
  *  vcpu: vcpu structure containing instruction info causing the exit
@@ -4825,8 +4837,8 @@ vmx_handle_rdmsr(struct vcpu *vcpu)
 int
 vmx_handle_xsetbv(struct vcpu *vcpu)
 {
-       uint64_t insn_length;
-       uint64_t *rax, *rdx, *rcx;
+       uint64_t insn_length, *rax;
+       int ret;
 
        if (vmread(VMCS_INSTRUCTION_LENGTH, &insn_length)) {
                printf("%s: can't obtain instruction length\n", __func__);
@@ -4837,6 +4849,64 @@ vmx_handle_xsetbv(struct vcpu *vcpu)
        KASSERT(insn_length == 3);
 
        rax = &vcpu->vc_gueststate.vg_rax;
+
+       ret = vmm_handle_xsetbv(vcpu, rax);
+
+       vcpu->vc_gueststate.vg_rip += insn_length;
+
+       return ret;
+}
+
+/*
+ * svm_handle_xsetbv
+ *
+ * SVM-specific part of the xsetbv instruction exit handler
+ *
+ * Parameters:
+ *  vcpu: vcpu structure containing instruction info causing the exit
+ *
+ * Return value:
+ *  0: The operation was successful
+ *  EINVAL: An error occurred
+ */
+int
+svm_handle_xsetbv(struct vcpu *vcpu)
+{
+       uint64_t insn_length, *rax;
+       int ret;
+       struct vmcb *vmcb = (struct vmcb *)vcpu->vc_control_va;
+
+       /* All XSETBV instructions are 3 bytes */
+       insn_length = 3;
+
+       rax = &vmcb->v_rax;
+
+       ret = vmm_handle_xsetbv(vcpu, rax);
+
+       vcpu->vc_gueststate.vg_rip += insn_length;
+
+       return ret;
+}
+
+/*
+ * vmm_handle_xsetbv
+ *
+ * Handler for xsetbv instructions. We allow the guest VM to set xcr0 values
+ * limited to the xsave_mask in use in the host.
+ *
+ * Parameters:
+ *  vcpu: vcpu structure containing instruction info causing the exit
+ *  rax: pointer to guest %rax
+ *
+ * Return value:
+ *  0: The operation was successful
+ *  EINVAL: An error occurred
+ */
+int
+vmm_handle_xsetbv(struct vcpu *vcpu, uint64_t *rax)
+{
+       uint64_t *rdx, *rcx;
+
        rcx = &vcpu->vc_gueststate.vg_rcx;
        rdx = &vcpu->vc_gueststate.vg_rdx;
 
@@ -4860,8 +4930,6 @@ vmx_handle_xsetbv(struct vcpu *vcpu)
 
        vcpu->vc_gueststate.vg_xcr0 = *rax;
 
-       vcpu->vc_gueststate.vg_rip += insn_length;
-
        return (0);
 }
        
@@ -5399,6 +5467,43 @@ vcpu_run_svm(struct vcpu *vcpu, struct vm_run_params *vrp)
 
                /* Start / resume the VCPU */
                KERNEL_ASSERT_LOCKED();
+
+               /* Disable interrupts and save the current FPU state. */
+               disable_intr();
+               clts();
+               vmm_fpusave();
+
+               /* Initialize the guest FPU if not inited already */
+               if (!vcpu->vc_fpuinited) {
+                       fninit();
+                       bzero(&vcpu->vc_g_fpu.fp_fxsave,
+                           sizeof(vcpu->vc_g_fpu.fp_fxsave));
+                       vcpu->vc_g_fpu.fp_fxsave.fx_fcw =
+                           __INITIAL_NPXCW__;
+                       vcpu->vc_g_fpu.fp_fxsave.fx_mxcsr =
+                           __INITIAL_MXCSR__;
+                       fxrstor(&vcpu->vc_g_fpu.fp_fxsave);
+
+                       vcpu->vc_fpuinited = 1;
+               }
+
+               if (xsave_mask) {
+                       /* Restore guest XCR0 and FPU context */
+                       if (vcpu->vc_gueststate.vg_xcr0 & ~xsave_mask) {
+                               DPRINTF("%s: guest attempted to set invalid "
+                                   "bits in xcr0\n", __func__);
+                               ret = EINVAL;
+                               stts();
+                               enable_intr();
+                               break;
+                       }
+
+                       /* Restore guest %xcr0 */
+                       xrstor(&vcpu->vc_g_fpu, xsave_mask);
+                       xsetbv(0, vcpu->vc_gueststate.vg_xcr0);
+               } else
+                       fxrstor(&vcpu->vc_g_fpu.fp_fxsave);
+
                KERNEL_UNLOCK();
 
                wrmsr(MSR_AMD_VM_HSAVE_PA, vcpu->vc_svm_hsa_pa);
@@ -5406,6 +5511,36 @@ vcpu_run_svm(struct vcpu *vcpu, struct vm_run_params *vrp)
                ret = svm_enter_guest(vcpu->vc_control_pa,
                    &vcpu->vc_gueststate, &gdt);
 
+               /*
+                * On exit, interrupts are disabled, and we are running with
+                * the guest FPU state still possibly on the CPU. Save the FPU
+                * state before re-enabling interrupts.
+                */
+               if (xsave_mask) {
+                       /* Save guest %xcr0 */
+                       vcpu->vc_gueststate.vg_xcr0 = xgetbv(0);
+
+                       /* Restore host %xcr0 */
+                       xsetbv(0, xsave_mask);
+
+                       /*
+                        * Save full copy of FPU state - guest content is
+                        * always a subset of host's save area (see xsetbv
+                        * exit handler)
+                        */     
+                       xsave(&vcpu->vc_g_fpu, xsave_mask);
+               } else
+                       fxsave(&vcpu->vc_g_fpu);
+
+               /*
+                * FPU state is invalid, set CR0_TS to force DNA trap on next
+                * access.
+                */
+               stts();
+
+               enable_intr();
+
+
                vcpu->vc_gueststate.vg_rip = vmcb->v_rip;
                vmcb->v_tlb_control = 0;
                vmcb->v_vmcb_clean_bits = 0xFFFFFFFF;
index 56b3dbd..7cb8d28 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: vmm_support.S,v 1.8 2017/04/27 06:16:39 mlarkin Exp $ */
+/*     $OpenBSD: vmm_support.S,v 1.9 2017/05/30 17:49:47 mlarkin Exp $ */
 /*
  * Copyright (c) 2014 Mike Larkin <mlarkin@openbsd.org>
  *
@@ -545,7 +545,7 @@ restore_host_svm:
         * first. This is to accommodate possibly lazy-switched
         * selectors from above
         */
-       cli     /* XXX not needed on amd due to implicit clgi on #vmexit */
+       cli
        popq    %rdx
        popq    %rax
        movq    $MSR_KERNELGSBASE, %rcx
@@ -586,6 +586,5 @@ restore_host_svm:
 
        movq    %rdi, %rax
        stgi
-       sti
 
        ret     
index af76bda..06571df 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: specialreg.h,v 1.55 2017/03/28 21:36:27 mlarkin Exp $ */
+/*     $OpenBSD: specialreg.h,v 1.56 2017/05/30 17:49:47 mlarkin Exp $ */
 /*     $NetBSD: specialreg.h,v 1.1 2003/04/26 18:39:48 fvdl Exp $      */
 /*     $NetBSD: x86/specialreg.h,v 1.2 2003/04/25 21:54:30 fvdl Exp $  */
 
 #define SVM_INTERCEPT_MONITOR          (1UL << 10)
 #define SVM_INTERCEPT_MWAIT_UNCOND     (1UL << 11)
 #define SVM_INTERCEPT_MWAIT_COND       (1UL << 12)
+#define SVM_INTERCEPT_XSETBV           (1UL << 13)
+#define SVM_INTERCEPT_EFER_WRITE       (1UL << 15)
+#define SVM_INTERCEPT_CR0_WRITE_POST   (1UL << 16)
+#define SVM_INTERCEPT_CR1_WRITE_POST   (1UL << 17)
+#define SVM_INTERCEPT_CR2_WRITE_POST   (1UL << 18)
+#define SVM_INTERCEPT_CR3_WRITE_POST   (1UL << 19)
+#define SVM_INTERCEPT_CR4_WRITE_POST   (1UL << 20)
+#define SVM_INTERCEPT_CR5_WRITE_POST   (1UL << 21)
+#define SVM_INTERCEPT_CR6_WRITE_POST   (1UL << 22)
+#define SVM_INTERCEPT_CR7_WRITE_POST   (1UL << 23)
+#define SVM_INTERCEPT_CR8_WRITE_POST   (1UL << 24)
+#define SVM_INTERCEPT_CR9_WRITE_POST   (1UL << 25)
+#define SVM_INTERCEPT_CR10_WRITE_POST  (1UL << 26)
+#define SVM_INTERCEPT_CR11_WRITE_POST  (1UL << 27)
+#define SVM_INTERCEPT_CR12_WRITE_POST  (1UL << 28)
+#define SVM_INTERCEPT_CR13_WRITE_POST  (1UL << 29)
+#define SVM_INTERCEPT_CR14_WRITE_POST  (1UL << 30)
+#define SVM_INTERCEPT_CR15_WRITE_POST  (1UL << 31)
 
 /*
  * PAT
index 736fc2e..9f8a4cb 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: vmmvar.h,v 1.39 2017/05/28 23:50:19 mlarkin Exp $     */
+/*     $OpenBSD: vmmvar.h,v 1.40 2017/05/30 17:49:47 mlarkin Exp $     */
 /*
  * Copyright (c) 2014 Mike Larkin <mlarkin@openbsd.org>
  *
 #define SVM_VMEXIT_MONITOR                     0x8A
 #define SVM_VMEXIT_MWAIT                       0x8B
 #define SVM_VMEXIT_MWAIT_CONDITIONAL           0x8C
+#define SVM_VMEXIT_XSETBV                      0x8D
+#define SVM_VMEXIT_EFER_WRITE_TRAP             0x8F
+#define SVM_VMEXIT_CR0_WRITE_TRAP              0x90
+#define SVM_VMEXIT_CR1_WRITE_TRAP              0x91
+#define SVM_VMEXIT_CR2_WRITE_TRAP              0x92
+#define SVM_VMEXIT_CR3_WRITE_TRAP              0x93
+#define SVM_VMEXIT_CR4_WRITE_TRAP              0x94
+#define SVM_VMEXIT_CR5_WRITE_TRAP              0x95
+#define SVM_VMEXIT_CR6_WRITE_TRAP              0x96
+#define SVM_VMEXIT_CR7_WRITE_TRAP              0x97
+#define SVM_VMEXIT_CR8_WRITE_TRAP              0x98
+#define SVM_VMEXIT_CR9_WRITE_TRAP              0x99
+#define SVM_VMEXIT_CR10_WRITE_TRAP             0x9A
+#define SVM_VMEXIT_CR11_WRITE_TRAP             0x9B
+#define SVM_VMEXIT_CR12_WRITE_TRAP             0x9C
+#define SVM_VMEXIT_CR13_WRITE_TRAP             0x9D
+#define SVM_VMEXIT_CR14_WRITE_TRAP             0x9E
+#define SVM_VMEXIT_CR15_WRITE_TRAP             0x9F
 #define SVM_VMEXIT_NPF                         0x400
+#define SVM_AVIC_INCOMPLETE_IPI                        0x401
+#define SVM_AVIC_NOACCEL                       0x402
+#define SVM_VMEXIT_VMGEXIT                     0x403
 #define SVM_VMEXIT_INVALID                     -1
 
 /*