untrusted comment: signature from openbsd 6.1 base secret key
RWQEQa33SgQSEk3iUQjZBwKN4yXDFvjYuePYUJ88IGAlHIYDhp9eYqQik/wsL4UjnTymi1j3Orp4BPFWWEU3y7O/IjBA1IGUZgg=
And then rebuild and install a new kernel:
KK=`sysctl -n kern.osversion | cut -d# -f1`
cd /usr/src/sys/arch/`machine`/compile/$KK
make obj
make config
make
make install
/*
+ * vmm_fpusave
+ *
+ * Modified version of fpusave_cpu from fpu.c that only saves the FPU context
+ * and does not call splipi/splx. Must be called with interrupts disabled.
+ */
+void
+vmm_fpusave(void)
+{
+ struct proc *p;
+ struct cpu_info *ci = curcpu();
+
+ p = ci->ci_fpcurproc;
+ if (p == NULL)
+ return;
+
+ if (ci->ci_fpsaving != 0)
+ panic("%s: recursive save!", __func__);
+ /*
+ * Set ci->ci_fpsaving, so that any pending exception will be
+ * thrown away. (It will be caught again if/when the FPU
+ * state is restored.)
+ */
+ ci->ci_fpsaving = 1;
+ if (xsave_mask)
+ xsave(&p->p_addr->u_pcb.pcb_savefpu, xsave_mask);
+ else
+ fxsave(&p->p_addr->u_pcb.pcb_savefpu);
+ ci->ci_fpsaving = 0;
+
+ p->p_addr->u_pcb.pcb_cr0 |= CR0_TS;
+
+ p->p_addr->u_pcb.pcb_fpcpu = NULL;
+ ci->ci_fpcurproc = NULL;
+}
+
+/*
* vcpu_run_vmx
*
* VMX main loop used to run a VCPU.
@@ -3404,6 +3446,8 @@ vcpu_run_vmx(struct vcpu *vcpu, struct v
break;
case VMX_EXIT_CPUID:
break;
+ case VMX_EXIT_XSETBV:
+ break;
#ifdef VMM_DEBUG
case VMX_EXIT_TRIPLE_FAULT:
DPRINTF("%s: vm %d vcpu %d triple fault\n",
@@ -3528,10 +3572,76 @@ vcpu_run_vmx(struct vcpu *vcpu, struct v
/* 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();
ret = vmx_enter_guest(&vcpu->vc_control_pa,
&vcpu->vc_gueststate, resume);
+ /*
+ * 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();
+
exit_reason = VM_EXIT_NONE;
if (ret == 0) {
/*
@@ -3545,6 +3655,7 @@ vcpu_run_vmx(struct vcpu *vcpu, struct v
printf("%s: can't read guest rflags during "
"exit\n", __func__);
ret = EINVAL;
+ KERNEL_LOCK();
break;
}
}
@@ -3826,6 +3937,10 @@ vmx_handle_exit(struct vcpu *vcpu)
ret = vmx_handle_wrmsr(vcpu);
update_rip = 1;
break;
+ case VMX_EXIT_XSETBV:
+ ret = vmx_handle_xsetbv(vcpu);
+ update_rip = 1;
+ break;
case VMX_EXIT_TRIPLE_FAULT:
#ifdef VMM_DEBUG
DPRINTF("%s: vm %d vcpu %d triple fault\n", __func__,
@@ -4351,6 +4466,62 @@ 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.
+ *
+ * Parameters:
+ * vcpu: vcpu structure containing instruction info causing the exit
+ *
+ * Return value:
+ * 0: The operation was successful
+ * EINVAL: An error occurred
+ */
+int
+vmx_handle_xsetbv(struct vcpu *vcpu)
+{
+ uint64_t insn_length;
+ uint64_t *rax, *rdx, *rcx;;
+
+ if (vmread(VMCS_INSTRUCTION_LENGTH, &insn_length)) {
+ printf("%s: can't obtain instruction length\n", __func__);
+ return (EINVAL);
+ }
+
+ /* All XSETBV instructions are 0x0F 0x01 0xD1 */
+ KASSERT(insn_length == 3);
+
+ rax = &vcpu->vc_gueststate.vg_rax;
+ rcx = &vcpu->vc_gueststate.vg_rcx;
+ rdx = &vcpu->vc_gueststate.vg_rdx;
+
+ if (*rcx != 0) {
+ DPRINTF("%s: guest specified invalid xcr register number "
+ "%lld\n", __func__, *rcx);
+ /* XXX this should #GP(0) instead of killing the guest */
+ return (EINVAL);
+ }
+
+ /*
+ * No bits in %edx are currently supported. Check this, and validate
+ * against the host mask.
+ */
+ if (*rdx != 0 || (*rax & ~xsave_mask)) {
+ DPRINTF("%s: guest specified invalid xcr0 content "
+ "(0x%llx:0x%llx)\n", __func__, *rdx, *rax);
+ /* XXX this should #GP(0) instead of killing the guest */
+ return (EINVAL);
+ }
+
+ vcpu->vc_gueststate.vg_xcr0 = *rax;
+
+ vcpu->vc_gueststate.vg_rip += insn_length;
+
+ return (0);
+}
+
+/*
* vmx_handle_wrmsr
*
* Handler for wrmsr instructions. This handler logs the access, and discards
@@ -4413,6 +4584,7 @@ vmm_handle_cpuid(struct vcpu *vcpu)
{
uint64_t insn_length;
uint64_t *rax, *rbx, *rcx, *rdx;
+ uint32_t eax, ebx, ecx, edx;