/*
* \brief Virtual CPU context for x86
* \author Alexander Boettcher
* \author Christian Helmuth
* \author Benjamin Lamowski
* \date 2018-10-09
*/
/*
* Copyright (C) 2018-2023 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _INCLUDE__SPEC__X86__CPU__VM_STATE_H_
#define _INCLUDE__SPEC__X86__CPU__VM_STATE_H_
#include
#include
namespace Genode { struct Vcpu_state; }
/*
* The state of one virtual CPU (vCPU) as available via the VM session for x86
*
* The state object is designed for bidirectional transfer of register state,
* which means it reflects vCPU state on VM exits but also supports loading
* updating register state on VM entry. Therefore, each register contains not
* only the actual register value but also a 'charged' state.
*
* The hypervisor charges registers as requested by the VMM on VM exit with the
* current virtual CPU state. The VMM for its part charges registers it intends
* to update with new values before VM entry (e.g., after I/O emulation). Both
* parties are required to 'discharge()' the vCPU state explicitly if registers
* charged by the other party should not be considered on return. The common
* case is to discharge all registers, charge some updates and transfer
* execution to the other party.
*/
class Genode::Vcpu_state
{
private:
Vcpu_state & operator = (Vcpu_state const &) = default;
Vcpu_state(Vcpu_state const&) = delete;
public:
Vcpu_state() = default;
template
class Register : Noncopyable
{
private:
friend class Vcpu_state;
T _value { };
bool _charged { false };
/*
* Trick used by Vcpu_state::discharge() to discharge all
* registers at once. Note, the register value is kept intact.
*/
Register & operator = (Register const &)
{
_charged = false;
return *this;
}
public:
bool charged() const { return _charged; }
T value() const { return _value; }
void charge(T const &value)
{
_charged = true;
_value = value;
}
/*
* Charge without changing value
*
* \noapi
*/
void set_charged() { _charged = true; }
/*
* Update value if not yet charged
*
* \noapi
*/
void update(T const &value)
{
if (!_charged) {
_value = value;
_charged = true;
}
}
};
struct Range
{
uint32_t limit;
addr_t base;
};
struct Segment
{
uint16_t sel, ar;
uint32_t limit;
addr_t base;
};
Register ax { };
Register cx { };
Register dx { };
Register bx { };
Register bp { };
Register si { };
Register di { };
Register sp { };
Register ip { };
Register ip_len { };
Register flags { };
Register es { };
Register ds { };
Register fs { };
Register gs { };
Register cs { };
Register ss { };
Register tr { };
Register ldtr { };
Register gdtr { };
Register idtr { };
Register cr0 { };
Register cr2 { };
Register cr3 { };
Register cr4 { };
Register dr7 { };
Register sysenter_ip { };
Register sysenter_sp { };
Register sysenter_cs { };
Register qual_primary { };
Register qual_secondary { };
Register ctrl_primary { };
Register ctrl_secondary { };
Register inj_info { };
Register inj_error { };
Register intr_state { };
Register actv_state { };
Register tsc { };
Register tsc_offset { };
Register tsc_aux { };
Register efer { };
Register pdpte_0 { };
Register pdpte_1 { };
Register pdpte_2 { };
Register pdpte_3 { };
Register r8 { };
Register r9 { };
Register r10 { };
Register r11 { };
Register r12 { };
Register r13 { };
Register r14 { };
Register r15 { };
Register star { };
Register lstar { };
Register cstar { };
Register fmask { };
Register kernel_gs_base { };
Register tpr { };
Register tpr_threshold { };
unsigned exit_reason { };
class Fpu : Noncopyable
{
public:
struct State
{
uint8_t _buffer[512] { };
} __attribute__((aligned(16)));
private:
friend class Vcpu_state;
State _state { };
bool _charged { false };
/* see comment for Register::operator=() */
Fpu & operator = (Fpu const &)
{
_charged = false;
return *this;
}
public:
bool charged() const { return _charged; }
template
void with_state(FN const &fn) const
{
fn(_state);
}
template
void charge(FN const &fn)
{
_charged = true;
fn(_state);
}
};
Fpu fpu __attribute__((aligned(16))) { };
/*
* Registers transfered by hypervisor from guest on VM exit are charged.
* Discharged registers are not loaded into guest on VM entry.
*/
void discharge()
{
/* invoke operator= for all registers with all charged flags reset */
*this = Vcpu_state { };
}
};
#endif /* _INCLUDE__SPEC__X86__CPU__VCPU_STATE_H_ */