-/* $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>
*
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 *);
* 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 |
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);
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;
}
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;
/*
* 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
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__);
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;
vcpu->vc_gueststate.vg_xcr0 = *rax;
- vcpu->vc_gueststate.vg_rip += insn_length;
-
return (0);
}
/* 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);
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;
-/* $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
-/* $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
/*