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:
Alexander Boettcher 2024-09-04 15:41:29 +02:00 committed by Norman Feske
parent 5993fa9c7f
commit ff506b0375
9 changed files with 33 additions and 20 deletions

View File

@ -400,6 +400,7 @@ struct Foc_vcpu : Thread, Noncopyable
if (state.fpu.charged()) { if (state.fpu.charged()) {
state.fpu.charge([&] (Vcpu_state::Fpu::State &fpu) { state.fpu.charge([&] (Vcpu_state::Fpu::State &fpu) {
asm volatile ("fxrstor %0" : : "m" (fpu) : "memory"); asm volatile ("fxrstor %0" : : "m" (fpu) : "memory");
return 512;
}); });
} else } else
asm volatile ("fxrstor %0" : : "m" (_fpu_vcpu) : "memory"); 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) { state.fpu.charge([&] (Vcpu_state::Fpu::State &fpu) {
asm volatile ("fxsave %0" : "=m" (fpu) :: "memory"); asm volatile ("fxsave %0" : "=m" (fpu) :: "memory");
asm volatile ("fxsave %0" : "=m" (_fpu_vcpu) :: "memory"); asm volatile ("fxsave %0" : "=m" (_fpu_vcpu) :: "memory");
return 512;
}); });
asm volatile ("fxrstor %0" : : "m" (_fpu_ep) : "memory"); asm volatile ("fxrstor %0" : : "m" (_fpu_ep) : "memory");

View File

@ -60,6 +60,7 @@ class Genode::Fpu_context
} }
addr_t fpu_context() const { return _fxsave_addr; } addr_t fpu_context() const { return _fxsave_addr; }
addr_t fpu_size() const { return sizeof(_fxsave_area); }
}; };
#endif /* _CORE__SPEC__X86_64__FPU_H_ */ #endif /* _CORE__SPEC__X86_64__FPU_H_ */

View File

@ -222,7 +222,7 @@ void Board::Vcpu_context::read_vcpu_state(Vcpu_state &state)
if (state.fpu.charged()) { if (state.fpu.charged()) {
state.fpu.with_state( state.fpu.with_state(
[&](Vcpu_state::Fpu::State const &fpu) { [&](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.exit_reason = (unsigned) exit_reason;
state.fpu.charge([&](Vcpu_state::Fpu::State &fpu) { 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. */ /* SVM will overwrite rax but VMX doesn't. */

View File

@ -694,7 +694,7 @@ namespace Nova {
} gdtr, idtr; } gdtr, idtr;
unsigned long long tsc_val, tsc_off, tsc_aux; unsigned long long tsc_val, tsc_off, tsc_aux;
unsigned long long exit_reason; unsigned long long exit_reason;
uint8_t fpu[512]; uint8_t fpu[2560];
} __attribute__((packed)); } __attribute__((packed));
mword_t mr[(4096 - 4 * sizeof(mword_t)) / sizeof(mword_t)]; mword_t mr[(4096 - 4 * sizeof(mword_t)) / sizeof(mword_t)];
}; };

View File

@ -177,7 +177,10 @@ void Nova_vcpu::_read_nova_state(Nova::Utcb &utcb)
if (utcb.mtd & Nova::Mtd::FPU) { if (utcb.mtd & Nova::Mtd::FPU) {
_vcpu_state.fpu.charge([&] (Vcpu_state::Fpu::State &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;
}); });
} }

View File

@ -206,15 +206,16 @@ class Genode::Vcpu_state
struct State struct State
{ {
uint8_t _buffer[512] { }; uint8_t _buffer[2560] { };
} __attribute__((aligned(16))); } __attribute__((aligned(64)));
private: private:
friend class Vcpu_state; friend class Vcpu_state;
State _state { }; State _state { };
bool _charged { false }; bool _charged { false };
uint64_t _state_size { };
/* see comment for Register::operator=() */ /* see comment for Register::operator=() */
Fpu & operator = (Fpu const &) Fpu & operator = (Fpu const &)
@ -232,12 +233,14 @@ class Genode::Vcpu_state
void charge(auto const &fn) void charge(auto const &fn)
{ {
_charged = true; _charged = true;
fn(_state); _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. * Registers transfered by hypervisor from guest on VM exit are charged.

View File

@ -120,8 +120,10 @@ class Vmm::Vcpu
void force_fpu_state_transfer(Vcpu_state & state) void force_fpu_state_transfer(Vcpu_state & state)
{ {
/* force FPU-state transfer on next entry */ /* force FPU-state transfer on next entry */
state.fpu.charge([] (Vcpu_state::Fpu::State &) { state.fpu.charge([] (Vcpu_state::Fpu::State &state) {
/* don't change state */ }); /* don't change state */
return sizeof(state);
});
} }
/* /*

View File

@ -85,7 +85,7 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher<Genode::Thread>,
private: private:
X86FXSTATE _emt_fpu_state __attribute__((aligned(0x10))); X86FXSTATE _emt_fpu_state __attribute__((aligned(64)));
pthread _pthread; pthread _pthread;
pthread_cond_t _cond_wait; pthread_cond_t _cond_wait;
@ -968,7 +968,7 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher<Genode::Thread>,
/* save current FPU state */ /* save current FPU state */
fpu_save(reinterpret_cast<char *>(&_emt_fpu_state)); fpu_save(reinterpret_cast<char *>(&_emt_fpu_state));
/* write FPU state from pCtx to utcb */ /* 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 */ /* tell kernel to transfer current fpu registers to vCPU */
utcb->mtd |= Mtd::FPU; utcb->mtd |= Mtd::FPU;
@ -984,7 +984,7 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher<Genode::Thread>,
_current_vcpu = 0; _current_vcpu = 0;
/* write FPU state of vCPU in utcb to pCtx */ /* 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)); Genode::memcpy(pCtx->pXStateR3, utcb->fpu, sizeof(X86FXSTATE));
/* load saved FPU state of EMT thread */ /* load saved FPU state of EMT thread */

View File

@ -266,10 +266,10 @@ template <typename VIRT> void Sup::Vcpu_impl<VIRT>::_transfer_state_to_vcpu(CPUM
} }
/* export FPU state */ /* export FPU state */
AssertCompile(sizeof(Vcpu_state::Fpu::State) >= sizeof(X86FXSTATE)); _state->ref.fpu.charge([&](Vcpu_state::Fpu::State &fpu) {
static_assert(sizeof(*ctx.pXStateR3) >= sizeof(fpu._buffer));
_state->ref.fpu.charge([&](Vcpu_state::Fpu::State &fpu) { ::memcpy(fpu._buffer, ctx.pXStateR3, sizeof(X86FXSTATE));
::memcpy(fpu._buffer, ctx.pXStateR3, sizeof(fpu)); return sizeof(X86FXSTATE);
}); });
{ {
@ -415,6 +415,7 @@ template <typename VIRT> void Sup::Vcpu_impl<VIRT>::_transfer_state_to_vbox(CPUM
/* import FPU state */ /* import FPU state */
_state->ref.fpu.with_state([&](Vcpu_state::Fpu::State const &fpu) { _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)); ::memcpy(ctx.pXStateR3, fpu._buffer, sizeof(X86FXSTATE));
return true; return true;
}); });