Commit c97d6d2c authored by Sergio Andres Gomez Del Real's avatar Sergio Andres Gomez Del Real Committed by Paolo Bonzini

i386: hvf: add code base from Google's QEMU repository

This file begins tracking the files that will be the code base for HVF
support in QEMU. This code base is part of Google's QEMU version of
their Android emulator, and can be found at
https://android.googlesource.com/platform/external/qemu/+/emu-master-dev

This code is based on Veertu Inc's vdhh (Veertu Desktop Hosted
Hypervisor), found at https://github.com/veertuinc/vdhh. Everything is
appropriately licensed under GPL v2-or-later, except for the code inside
x86_task.c and x86_task.h, which, deriving from KVM (the Linux kernel),
is licensed GPL v2-only.

This code base already implements a very great deal of functionality,
although Google's version removed from Vertuu's the support for APIC
page and hyperv-related stuff. According to the Android Emulator Release
Notes, Revision 26.1.3 (August 2017), "Hypervisor.framework is now
enabled by default on macOS for 32-bit x86 images to improve performance
and macOS compatibility", although we better use with caution for, as the
same Revision warns us, "If you experience issues with it specifically,
please file a bug report...". The code hasn't seen much update in the
last 5 months, so I think that we can further develop the code with
occasional visiting Google's repository to see if there has been any
update.

On top of Google's code, the following changes were made:

- add code to the configure script to support the --enable-hvf argument.
If the OS is Darwin, it checks for presence of HVF in the system. The
patch also adds strings related to HVF in the file qemu-options.hx.
QEMU will only support the modern syntax style '-M accel=hvf' no enable
hvf; the legacy '-enable-hvf' will not be supported.

- fix styling issues

- add glue code to cpus.c

