mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-18 21:27:56 +00:00
vm/x86: support extended fpu state transfer
Extend Genode's vCPU FPU state and adjust all users to copy at most FPU data they actually support. Issue #5314
This commit is contained in:
parent
5993fa9c7f
commit
ff506b0375
@ -400,6 +400,7 @@ struct Foc_vcpu : Thread, Noncopyable
|
||||
if (state.fpu.charged()) {
|
||||
state.fpu.charge([&] (Vcpu_state::Fpu::State &fpu) {
|
||||
asm volatile ("fxrstor %0" : : "m" (fpu) : "memory");
|
||||
return 512;
|
||||
});
|
||||
} else
|
||||
asm volatile ("fxrstor %0" : : "m" (_fpu_vcpu) : "memory");
|
||||
@ -412,6 +413,7 @@ struct Foc_vcpu : Thread, Noncopyable
|
||||
state.fpu.charge([&] (Vcpu_state::Fpu::State &fpu) {
|
||||
asm volatile ("fxsave %0" : "=m" (fpu) :: "memory");
|
||||
asm volatile ("fxsave %0" : "=m" (_fpu_vcpu) :: "memory");
|
||||
return 512;
|
||||
});
|
||||
asm volatile ("fxrstor %0" : : "m" (_fpu_ep) : "memory");
|
||||
|
||||
|
@ -60,6 +60,7 @@ class Genode::Fpu_context
|
||||
}
|
||||
|
||||
addr_t fpu_context() const { return _fxsave_addr; }
|
||||
addr_t fpu_size() const { return sizeof(_fxsave_area); }
|
||||
};
|
||||
|
||||
#endif /* _CORE__SPEC__X86_64__FPU_H_ */
|
||||
|
@ -222,7 +222,7 @@ void Board::Vcpu_context::read_vcpu_state(Vcpu_state &state)
|
||||
if (state.fpu.charged()) {
|
||||
state.fpu.with_state(
|
||||
[&](Vcpu_state::Fpu::State const &fpu) {
|
||||
memcpy((void *) regs->fpu_context(), &fpu, sizeof(fpu));
|
||||
memcpy((void *) regs->fpu_context(), &fpu, regs->fpu_size());
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -233,7 +233,8 @@ void Board::Vcpu_context::write_vcpu_state(Vcpu_state &state)
|
||||
state.exit_reason = (unsigned) exit_reason;
|
||||
|
||||
state.fpu.charge([&](Vcpu_state::Fpu::State &fpu) {
|
||||
memcpy(&fpu, (void *) regs->fpu_context(), sizeof(fpu));
|
||||
memcpy(&fpu, (void *) regs->fpu_context(), regs->fpu_size());
|
||||
return regs->fpu_size();
|
||||
});
|
||||
|
||||
/* SVM will overwrite rax but VMX doesn't. */
|
||||
|
@ -694,7 +694,7 @@ namespace Nova {
|
||||
} gdtr, idtr;
|
||||
unsigned long long tsc_val, tsc_off, tsc_aux;
|
||||
unsigned long long exit_reason;
|
||||
uint8_t fpu[512];
|
||||
uint8_t fpu[2560];
|
||||
} __attribute__((packed));
|
||||
mword_t mr[(4096 - 4 * sizeof(mword_t)) / sizeof(mword_t)];
|
||||
};
|
||||
|
@ -177,7 +177,10 @@ void Nova_vcpu::_read_nova_state(Nova::Utcb &utcb)
|
||||
|
||||
if (utcb.mtd & Nova::Mtd::FPU) {
|
||||
_vcpu_state.fpu.charge([&] (Vcpu_state::Fpu::State &fpu) {
|
||||
memcpy(&fpu, utcb.fpu, sizeof(fpu));
|
||||
auto const fpu_size = unsigned(min(_vcpu_state.fpu.size(),
|
||||
sizeof(utcb.fpu)));
|
||||
memcpy(&fpu, utcb.fpu, fpu_size);
|
||||
return fpu_size;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -206,15 +206,16 @@ class Genode::Vcpu_state
|
||||
|
||||
struct State
|
||||
{
|
||||
uint8_t _buffer[512] { };
|
||||
} __attribute__((aligned(16)));
|
||||
uint8_t _buffer[2560] { };
|
||||
} __attribute__((aligned(64)));
|
||||
|
||||
private:
|
||||
|
||||
friend class Vcpu_state;
|
||||
|
||||
State _state { };
|
||||
bool _charged { false };
|
||||
State _state { };
|
||||
bool _charged { false };
|
||||
uint64_t _state_size { };
|
||||
|
||||
/* see comment for Register::operator=() */
|
||||
Fpu & operator = (Fpu const &)
|
||||
@ -232,12 +233,14 @@ class Genode::Vcpu_state
|
||||
|
||||
void charge(auto const &fn)
|
||||
{
|
||||
_charged = true;
|
||||
fn(_state);
|
||||
_charged = true;
|
||||
_state_size = fn(_state);
|
||||
}
|
||||
|
||||
auto size() const { return _state_size; }
|
||||
};
|
||||
|
||||
Fpu fpu __attribute__((aligned(16))) { };
|
||||
Fpu fpu __attribute__((aligned(64))) { };
|
||||
|
||||
/*
|
||||
* Registers transfered by hypervisor from guest on VM exit are charged.
|
||||
|
@ -120,8 +120,10 @@ class Vmm::Vcpu
|
||||
void force_fpu_state_transfer(Vcpu_state & state)
|
||||
{
|
||||
/* force FPU-state transfer on next entry */
|
||||
state.fpu.charge([] (Vcpu_state::Fpu::State &) {
|
||||
/* don't change state */ });
|
||||
state.fpu.charge([] (Vcpu_state::Fpu::State &state) {
|
||||
/* don't change state */
|
||||
return sizeof(state);
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -85,7 +85,7 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher<Genode::Thread>,
|
||||
|
||||
private:
|
||||
|
||||
X86FXSTATE _emt_fpu_state __attribute__((aligned(0x10)));
|
||||
X86FXSTATE _emt_fpu_state __attribute__((aligned(64)));
|
||||
|
||||
pthread _pthread;
|
||||
pthread_cond_t _cond_wait;
|
||||
@ -968,7 +968,7 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher<Genode::Thread>,
|
||||
/* save current FPU state */
|
||||
fpu_save(reinterpret_cast<char *>(&_emt_fpu_state));
|
||||
/* write FPU state from pCtx to utcb */
|
||||
memcpy(utcb->fpu, pCtx->pXStateR3, sizeof(utcb->fpu));
|
||||
memcpy(utcb->fpu, pCtx->pXStateR3, sizeof(X86FXSTATE));
|
||||
/* tell kernel to transfer current fpu registers to vCPU */
|
||||
utcb->mtd |= Mtd::FPU;
|
||||
|
||||
@ -984,7 +984,7 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher<Genode::Thread>,
|
||||
_current_vcpu = 0;
|
||||
|
||||
/* write FPU state of vCPU in utcb to pCtx */
|
||||
static_assert(sizeof(X86FXSTATE) == sizeof(utcb->fpu), "fpu size mismatch");
|
||||
static_assert(sizeof(X86FXSTATE) <= sizeof(utcb->fpu), "fpu size mismatch");
|
||||
Genode::memcpy(pCtx->pXStateR3, utcb->fpu, sizeof(X86FXSTATE));
|
||||
|
||||
/* load saved FPU state of EMT thread */
|
||||
|
@ -266,10 +266,10 @@ template <typename VIRT> void Sup::Vcpu_impl<VIRT>::_transfer_state_to_vcpu(CPUM
|
||||
}
|
||||
|
||||
/* export FPU state */
|
||||
AssertCompile(sizeof(Vcpu_state::Fpu::State) >= sizeof(X86FXSTATE));
|
||||
|
||||
_state->ref.fpu.charge([&](Vcpu_state::Fpu::State &fpu) {
|
||||
::memcpy(fpu._buffer, ctx.pXStateR3, sizeof(fpu));
|
||||
_state->ref.fpu.charge([&](Vcpu_state::Fpu::State &fpu) {
|
||||
static_assert(sizeof(*ctx.pXStateR3) >= sizeof(fpu._buffer));
|
||||
::memcpy(fpu._buffer, ctx.pXStateR3, sizeof(X86FXSTATE));
|
||||
return sizeof(X86FXSTATE);
|
||||
});
|
||||
|
||||
{
|
||||
@ -415,6 +415,7 @@ template <typename VIRT> void Sup::Vcpu_impl<VIRT>::_transfer_state_to_vbox(CPUM
|
||||
|
||||
/* import FPU state */
|
||||
_state->ref.fpu.with_state([&](Vcpu_state::Fpu::State const &fpu) {
|
||||
static_assert(sizeof(*ctx.pXStateR3) >= sizeof(fpu._buffer));
|
||||
::memcpy(ctx.pXStateR3, fpu._buffer, sizeof(X86FXSTATE));
|
||||
return true;
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user