/* * \brief Virtual CPU context for x86 * \author Alexander Boettcher * \author Christian Helmuth * \date 2018-10-09 */ /* * Copyright (C) 2018-2021 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: 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; } }; 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_ */