mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-19 05:37:54 +00:00
nova: FPU handling for vCPUs in VM session
- request FPU state on VM exit in portal config - transfer FPU state on VM entry - save fpu state early Avoid any FPU instructions (for example during base API calls), which use the FPU and overwrite FPU registers holding the guest vCPU FPU state. - don't save fpu state of EP Issue #4313
This commit is contained in:
parent
1a79bf2be2
commit
7a06633173
@ -64,17 +64,16 @@ struct Nova_vcpu : Rpc_client<Vm_session::Native_vcpu>, Noncopyable
|
||||
|
||||
Vcpu_state _vcpu_state __attribute__((aligned(0x10))) { };
|
||||
|
||||
uint8_t _fpu_ep[512] __attribute__((aligned(0x10)));
|
||||
|
||||
enum Remote_state_requested {
|
||||
NONE = 0,
|
||||
PAUSE = 1,
|
||||
RUN = 2
|
||||
} _remote { NONE };
|
||||
|
||||
inline void _read_nova_state(Nova::Utcb &utcb, unsigned exit_reason);
|
||||
inline void _read_nova_state(Nova::Utcb &, unsigned exit_reason,
|
||||
uint8_t const &);
|
||||
|
||||
inline void _write_nova_state(Nova::Utcb &utcb);
|
||||
inline void _write_nova_state(Nova::Utcb &);
|
||||
|
||||
addr_t _sm_sel() const {
|
||||
return Nova::NUM_INITIAL_PT_RESERVED + _id_elem.id().value * 4; }
|
||||
@ -82,7 +81,8 @@ struct Nova_vcpu : Rpc_client<Vm_session::Native_vcpu>, Noncopyable
|
||||
addr_t _ec_sel() const { return _sm_sel() + 1; }
|
||||
|
||||
/**
|
||||
* NOVA badge with 16-bit exit reason and 16-bit artificial vCPU I
|
||||
* NOVA badge with 15-bit exit reason, 1-bit fpu usage and
|
||||
* 16-bit artificial vCPU I
|
||||
*/
|
||||
struct Badge
|
||||
{
|
||||
@ -91,15 +91,19 @@ struct Nova_vcpu : Rpc_client<Vm_session::Native_vcpu>, Noncopyable
|
||||
Badge(unsigned long value)
|
||||
: _value((uint32_t)value) { }
|
||||
|
||||
Badge(uint16_t vcpu_id, uint16_t exit_reason)
|
||||
: _value((uint32_t)(vcpu_id << 16) | exit_reason) { }
|
||||
Badge(uint16_t vcpu_id, uint16_t exit_reason, bool fpu_usage)
|
||||
: _value((uint32_t)(vcpu_id << 16) |
|
||||
(exit_reason & 0x7fffu) |
|
||||
(fpu_usage ? 0x8000u : 0u)
|
||||
) { }
|
||||
|
||||
uint16_t exit_reason() const { return (uint16_t)( _value & 0xffff); }
|
||||
uint16_t exit_reason() const { return (uint16_t)( _value & 0x7fff); }
|
||||
uint16_t vcpu_id() const { return (uint16_t)((_value >> 16) & 0xffff); }
|
||||
bool fpu_usage() const { return _value & 0x8000; }
|
||||
uint32_t value() const { return _value; }
|
||||
};
|
||||
|
||||
bool _handle_exit(Nova::Utcb &utcb, uint16_t exit_reason);
|
||||
bool _handle_exit(Nova::Utcb &, uint16_t exit_reason, uint8_t const &);
|
||||
|
||||
__attribute__((regparm(1))) static void _exit_entry(addr_t badge);
|
||||
|
||||
@ -109,8 +113,6 @@ struct Nova_vcpu : Rpc_client<Vm_session::Native_vcpu>, Noncopyable
|
||||
(void)exit;
|
||||
(void)config;
|
||||
|
||||
return Nova::Mtd(Nova::Mtd::ALL);
|
||||
|
||||
Genode::addr_t mtd = 0;
|
||||
|
||||
mtd |= Nova::Mtd::ACDB;
|
||||
@ -172,7 +174,8 @@ struct Nova_vcpu : Rpc_client<Vm_session::Native_vcpu>, Noncopyable
|
||||
};
|
||||
|
||||
|
||||
void Nova_vcpu::_read_nova_state(Nova::Utcb &utcb, unsigned exit_reason)
|
||||
void Nova_vcpu::_read_nova_state(Nova::Utcb &utcb, unsigned exit_reason,
|
||||
uint8_t const &fpu_at_exit)
|
||||
{
|
||||
typedef Genode::Vcpu_state::Segment Segment;
|
||||
typedef Genode::Vcpu_state::Range Range;
|
||||
@ -181,10 +184,9 @@ void Nova_vcpu::_read_nova_state(Nova::Utcb &utcb, unsigned exit_reason)
|
||||
state().exit_reason = exit_reason;
|
||||
|
||||
if (utcb.mtd & Nova::Mtd::FPU) {
|
||||
state().fpu.charge([] (Vcpu_state::Fpu::State &fpu) {
|
||||
asm volatile ("fxsave %0" : "=m" (fpu) :: "memory");
|
||||
state().fpu.charge([&] (Vcpu_state::Fpu::State &fpu) {
|
||||
memcpy(&fpu, &fpu_at_exit, sizeof(fpu));
|
||||
});
|
||||
asm volatile ("fxrstor %0" : : "m" (*_fpu_ep) : "memory");
|
||||
}
|
||||
|
||||
if (utcb.mtd & Nova::Mtd::ACDB) {
|
||||
@ -539,11 +541,8 @@ void Nova_vcpu::_write_nova_state(Nova::Utcb &utcb)
|
||||
utcb.write_tpr_threshold(state().tpr_threshold.value());
|
||||
}
|
||||
|
||||
if (_use_guest_fpu || state().fpu.charged()) {
|
||||
asm volatile ("fxsave %0" : "=m" (*_fpu_ep) :: "memory");
|
||||
}
|
||||
|
||||
if (state().fpu.charged()) {
|
||||
utcb.mtd |= Nova::Mtd::FPU;
|
||||
state().fpu.with_state([] (Vcpu_state::Fpu::State const &fpu) {
|
||||
asm volatile ("fxrstor %0" : : "m" (fpu) : "memory");
|
||||
});
|
||||
@ -581,7 +580,7 @@ void Nova_vcpu::run()
|
||||
* Do not touch the UTCB before _read_nova_state() and after
|
||||
* _write_nova_state(), particularly not by logging diagnostics.
|
||||
*/
|
||||
bool Nova_vcpu::_handle_exit(Nova::Utcb &utcb, uint16_t exit_reason)
|
||||
bool Nova_vcpu::_handle_exit(Nova::Utcb &utcb, uint16_t exit_reason, uint8_t const &fpu)
|
||||
{
|
||||
/* reset blocking state */
|
||||
bool const previous_blocked = _block;
|
||||
@ -595,7 +594,7 @@ bool Nova_vcpu::_handle_exit(Nova::Utcb &utcb, uint16_t exit_reason)
|
||||
|
||||
/* transform state from NOVA to Genode */
|
||||
if (exit_reason != VM_EXIT_RECALL || !previous_blocked)
|
||||
_read_nova_state(utcb, exit_reason);
|
||||
_read_nova_state(utcb, exit_reason, fpu);
|
||||
|
||||
if (exit_reason == VM_EXIT_RECALL) {
|
||||
if (previous_blocked)
|
||||
@ -653,6 +652,10 @@ bool Nova_vcpu::_handle_exit(Nova::Utcb &utcb, uint16_t exit_reason)
|
||||
|
||||
void Nova_vcpu::_exit_entry(addr_t badge)
|
||||
{
|
||||
uint8_t _fpu_at_exit[512] __attribute__((aligned(0x10)));
|
||||
if (Badge(badge).fpu_usage())
|
||||
asm volatile ("fxsave %0" : "=m" (*_fpu_at_exit) :: "memory");
|
||||
|
||||
Thread &myself = *Thread::myself();
|
||||
Nova::Utcb &utcb = *reinterpret_cast<Nova::Utcb *>(myself.utcb());
|
||||
|
||||
@ -662,7 +665,8 @@ void Nova_vcpu::_exit_entry(addr_t badge)
|
||||
try {
|
||||
_vcpu_space().apply<Nova_vcpu>(vcpu_id, [&] (Nova_vcpu &vcpu)
|
||||
{
|
||||
bool const block = vcpu._handle_exit(utcb, exit_reason);
|
||||
bool const block = vcpu._handle_exit(utcb, exit_reason,
|
||||
*_fpu_at_exit);
|
||||
|
||||
if (block) {
|
||||
Nova::reply(myself.stack_top(), vcpu._sm_sel());
|
||||
@ -729,7 +733,7 @@ Signal_context_capability Nova_vcpu::_create_exit_handler(Pd_session &pd,
|
||||
Native_capability vm_exit_cap =
|
||||
native_pd.alloc_rpc_cap(thread_cap, (addr_t)Nova_vcpu::_exit_entry, mtd.value());
|
||||
|
||||
Badge const badge { vcpu_id, exit_reason };
|
||||
Badge const badge { vcpu_id, exit_reason, !!(mtd.value() & Nova::Mtd::FPU) };
|
||||
native_pd.imprint_rpc_cap(vm_exit_cap, badge.value());
|
||||
|
||||
return reinterpret_cap_cast<Signal_context>(vm_exit_cap);
|
||||
|
Loading…
Reference in New Issue
Block a user