mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-19 13:47: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()) {
|
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");
|
||||||
|
|
||||||
|
@ -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_ */
|
||||||
|
@ -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. */
|
||||||
|
@ -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)];
|
||||||
};
|
};
|
||||||
|
@ -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;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
@ -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);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -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 */
|
||||||
|
@ -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;
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user