- move HVFX86EmulatorState field to CPUX86State, changing the
the emulation functions to have a parameter with signature 'CPUX86State *'
instead of 'CPUState *' so we don't have to get the 'env'.
Signed-off-by: 's avatarSergio Andres Gomez Del Real <Sergio.G.DelReal@gmail.com>
Message-Id: <20170913090522.4022-2-Sergio.G.DelReal@gmail.com>
Message-Id: <20170913090522.4022-3-Sergio.G.DelReal@gmail.com>
Message-Id: <20170913090522.4022-5-Sergio.G.DelReal@gmail.com>
Message-Id: <20170913090522.4022-6-Sergio.G.DelReal@gmail.com>
Message-Id: <20170905035457.3753-7-Sergio.G.DelReal@gmail.com>
Signed-off-by: 's avatarPaolo Bonzini <pbonzini@redhat.com>
parent 2cb9f06e
obj-$(call lnot,$(CONFIG_HAX)) += hax-stub.o
obj-$(call lnot,$(CONFIG_HVF)) += hvf-stub.o
obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o
obj-$(call lnot,$(CONFIG_TCG)) += tcg-stub.o
/*
* QEMU HVF support
*
* Copyright 2017 Red Hat, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2 or later, as published by the Free Software Foundation,
* and may be copied, distributed, and modified under those terms.
*
* See the COPYING file in the top-level directory.
*
*/
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "cpu.h"
#include "sysemu/hvf.h"
int hvf_init_vcpu(CPUState *cpu)
{
return -ENOSYS;
}
int hvf_vcpu_exec(CPUState *cpu)
{
return -ENOSYS;
}
void hvf_vcpu_destroy(CPUState *cpu)
{
}
......@@ -211,6 +211,17 @@ supported_xen_target() {
return 1
}
supported_hvf_target() {
test "$hvf" = "yes" || return 1
glob "$1" "*-softmmu" || return 1
case "${1%-softmmu}" in
x86_64)
return 0
;;
esac
return 1
}
supported_target() {
case "$1" in
*-softmmu)
......@@ -236,6 +247,7 @@ supported_target() {
supported_kvm_target "$1" && return 0
supported_xen_target "$1" && return 0
supported_hax_target "$1" && return 0
supported_hvf_target "$1" && return 0
print_error "TCG disabled, but hardware accelerator not available for '$target'"
return 1
}
......@@ -325,6 +337,7 @@ vhost_vsock="no"
vhost_user=""
kvm="no"
hax="no"
hvf="no"
rdma=""
gprof="no"
debug_tcg="no"
......@@ -741,6 +754,7 @@ Darwin)
bsd="yes"
darwin="yes"
hax="yes"
hvf="yes"
LDFLAGS_SHARED="-bundle -undefined dynamic_lookup"
if [ "$cpu" = "x86_64" ] ; then
QEMU_CFLAGS="-arch x86_64 $QEMU_CFLAGS"
......@@ -1036,6 +1050,10 @@ for opt do
;;
--enable-hax) hax="yes"
;;
--disable-hvf) hvf="no"
;;
--enable-hvf) hvf="yes"
;;
--disable-tcg-interpreter) tcg_interpreter="no"
;;
--enable-tcg-interpreter) tcg_interpreter="yes"
......@@ -1529,6 +1547,7 @@ disabled with --disable-FEATURE, default is enabled if available:
bluez bluez stack connectivity
kvm KVM acceleration support
hax HAX acceleration support
hvf Hypervisor.framework acceleration support
rdma RDMA-based migration support
vde support for vde network
netmap support for netmap network
......@@ -5055,6 +5074,21 @@ then
fi
#################################################
# Check to see if we have the Hypervisor framework
if [ "$darwin" == "yes" ] ; then
cat > $TMPC << EOF
#include <Hypervisor/hv.h>
int main() { return 0;}
EOF
if ! compile_object ""; then
hvf='no'
else
hvf='yes'
LDFLAGS="-framework Hypervisor $LDFLAGS"
fi
fi
#################################################
# Sparc implicitly links with --relax, which is
# incompatible with -r, so --no-relax should be
......@@ -5530,6 +5564,7 @@ echo "ATTR/XATTR support $attr"
echo "Install blobs $blobs"
echo "KVM support $kvm"
echo "HAX support $hax"
echo "HVF support $hvf"
echo "TCG support $tcg"
if test "$tcg" = "yes" ; then
echo "TCG debug enabled $debug_tcg"
......@@ -6602,6 +6637,9 @@ fi
if supported_hax_target $target; then
echo "CONFIG_HAX=y" >> $config_target_mak
fi
if supported_hvf_target $target; then
echo "CONFIG_HVF=y" >> $config_target_mak
fi
if test "$target_bigendian" = "yes" ; then
echo "TARGET_WORDS_BIGENDIAN=y" >> $config_target_mak
fi
......
......@@ -37,6 +37,7 @@
#include "sysemu/hw_accel.h"
#include "sysemu/kvm.h"
#include "sysemu/hax.h"
#include "sysemu/hvf.h"
#include "qmp-commands.h"
#include "exec/exec-all.h"
......@@ -900,6 +901,10 @@ void cpu_synchronize_all_states(void)
CPU_FOREACH(cpu) {
cpu_synchronize_state(cpu);
/* TODO: move to cpu_synchronize_state() */
if (hvf_enabled()) {
hvf_cpu_synchronize_state(cpu);
}
}
}
......@@ -909,6 +914,10 @@ void cpu_synchronize_all_post_reset(void)
CPU_FOREACH(cpu) {
cpu_synchronize_post_reset(cpu);
/* TODO: move to cpu_synchronize_post_reset() */
if (hvf_enabled()) {
hvf_cpu_synchronize_post_reset(cpu);
}
}
}
......@@ -918,6 +927,10 @@ void cpu_synchronize_all_post_init(void)
CPU_FOREACH(cpu) {
cpu_synchronize_post_init(cpu);
/* TODO: move to cpu_synchronize_post_init() */
if (hvf_enabled()) {
hvf_cpu_synchronize_post_init(cpu);
}
}
}
......@@ -1107,6 +1120,14 @@ static void qemu_kvm_wait_io_event(CPUState *cpu)
qemu_wait_io_event_common(cpu);
}
static void qemu_hvf_wait_io_event(CPUState *cpu)
{
while (cpu_thread_is_idle(cpu)) {
qemu_cond_wait(cpu->halt_cond, &qemu_global_mutex);
}
qemu_wait_io_event_common(cpu);
}
static void *qemu_kvm_cpu_thread_fn(void *arg)
{
CPUState *cpu = arg;
......@@ -1444,6 +1465,48 @@ static void *qemu_hax_cpu_thread_fn(void *arg)
return NULL;
}
/* The HVF-specific vCPU thread function. This one should only run when the host
* CPU supports the VMX "unrestricted guest" feature. */
static void *qemu_hvf_cpu_thread_fn(void *arg)
{
CPUState *cpu = arg;
int r;
assert(hvf_enabled());
rcu_register_thread();
qemu_mutex_lock_iothread();
qemu_thread_get_self(cpu->thread);
cpu->thread_id = qemu_get_thread_id();
cpu->can_do_io = 1;
current_cpu = cpu;
hvf_init_vcpu(cpu);
/* signal CPU creation */
cpu->created = true;
qemu_cond_signal(&qemu_cpu_cond);
do {
if (cpu_can_run(cpu)) {
r = hvf_vcpu_exec(cpu);
if (r == EXCP_DEBUG) {
cpu_handle_guest_debug(cpu);
}
}
qemu_hvf_wait_io_event(cpu);
} while (!cpu->unplug || cpu_can_run(cpu));
hvf_vcpu_destroy(cpu);
cpu->created = false;
qemu_cond_signal(&qemu_cpu_cond);
qemu_mutex_unlock_iothread();
return NULL;
}
#ifdef _WIN32
static void CALLBACK dummy_apc_func(ULONG_PTR unused)
{
......@@ -1761,6 +1824,27 @@ static void qemu_kvm_start_vcpu(CPUState *cpu)
}
}
static void qemu_hvf_start_vcpu(CPUState *cpu)
{
char thread_name[VCPU_THREAD_NAME_SIZE];
/* HVF currently does not support TCG, and only runs in
* unrestricted-guest mode. */
assert(hvf_enabled());
cpu->thread = g_malloc0(sizeof(QemuThread));
cpu->halt_cond = g_malloc0(sizeof(QemuCond));
qemu_cond_init(cpu->halt_cond);
snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/HVF",
cpu->cpu_index);
qemu_thread_create(cpu->thread, thread_name, qemu_hvf_cpu_thread_fn,
cpu, QEMU_THREAD_JOINABLE);
while (!cpu->created) {
qemu_cond_wait(&qemu_cpu_cond, &qemu_global_mutex);
}
}
static void qemu_dummy_start_vcpu(CPUState *cpu)
{
char thread_name[VCPU_THREAD_NAME_SIZE];
......@@ -1795,6 +1879,8 @@ void qemu_init_vcpu(CPUState *cpu)
qemu_kvm_start_vcpu(cpu);
} else if (hax_enabled()) {
qemu_hax_start_vcpu(cpu);
} else if (hvf_enabled()) {
qemu_hvf_start_vcpu(cpu);
} else if (tcg_enabled()) {
qemu_tcg_init_vcpu(cpu);
} else {
......
......@@ -36,6 +36,7 @@ typedef struct FWCfgIoState FWCfgIoState;
typedef struct FWCfgMemState FWCfgMemState;
typedef struct FWCfgState FWCfgState;
typedef struct HCIInfo HCIInfo;
typedef struct HVFX86EmulatorState HVFX86EmulatorState;
typedef struct I2CBus I2CBus;
typedef struct I2SCodec I2SCodec;
typedef struct ISABus ISABus;
......
......@@ -423,6 +423,8 @@ struct CPUState {
* unnecessary flushes.
*/
uint16_t pending_tlb_flush;
int hvf_fd;
};
QTAILQ_HEAD(CPUTailQ, CPUState);
......
/*
* QEMU Hypervisor.framework (HVF) support
*
* Copyright Google Inc., 2017
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/
/* header to be included in non-HVF-specific code */
#ifndef _HVF_H
#define _HVF_H
#include "config-host.h"
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "qemu/bitops.h"
#include "exec/memory.h"
#include "sysemu/accel.h"
extern int hvf_disabled;
#ifdef CONFIG_HVF
#include <Hypervisor/hv.h>
#include <Hypervisor/hv_vmx.h>
#include <Hypervisor/hv_error.h>
#include "target/i386/cpu.h"
#include "hw/hw.h"
uint32_t hvf_get_supported_cpuid(uint32_t func, uint32_t idx,
int reg);
#define hvf_enabled() !hvf_disabled
#else
#define hvf_enabled() 0
#define hvf_get_supported_cpuid(func, idx, reg) 0
#endif
typedef struct hvf_slot {
uint64_t start;
uint64_t size;
uint8_t *mem;
int slot_id;
} hvf_slot;
typedef struct hvf_vcpu_caps {
uint64_t vmx_cap_pinbased;
uint64_t vmx_cap_procbased;
uint64_t vmx_cap_procbased2;
uint64_t vmx_cap_entry;
uint64_t vmx_cap_exit;
uint64_t vmx_cap_preemption_timer;
} hvf_vcpu_caps;
typedef struct HVFState {
AccelState parent;
hvf_slot slots[32];
int num_slots;
hvf_vcpu_caps *hvf_caps;
} HVFState;
extern HVFState *hvf_state;
void hvf_set_phys_mem(MemoryRegionSection *, bool);
void hvf_handle_io(CPUArchState *, uint16_t, void *,
int, int, int);
hvf_slot *hvf_find_overlap_slot(uint64_t, uint64_t);
/* Disable HVF if |disable| is 1, otherwise, enable it iff it is supported by
* the host CPU. Use hvf_enabled() after this to get the result. */
void hvf_disable(int disable);
/* Returns non-0 if the host CPU supports the VMX "unrestricted guest" feature
* which allows the virtual CPU to directly run in "real mode". If true, this
* allows QEMU to run several vCPU threads in parallel (see cpus.c). Otherwise,
* only a a single TCG thread can run, and it will call HVF to run the current
* instructions, except in case of "real mode" (paging disabled, typically at
* boot time), or MMIO operations. */
int hvf_sync_vcpus(void);
int hvf_init_vcpu(CPUState *);
int hvf_vcpu_exec(CPUState *);
int hvf_smp_cpu_exec(CPUState *);
void hvf_cpu_synchronize_state(CPUState *);
void hvf_cpu_synchronize_post_reset(CPUState *);
void hvf_cpu_synchronize_post_init(CPUState *);
void _hvf_cpu_synchronize_post_init(CPUState *, run_on_cpu_data);
void hvf_vcpu_destroy(CPUState *);
void hvf_raise_event(CPUState *);
/* void hvf_reset_vcpu_state(void *opaque); */
void hvf_reset_vcpu(CPUState *);
void vmx_update_tpr(CPUState *);
void update_apic_tpr(CPUState *);
int hvf_put_registers(CPUState *);
void vmx_clear_int_window_exiting(CPUState *cpu);
#define TYPE_HVF_ACCEL ACCEL_CLASS_NAME("hvf")
#define HVF_STATE(obj) \
OBJECT_CHECK(HVFState, (obj), TYPE_HVF_ACCEL)
#endif
......@@ -31,7 +31,7 @@ DEF("machine", HAS_ARG, QEMU_OPTION_machine, \
"-machine [type=]name[,prop[=value][,...]]\n"
" selects emulated machine ('-machine help' for list)\n"
" property accel=accel1[:accel2[:...]] selects accelerator\n"
" supported accelerators are kvm, xen, hax or tcg (default: tcg)\n"
" supported accelerators are kvm, xen, hax, hvf or tcg (default: tcg)\n"
" kernel_irqchip=on|off|split controls accelerated irqchip support (default=off)\n"
" vmport=on|off|auto controls emulation of vmport (default: auto)\n"
" kvm_shadow_mem=size of KVM shadow MMU in bytes\n"
......@@ -66,7 +66,7 @@ Supported machine properties are:
@table @option
@item accel=@var{accels1}[:@var{accels2}[:...]]
This is used to enable an accelerator. Depending on the target architecture,
kvm, xen, hax or tcg can be available. By default, tcg is used. If there is
kvm, xen, hax, hvf or tcg can be available. By default, tcg is used. If there is
more than one accelerator specified, the next one is used if the previous one
fails to initialize.
@item kernel_irqchip=on|off
......@@ -126,13 +126,13 @@ ETEXI
DEF("accel", HAS_ARG, QEMU_OPTION_accel,
"-accel [accel=]accelerator[,thread=single|multi]\n"
" select accelerator (kvm, xen, hax or tcg; use 'help' for a list)\n"
" thread=single|multi (enable multi-threaded TCG)\n", QEMU_ARCH_ALL)
" select accelerator (kvm, xen, hax, hvf or tcg; use 'help' for a list)\n"
" thread=single|multi (enable multi-threaded TCG)", QEMU_ARCH_ALL)
STEXI
@item -accel @var{name}[,prop=@var{value}[,...]]
@findex -accel
This is used to enable an accelerator. Depending on the target architecture,
kvm, xen, hax or tcg can be available. By default, tcg is used. If there is
kvm, xen, hax, hvf or tcg can be available. By default, tcg is used. If there is
more than one accelerator specified, the next one is used if the previous one
fails to initialize.
@table @option
......
......@@ -12,4 +12,5 @@ obj-$(CONFIG_HAX) += hax-all.o hax-mem.o hax-windows.o
endif
ifdef CONFIG_DARWIN
obj-$(CONFIG_HAX) += hax-all.o hax-mem.o hax-darwin.o
obj-$(CONFIG_HVF) += hvf-utils/ hvf-all.o
endif
......@@ -30,6 +30,8 @@
#define TARGET_LONG_BITS 32
#endif
#include "exec/cpu-defs.h"
/* The x86 has a strong memory model with some store-after-load re-ordering */
#define TCG_GUEST_DEFAULT_MO (TCG_MO_ALL & ~TCG_MO_ST_LD)
......@@ -50,8 +52,6 @@
#define CPUArchState struct CPUX86State
#include "exec/cpu-defs.h"
#ifdef CONFIG_TCG
#include "fpu/softfloat.h"
#endif
......@@ -82,16 +82,20 @@
#define R_GS 5
/* segment descriptor fields */
#define DESC_G_MASK (1 << 23)
#define DESC_G_SHIFT 23
#define DESC_G_MASK (1 << DESC_G_SHIFT)
#define DESC_B_SHIFT 22
#define DESC_B_MASK (1 << DESC_B_SHIFT)
#define DESC_L_SHIFT 21 /* x86_64 only : 64 bit code segment */
#define DESC_L_MASK (1 << DESC_L_SHIFT)
#define DESC_AVL_MASK (1 << 20)
#define DESC_P_MASK (1 << 15)
#define DESC_AVL_SHIFT 20
#define DESC_AVL_MASK (1 << DESC_AVL_SHIFT)
#define DESC_P_SHIFT 15
#define DESC_P_MASK (1 << DESC_P_SHIFT)
#define DESC_DPL_SHIFT 13
#define DESC_DPL_MASK (3 << DESC_DPL_SHIFT)
#define DESC_S_MASK (1 << 12)
#define DESC_S_SHIFT 12
#define DESC_S_MASK (1 << DESC_S_SHIFT)
#define DESC_TYPE_SHIFT 8
#define DESC_TYPE_MASK (15 << DESC_TYPE_SHIFT)
#define DESC_A_MASK (1 << 8)
......@@ -631,6 +635,7 @@ typedef uint32_t FeatureWordArray[FEATURE_WORDS];
#define CPUID_7_0_EBX_AVX512BW (1U << 30) /* AVX-512 Byte and Word Instructions */
#define CPUID_7_0_EBX_AVX512VL (1U << 31) /* AVX-512 Vector Length Extensions */
#define CPUID_7_0_ECX_AVX512BMI (1U << 1)
#define CPUID_7_0_ECX_VBMI (1U << 1) /* AVX-512 Vector Byte Manipulation Instrs */
#define CPUID_7_0_ECX_UMIP (1U << 2)
#define CPUID_7_0_ECX_PKU (1U << 3)
......@@ -812,6 +817,20 @@ typedef struct SegmentCache {
float64 _d_##n[(bits)/64]; \
}
typedef union {
uint8_t _b[16];
uint16_t _w[8];
uint32_t _l[4];
uint64_t _q[2];
} XMMReg;
typedef union {
uint8_t _b[32];
uint16_t _w[16];
uint32_t _l[8];
uint64_t _q[4];
} YMMReg;
typedef MMREG_UNION(ZMMReg, 512) ZMMReg;
typedef MMREG_UNION(MMXReg, 64) MMXReg;
......@@ -1047,7 +1066,11 @@ typedef struct CPUX86State {
ZMMReg xmm_t0;
MMXReg mmx_t0;
XMMReg ymmh_regs[CPU_NB_REGS];
uint64_t opmask_regs[NB_OPMASK_REGS];
YMMReg zmmh_regs[CPU_NB_REGS];
ZMMReg hi16_zmm_regs[CPU_NB_REGS];
/* sysenter registers */
uint32_t sysenter_cs;
......@@ -1172,11 +1195,15 @@ typedef struct CPUX86State {
int32_t interrupt_injected;
uint8_t soft_interrupt;
uint8_t has_error_code;
uint32_t ins_len;
uint32_t sipi_vector;
bool tsc_valid;
int64_t tsc_khz;
int64_t user_tsc_khz; /* for sanity check only */
void *kvm_xsave_buf;
#if defined(CONFIG_HVF)
HVFX86EmulatorState *hvf_emul;
#endif
uint64_t mcg_cap;
uint64_t mcg_ctl;
......
This diff is collapsed.
/*
* QEMU Hypervisor.framework (HVF) support
*
* Copyright 2017 Google Inc
*
* Adapted from target-i386/hax-i386.h:
* Copyright (c) 2011 Intel Corporation
* Written by:
* Jiang Yunhong<yunhong.jiang@intel.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/
#ifndef _HVF_I386_H
#define _HVF_I386_H
#include "sysemu/hvf.h"
#include "cpu.h"
#include "hvf-utils/x86.h"
#define HVF_MAX_VCPU 0x10
#define MAX_VM_ID 0x40
#define MAX_VCPU_ID 0x40
extern struct hvf_state hvf_global;
struct hvf_vm {
int id;
struct hvf_vcpu_state *vcpus[HVF_MAX_VCPU];
};
struct hvf_state {
uint32_t version;
struct hvf_vm *vm;
uint64_t mem_quota;
};
#ifdef NEED_CPU_H
/* Functions exported to host specific mode */
/* Host specific functions */
int hvf_inject_interrupt(CPUArchState *env, int vector);
int hvf_vcpu_run(struct hvf_vcpu_state *vcpu);
#endif
#endif
obj-y += x86.o x86_cpuid.o x86_decode.o x86_descr.o x86_emu.o x86_flags.o x86_mmu.o x86hvf.o
# OS X Hypervisor.framework support in QEMU
These sources (and ../hvf-all.c) are adapted from Veertu Inc's vdhh (Veertu Desktop Hosted Hypervisor) (last known location: https://github.com/veertuinc/vdhh) with some minor changes, the most significant of which were:
1. Adapt to our current QEMU's `CPUState` structure and `address_space_rw` API; many struct members have been moved around (emulated x86 state, kvm_xsave_buf) due to historical differences + QEMU needing to handle more emulation targets.
2. Removal of `apic_page` and hyperv-related functionality.
3. More relaxed use of `qemu_mutex_lock_iothread`.
This diff is collapsed.
/*
* Copyright (C) 2016 Veertu Inc,
* Copyright (C) 2017 Google Inc,
* Based on Veertu vddh/vmm/vmx.h
*
* Interfaces to Hypervisor.framework to read/write X86 registers and VMCS.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 or
* (at your option) version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef VMX_H
#define VMX_H
#include <stdint.h>
#include <Hypervisor/hv.h>
#include <Hypervisor/hv_vmx.h>
#include "vmcs.h"
#include "cpu.h"
#include "x86.h"
#include "exec/address-spaces.h"
static inline uint64_t rreg(hv_vcpuid_t vcpu, hv_x86_reg_t reg)
{
uint64_t v;
if (hv_vcpu_read_register(vcpu, reg, &v)) {
abort();
}
return v;
}
/* write GPR */
static inline void wreg(hv_vcpuid_t vcpu, hv_x86_reg_t reg, uint64_t v)
{
if (hv_vcpu_write_register(vcpu, reg, v)) {
abort();
}
}
/* read VMCS field */
static inline uint64_t rvmcs(hv_vcpuid_t vcpu, uint32_t field)
{
uint64_t v;
hv_vmx_vcpu_read_vmcs(vcpu, field, &v);
return v;
}
/* write VMCS field */
static inline void wvmcs(hv_vcpuid_t vcpu, uint32_t field, uint64_t v)
{
hv_vmx_vcpu_write_vmcs(vcpu, field, v);
}
/* desired control word constrained by hardware/hypervisor capabilities */
static inline uint64_t cap2ctrl(uint64_t cap, uint64_t ctrl)
{
return (ctrl | (cap & 0xffffffff)) & (cap >> 32);
}
#define VM_ENTRY_GUEST_LMA (1LL << 9)
#define AR_TYPE_ACCESSES_MASK 1
#define AR_TYPE_READABLE_MASK (1 << 1)
#define AR_TYPE_WRITEABLE_MASK (1 << 2)
#define AR_TYPE_CODE_MASK (1 << 3)
#define AR_TYPE_MASK 0x0f
#define AR_TYPE_BUSY_64_TSS 11
#define AR_TYPE_BUSY_32_TSS 11
#define AR_TYPE_BUSY_16_TSS 3
#define AR_TYPE_LDT 2
static void enter_long_mode(hv_vcpuid_t vcpu, uint64_t cr0, uint64_t efer)
{
uint64_t entry_ctls;
efer |= EFER_LMA;
wvmcs(vcpu, VMCS_GUEST_IA32_EFER, efer);
entry_ctls = rvmcs(vcpu, VMCS_ENTRY_CTLS);
wvmcs(vcpu, VMCS_ENTRY_CTLS, rvmcs(vcpu, VMCS_ENTRY_CTLS) |
VM_ENTRY_GUEST_LMA);
uint64_t guest_tr_ar = rvmcs(vcpu, VMCS_GUEST_TR_ACCESS_RIGHTS);
if ((efer & EFER_LME) &&
(guest_tr_ar & AR_TYPE_MASK) != AR_TYPE_BUSY_64_TSS) {
wvmcs(vcpu, VMCS_GUEST_TR_ACCESS_RIGHTS,
(guest_tr_ar & ~AR_TYPE_MASK) | AR_TYPE_BUSY_64_TSS);
}
}
static void exit_long_mode(hv_vcpuid_t vcpu, uint64_t cr0, uint64_t efer)
{
uint64_t entry_ctls;
entry_ctls = rvmcs(vcpu, VMCS_ENTRY_CTLS);
wvmcs(vcpu, VMCS_ENTRY_CTLS, entry_ctls & ~VM_ENTRY_GUEST_LMA);
efer &= ~EFER_LMA;
wvmcs(vcpu, VMCS_GUEST_IA32_EFER, efer);
}
static inline void macvm_set_cr0(hv_vcpuid_t vcpu, uint64_t cr0)
{
int i;
uint64_t pdpte[4] = {0, 0, 0, 0};
uint64_t efer = rvmcs(vcpu, VMCS_GUEST_IA32_EFER);
uint64_t old_cr0 = rvmcs(vcpu, VMCS_GUEST_CR0);
if ((cr0 & CR0_PG) && (rvmcs(vcpu, VMCS_GUEST_CR4) & CR4_PAE) &&
!(efer & EFER_LME)) {
address_space_rw(&address_space_memory,
rvmcs(vcpu, VMCS_GUEST_CR3) & ~0x1f,
MEMTXATTRS_UNSPECIFIED,
(uint8_t *)pdpte, 32, 0);
}
for (i = 0; i < 4; i++) {
wvmcs(vcpu, VMCS_GUEST_PDPTE0 + i * 2, pdpte[i]);
}
wvmcs(vcpu, VMCS_CR0_MASK, CR0_CD | CR0_NE | CR0_PG);
wvmcs(vcpu, VMCS_CR0_SHADOW, cr0);
cr0 &= ~CR0_CD;
wvmcs(vcpu, VMCS_GUEST_CR0, cr0 | CR0_NE | CR0_ET);
if (efer & EFER_LME) {
if (!(old_cr0 & CR0_PG) && (cr0 & CR0_PG)) {
enter_long_mode(vcpu, cr0, efer);
}
if (/*(old_cr0 & CR0_PG) &&*/ !(cr0 & CR0_PG)) {
exit_long_mode(vcpu, cr0, efer);
}
}
hv_vcpu_invalidate_tlb(vcpu);
hv_vcpu_flush(vcpu);
}