From: mlarkin Date: Tue, 30 May 2017 17:49:47 +0000 (+0000) Subject: FPU context save/restore for SVM in vmm(4), matches a previous diff X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=78080c4e6cb000d4e3a7d251f8b4ae1db7c1163f;p=openbsd FPU context save/restore for SVM in vmm(4), matches a previous diff from a few weeks ago that did the same for Intel/VMX. ok deraadt --- diff --git a/sys/arch/amd64/amd64/vmm.c b/sys/arch/amd64/amd64/vmm.c index 0daf85e1a3f..a8b606696fd 100644 --- a/sys/arch/amd64/amd64/vmm.c +++ b/sys/arch/amd64/amd64/vmm.c @@ -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 * @@ -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; diff --git a/sys/arch/amd64/amd64/vmm_support.S b/sys/arch/amd64/amd64/vmm_support.S index 56b3dbdfda8..7cb8d28458d 100644 --- a/sys/arch/amd64/amd64/vmm_support.S +++ b/sys/arch/amd64/amd64/vmm_support.S @@ -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 * @@ -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 diff --git a/sys/arch/amd64/include/specialreg.h b/sys/arch/amd64/include/specialreg.h index af76bda5579..06571df0dda 100644 --- a/sys/arch/amd64/include/specialreg.h +++ b/sys/arch/amd64/include/specialreg.h @@ -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 $ */ @@ -1289,6 +1289,24 @@ #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 diff --git a/sys/arch/amd64/include/vmmvar.h b/sys/arch/amd64/include/vmmvar.h index 736fc2efa7b..9f8a4cbb863 100644 --- a/sys/arch/amd64/include/vmmvar.h +++ b/sys/arch/amd64/include/vmmvar.h @@ -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 * @@ -253,7 +253,28 @@ #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 /*