From e3fa8c9f22e1017e3f207dbb486a0dbb0fe20176 Mon Sep 17 00:00:00 2001 From: Christian Prochaska Date: Fri, 24 Oct 2014 23:23:16 +0200 Subject: [PATCH] vbox: save the guest FPU state before 'longjmp()' 'longjmp()' restores the (partial) FPU state saved by 'setjmp()', so it's necessary to save the guest FPU state before calling 'longjmp()'. Fixes #1282 --- repos/ports/src/virtualbox/nova/vcpu.h | 32 ++++++++++++++++++-------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/repos/ports/src/virtualbox/nova/vcpu.h b/repos/ports/src/virtualbox/nova/vcpu.h index e200a972f9..d575cc25aa 100644 --- a/repos/ports/src/virtualbox/nova/vcpu.h +++ b/repos/ports/src/virtualbox/nova/vcpu.h @@ -73,7 +73,8 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher { private: - X86FXSTATE _fpu_state __attribute__((aligned(0x10))); + X86FXSTATE _guest_fpu_state __attribute__((aligned(0x10))); + X86FXSTATE _emt_fpu_state __attribute__((aligned(0x10))); Genode::Cap_connection _cap_connection; Vmm::Vcpu_other_pd _vcpu; @@ -107,6 +108,16 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher INTERRUPT_STATE_NONE = 0U, }; + /* + * 'longjmp()' restores some FPU registers saved by 'setjmp()', + * so we need to save the guest FPU state before calling 'longjmp()' + */ + __attribute__((noreturn)) void _fpu_save_and_longjmp() + { + fpu_save(reinterpret_cast(&_guest_fpu_state)); + longjmp(_env, 1); + } + protected: struct { @@ -120,7 +131,8 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher void * _stack_reply; jmp_buf _env; - void switch_to_hw(PCPUMCTX pCtx) { + void switch_to_hw() + { unsigned long value; if (!setjmp(_env)) { @@ -129,7 +141,6 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher } } - __attribute__((noreturn)) void _default_handler() { Nova::Utcb * utcb = reinterpret_cast(Thread_base::utcb()); @@ -138,7 +149,7 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher Assert(!(utcb->inj_info & IRQ_INJ_VALID_MASK)); /* go back to re-compiler */ - longjmp(_env, 1); + _fpu_save_and_longjmp(); } __attribute__((noreturn)) void _recall_handler() @@ -162,7 +173,7 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher /* are we forced to go back to emulation mode ? */ if (!continue_hw_accelerated(utcb)) /* go back to emulation mode */ - longjmp(_env, 1); + _fpu_save_and_longjmp(); /* check whether we have to request irq injection window */ utcb->mtd = 0; @@ -216,7 +227,7 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher /* emulator has to take over if fault region is not ram */ if (!pv) - longjmp(_env, 1); + _fpu_save_and_longjmp(); /* fault region is ram - so map it */ enum { @@ -675,7 +686,7 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher VMCPU_SET_STATE(pVCpu, VMCPUSTATE_STARTED_EXEC); /* save current FPU state */ - fpu_save(reinterpret_cast(&_fpu_state)); + fpu_save(reinterpret_cast(&_emt_fpu_state)); /* write FPU state from pCtx to FPU registers */ fpu_load(reinterpret_cast(&pCtx->fpu)); /* tell kernel to transfer current fpu registers to vCPU */ @@ -685,7 +696,7 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher _current_vcpu = pVCpu; /* switch to hardware accelerated mode */ - switch_to_hw(pCtx); + switch_to_hw(); Assert(utcb->actv_state == ACTIVITY_STATE_ACTIVE); @@ -693,9 +704,10 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher _current_vcpu = 0; /* write FPU state of vCPU (in current FPU registers) to pCtx */ - fpu_save(reinterpret_cast(&pCtx->fpu)); + Genode::memcpy(&pCtx->fpu, &_guest_fpu_state, sizeof(X86FXSTATE)); + /* load saved FPU state of EMT thread */ - fpu_load(reinterpret_cast(&_fpu_state)); + fpu_load(reinterpret_cast(&_emt_fpu_state)); // CPUMSetChangedFlags(pVCpu, CPUM_CHANGED_GLOBAL_TLB_FLUSH);