/* * \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_ */