* [Qemu-devel] [PATCH v4 0/4] Add HAX support
@ 2016-12-19 10:29 Vincent Palatin
2016-12-19 10:29 ` [Qemu-devel] [PATCH v4 1/4] kvm: move cpu synchronization code Vincent Palatin
` (3 more replies)
0 siblings, 4 replies; 6+ messages in thread
From: Vincent Palatin @ 2016-12-19 10:29 UTC (permalink / raw)
To: qemu-devel
Cc: Yu Ning, Stefan Weil, Paolo Bonzini, Michael S . Tsirkin,
Eduardo Habkost, Marcelo Tosatti, Vincent Palatin
I took a stab at trying to rebase/upstream the support for Intel HAXM.
(Hardware Accelerated Execution Manager).
Intel HAX is kernel-based hardware acceleration module for Windows and MacOSX.
I have based my work on the last version of the source code I found:
the emu-2.2-release branch in the external/qemu-android repository as used by
the Android emulator.
In patch 2/4, I have forward-ported the core HAX code from there.
It has been modified to build and run along with the current code base.
It has been simplifying by removing non-UG hardware support / Darwin support /
Android-specific leftovers.
Intel nicely fixed the 2 remaining issues on the kernel side:
- the spurious request to emulate MMIO access in un-paged mode is no longer
happening (as seen in iPXE).
- the kernel API now provides a way to remove a memory mapping, so we can
do a proper MemoryListener implementation.
They will publish soon a new version 6.1.0 of the HAX kernel module including
the fixes (once their QA cycle is completed).
Thanks Yu Ning for making this happen.
In patch 3/4, I have put the plumbing into the QEMU code base, I did some clean
up there and it is reasonably intrusive: i.e.
Makefile.target | 1 +
configure | 18 ++++++++++
cpus.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++-
exec.c | 16 +++++++++
hw/intc/apic_common.c | 3 +-
include/qom/cpu.h | 5 +++
include/sysemu/hw_accel.h | 9 +++++
qemu-options.hx | 11 ++++++
target-i386/Makefile.objs | 4 +++
vl.c | 15 ++++++--
10 files changed, 164 insertions(+), 5 deletions(-)
The patch 1/4 just extracts from KVM specific header the cpu_synchronize_
functions that HAX is also using.
The patch 4/4 is the Darwin support. This part is only lightly tested for now,
so it can be considered as 'experimental'.
I have tested the end result on a Windows 10 Pro machine (with UG support)
with the Intel HAXM module dev version and a large ChromiumOS x86_64 image to
exercise various code paths. It looks stable.
I also did a quick regression testing of the integration by running a Linux
build with KVM enabled.
Changes from v3 to v4:
- add RAM unmapping in the MemoryListener thanks to new API in HAX module 6.1.0
and re-wrote the memory mappings management to deal with this.
- marked no longer used MMIO emulation as unsupported.
- clean-up a few left-overs from removed code.
- re-add an experimental version of the Darwin support.
Changes from v2 to v3:
- fix saving/restoring FPU registers as suggested by Paolo.
- fix Windows build on all targets as contributed by Stefan Weil.
- clean-up IO / MMIO emulation.
- more clean-up of emulation leftovers.
Changes from v1 to v2:
- fix all style issues in the original code to get it through checkpatch.pl.
- remove Darwin support, it was barely tested and not fully functional.
- remove the support for CPU without UG mode.
- fix most review comments
Vincent Palatin (4):
kvm: move cpu synchronization code
target-i386: Add Intel HAX files
Plumb the HAXM-based hardware acceleration support
hax: add Darwin support
Makefile.target | 1 +
configure | 18 +
cpus.c | 93 +++-
exec.c | 16 +
gdbstub.c | 1 +
hax-stub.c | 39 ++
hw/i386/kvm/apic.c | 1 +
hw/i386/kvmvapic.c | 1 +
hw/intc/apic_common.c | 3 +-
hw/misc/vmport.c | 2 +-
hw/ppc/pnv_xscom.c | 2 +-
hw/ppc/ppce500_spin.c | 4 +-
hw/ppc/spapr.c | 2 +-
hw/ppc/spapr_hcall.c | 2 +-
hw/s390x/s390-pci-inst.c | 1 +
include/qom/cpu.h | 5 +
include/sysemu/hax.h | 56 +++
include/sysemu/hw_accel.h | 48 ++
include/sysemu/kvm.h | 23 -
monitor.c | 2 +-
qemu-options.hx | 11 +
qom/cpu.c | 2 +-
target-arm/cpu.c | 2 +-
target-i386/Makefile.objs | 7 +
target-i386/hax-all.c | 1138 +++++++++++++++++++++++++++++++++++++++++++
target-i386/hax-darwin.c | 316 ++++++++++++
target-i386/hax-darwin.h | 63 +++
target-i386/hax-i386.h | 94 ++++
target-i386/hax-interface.h | 358 ++++++++++++++
target-i386/hax-mem.c | 271 +++++++++++
target-i386/hax-windows.c | 479 ++++++++++++++++++
target-i386/hax-windows.h | 89 ++++
target-i386/helper.c | 1 +
target-i386/kvm.c | 1 +
target-ppc/mmu-hash64.c | 2 +-
target-ppc/translate_init.c | 2 +-
target-s390x/gdbstub.c | 1 +
vl.c | 15 +-
38 files changed, 3133 insertions(+), 39 deletions(-)
create mode 100644 hax-stub.c
create mode 100644 include/sysemu/hax.h
create mode 100644 include/sysemu/hw_accel.h
create mode 100644 target-i386/hax-all.c
create mode 100644 target-i386/hax-darwin.c
create mode 100644 target-i386/hax-darwin.h
create mode 100644 target-i386/hax-i386.h
create mode 100644 target-i386/hax-interface.h
create mode 100644 target-i386/hax-mem.c
create mode 100644 target-i386/hax-windows.c
create mode 100644 target-i386/hax-windows.h
--
2.8.0.rc3.226.g39d4020
^ permalink raw reply [flat|nested] 6+ messages in thread
* [Qemu-devel] [PATCH v4 1/4] kvm: move cpu synchronization code
2016-12-19 10:29 [Qemu-devel] [PATCH v4 0/4] Add HAX support Vincent Palatin
@ 2016-12-19 10:29 ` Vincent Palatin
2016-12-19 10:29 ` [Qemu-devel] [PATCH v4 2/4] target-i386: Add Intel HAX files Vincent Palatin
` (2 subsequent siblings)
3 siblings, 0 replies; 6+ messages in thread
From: Vincent Palatin @ 2016-12-19 10:29 UTC (permalink / raw)
To: qemu-devel
Cc: Yu Ning, Stefan Weil, Paolo Bonzini, Michael S . Tsirkin,
Eduardo Habkost, Marcelo Tosatti, Vincent Palatin
Move the generic cpu_synchronize_ functions to the common hw_accel.h header,
in order to prepare for the addition of a second hardware accelerator.
Signed-off-by: Stefan Weil <sw@weilnetz.de>
Signed-off-by: Vincent Palatin <vpalatin@chromium.org>
---
cpus.c | 1 +
gdbstub.c | 1 +
hw/i386/kvm/apic.c | 1 +
hw/i386/kvmvapic.c | 1 +
hw/misc/vmport.c | 2 +-
hw/ppc/pnv_xscom.c | 2 +-
hw/ppc/ppce500_spin.c | 4 ++--
hw/ppc/spapr.c | 2 +-
hw/ppc/spapr_hcall.c | 2 +-
hw/s390x/s390-pci-inst.c | 1 +
include/sysemu/hw_accel.h | 39 +++++++++++++++++++++++++++++++++++++++
include/sysemu/kvm.h | 23 -----------------------
monitor.c | 2 +-
qom/cpu.c | 2 +-
target-arm/cpu.c | 2 +-
target-i386/helper.c | 1 +
target-i386/kvm.c | 1 +
target-ppc/mmu-hash64.c | 2 +-
target-ppc/translate_init.c | 2 +-
target-s390x/gdbstub.c | 1 +
20 files changed, 58 insertions(+), 34 deletions(-)
create mode 100644 include/sysemu/hw_accel.h
diff --git a/cpus.c b/cpus.c
index 5213351..fc78502 100644
--- a/cpus.c
+++ b/cpus.c
@@ -33,6 +33,7 @@
#include "sysemu/block-backend.h"
#include "exec/gdbstub.h"
#include "sysemu/dma.h"
+#include "sysemu/hw_accel.h"
#include "sysemu/kvm.h"
#include "qmp-commands.h"
#include "exec/exec-all.h"
diff --git a/gdbstub.c b/gdbstub.c
index de62d26..de9b62b 100644
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -32,6 +32,7 @@
#define MAX_PACKET_LENGTH 4096
#include "qemu/sockets.h"
+#include "sysemu/hw_accel.h"
#include "sysemu/kvm.h"
#include "exec/semihost.h"
#include "exec/exec-all.h"
diff --git a/hw/i386/kvm/apic.c b/hw/i386/kvm/apic.c
index 01cbaa8..328f80c 100644
--- a/hw/i386/kvm/apic.c
+++ b/hw/i386/kvm/apic.c
@@ -14,6 +14,7 @@
#include "cpu.h"
#include "hw/i386/apic_internal.h"
#include "hw/pci/msi.h"
+#include "sysemu/hw_accel.h"
#include "sysemu/kvm.h"
#include "target-i386/kvm_i386.h"
diff --git a/hw/i386/kvmvapic.c b/hw/i386/kvmvapic.c
index b30d1b9..2f767b6 100644
--- a/hw/i386/kvmvapic.c
+++ b/hw/i386/kvmvapic.c
@@ -14,6 +14,7 @@
#include "exec/exec-all.h"
#include "sysemu/sysemu.h"
#include "sysemu/cpus.h"
+#include "sysemu/hw_accel.h"
#include "sysemu/kvm.h"
#include "hw/i386/apic_internal.h"
#include "hw/sysbus.h"
diff --git a/hw/misc/vmport.c b/hw/misc/vmport.c
index c763811..be40930 100644
--- a/hw/misc/vmport.c
+++ b/hw/misc/vmport.c
@@ -25,7 +25,7 @@
#include "hw/hw.h"
#include "hw/isa/isa.h"
#include "hw/i386/pc.h"
-#include "sysemu/kvm.h"
+#include "sysemu/hw_accel.h"
#include "hw/qdev.h"
//#define VMPORT_DEBUG
diff --git a/hw/ppc/pnv_xscom.c b/hw/ppc/pnv_xscom.c
index 8da2718..cd5c2b8 100644
--- a/hw/ppc/pnv_xscom.c
+++ b/hw/ppc/pnv_xscom.c
@@ -20,7 +20,7 @@
#include "qapi/error.h"
#include "hw/hw.h"
#include "qemu/log.h"
-#include "sysemu/kvm.h"
+#include "sysemu/hw_accel.h"
#include "target-ppc/cpu.h"
#include "hw/sysbus.h"
diff --git a/hw/ppc/ppce500_spin.c b/hw/ppc/ppce500_spin.c
index cf958a9..eb219ab 100644
--- a/hw/ppc/ppce500_spin.c
+++ b/hw/ppc/ppce500_spin.c
@@ -29,9 +29,9 @@
#include "qemu/osdep.h"
#include "hw/hw.h"
-#include "sysemu/sysemu.h"
#include "hw/sysbus.h"
-#include "sysemu/kvm.h"
+#include "sysemu/hw_accel.h"
+#include "sysemu/sysemu.h"
#include "e500.h"
#define MAX_CPUS 32
diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index 208ef7b..a642e66 100644
--- a/hw/ppc/spapr.c
+++ b/hw/ppc/spapr.c
@@ -36,7 +36,7 @@
#include "sysemu/device_tree.h"
#include "sysemu/block-backend.h"
#include "sysemu/cpus.h"
-#include "sysemu/kvm.h"
+#include "sysemu/hw_accel.h"
#include "kvm_ppc.h"
#include "migration/migration.h"
#include "mmu-hash64.h"
diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c
index 9a9bedf..b2a8e48 100644
--- a/hw/ppc/spapr_hcall.c
+++ b/hw/ppc/spapr_hcall.c
@@ -1,5 +1,6 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
+#include "sysemu/hw_accel.h"
#include "sysemu/sysemu.h"
#include "qemu/log.h"
#include "cpu.h"
@@ -9,7 +10,6 @@
#include "mmu-hash64.h"
#include "cpu-models.h"
#include "trace.h"
-#include "sysemu/kvm.h"
#include "kvm_ppc.h"
#include "hw/ppc/spapr_ovec.h"
diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c
index 0864d9b..4d0775c 100644
--- a/hw/s390x/s390-pci-inst.c
+++ b/hw/s390x/s390-pci-inst.c
@@ -18,6 +18,7 @@
#include "s390-pci-bus.h"
#include "exec/memory-internal.h"
#include "qemu/error-report.h"
+#include "sysemu/hw_accel.h"
/* #define DEBUG_S390PCI_INST */
#ifdef DEBUG_S390PCI_INST
diff --git a/include/sysemu/hw_accel.h b/include/sysemu/hw_accel.h
new file mode 100644
index 0000000..03812cf
--- /dev/null
+++ b/include/sysemu/hw_accel.h
@@ -0,0 +1,39 @@
+/*
+ * QEMU Hardware accelertors support
+ *
+ * Copyright 2016 Google, Inc.
+ *
+ * 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 QEMU_HW_ACCEL_H
+#define QEMU_HW_ACCEL_H
+
+#include "qom/cpu.h"
+#include "sysemu/hax.h"
+#include "sysemu/kvm.h"
+
+static inline void cpu_synchronize_state(CPUState *cpu)
+{
+ if (kvm_enabled()) {
+ kvm_cpu_synchronize_state(cpu);
+ }
+}
+
+static inline void cpu_synchronize_post_reset(CPUState *cpu)
+{
+ if (kvm_enabled()) {
+ kvm_cpu_synchronize_post_reset(cpu);
+ }
+}
+
+static inline void cpu_synchronize_post_init(CPUState *cpu)
+{
+ if (kvm_enabled()) {
+ kvm_cpu_synchronize_post_init(cpu);
+ }
+}
+
+#endif /* QEMU_HW_ACCEL_H */
diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h
index df67cc0..3045ee7 100644
--- a/include/sysemu/kvm.h
+++ b/include/sysemu/kvm.h
@@ -461,29 +461,6 @@ void kvm_cpu_synchronize_state(CPUState *cpu);
void kvm_cpu_synchronize_post_reset(CPUState *cpu);
void kvm_cpu_synchronize_post_init(CPUState *cpu);
-/* generic hooks - to be moved/refactored once there are more users */
-
-static inline void cpu_synchronize_state(CPUState *cpu)
-{
- if (kvm_enabled()) {
- kvm_cpu_synchronize_state(cpu);
- }
-}
-
-static inline void cpu_synchronize_post_reset(CPUState *cpu)
-{
- if (kvm_enabled()) {
- kvm_cpu_synchronize_post_reset(cpu);
- }
-}
-
-static inline void cpu_synchronize_post_init(CPUState *cpu)
-{
- if (kvm_enabled()) {
- kvm_cpu_synchronize_post_init(cpu);
- }
-}
-
/**
* kvm_irqchip_add_msi_route - Add MSI route for specific vector
* @s: KVM state
diff --git a/monitor.c b/monitor.c
index 0841d43..d38956f 100644
--- a/monitor.c
+++ b/monitor.c
@@ -50,7 +50,7 @@
#include "sysemu/balloon.h"
#include "qemu/timer.h"
#include "migration/migration.h"
-#include "sysemu/kvm.h"
+#include "sysemu/hw_accel.h"
#include "qemu/acl.h"
#include "sysemu/tpm.h"
#include "qapi/qmp/qerror.h"
diff --git a/qom/cpu.c b/qom/cpu.c
index 03d9190..2c5274f 100644
--- a/qom/cpu.c
+++ b/qom/cpu.c
@@ -22,7 +22,7 @@
#include "qapi/error.h"
#include "qemu-common.h"
#include "qom/cpu.h"
-#include "sysemu/kvm.h"
+#include "sysemu/hw_accel.h"
#include "qemu/notify.h"
#include "qemu/log.h"
#include "exec/log.h"
diff --git a/target-arm/cpu.c b/target-arm/cpu.c
index 99f0dbe..744cc11 100644
--- a/target-arm/cpu.c
+++ b/target-arm/cpu.c
@@ -31,7 +31,7 @@
#endif
#include "hw/arm/arm.h"
#include "sysemu/sysemu.h"
-#include "sysemu/kvm.h"
+#include "sysemu/hw_accel.h"
#include "kvm_arm.h"
static void arm_cpu_set_pc(CPUState *cs, vaddr value)
diff --git a/target-i386/helper.c b/target-i386/helper.c
index 4ecc091..7530280 100644
--- a/target-i386/helper.c
+++ b/target-i386/helper.c
@@ -24,6 +24,7 @@
#include "kvm_i386.h"
#ifndef CONFIG_USER_ONLY
#include "sysemu/sysemu.h"
+#include "sysemu/hw_accel.h"
#include "monitor/monitor.h"
#include "hw/i386/apic_internal.h"
#endif
diff --git a/target-i386/kvm.c b/target-i386/kvm.c
index f62264a..4ed90f6 100644
--- a/target-i386/kvm.c
+++ b/target-i386/kvm.c
@@ -23,6 +23,7 @@
#include "qemu-common.h"
#include "cpu.h"
#include "sysemu/sysemu.h"
+#include "sysemu/hw_accel.h"
#include "sysemu/kvm_int.h"
#include "kvm_i386.h"
#include "hyperv.h"
diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c
index fdb7a78..0efc8c6 100644
--- a/target-ppc/mmu-hash64.c
+++ b/target-ppc/mmu-hash64.c
@@ -23,7 +23,7 @@
#include "exec/exec-all.h"
#include "exec/helper-proto.h"
#include "qemu/error-report.h"
-#include "sysemu/kvm.h"
+#include "sysemu/hw_accel.h"
#include "kvm_ppc.h"
#include "mmu-hash64.h"
#include "exec/log.h"
diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c
index 626e031..98b8760 100644
--- a/target-ppc/translate_init.c
+++ b/target-ppc/translate_init.c
@@ -21,10 +21,10 @@
#include "qemu/osdep.h"
#include "disas/bfd.h"
#include "exec/gdbstub.h"
-#include "sysemu/kvm.h"
#include "kvm_ppc.h"
#include "sysemu/arch_init.h"
#include "sysemu/cpus.h"
+#include "sysemu/hw_accel.h"
#include "cpu-models.h"
#include "mmu-hash32.h"
#include "mmu-hash64.h"
diff --git a/target-s390x/gdbstub.c b/target-s390x/gdbstub.c
index 3d223de..3c652fb 100644
--- a/target-s390x/gdbstub.c
+++ b/target-s390x/gdbstub.c
@@ -23,6 +23,7 @@
#include "exec/exec-all.h"
#include "exec/gdbstub.h"
#include "qemu/bitops.h"
+#include "sysemu/hw_accel.h"
int s390_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n)
{
--
2.8.0.rc3.226.g39d4020
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [Qemu-devel] [PATCH v4 2/4] target-i386: Add Intel HAX files
2016-12-19 10:29 [Qemu-devel] [PATCH v4 0/4] Add HAX support Vincent Palatin
2016-12-19 10:29 ` [Qemu-devel] [PATCH v4 1/4] kvm: move cpu synchronization code Vincent Palatin
@ 2016-12-19 10:29 ` Vincent Palatin
2016-12-19 14:09 ` Vincent Palatin
2016-12-19 10:29 ` [Qemu-devel] [PATCH v4 3/4] Plumb the HAXM-based hardware acceleration support Vincent Palatin
2016-12-19 10:29 ` [Qemu-devel] [PATCH v4 4/4] hax: add Darwin support Vincent Palatin
3 siblings, 1 reply; 6+ messages in thread
From: Vincent Palatin @ 2016-12-19 10:29 UTC (permalink / raw)
To: qemu-devel
Cc: Yu Ning, Stefan Weil, Paolo Bonzini, Michael S . Tsirkin,
Eduardo Habkost, Marcelo Tosatti, Vincent Palatin
That's a forward port of the core HAX interface code from the
emu-2.2-release branch in the external/qemu-android repository as used by
the Android emulator.
The original commit was "target-i386: Add Intel HAX to android emulator"
saying:
"""
Backport of 2b3098ff27bab079caab9b46b58546b5036f5c0c
from studio-1.4-dev into emu-master-dev
Intel HAX (harware acceleration) will enhance android emulator performance
in Windows and Mac OS X in the systems powered by Intel processors with
"Intel Hardware Accelerated Execution Manager" package installed when
user runs android emulator with Intel target.
Signed-off-by: David Chou <david.j.chou@intel.com>
"""
It has been modified to build and run along with the current code base.
The formatting has been fixed to go through scripts/checkpatch.pl,
and the DPRINTF macros have been updated to get the instanciations checked by
the compiler.
The FPU registers saving/restoring has been updated to match the current
QEMU registers layout.
The implementation has been simplified by doing the following modifications:
- removing the code for supporting the hardware without Unrestricted Guest (UG)
mode (including all the code to fallback on TCG emulation).
- not including the Darwin support (which is not yet debugged/tested).
- simplifying the initialization by removing the leftovers from the Android
specific code, then trimming down the remaining logic.
- removing the unused MemoryListener callbacks.
Signed-off-by: Vincent Palatin <vpalatin@chromium.org>
---
hax-stub.c | 39 ++
include/sysemu/hax.h | 56 +++
target-i386/hax-all.c | 1138 +++++++++++++++++++++++++++++++++++++++++++
target-i386/hax-i386.h | 86 ++++
target-i386/hax-interface.h | 358 ++++++++++++++
target-i386/hax-mem.c | 271 +++++++++++
target-i386/hax-windows.c | 479 ++++++++++++++++++
target-i386/hax-windows.h | 89 ++++
8 files changed, 2516 insertions(+)
create mode 100644 hax-stub.c
create mode 100644 include/sysemu/hax.h
create mode 100644 target-i386/hax-all.c
create mode 100644 target-i386/hax-i386.h
create mode 100644 target-i386/hax-interface.h
create mode 100644 target-i386/hax-mem.c
create mode 100644 target-i386/hax-windows.c
create mode 100644 target-i386/hax-windows.h
diff --git a/hax-stub.c b/hax-stub.c
new file mode 100644
index 0000000..a532dba
--- /dev/null
+++ b/hax-stub.c
@@ -0,0 +1,39 @@
+/*
+ * QEMU HAXM support
+ *
+ * Copyright (c) 2015, Intel Corporation
+ *
+ * Copyright 2016 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, 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/hax.h"
+
+int hax_sync_vcpus(void)
+{
+ return 0;
+}
+
+int hax_populate_ram(uint64_t va, uint32_t size)
+{
+ return -ENOSYS;
+}
+
+int hax_init_vcpu(CPUState *cpu)
+{
+ return -ENOSYS;
+}
+
+int hax_smp_cpu_exec(CPUState *cpu)
+{
+ return -ENOSYS;
+}
diff --git a/include/sysemu/hax.h b/include/sysemu/hax.h
new file mode 100644
index 0000000..51c8fd5
--- /dev/null
+++ b/include/sysemu/hax.h
@@ -0,0 +1,56 @@
+/*
+ * QEMU HAXM support
+ *
+ * Copyright IBM, Corp. 2008
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * Copyright (c) 2011 Intel Corporation
+ * Written by:
+ * Jiang Yunhong<yunhong.jiang@intel.com>
+ * Xin Xiaohui<xiaohui.xin@intel.com>
+ * Zhang Xiantao<xiantao.zhang@intel.com>
+ *
+ * Copyright 2016 Google, Inc.
+ *
+ * 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 QEMU_HAX_H
+#define QEMU_HAX_H
+
+#include "config-host.h"
+#include "qemu-common.h"
+
+int hax_sync_vcpus(void);
+int hax_init_vcpu(CPUState *cpu);
+int hax_smp_cpu_exec(CPUState *cpu);
+int hax_populate_ram(uint64_t va, uint32_t size);
+
+void hax_cpu_synchronize_state(CPUState *cpu);
+void hax_cpu_synchronize_post_reset(CPUState *cpu);
+void hax_cpu_synchronize_post_init(CPUState *cpu);
+
+#ifdef CONFIG_HAX
+
+int hax_enabled(void);
+
+#include "hw/hw.h"
+#include "qemu/bitops.h"
+#include "exec/memory.h"
+int hax_vcpu_destroy(CPUState *cpu);
+void hax_raise_event(CPUState *cpu);
+void hax_reset_vcpu_state(void *opaque);
+#include "target-i386/hax-interface.h"
+#include "target-i386/hax-i386.h"
+
+#else /* CONFIG_HAX */
+
+#define hax_enabled() (0)
+
+#endif /* CONFIG_HAX */
+
+#endif /* QEMU_HAX_H */
diff --git a/target-i386/hax-all.c b/target-i386/hax-all.c
new file mode 100644
index 0000000..1f0ef7c
--- /dev/null
+++ b/target-i386/hax-all.c
@@ -0,0 +1,1138 @@
+/*
+ * QEMU HAX support
+ *
+ * Copyright IBM, Corp. 2008
+ * Red Hat, Inc. 2008
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ * Glauber Costa <gcosta@redhat.com>
+ *
+ * Copyright (c) 2011 Intel Corporation
+ * Written by:
+ * Jiang Yunhong<yunhong.jiang@intel.com>
+ * Xin Xiaohui<xiaohui.xin@intel.com>
+ * Zhang Xiantao<xiantao.zhang@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.
+ *
+ */
+
+/*
+ * HAX common code for both windows and darwin
+ */
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "exec/address-spaces.h"
+#include "exec/exec-all.h"
+#include "exec/ioport.h"
+
+#include "qemu-common.h"
+#include "strings.h"
+#include "hax-i386.h"
+#include "sysemu/accel.h"
+#include "sysemu/sysemu.h"
+#include "qemu/main-loop.h"
+#include "hw/boards.h"
+
+#define DEBUG_HAX 0
+
+#define DPRINTF(fmt, ...) \
+ do { \
+ if (DEBUG_HAX) { \
+ fprintf(stdout, fmt, ## __VA_ARGS__); \
+ } \
+ } while (0)
+
+/* Current version */
+const uint32_t hax_cur_version = 0x3; /* ver 2.0: support fast mmio */
+/* Minimum HAX kernel version */
+const uint32_t hax_min_version = 0x4; /* ver 4.0 supports unmapping */
+
+static bool hax_allowed;
+
+struct hax_state hax_global;
+
+static void hax_vcpu_sync_state(CPUArchState *env, int modified);
+static int hax_arch_get_registers(CPUArchState *env);
+
+int hax_enabled(void)
+{
+ return hax_allowed;
+}
+
+int valid_hax_tunnel_size(uint16_t size)
+{
+ return size >= sizeof(struct hax_tunnel);
+}
+
+hax_fd hax_vcpu_get_fd(CPUArchState *env)
+{
+ struct hax_vcpu_state *vcpu = ENV_GET_CPU(env)->hax_vcpu;
+ if (!vcpu) {
+ return HAX_INVALID_FD;
+ }
+ return vcpu->fd;
+}
+
+static int hax_get_capability(struct hax_state *hax)
+{
+ int ret;
+ struct hax_capabilityinfo capinfo, *cap = &capinfo;
+
+ ret = hax_capability(hax, cap);
+ if (ret) {
+ return ret;
+ }
+
+ if ((cap->wstatus & HAX_CAP_WORKSTATUS_MASK) == HAX_CAP_STATUS_NOTWORKING) {
+ if (cap->winfo & HAX_CAP_FAILREASON_VT) {
+ DPRINTF
+ ("VTX feature is not enabled, HAX driver will not work.\n");
+ } else if (cap->winfo & HAX_CAP_FAILREASON_NX) {
+ DPRINTF
+ ("NX feature is not enabled, HAX driver will not work.\n");
+ }
+ return -ENXIO;
+
+ }
+
+ if (!(cap->winfo & HAX_CAP_UG)) {
+ fprintf(stderr, "UG mode is not supported by the hardware.\n");
+ return -ENOTSUP;
+ }
+
+ if (cap->wstatus & HAX_CAP_MEMQUOTA) {
+ if (cap->mem_quota < hax->mem_quota) {
+ fprintf(stderr, "The VM memory needed exceeds the driver limit.\n");
+ return -ENOSPC;
+ }
+ }
+ return 0;
+}
+
+static int hax_version_support(struct hax_state *hax)
+{
+ int ret;
+ struct hax_module_version version;
+
+ ret = hax_mod_version(hax, &version);
+ if (ret < 0) {
+ return 0;
+ }
+
+ if ((hax_min_version > version.cur_version) ||
+ (hax_cur_version < version.compat_version)) {
+ return 0;
+ }
+
+ return 1;
+}
+
+int hax_vcpu_create(int id)
+{
+ struct hax_vcpu_state *vcpu = NULL;
+ int ret;
+
+ if (!hax_global.vm) {
+ fprintf(stderr, "vcpu %x created failed, vm is null\n", id);
+ return -1;
+ }
+
+ if (hax_global.vm->vcpus[id]) {
+ fprintf(stderr, "vcpu %x allocated already\n", id);
+ return 0;
+ }
+
+ vcpu = g_malloc(sizeof(struct hax_vcpu_state));
+ if (!vcpu) {
+ fprintf(stderr, "Failed to alloc vcpu state\n");
+ return -ENOMEM;
+ }
+
+ memset(vcpu, 0, sizeof(struct hax_vcpu_state));
+
+ ret = hax_host_create_vcpu(hax_global.vm->fd, id);
+ if (ret) {
+ fprintf(stderr, "Failed to create vcpu %x\n", id);
+ goto error;
+ }
+
+ vcpu->vcpu_id = id;
+ vcpu->fd = hax_host_open_vcpu(hax_global.vm->id, id);
+ if (hax_invalid_fd(vcpu->fd)) {
+ fprintf(stderr, "Failed to open the vcpu\n");
+ ret = -ENODEV;
+ goto error;
+ }
+
+ hax_global.vm->vcpus[id] = vcpu;
+
+ ret = hax_host_setup_vcpu_channel(vcpu);
+ if (ret) {
+ fprintf(stderr, "Invalid hax tunnel size\n");
+ ret = -EINVAL;
+ goto error;
+ }
+ return 0;
+
+ error:
+ /* vcpu and tunnel will be closed automatically */
+ if (vcpu && !hax_invalid_fd(vcpu->fd)) {
+ hax_close_fd(vcpu->fd);
+ }
+
+ hax_global.vm->vcpus[id] = NULL;
+ g_free(vcpu);
+ return -1;
+}
+
+int hax_vcpu_destroy(CPUState *cpu)
+{
+ struct hax_vcpu_state *vcpu = cpu->hax_vcpu;
+
+ if (!hax_global.vm) {
+ fprintf(stderr, "vcpu %x destroy failed, vm is null\n", vcpu->vcpu_id);
+ return -1;
+ }
+
+ if (!vcpu) {
+ return 0;
+ }
+
+ /*
+ * 1. The hax_tunnel is also destroied when vcpu destroy
+ * 2. close fd will cause hax module vcpu be cleaned
+ */
+ hax_close_fd(vcpu->fd);
+ hax_global.vm->vcpus[vcpu->vcpu_id] = NULL;
+ g_free(vcpu);
+ return 0;
+}
+
+int hax_init_vcpu(CPUState *cpu)
+{
+ int ret;
+
+ ret = hax_vcpu_create(cpu->cpu_index);
+ if (ret < 0) {
+ fprintf(stderr, "Failed to create HAX vcpu\n");
+ exit(-1);
+ }
+
+ cpu->hax_vcpu = hax_global.vm->vcpus[cpu->cpu_index];
+ cpu->hax_vcpu_dirty = true;
+ qemu_register_reset(hax_reset_vcpu_state, (CPUArchState *) (cpu->env_ptr));
+
+ return ret;
+}
+
+struct hax_vm *hax_vm_create(struct hax_state *hax)
+{
+ struct hax_vm *vm;
+ int vm_id = 0, ret;
+
+ if (hax_invalid_fd(hax->fd)) {
+ return NULL;
+ }
+
+ if (hax->vm) {
+ return hax->vm;
+ }
+
+ vm = g_malloc(sizeof(struct hax_vm));
+ if (!vm) {
+ return NULL;
+ }
+ memset(vm, 0, sizeof(struct hax_vm));
+ ret = hax_host_create_vm(hax, &vm_id);
+ if (ret) {
+ fprintf(stderr, "Failed to create vm %x\n", ret);
+ goto error;
+ }
+ vm->id = vm_id;
+ vm->fd = hax_host_open_vm(hax, vm_id);
+ if (hax_invalid_fd(vm->fd)) {
+ fprintf(stderr, "Failed to open vm %d\n", vm_id);
+ goto error;
+ }
+
+ hax->vm = vm;
+ return vm;
+
+ error:
+ g_free(vm);
+ hax->vm = NULL;
+ return NULL;
+}
+
+int hax_vm_destroy(struct hax_vm *vm)
+{
+ int i;
+
+ for (i = 0; i < HAX_MAX_VCPU; i++)
+ if (vm->vcpus[i]) {
+ fprintf(stderr, "VCPU should be cleaned before vm clean\n");
+ return -1;
+ }
+ hax_close_fd(vm->fd);
+ g_free(vm);
+ hax_global.vm = NULL;
+ return 0;
+}
+
+static void hax_handle_interrupt(CPUState *cpu, int mask)
+{
+ cpu->interrupt_request |= mask;
+
+ if (!qemu_cpu_is_self(cpu)) {
+ qemu_cpu_kick(cpu);
+ }
+}
+
+static int hax_init(ram_addr_t ram_size)
+{
+ struct hax_state *hax = NULL;
+ struct hax_qemu_version qversion;
+ int ret;
+
+ hax = &hax_global;
+
+ memset(hax, 0, sizeof(struct hax_state));
+ hax->mem_quota = ram_size;
+
+ hax->fd = hax_mod_open();
+ if (hax_invalid_fd(hax->fd)) {
+ hax->fd = 0;
+ ret = -ENODEV;
+ goto error;
+ }
+
+ ret = hax_get_capability(hax);
+
+ if (ret) {
+ if (ret != -ENOSPC) {
+ ret = -EINVAL;
+ }
+ goto error;
+ }
+
+ if (!hax_version_support(hax)) {
+ fprintf(stderr, "Incompat HAX version. QEMU current version %x ",
+ hax_cur_version);
+ fprintf(stderr, "requires minimum HAX version %x\n", hax_min_version);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ hax->vm = hax_vm_create(hax);
+ if (!hax->vm) {
+ fprintf(stderr, "Failed to create HAX VM\n");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ hax_memory_region_init();
+
+ qversion.cur_version = hax_cur_version;
+ qversion.min_version = hax_min_version;
+ hax_notify_qemu_version(hax->vm->fd, &qversion);
+ cpu_interrupt_handler = hax_handle_interrupt;
+
+ return ret;
+ error:
+ if (hax->vm) {
+ hax_vm_destroy(hax->vm);
+ }
+ if (hax->fd) {
+ hax_mod_close(hax);
+ }
+
+ return ret;
+}
+
+static int hax_accel_init(MachineState *ms)
+{
+ int ret = hax_init(ms->ram_size);
+
+ if (ret && (ret != -ENOSPC)) {
+ fprintf(stderr, "No accelerator found.\n");
+ } else {
+ fprintf(stdout, "HAX is %s and emulator runs in %s mode.\n",
+ !ret ? "working" : "not working",
+ !ret ? "fast virt" : "emulation");
+ }
+ return ret;
+}
+
+static int hax_handle_fastmmio(CPUArchState *env, struct hax_fastmmio *hft)
+{
+ cpu_physical_memory_rw(hft->gpa, (uint8_t *) &hft->value, hft->size,
+ hft->direction);
+
+ return 0;
+}
+
+static int hax_handle_io(CPUArchState *env, uint32_t df, uint16_t port,
+ int direction, int size, int count, void *buffer)
+{
+ uint8_t *ptr;
+ int i;
+ MemTxAttrs attrs = { 0 };
+
+ if (!df) {
+ ptr = (uint8_t *) buffer;
+ } else {
+ ptr = buffer + size * count - size;
+ }
+ for (i = 0; i < count; i++) {
+ address_space_rw(&address_space_io, port, attrs,
+ ptr, size, direction == HAX_EXIT_IO_OUT);
+ if (!df) {
+ ptr += size;
+ } else {
+ ptr -= size;
+ }
+ }
+
+ return 0;
+}
+
+static int hax_vcpu_interrupt(CPUArchState *env)
+{
+ CPUState *cpu = ENV_GET_CPU(env);
+ struct hax_vcpu_state *vcpu = cpu->hax_vcpu;
+ struct hax_tunnel *ht = vcpu->tunnel;
+
+ /*
+ * Try to inject an interrupt if the guest can accept it
+ * Unlike KVM, HAX kernel check for the eflags, instead of qemu
+ */
+ if (ht->ready_for_interrupt_injection &&
+ (cpu->interrupt_request & CPU_INTERRUPT_HARD)) {
+ int irq;
+
+ irq = cpu_get_pic_interrupt(env);
+ if (irq >= 0) {
+ hax_inject_interrupt(env, irq);
+ cpu->interrupt_request &= ~CPU_INTERRUPT_HARD;
+ }
+ }
+
+ /* If we have an interrupt but the guest is not ready to receive an
+ * interrupt, request an interrupt window exit. This will
+ * cause a return to userspace as soon as the guest is ready to
+ * receive interrupts. */
+ if ((cpu->interrupt_request & CPU_INTERRUPT_HARD)) {
+ ht->request_interrupt_window = 1;
+ } else {
+ ht->request_interrupt_window = 0;
+ }
+ return 0;
+}
+
+void hax_raise_event(CPUState *cpu)
+{
+ struct hax_vcpu_state *vcpu = cpu->hax_vcpu;
+
+ if (!vcpu) {
+ return;
+ }
+ vcpu->tunnel->user_event_pending = 1;
+}
+
+/*
+ * Ask hax kernel module to run the CPU for us till:
+ * 1. Guest crash or shutdown
+ * 2. Need QEMU's emulation like guest execute MMIO instruction
+ * 3. Guest execute HLT
+ * 4. QEMU have Signal/event pending
+ * 5. An unknown VMX exit happens
+ */
+static int hax_vcpu_hax_exec(CPUArchState *env)
+{
+ int ret = 0;
+ CPUState *cpu = ENV_GET_CPU(env);
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ struct hax_vcpu_state *vcpu = cpu->hax_vcpu;
+ struct hax_tunnel *ht = vcpu->tunnel;
+
+ if (!hax_enabled()) {
+ DPRINTF("Trying to vcpu execute at eip:" TARGET_FMT_lx "\n", env->eip);
+ return 0;
+ }
+
+ cpu->halted = 0;
+
+ if (cpu->interrupt_request & CPU_INTERRUPT_POLL) {
+ cpu->interrupt_request &= ~CPU_INTERRUPT_POLL;
+ apic_poll_irq(x86_cpu->apic_state);
+ }
+
+ if (cpu->interrupt_request & CPU_INTERRUPT_INIT) {
+ DPRINTF("\nhax_vcpu_hax_exec: handling INIT for %d\n",
+ cpu->cpu_index);
+ do_cpu_init(x86_cpu);
+ hax_vcpu_sync_state(env, 1);
+ }
+
+ if (cpu->interrupt_request & CPU_INTERRUPT_SIPI) {
+ DPRINTF("hax_vcpu_hax_exec: handling SIPI for %d\n",
+ cpu->cpu_index);
+ hax_vcpu_sync_state(env, 0);
+ do_cpu_sipi(x86_cpu);
+ hax_vcpu_sync_state(env, 1);
+ }
+
+ do {
+ int hax_ret;
+
+ if (cpu->exit_request) {
+ ret = 1;
+ break;
+ }
+
+ hax_vcpu_interrupt(env);
+
+ qemu_mutex_unlock_iothread();
+ hax_ret = hax_vcpu_run(vcpu);
+ qemu_mutex_lock_iothread();
+ current_cpu = cpu;
+
+ /* Simply continue the vcpu_run if system call interrupted */
+ if (hax_ret == -EINTR || hax_ret == -EAGAIN) {
+ DPRINTF("io window interrupted\n");
+ continue;
+ }
+
+ if (hax_ret < 0) {
+ fprintf(stderr, "vcpu run failed for vcpu %x\n", vcpu->vcpu_id);
+ abort();
+ }
+ switch (ht->_exit_status) {
+ case HAX_EXIT_IO:
+ ret = hax_handle_io(env, ht->pio._df, ht->pio._port,
+ ht->pio._direction,
+ ht->pio._size, ht->pio._count, vcpu->iobuf);
+ break;
+ case HAX_EXIT_FAST_MMIO:
+ ret = hax_handle_fastmmio(env, (struct hax_fastmmio *) vcpu->iobuf);
+ break;
+ /* Guest state changed, currently only for shutdown */
+ case HAX_EXIT_STATECHANGE:
+ fprintf(stdout, "VCPU shutdown request\n");
+ qemu_system_shutdown_request();
+ hax_vcpu_sync_state(env, 0);
+ ret = 1;
+ break;
+ case HAX_EXIT_UNKNOWN_VMEXIT:
+ fprintf(stderr, "Unknown VMX exit %x from guest\n",
+ ht->_exit_reason);
+ qemu_system_reset_request();
+ hax_vcpu_sync_state(env, 0);
+ cpu_dump_state(cpu, stderr, fprintf, 0);
+ ret = -1;
+ break;
+ case HAX_EXIT_HLT:
+ if (!(cpu->interrupt_request & CPU_INTERRUPT_HARD) &&
+ !(cpu->interrupt_request & CPU_INTERRUPT_NMI)) {
+ /* hlt instruction with interrupt disabled is shutdown */
+ env->eflags |= IF_MASK;
+ cpu->halted = 1;
+ cpu->exception_index = EXCP_HLT;
+ ret = 1;
+ }
+ break;
+ /* these situations will continue to hax module */
+ case HAX_EXIT_INTERRUPT:
+ case HAX_EXIT_PAUSED:
+ break;
+ case HAX_EXIT_MMIO:
+ /* Should not happen on UG system */
+ fprintf(stderr, "HAX: unsupported MMIO emulation\n");
+ ret = -1;
+ break;
+ case HAX_EXIT_REAL:
+ /* Should not happen on UG system */
+ fprintf(stderr, "HAX: unimplemented real mode emulation\n");
+ ret = -1;
+ break;
+ default:
+ fprintf(stderr, "Unknown exit %x from HAX\n", ht->_exit_status);
+ qemu_system_reset_request();
+ hax_vcpu_sync_state(env, 0);
+ cpu_dump_state(cpu, stderr, fprintf, 0);
+ ret = 1;
+ break;
+ }
+ } while (!ret);
+
+ if (cpu->exit_request) {
+ cpu->exit_request = 0;
+ cpu->exception_index = EXCP_INTERRUPT;
+ }
+ return ret < 0;
+}
+
+static void do_hax_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg)
+{
+ CPUArchState *env = cpu->env_ptr;
+
+ hax_arch_get_registers(env);
+ cpu->hax_vcpu_dirty = true;
+}
+
+void hax_cpu_synchronize_state(CPUState *cpu)
+{
+ if (!cpu->hax_vcpu_dirty) {
+ run_on_cpu(cpu, do_hax_cpu_synchronize_state, RUN_ON_CPU_NULL);
+ }
+}
+
+static void do_hax_cpu_synchronize_post_reset(CPUState *cpu,
+ run_on_cpu_data arg)
+{
+ CPUArchState *env = cpu->env_ptr;
+
+ hax_vcpu_sync_state(env, 1);
+ cpu->hax_vcpu_dirty = false;
+}
+
+void hax_cpu_synchronize_post_reset(CPUState *cpu)
+{
+ run_on_cpu(cpu, do_hax_cpu_synchronize_post_reset, RUN_ON_CPU_NULL);
+}
+
+static void do_hax_cpu_synchronize_post_init(CPUState *cpu, run_on_cpu_data arg)
+{
+ CPUArchState *env = cpu->env_ptr;
+
+ hax_vcpu_sync_state(env, 1);
+ cpu->hax_vcpu_dirty = false;
+}
+
+void hax_cpu_synchronize_post_init(CPUState *cpu)
+{
+ run_on_cpu(cpu, do_hax_cpu_synchronize_post_init, RUN_ON_CPU_NULL);
+}
+
+int hax_smp_cpu_exec(CPUState *cpu)
+{
+ CPUArchState *env = (CPUArchState *) (cpu->env_ptr);
+ int fatal;
+ int ret;
+
+ while (1) {
+ if (cpu->exception_index >= EXCP_INTERRUPT) {
+ ret = cpu->exception_index;
+ cpu->exception_index = -1;
+ break;
+ }
+
+ fatal = hax_vcpu_hax_exec(env);
+
+ if (fatal) {
+ fprintf(stderr, "Unsupported HAX vcpu return\n");
+ abort();
+ }
+ }
+
+ return ret;
+}
+
+static void set_v8086_seg(struct segment_desc_t *lhs, const SegmentCache *rhs)
+{
+ memset(lhs, 0, sizeof(struct segment_desc_t));
+ lhs->selector = rhs->selector;
+ lhs->base = rhs->base;
+ lhs->limit = rhs->limit;
+ lhs->type = 3;
+ lhs->present = 1;
+ lhs->dpl = 3;
+ lhs->operand_size = 0;
+ lhs->desc = 1;
+ lhs->long_mode = 0;
+ lhs->granularity = 0;
+ lhs->available = 0;
+}
+
+static void get_seg(SegmentCache *lhs, const struct segment_desc_t *rhs)
+{
+ lhs->selector = rhs->selector;
+ lhs->base = rhs->base;
+ lhs->limit = rhs->limit;
+ lhs->flags = (rhs->type << DESC_TYPE_SHIFT)
+ | (rhs->present * DESC_P_MASK)
+ | (rhs->dpl << DESC_DPL_SHIFT)
+ | (rhs->operand_size << DESC_B_SHIFT)
+ | (rhs->desc * DESC_S_MASK)
+ | (rhs->long_mode << DESC_L_SHIFT)
+ | (rhs->granularity * DESC_G_MASK) | (rhs->available * DESC_AVL_MASK);
+}
+
+static void set_seg(struct segment_desc_t *lhs, const SegmentCache *rhs)
+{
+ unsigned flags = rhs->flags;
+
+ memset(lhs, 0, sizeof(struct segment_desc_t));
+ lhs->selector = rhs->selector;
+ lhs->base = rhs->base;
+ lhs->limit = rhs->limit;
+ lhs->type = (flags >> DESC_TYPE_SHIFT) & 15;
+ lhs->present = (flags & DESC_P_MASK) != 0;
+ lhs->dpl = rhs->selector & 3;
+ lhs->operand_size = (flags >> DESC_B_SHIFT) & 1;
+ lhs->desc = (flags & DESC_S_MASK) != 0;
+ lhs->long_mode = (flags >> DESC_L_SHIFT) & 1;
+ lhs->granularity = (flags & DESC_G_MASK) != 0;
+ lhs->available = (flags & DESC_AVL_MASK) != 0;
+}
+
+static void hax_getput_reg(uint64_t *hax_reg, target_ulong *qemu_reg, int set)
+{
+ target_ulong reg = *hax_reg;
+
+ if (set) {
+ *hax_reg = *qemu_reg;
+ } else {
+ *qemu_reg = reg;
+ }
+}
+
+/* The sregs has been synced with HAX kernel already before this call */
+static int hax_get_segments(CPUArchState *env, struct vcpu_state_t *sregs)
+{
+ get_seg(&env->segs[R_CS], &sregs->_cs);
+ get_seg(&env->segs[R_DS], &sregs->_ds);
+ get_seg(&env->segs[R_ES], &sregs->_es);
+ get_seg(&env->segs[R_FS], &sregs->_fs);
+ get_seg(&env->segs[R_GS], &sregs->_gs);
+ get_seg(&env->segs[R_SS], &sregs->_ss);
+
+ get_seg(&env->tr, &sregs->_tr);
+ get_seg(&env->ldt, &sregs->_ldt);
+ env->idt.limit = sregs->_idt.limit;
+ env->idt.base = sregs->_idt.base;
+ env->gdt.limit = sregs->_gdt.limit;
+ env->gdt.base = sregs->_gdt.base;
+ return 0;
+}
+
+static int hax_set_segments(CPUArchState *env, struct vcpu_state_t *sregs)
+{
+ if ((env->eflags & VM_MASK)) {
+ set_v8086_seg(&sregs->_cs, &env->segs[R_CS]);
+ set_v8086_seg(&sregs->_ds, &env->segs[R_DS]);
+ set_v8086_seg(&sregs->_es, &env->segs[R_ES]);
+ set_v8086_seg(&sregs->_fs, &env->segs[R_FS]);
+ set_v8086_seg(&sregs->_gs, &env->segs[R_GS]);
+ set_v8086_seg(&sregs->_ss, &env->segs[R_SS]);
+ } else {
+ set_seg(&sregs->_cs, &env->segs[R_CS]);
+ set_seg(&sregs->_ds, &env->segs[R_DS]);
+ set_seg(&sregs->_es, &env->segs[R_ES]);
+ set_seg(&sregs->_fs, &env->segs[R_FS]);
+ set_seg(&sregs->_gs, &env->segs[R_GS]);
+ set_seg(&sregs->_ss, &env->segs[R_SS]);
+
+ if (env->cr[0] & CR0_PE_MASK) {
+ /* force ss cpl to cs cpl */
+ sregs->_ss.selector = (sregs->_ss.selector & ~3) |
+ (sregs->_cs.selector & 3);
+ sregs->_ss.dpl = sregs->_ss.selector & 3;
+ }
+ }
+
+ set_seg(&sregs->_tr, &env->tr);
+ set_seg(&sregs->_ldt, &env->ldt);
+ sregs->_idt.limit = env->idt.limit;
+ sregs->_idt.base = env->idt.base;
+ sregs->_gdt.limit = env->gdt.limit;
+ sregs->_gdt.base = env->gdt.base;
+ return 0;
+}
+
+/*
+ * After get the state from the kernel module, some
+ * qemu emulator state need be updated also
+ */
+static int hax_setup_qemu_emulator(CPUArchState *env)
+{
+
+#define HFLAG_COPY_MASK (~( \
+ HF_CPL_MASK | HF_PE_MASK | HF_MP_MASK | HF_EM_MASK | \
+ HF_TS_MASK | HF_TF_MASK | HF_VM_MASK | HF_IOPL_MASK | \
+ HF_OSFXSR_MASK | HF_LMA_MASK | HF_CS32_MASK | \
+ HF_SS32_MASK | HF_CS64_MASK | HF_ADDSEG_MASK))
+
+ uint32_t hflags;
+
+ hflags = (env->segs[R_CS].flags >> DESC_DPL_SHIFT) & HF_CPL_MASK;
+ hflags |= (env->cr[0] & CR0_PE_MASK) << (HF_PE_SHIFT - CR0_PE_SHIFT);
+ hflags |= (env->cr[0] << (HF_MP_SHIFT - CR0_MP_SHIFT)) &
+ (HF_MP_MASK | HF_EM_MASK | HF_TS_MASK);
+ hflags |= (env->eflags & (HF_TF_MASK | HF_VM_MASK | HF_IOPL_MASK));
+ hflags |= (env->cr[4] & CR4_OSFXSR_MASK) <<
+ (HF_OSFXSR_SHIFT - CR4_OSFXSR_SHIFT);
+
+ if (env->efer & MSR_EFER_LMA) {
+ hflags |= HF_LMA_MASK;
+ }
+
+ if ((hflags & HF_LMA_MASK) && (env->segs[R_CS].flags & DESC_L_MASK)) {
+ hflags |= HF_CS32_MASK | HF_SS32_MASK | HF_CS64_MASK;
+ } else {
+ hflags |= (env->segs[R_CS].flags & DESC_B_MASK) >>
+ (DESC_B_SHIFT - HF_CS32_SHIFT);
+ hflags |= (env->segs[R_SS].flags & DESC_B_MASK) >>
+ (DESC_B_SHIFT - HF_SS32_SHIFT);
+ if (!(env->cr[0] & CR0_PE_MASK) ||
+ (env->eflags & VM_MASK) || !(hflags & HF_CS32_MASK)) {
+ hflags |= HF_ADDSEG_MASK;
+ } else {
+ hflags |= ((env->segs[R_DS].base |
+ env->segs[R_ES].base |
+ env->segs[R_SS].base) != 0) << HF_ADDSEG_SHIFT;
+ }
+ }
+
+ hflags &= ~HF_SMM_MASK;
+
+ env->hflags = (env->hflags & HFLAG_COPY_MASK) | hflags;
+ return 0;
+}
+
+static int hax_sync_vcpu_register(CPUArchState *env, int set)
+{
+ struct vcpu_state_t regs;
+ int ret;
+ memset(®s, 0, sizeof(struct vcpu_state_t));
+
+ if (!set) {
+ ret = hax_sync_vcpu_state(env, ®s, 0);
+ if (ret < 0) {
+ return -1;
+ }
+ }
+
+ /* generic register */
+ hax_getput_reg(®s._rax, &env->regs[R_EAX], set);
+ hax_getput_reg(®s._rbx, &env->regs[R_EBX], set);
+ hax_getput_reg(®s._rcx, &env->regs[R_ECX], set);
+ hax_getput_reg(®s._rdx, &env->regs[R_EDX], set);
+ hax_getput_reg(®s._rsi, &env->regs[R_ESI], set);
+ hax_getput_reg(®s._rdi, &env->regs[R_EDI], set);
+ hax_getput_reg(®s._rsp, &env->regs[R_ESP], set);
+ hax_getput_reg(®s._rbp, &env->regs[R_EBP], set);
+#ifdef TARGET_X86_64
+ hax_getput_reg(®s._r8, &env->regs[8], set);
+ hax_getput_reg(®s._r9, &env->regs[9], set);
+ hax_getput_reg(®s._r10, &env->regs[10], set);
+ hax_getput_reg(®s._r11, &env->regs[11], set);
+ hax_getput_reg(®s._r12, &env->regs[12], set);
+ hax_getput_reg(®s._r13, &env->regs[13], set);
+ hax_getput_reg(®s._r14, &env->regs[14], set);
+ hax_getput_reg(®s._r15, &env->regs[15], set);
+#endif
+ hax_getput_reg(®s._rflags, &env->eflags, set);
+ hax_getput_reg(®s._rip, &env->eip, set);
+
+ if (set) {
+ regs._cr0 = env->cr[0];
+ regs._cr2 = env->cr[2];
+ regs._cr3 = env->cr[3];
+ regs._cr4 = env->cr[4];
+ hax_set_segments(env, ®s);
+ } else {
+ env->cr[0] = regs._cr0;
+ env->cr[2] = regs._cr2;
+ env->cr[3] = regs._cr3;
+ env->cr[4] = regs._cr4;
+ hax_get_segments(env, ®s);
+ }
+
+ if (set) {
+ ret = hax_sync_vcpu_state(env, ®s, 1);
+ if (ret < 0) {
+ return -1;
+ }
+ }
+ if (!set) {
+ hax_setup_qemu_emulator(env);
+ }
+ return 0;
+}
+
+static void hax_msr_entry_set(struct vmx_msr *item, uint32_t index,
+ uint64_t value)
+{
+ item->entry = index;
+ item->value = value;
+}
+
+static int hax_get_msrs(CPUArchState *env)
+{
+ struct hax_msr_data md;
+ struct vmx_msr *msrs = md.entries;
+ int ret, i, n;
+
+ n = 0;
+ msrs[n++].entry = MSR_IA32_SYSENTER_CS;
+ msrs[n++].entry = MSR_IA32_SYSENTER_ESP;
+ msrs[n++].entry = MSR_IA32_SYSENTER_EIP;
+ msrs[n++].entry = MSR_IA32_TSC;
+#ifdef TARGET_X86_64
+ msrs[n++].entry = MSR_EFER;
+ msrs[n++].entry = MSR_STAR;
+ msrs[n++].entry = MSR_LSTAR;
+ msrs[n++].entry = MSR_CSTAR;
+ msrs[n++].entry = MSR_FMASK;
+ msrs[n++].entry = MSR_KERNELGSBASE;
+#endif
+ md.nr_msr = n;
+ ret = hax_sync_msr(env, &md, 0);
+ if (ret < 0) {
+ return ret;
+ }
+
+ for (i = 0; i < md.done; i++) {
+ switch (msrs[i].entry) {
+ case MSR_IA32_SYSENTER_CS:
+ env->sysenter_cs = msrs[i].value;
+ break;
+ case MSR_IA32_SYSENTER_ESP:
+ env->sysenter_esp = msrs[i].value;
+ break;
+ case MSR_IA32_SYSENTER_EIP:
+ env->sysenter_eip = msrs[i].value;
+ break;
+ case MSR_IA32_TSC:
+ env->tsc = msrs[i].value;
+ break;
+#ifdef TARGET_X86_64
+ case MSR_EFER:
+ env->efer = msrs[i].value;
+ break;
+ case MSR_STAR:
+ env->star = msrs[i].value;
+ break;
+ case MSR_LSTAR:
+ env->lstar = msrs[i].value;
+ break;
+ case MSR_CSTAR:
+ env->cstar = msrs[i].value;
+ break;
+ case MSR_FMASK:
+ env->fmask = msrs[i].value;
+ break;
+ case MSR_KERNELGSBASE:
+ env->kernelgsbase = msrs[i].value;
+ break;
+#endif
+ }
+ }
+
+ return 0;
+}
+
+static int hax_set_msrs(CPUArchState *env)
+{
+ struct hax_msr_data md;
+ struct vmx_msr *msrs;
+ msrs = md.entries;
+ int n = 0;
+
+ memset(&md, 0, sizeof(struct hax_msr_data));
+ hax_msr_entry_set(&msrs[n++], MSR_IA32_SYSENTER_CS, env->sysenter_cs);
+ hax_msr_entry_set(&msrs[n++], MSR_IA32_SYSENTER_ESP, env->sysenter_esp);
+ hax_msr_entry_set(&msrs[n++], MSR_IA32_SYSENTER_EIP, env->sysenter_eip);
+ hax_msr_entry_set(&msrs[n++], MSR_IA32_TSC, env->tsc);
+#ifdef TARGET_X86_64
+ hax_msr_entry_set(&msrs[n++], MSR_EFER, env->efer);
+ hax_msr_entry_set(&msrs[n++], MSR_STAR, env->star);
+ hax_msr_entry_set(&msrs[n++], MSR_LSTAR, env->lstar);
+ hax_msr_entry_set(&msrs[n++], MSR_CSTAR, env->cstar);
+ hax_msr_entry_set(&msrs[n++], MSR_FMASK, env->fmask);
+ hax_msr_entry_set(&msrs[n++], MSR_KERNELGSBASE, env->kernelgsbase);
+#endif
+ md.nr_msr = n;
+ md.done = 0;
+
+ return hax_sync_msr(env, &md, 1);
+}
+
+static int hax_get_fpu(CPUArchState *env)
+{
+ struct fx_layout fpu;
+ int i, ret;
+
+ ret = hax_sync_fpu(env, &fpu, 0);
+ if (ret < 0) {
+ return ret;
+ }
+
+ env->fpstt = (fpu.fsw >> 11) & 7;
+ env->fpus = fpu.fsw;
+ env->fpuc = fpu.fcw;
+ for (i = 0; i < 8; ++i) {
+ env->fptags[i] = !((fpu.ftw >> i) & 1);
+ }
+ memcpy(env->fpregs, fpu.st_mm, sizeof(env->fpregs));
+
+ for (i = 0; i < 8; i++) {
+ env->xmm_regs[i].ZMM_Q(0) = ldq_p(&fpu.mmx_1[i][0]);
+ env->xmm_regs[i].ZMM_Q(1) = ldq_p(&fpu.mmx_1[i][8]);
+ if (CPU_NB_REGS > 8) {
+ env->xmm_regs[i + 8].ZMM_Q(0) = ldq_p(&fpu.mmx_2[i][0]);
+ env->xmm_regs[i + 8].ZMM_Q(1) = ldq_p(&fpu.mmx_2[i][8]);
+ }
+ }
+ env->mxcsr = fpu.mxcsr;
+
+ return 0;
+}
+
+static int hax_set_fpu(CPUArchState *env)
+{
+ struct fx_layout fpu;
+ int i;
+
+ memset(&fpu, 0, sizeof(fpu));
+ fpu.fsw = env->fpus & ~(7 << 11);
+ fpu.fsw |= (env->fpstt & 7) << 11;
+ fpu.fcw = env->fpuc;
+
+ for (i = 0; i < 8; ++i) {
+ fpu.ftw |= (!env->fptags[i]) << i;
+ }
+
+ memcpy(fpu.st_mm, env->fpregs, sizeof(env->fpregs));
+ for (i = 0; i < 8; i++) {
+ stq_p(&fpu.mmx_1[i][0], env->xmm_regs[i].ZMM_Q(0));
+ stq_p(&fpu.mmx_1[i][8], env->xmm_regs[i].ZMM_Q(1));
+ if (CPU_NB_REGS > 8) {
+ stq_p(&fpu.mmx_2[i][0], env->xmm_regs[i + 8].ZMM_Q(0));
+ stq_p(&fpu.mmx_2[i][8], env->xmm_regs[i + 8].ZMM_Q(1));
+ }
+ }
+
+ fpu.mxcsr = env->mxcsr;
+
+ return hax_sync_fpu(env, &fpu, 1);
+}
+
+static int hax_arch_get_registers(CPUArchState *env)
+{
+ int ret;
+
+ ret = hax_sync_vcpu_register(env, 0);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = hax_get_fpu(env);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = hax_get_msrs(env);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return 0;
+}
+
+static int hax_arch_set_registers(CPUArchState *env)
+{
+ int ret;
+ ret = hax_sync_vcpu_register(env, 1);
+
+ if (ret < 0) {
+ fprintf(stderr, "Failed to sync vcpu reg\n");
+ return ret;
+ }
+ ret = hax_set_fpu(env);
+ if (ret < 0) {
+ fprintf(stderr, "FPU failed\n");
+ return ret;
+ }
+ ret = hax_set_msrs(env);
+ if (ret < 0) {
+ fprintf(stderr, "MSR failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static void hax_vcpu_sync_state(CPUArchState *env, int modified)
+{
+ if (hax_enabled()) {
+ if (modified) {
+ hax_arch_set_registers(env);
+ } else {
+ hax_arch_get_registers(env);
+ }
+ }
+}
+
+/*
+ * much simpler than kvm, at least in first stage because:
+ * We don't need consider the device pass-through, we don't need
+ * consider the framebuffer, and we may even remove the bios at all
+ */
+int hax_sync_vcpus(void)
+{
+ if (hax_enabled()) {
+ CPUState *cpu;
+
+ cpu = first_cpu;
+ if (!cpu) {
+ return 0;
+ }
+
+ for (; cpu != NULL; cpu = CPU_NEXT(cpu)) {
+ int ret;
+
+ ret = hax_arch_set_registers(cpu->env_ptr);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+ }
+
+ return 0;
+}
+
+void hax_reset_vcpu_state(void *opaque)
+{
+ CPUState *cpu;
+ for (cpu = first_cpu; cpu != NULL; cpu = CPU_NEXT(cpu)) {
+ cpu->hax_vcpu->tunnel->user_event_pending = 0;
+ cpu->hax_vcpu->tunnel->ready_for_interrupt_injection = 0;
+ }
+}
+
+static void hax_accel_class_init(ObjectClass *oc, void *data)
+{
+ AccelClass *ac = ACCEL_CLASS(oc);
+ ac->name = "HAX";
+ ac->init_machine = hax_accel_init;
+ ac->allowed = &hax_allowed;
+}
+
+static const TypeInfo hax_accel_type = {
+ .name = ACCEL_CLASS_NAME("hax"),
+ .parent = TYPE_ACCEL,
+ .class_init = hax_accel_class_init,
+};
+
+static void hax_type_init(void)
+{
+ type_register_static(&hax_accel_type);
+}
+
+type_init(hax_type_init);
diff --git a/target-i386/hax-i386.h b/target-i386/hax-i386.h
new file mode 100644
index 0000000..3f50bd9
--- /dev/null
+++ b/target-i386/hax-i386.h
@@ -0,0 +1,86 @@
+/*
+ * QEMU HAXM support
+ *
+ * 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 _HAX_I386_H
+#define _HAX_I386_H
+
+#include "cpu.h"
+#include "sysemu/hax.h"
+
+#ifdef CONFIG_WIN32
+typedef HANDLE hax_fd;
+#endif
+
+extern struct hax_state hax_global;
+struct hax_vcpu_state {
+ hax_fd fd;
+ int vcpu_id;
+ struct hax_tunnel *tunnel;
+ unsigned char *iobuf;
+};
+
+struct hax_state {
+ hax_fd fd; /* the global hax device interface */
+ uint32_t version;
+ struct hax_vm *vm;
+ uint64_t mem_quota;
+};
+
+#define HAX_MAX_VCPU 0x10
+#define MAX_VM_ID 0x40
+#define MAX_VCPU_ID 0x40
+
+struct hax_vm {
+ hax_fd fd;
+ int id;
+ struct hax_vcpu_state *vcpus[HAX_MAX_VCPU];
+};
+
+#ifdef NEED_CPU_H
+/* Functions exported to host specific mode */
+hax_fd hax_vcpu_get_fd(CPUArchState *env);
+int valid_hax_tunnel_size(uint16_t size);
+
+/* Host specific functions */
+int hax_mod_version(struct hax_state *hax, struct hax_module_version *version);
+int hax_inject_interrupt(CPUArchState *env, int vector);
+struct hax_vm *hax_vm_create(struct hax_state *hax);
+int hax_vcpu_run(struct hax_vcpu_state *vcpu);
+int hax_vcpu_create(int id);
+int hax_sync_vcpu_state(CPUArchState *env, struct vcpu_state_t *state,
+ int set);
+int hax_sync_msr(CPUArchState *env, struct hax_msr_data *msrs, int set);
+int hax_sync_fpu(CPUArchState *env, struct fx_layout *fl, int set);
+#endif
+
+int hax_vm_destroy(struct hax_vm *vm);
+int hax_capability(struct hax_state *hax, struct hax_capabilityinfo *cap);
+int hax_notify_qemu_version(hax_fd vm_fd, struct hax_qemu_version *qversion);
+int hax_set_ram(uint64_t start_pa, uint32_t size, uint64_t host_va, int flags);
+
+/* Common host function */
+int hax_host_create_vm(struct hax_state *hax, int *vm_id);
+hax_fd hax_host_open_vm(struct hax_state *hax, int vm_id);
+int hax_host_create_vcpu(hax_fd vm_fd, int vcpuid);
+hax_fd hax_host_open_vcpu(int vmid, int vcpuid);
+int hax_host_setup_vcpu_channel(struct hax_vcpu_state *vcpu);
+hax_fd hax_mod_open(void);
+void hax_memory_region_init(void);
+
+
+#ifdef CONFIG_WIN32
+#include "target-i386/hax-windows.h"
+#endif
+
+#include "target-i386/hax-interface.h"
+
+#endif
diff --git a/target-i386/hax-interface.h b/target-i386/hax-interface.h
new file mode 100644
index 0000000..add6b82
--- /dev/null
+++ b/target-i386/hax-interface.h
@@ -0,0 +1,358 @@
+/*
+ * QEMU HAXM support
+ *
+ * Copyright (c) 2011 Intel Corporation
+ * Written by:
+ * Jiang Yunhong<yunhong.jiang@intel.com>
+ * Xin Xiaohui<xiaohui.xin@intel.com>
+ * Zhang Xiantao<xiantao.zhang@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.
+ *
+ */
+
+/* Interface with HAX kernel module */
+
+#ifndef _HAX_INTERFACE_H
+#define _HAX_INTERFACE_H
+
+/* fx_layout has 3 formats table 3-56, 512bytes */
+struct fx_layout {
+ uint16_t fcw;
+ uint16_t fsw;
+ uint8_t ftw;
+ uint8_t res1;
+ uint16_t fop;
+ union {
+ struct {
+ uint32_t fip;
+ uint16_t fcs;
+ uint16_t res2;
+ };
+ uint64_t fpu_ip;
+ };
+ union {
+ struct {
+ uint32_t fdp;
+ uint16_t fds;
+ uint16_t res3;
+ };
+ uint64_t fpu_dp;
+ };
+ uint32_t mxcsr;
+ uint32_t mxcsr_mask;
+ uint8_t st_mm[8][16];
+ uint8_t mmx_1[8][16];
+ uint8_t mmx_2[8][16];
+ uint8_t pad[96];
+} __attribute__ ((aligned(8)));
+
+struct vmx_msr {
+ uint64_t entry;
+ uint64_t value;
+} __attribute__ ((__packed__));
+
+/*
+ * Fixed array is not good, but it makes Mac support a bit easier by avoiding
+ * memory map or copyin staff.
+ */
+#define HAX_MAX_MSR_ARRAY 0x20
+struct hax_msr_data {
+ uint16_t nr_msr;
+ uint16_t done;
+ uint16_t pad[2];
+ struct vmx_msr entries[HAX_MAX_MSR_ARRAY];
+} __attribute__ ((__packed__));
+
+union interruptibility_state_t {
+ uint32_t raw;
+ struct {
+ uint32_t sti_blocking:1;
+ uint32_t movss_blocking:1;
+ uint32_t smi_blocking:1;
+ uint32_t nmi_blocking:1;
+ uint32_t reserved:28;
+ };
+ uint64_t pad;
+};
+
+typedef union interruptibility_state_t interruptibility_state_t;
+
+/* Segment descriptor */
+struct segment_desc_t {
+ uint16_t selector;
+ uint16_t _dummy;
+ uint32_t limit;
+ uint64_t base;
+ union {
+ struct {
+ uint32_t type:4;
+ uint32_t desc:1;
+ uint32_t dpl:2;
+ uint32_t present:1;
+ uint32_t:4;
+ uint32_t available:1;
+ uint32_t long_mode:1;
+ uint32_t operand_size:1;
+ uint32_t granularity:1;
+ uint32_t null:1;
+ uint32_t:15;
+ };
+ uint32_t ar;
+ };
+ uint32_t ipad;
+};
+
+typedef struct segment_desc_t segment_desc_t;
+
+struct vcpu_state_t {
+ union {
+ uint64_t _regs[16];
+ struct {
+ union {
+ struct {
+ uint8_t _al, _ah;
+ };
+ uint16_t _ax;
+ uint32_t _eax;
+ uint64_t _rax;
+ };
+ union {
+ struct {
+ uint8_t _cl, _ch;
+ };
+ uint16_t _cx;
+ uint32_t _ecx;
+ uint64_t _rcx;
+ };
+ union {
+ struct {
+ uint8_t _dl, _dh;
+ };
+ uint16_t _dx;
+ uint32_t _edx;
+ uint64_t _rdx;
+ };
+ union {
+ struct {
+ uint8_t _bl, _bh;
+ };
+ uint16_t _bx;
+ uint32_t _ebx;
+ uint64_t _rbx;
+ };
+ union {
+ uint16_t _sp;
+ uint32_t _esp;
+ uint64_t _rsp;
+ };
+ union {
+ uint16_t _bp;
+ uint32_t _ebp;
+ uint64_t _rbp;
+ };
+ union {
+ uint16_t _si;
+ uint32_t _esi;
+ uint64_t _rsi;
+ };
+ union {
+ uint16_t _di;
+ uint32_t _edi;
+ uint64_t _rdi;
+ };
+
+ uint64_t _r8;
+ uint64_t _r9;
+ uint64_t _r10;
+ uint64_t _r11;
+ uint64_t _r12;
+ uint64_t _r13;
+ uint64_t _r14;
+ uint64_t _r15;
+ };
+ };
+
+ union {
+ uint32_t _eip;
+ uint64_t _rip;
+ };
+
+ union {
+ uint32_t _eflags;
+ uint64_t _rflags;
+ };
+
+ segment_desc_t _cs;
+ segment_desc_t _ss;
+ segment_desc_t _ds;
+ segment_desc_t _es;
+ segment_desc_t _fs;
+ segment_desc_t _gs;
+ segment_desc_t _ldt;
+ segment_desc_t _tr;
+
+ segment_desc_t _gdt;
+ segment_desc_t _idt;
+
+ uint64_t _cr0;
+ uint64_t _cr2;
+ uint64_t _cr3;
+ uint64_t _cr4;
+
+ uint64_t _dr0;
+ uint64_t _dr1;
+ uint64_t _dr2;
+ uint64_t _dr3;
+ uint64_t _dr6;
+ uint64_t _dr7;
+ uint64_t _pde;
+
+ uint32_t _efer;
+
+ uint32_t _sysenter_cs;
+ uint64_t _sysenter_eip;
+ uint64_t _sysenter_esp;
+
+ uint32_t _activity_state;
+ uint32_t pad;
+ interruptibility_state_t _interruptibility_state;
+};
+
+/* HAX exit status */
+enum exit_status {
+ /* IO port request */
+ HAX_EXIT_IO = 1,
+ /* MMIO instruction emulation */
+ HAX_EXIT_MMIO,
+ /* QEMU emulation mode request, currently means guest enter non-PG mode */
+ HAX_EXIT_REAL,
+ /*
+ * Interrupt window open, qemu can inject interrupt now
+ * Also used when signal pending since at that time qemu usually need
+ * check interrupt
+ */
+ HAX_EXIT_INTERRUPT,
+ /* Unknown vmexit, mostly trigger reboot */
+ HAX_EXIT_UNKNOWN_VMEXIT,
+ /* HALT from guest */
+ HAX_EXIT_HLT,
+ /* Reboot request, like because of tripple fault in guest */
+ HAX_EXIT_STATECHANGE,
+ /* the vcpu is now only paused when destroy, so simply return to hax */
+ HAX_EXIT_PAUSED,
+ HAX_EXIT_FAST_MMIO,
+};
+
+/*
+ * The interface definition:
+ * 1. vcpu_run execute will return 0 on success, otherwise mean failed
+ * 2. exit_status return the exit reason, as stated in enum exit_status
+ * 3. exit_reason is the vmx exit reason
+ */
+struct hax_tunnel {
+ uint32_t _exit_reason;
+ uint32_t _exit_flag;
+ uint32_t _exit_status;
+ uint32_t user_event_pending;
+ int ready_for_interrupt_injection;
+ int request_interrupt_window;
+ union {
+ struct {
+ /* 0: read, 1: write */
+#define HAX_EXIT_IO_IN 1
+#define HAX_EXIT_IO_OUT 0
+ uint8_t _direction;
+ uint8_t _df;
+ uint16_t _size;
+ uint16_t _port;
+ uint16_t _count;
+ uint8_t _flags;
+ uint8_t _pad0;
+ uint16_t _pad1;
+ uint32_t _pad2;
+ uint64_t _vaddr;
+ } pio;
+ struct {
+ uint64_t gla;
+ } mmio;
+ struct {
+ } state;
+ };
+} __attribute__ ((__packed__));
+
+struct hax_module_version {
+ uint32_t compat_version;
+ uint32_t cur_version;
+} __attribute__ ((__packed__));
+
+/* This interface is support only after API version 2 */
+struct hax_qemu_version {
+ /* Current API version in QEMU */
+ uint32_t cur_version;
+ /* The minimum API version supported by QEMU */
+ uint32_t min_version;
+} __attribute__ ((__packed__));
+
+/* The mac specfic interface to qemu, mostly is ioctl related */
+struct hax_tunnel_info {
+ uint64_t va;
+ uint64_t io_va;
+ uint16_t size;
+ uint16_t pad[3];
+} __attribute__ ((__packed__));
+
+struct hax_alloc_ram_info {
+ uint32_t size;
+ uint32_t pad;
+ uint64_t va;
+} __attribute__ ((__packed__));
+#define HAX_RAM_INFO_ROM 0x01 /* Read-Only */
+#define HAX_RAM_INFO_INVALID 0x80 /* Unmapped, usually used for MMIO */
+struct hax_set_ram_info {
+ uint64_t pa_start;
+ uint32_t size;
+ uint8_t flags;
+ uint8_t pad[3];
+ uint64_t va;
+} __attribute__ ((__packed__));
+
+#define HAX_CAP_STATUS_WORKING 0x1
+#define HAX_CAP_STATUS_NOTWORKING 0x0
+#define HAX_CAP_WORKSTATUS_MASK 0x1
+
+#define HAX_CAP_FAILREASON_VT 0x1
+#define HAX_CAP_FAILREASON_NX 0x2
+
+#define HAX_CAP_MEMQUOTA 0x2
+#define HAX_CAP_UG 0x4
+
+struct hax_capabilityinfo {
+ /* bit 0: 1 - working
+ * 0 - not working, possibly because NT/NX disabled
+ * bit 1: 1 - memory limitation working
+ * 0 - no memory limitation
+ */
+ uint16_t wstatus;
+ /* valid when not working
+ * bit 0: VT not enabeld
+ * bit 1: NX not enabled*/
+ uint16_t winfo;
+ uint32_t pad;
+ uint64_t mem_quota;
+} __attribute__ ((__packed__));
+
+struct hax_fastmmio {
+ uint64_t gpa;
+ uint64_t value;
+ uint8_t size;
+ uint8_t direction;
+ uint16_t reg_index;
+ uint32_t pad0;
+ uint64_t _cr0;
+ uint64_t _cr2;
+ uint64_t _cr3;
+ uint64_t _cr4;
+} __attribute__ ((__packed__));
+#endif
diff --git a/target-i386/hax-mem.c b/target-i386/hax-mem.c
new file mode 100644
index 0000000..dc8c553
--- /dev/null
+++ b/target-i386/hax-mem.c
@@ -0,0 +1,271 @@
+/*
+ * HAX memory mapping operations
+ *
+ * Copyright (c) 2015-16 Intel Corporation
+ * Copyright 2016 Google, Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "exec/address-spaces.h"
+#include "exec/exec-all.h"
+
+#include "target-i386/hax-i386.h"
+#include "qemu/queue.h"
+
+#define DEBUG_HAX_MEM 0
+
+#define DPRINTF(fmt, ...) \
+ do { \
+ if (DEBUG_HAX_MEM) { \
+ fprintf(stdout, fmt, ## __VA_ARGS__); \
+ } \
+ } while (0)
+
+/**
+ * HAXMapping: describes a pending guest physical memory mapping
+ *
+ * @start_pa: a guest physical address marking the start of the region; must be
+ * page-aligned
+ * @size: a guest physical address marking the end of the region; must be
+ * page-aligned
+ * @host_va: the host virtual address of the start of the mapping
+ * @flags: mapping parameters e.g. HAX_RAM_INFO_ROM or HAX_RAM_INFO_INVALID
+ * @entry: additional fields for linking #HAXMapping instances together
+ */
+typedef struct HAXMapping {
+ uint64_t start_pa;
+ uint32_t size;
+ uint64_t host_va;
+ int flags;
+ QTAILQ_ENTRY(HAXMapping) entry;
+} HAXMapping;
+
+/*
+ * A doubly-linked list (actually a tail queue) of the pending page mappings
+ * for the ongoing memory transaction.
+ *
+ * It is used to optimize the number of page mapping updates done through the
+ * kernel module. For example, it's effective when a driver is digging an MMIO
+ * hole inside an existing memory mapping. It will get a deletion of the whole
+ * region, then the addition of the 2 remaining RAM areas around the hole and
+ * finally the memory transaction commit. During the commit, it will effectively
+ * send to the kernel only the removal of the pages from the MMIO hole after
+ * having computed locally the result of the deletion and additions.
+ */
+static QTAILQ_HEAD(HAXMappingListHead, HAXMapping) mappings =
+ QTAILQ_HEAD_INITIALIZER(mappings);
+
+/**
+ * hax_mapping_dump_list: dumps @mappings to stdout (for debugging)
+ */
+static void hax_mapping_dump_list(void)
+{
+ HAXMapping *entry;
+
+ DPRINTF("%s updates:\n", __func__);
+ QTAILQ_FOREACH(entry, &mappings, entry) {
+ DPRINTF("\t%c 0x%016" PRIx64 "->0x%016" PRIx64 " VA 0x%016" PRIx64
+ "%s\n", entry->flags & HAX_RAM_INFO_INVALID ? '-' : '+',
+ entry->start_pa, entry->start_pa + entry->size, entry->host_va,
+ entry->flags & HAX_RAM_INFO_ROM ? " ROM" : "");
+ }
+}
+
+static void hax_insert_mapping_before(HAXMapping *next, uint64_t start_pa,
+ uint32_t size, uint64_t host_va,
+ uint8_t flags)
+{
+ HAXMapping *entry;
+
+ entry = g_malloc0(sizeof(*entry));
+ entry->start_pa = start_pa;
+ entry->size = size;
+ entry->host_va = host_va;
+ entry->flags = flags;
+ if (!next) {
+ QTAILQ_INSERT_TAIL(&mappings, entry, entry);
+ } else {
+ QTAILQ_INSERT_BEFORE(next, entry, entry);
+ }
+}
+
+static bool hax_mapping_is_opposite(HAXMapping *entry, uint64_t host_va,
+ uint8_t flags)
+{
+ /* removed then added without change for the read-only flag */
+ bool nop_flags = (entry->flags ^ flags) == HAX_RAM_INFO_INVALID;
+
+ return (entry->host_va == host_va) && nop_flags;
+}
+
+static void hax_update_mapping(uint64_t start_pa, uint32_t size,
+ uint64_t host_va, uint8_t flags)
+{
+ uint64_t end_pa = start_pa + size;
+ uint32_t chunk_sz;
+ HAXMapping *entry, *next;
+
+ QTAILQ_FOREACH_SAFE(entry, &mappings, entry, next) {
+ if (start_pa >= entry->start_pa + entry->size) {
+ continue;
+ }
+ if (start_pa < entry->start_pa) {
+ chunk_sz = end_pa <= entry->start_pa ? size
+ : entry->start_pa - start_pa;
+ hax_insert_mapping_before(entry, start_pa, chunk_sz,
+ host_va, flags);
+ start_pa += chunk_sz;
+ host_va += chunk_sz;
+ size -= chunk_sz;
+ }
+ chunk_sz = MIN(size, entry->size);
+ if (chunk_sz) {
+ bool nop = hax_mapping_is_opposite(entry, host_va, flags);
+ bool partial = chunk_sz < entry->size;
+ if (partial) {
+ /* remove the beginning of the existing chunk */
+ entry->start_pa += chunk_sz;
+ entry->host_va += chunk_sz;
+ entry->size -= chunk_sz;
+ if (!nop) {
+ hax_insert_mapping_before(entry, start_pa, chunk_sz,
+ host_va, flags);
+ }
+ } else { /* affects the full mapping entry */
+ if (nop) { /* no change to this mapping, remove it */
+ QTAILQ_REMOVE(&mappings, entry, entry);
+ g_free(entry);
+ } else { /* update mapping properties */
+ entry->host_va = host_va;
+ entry->flags = flags;
+ }
+ }
+ start_pa += chunk_sz;
+ host_va += chunk_sz;
+ size -= chunk_sz;
+ }
+ if (!size) { /* we are done */
+ break;
+ }
+ }
+ if (size) { /* add the leftover */
+ hax_insert_mapping_before(NULL, start_pa, size, host_va, flags);
+ }
+}
+
+static void hax_process_section(MemoryRegionSection *section, uint8_t flags)
+{
+ MemoryRegion *mr = section->mr;
+ hwaddr start_pa = section->offset_within_address_space;
+ ram_addr_t size = int128_get64(section->size);
+ unsigned int delta;
+ uint64_t host_va;
+
+ /* We only care about RAM pages */
+ if (!memory_region_is_ram(mr)) {
+ return;
+ }
+
+ /* Adjust start_pa and size so that they are page-aligned. (Cf
+ * kvm_set_phys_mem() in kvm-all.c).
+ */
+ delta = qemu_real_host_page_size - (start_pa & ~qemu_real_host_page_mask);
+ delta &= ~qemu_real_host_page_mask;
+ if (delta > size) {
+ return;
+ }
+ start_pa += delta;
+ size -= delta;
+ size &= qemu_real_host_page_mask;
+ if (!size || (start_pa & ~qemu_real_host_page_mask)) {
+ return;
+ }
+
+ host_va = (uintptr_t)memory_region_get_ram_ptr(mr)
+ + section->offset_within_region + delta;
+ if (memory_region_is_rom(section->mr)) {
+ flags |= HAX_RAM_INFO_ROM;
+ }
+
+ /* the kernel module interface uses 32-bit sizes (but we could split...) */
+ g_assert(size <= UINT32_MAX);
+
+ hax_update_mapping(start_pa, size, host_va, flags);
+}
+
+static void hax_region_add(MemoryListener *listener,
+ MemoryRegionSection *section)
+{
+ memory_region_ref(section->mr);
+ hax_process_section(section, 0);
+}
+
+static void hax_region_del(MemoryListener *listener,
+ MemoryRegionSection *section)
+{
+ hax_process_section(section, HAX_RAM_INFO_INVALID);
+ memory_region_unref(section->mr);
+}
+
+static void hax_transaction_begin(MemoryListener *listener)
+{
+ g_assert(QTAILQ_EMPTY(&mappings));
+}
+
+static void hax_transaction_commit(MemoryListener *listener)
+{
+ if (!QTAILQ_EMPTY(&mappings)) {
+ HAXMapping *entry, *next;
+
+ if (DEBUG_HAX_MEM) {
+ hax_mapping_dump_list();
+ }
+ QTAILQ_FOREACH_SAFE(entry, &mappings, entry, next) {
+ if (entry->flags & HAX_RAM_INFO_INVALID) {
+ /* for unmapping, put the values expected by the kernel */
+ entry->flags = HAX_RAM_INFO_INVALID;
+ entry->host_va = 0;
+ }
+ if (hax_set_ram(entry->start_pa, entry->size,
+ entry->host_va, entry->flags)) {
+ fprintf(stderr, "%s: Failed mapping @0x%016" PRIx64 "+0x%"
+ PRIx32 " flags %02x\n", __func__, entry->start_pa,
+ entry->size, entry->flags);
+ }
+ QTAILQ_REMOVE(&mappings, entry, entry);
+ g_free(entry);
+ }
+ }
+}
+
+/* currently we fake the dirty bitmap sync, always dirty */
+static void hax_log_sync(MemoryListener *listener,
+ MemoryRegionSection *section)
+{
+ MemoryRegion *mr = section->mr;
+
+ if (!memory_region_is_ram(mr)) {
+ /* Skip MMIO regions */
+ return;
+ }
+
+ memory_region_set_dirty(mr, 0, int128_get64(section->size));
+}
+
+static MemoryListener hax_memory_listener = {
+ .begin = hax_transaction_begin,
+ .commit = hax_transaction_commit,
+ .region_add = hax_region_add,
+ .region_del = hax_region_del,
+ .log_sync = hax_log_sync,
+ .priority = 10,
+};
+
+void hax_memory_region_init(void)
+{
+ memory_listener_register(&hax_memory_listener, &address_space_memory);
+}
diff --git a/target-i386/hax-windows.c b/target-i386/hax-windows.c
new file mode 100644
index 0000000..15a180b
--- /dev/null
+++ b/target-i386/hax-windows.c
@@ -0,0 +1,479 @@
+/*
+ * QEMU HAXM support
+ *
+ * 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.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "exec/exec-all.h"
+#include "hax-i386.h"
+
+/*
+ * return 0 when success, -1 when driver not loaded,
+ * other negative value for other failure
+ */
+static int hax_open_device(hax_fd *fd)
+{
+ uint32_t errNum = 0;
+ HANDLE hDevice;
+
+ if (!fd) {
+ return -2;
+ }
+
+ hDevice = CreateFile("\\\\.\\HAX",
+ GENERIC_READ | GENERIC_WRITE,
+ 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+
+ if (hDevice == INVALID_HANDLE_VALUE) {
+ fprintf(stderr, "Failed to open the HAX device!\n");
+ errNum = GetLastError();
+ if (errNum == ERROR_FILE_NOT_FOUND) {
+ return -1;
+ }
+ return -2;
+ }
+ *fd = hDevice;
+ return 0;
+}
+
+/* hax_fd hax_mod_open */
+ hax_fd hax_mod_open(void)
+{
+ int ret;
+ hax_fd fd = NULL;
+
+ ret = hax_open_device(&fd);
+ if (ret != 0) {
+ fprintf(stderr, "Open HAX device failed\n");
+ }
+
+ return fd;
+}
+
+int hax_populate_ram(uint64_t va, uint32_t size)
+{
+ int ret;
+ struct hax_alloc_ram_info info;
+ HANDLE hDeviceVM;
+ DWORD dSize = 0;
+
+ if (!hax_global.vm || !hax_global.vm->fd) {
+ fprintf(stderr, "Allocate memory before vm create?\n");
+ return -EINVAL;
+ }
+
+ info.size = size;
+ info.va = va;
+
+ hDeviceVM = hax_global.vm->fd;
+
+ ret = DeviceIoControl(hDeviceVM,
+ HAX_VM_IOCTL_ALLOC_RAM,
+ &info, sizeof(info), NULL, 0, &dSize,
+ (LPOVERLAPPED) NULL);
+
+ if (!ret) {
+ fprintf(stderr, "Failed to allocate %x memory\n", size);
+ return ret;
+ }
+
+ return 0;
+}
+
+int hax_set_ram(uint64_t start_pa, uint32_t size, uint64_t host_va, int flags)
+{
+ struct hax_set_ram_info info;
+ HANDLE hDeviceVM = hax_global.vm->fd;
+ DWORD dSize = 0;
+ int ret;
+
+ info.pa_start = start_pa;
+ info.size = size;
+ info.va = host_va;
+ info.flags = (uint8_t) flags;
+
+ ret = DeviceIoControl(hDeviceVM, HAX_VM_IOCTL_SET_RAM,
+ &info, sizeof(info), NULL, 0, &dSize,
+ (LPOVERLAPPED) NULL);
+
+ if (!ret) {
+ return -EFAULT;
+ } else {
+ return 0;
+ }
+}
+
+int hax_capability(struct hax_state *hax, struct hax_capabilityinfo *cap)
+{
+ int ret;
+ HANDLE hDevice = hax->fd; /* handle to hax module */
+ DWORD dSize = 0;
+ DWORD err = 0;
+
+ if (hax_invalid_fd(hDevice)) {
+ fprintf(stderr, "Invalid fd for hax device!\n");
+ return -ENODEV;
+ }
+
+ ret = DeviceIoControl(hDevice, HAX_IOCTL_CAPABILITY, NULL, 0, cap,
+ sizeof(*cap), &dSize, (LPOVERLAPPED) NULL);
+
+ if (!ret) {
+ err = GetLastError();
+ if (err == ERROR_INSUFFICIENT_BUFFER || err == ERROR_MORE_DATA) {
+ fprintf(stderr, "hax capability is too long to hold.\n");
+ }
+ fprintf(stderr, "Failed to get Hax capability:%luu\n", err);
+ return -EFAULT;
+ } else {
+ return 0;
+ }
+}
+
+int hax_mod_version(struct hax_state *hax, struct hax_module_version *version)
+{
+ int ret;
+ HANDLE hDevice = hax->fd; /* handle to hax module */
+ DWORD dSize = 0;
+ DWORD err = 0;
+
+ if (hax_invalid_fd(hDevice)) {
+ fprintf(stderr, "Invalid fd for hax device!\n");
+ return -ENODEV;
+ }
+
+ ret = DeviceIoControl(hDevice,
+ HAX_IOCTL_VERSION,
+ NULL, 0,
+ version, sizeof(*version), &dSize,
+ (LPOVERLAPPED) NULL);
+
+ if (!ret) {
+ err = GetLastError();
+ if (err == ERROR_INSUFFICIENT_BUFFER || err == ERROR_MORE_DATA) {
+ fprintf(stderr, "hax module verion is too long to hold.\n");
+ }
+ fprintf(stderr, "Failed to get Hax module version:%lu\n", err);
+ return -EFAULT;
+ } else {
+ return 0;
+ }
+}
+
+static char *hax_vm_devfs_string(int vm_id)
+{
+ char *name;
+
+ if (vm_id > MAX_VM_ID) {
+ fprintf(stderr, "Too big VM id\n");
+ return NULL;
+ }
+
+#define HAX_VM_DEVFS "\\\\.\\hax_vmxx"
+ name = g_strdup(HAX_VM_DEVFS);
+ if (!name) {
+ return NULL;
+ }
+
+ snprintf(name, sizeof HAX_VM_DEVFS, "\\\\.\\hax_vm%02d", vm_id);
+ return name;
+}
+
+static char *hax_vcpu_devfs_string(int vm_id, int vcpu_id)
+{
+ char *name;
+
+ if (vm_id > MAX_VM_ID || vcpu_id > MAX_VCPU_ID) {
+ fprintf(stderr, "Too big vm id %x or vcpu id %x\n", vm_id, vcpu_id);
+ return NULL;
+ }
+
+#define HAX_VCPU_DEVFS "\\\\.\\hax_vmxx_vcpuxx"
+ name = g_strdup(HAX_VCPU_DEVFS);
+ if (!name) {
+ return NULL;
+ }
+
+ snprintf(name, sizeof HAX_VCPU_DEVFS, "\\\\.\\hax_vm%02d_vcpu%02d",
+ vm_id, vcpu_id);
+ return name;
+}
+
+int hax_host_create_vm(struct hax_state *hax, int *vmid)
+{
+ int ret;
+ int vm_id = 0;
+ DWORD dSize = 0;
+
+ if (hax_invalid_fd(hax->fd)) {
+ return -EINVAL;
+ }
+
+ if (hax->vm) {
+ return 0;
+ }
+
+ ret = DeviceIoControl(hax->fd,
+ HAX_IOCTL_CREATE_VM,
+ NULL, 0, &vm_id, sizeof(vm_id), &dSize,
+ (LPOVERLAPPED) NULL);
+ if (!ret) {
+ fprintf(stderr, "Failed to create VM. Error code: %lu\n",
+ GetLastError());
+ return -1;
+ }
+ *vmid = vm_id;
+ return 0;
+}
+
+hax_fd hax_host_open_vm(struct hax_state *hax, int vm_id)
+{
+ char *vm_name = NULL;
+ hax_fd hDeviceVM;
+
+ vm_name = hax_vm_devfs_string(vm_id);
+ if (!vm_name) {
+ fprintf(stderr, "Failed to open VM. VM name is null\n");
+ return INVALID_HANDLE_VALUE;
+ }
+
+ hDeviceVM = CreateFile(vm_name,
+ GENERIC_READ | GENERIC_WRITE,
+ 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hDeviceVM == INVALID_HANDLE_VALUE) {
+ fprintf(stderr, "Open the vm device error:%s, ec:%lu\n",
+ vm_name, GetLastError());
+ }
+
+ g_free(vm_name);
+ return hDeviceVM;
+}
+
+int hax_notify_qemu_version(hax_fd vm_fd, struct hax_qemu_version *qversion)
+{
+ int ret;
+ DWORD dSize = 0;
+ if (hax_invalid_fd(vm_fd)) {
+ return -EINVAL;
+ }
+ ret = DeviceIoControl(vm_fd,
+ HAX_VM_IOCTL_NOTIFY_QEMU_VERSION,
+ qversion, sizeof(struct hax_qemu_version),
+ NULL, 0, &dSize, (LPOVERLAPPED) NULL);
+ if (!ret) {
+ fprintf(stderr, "Failed to notify qemu API version\n");
+ return -1;
+ }
+ return 0;
+}
+
+int hax_host_create_vcpu(hax_fd vm_fd, int vcpuid)
+{
+ int ret;
+ DWORD dSize = 0;
+
+ ret = DeviceIoControl(vm_fd,
+ HAX_VM_IOCTL_VCPU_CREATE,
+ &vcpuid, sizeof(vcpuid), NULL, 0, &dSize,
+ (LPOVERLAPPED) NULL);
+ if (!ret) {
+ fprintf(stderr, "Failed to create vcpu %x\n", vcpuid);
+ return -1;
+ }
+
+ return 0;
+}
+
+hax_fd hax_host_open_vcpu(int vmid, int vcpuid)
+{
+ char *devfs_path = NULL;
+ hax_fd hDeviceVCPU;
+
+ devfs_path = hax_vcpu_devfs_string(vmid, vcpuid);
+ if (!devfs_path) {
+ fprintf(stderr, "Failed to get the devfs\n");
+ return INVALID_HANDLE_VALUE;
+ }
+
+ hDeviceVCPU = CreateFile(devfs_path,
+ GENERIC_READ | GENERIC_WRITE,
+ 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
+ NULL);
+
+ if (hDeviceVCPU == INVALID_HANDLE_VALUE) {
+ fprintf(stderr, "Failed to open the vcpu devfs\n");
+ }
+ g_free(devfs_path);
+ return hDeviceVCPU;
+}
+
+int hax_host_setup_vcpu_channel(struct hax_vcpu_state *vcpu)
+{
+ hax_fd hDeviceVCPU = vcpu->fd;
+ int ret;
+ struct hax_tunnel_info info;
+ DWORD dSize = 0;
+
+ ret = DeviceIoControl(hDeviceVCPU,
+ HAX_VCPU_IOCTL_SETUP_TUNNEL,
+ NULL, 0, &info, sizeof(info), &dSize,
+ (LPOVERLAPPED) NULL);
+ if (!ret) {
+ fprintf(stderr, "Failed to setup the hax tunnel\n");
+ return -1;
+ }
+
+ if (!valid_hax_tunnel_size(info.size)) {
+ fprintf(stderr, "Invalid hax tunnel size %x\n", info.size);
+ ret = -EINVAL;
+ return ret;
+ }
+ vcpu->tunnel = (struct hax_tunnel *) (intptr_t) (info.va);
+ vcpu->iobuf = (unsigned char *) (intptr_t) (info.io_va);
+ return 0;
+}
+
+int hax_vcpu_run(struct hax_vcpu_state *vcpu)
+{
+ int ret;
+ HANDLE hDeviceVCPU = vcpu->fd;
+ DWORD dSize = 0;
+
+ ret = DeviceIoControl(hDeviceVCPU,
+ HAX_VCPU_IOCTL_RUN,
+ NULL, 0, NULL, 0, &dSize, (LPOVERLAPPED) NULL);
+ if (!ret) {
+ return -EFAULT;
+ } else {
+ return 0;
+ }
+}
+
+int hax_sync_fpu(CPUArchState *env, struct fx_layout *fl, int set)
+{
+ int ret;
+ hax_fd fd;
+ HANDLE hDeviceVCPU;
+ DWORD dSize = 0;
+
+ fd = hax_vcpu_get_fd(env);
+ if (hax_invalid_fd(fd)) {
+ return -1;
+ }
+
+ hDeviceVCPU = fd;
+
+ if (set) {
+ ret = DeviceIoControl(hDeviceVCPU,
+ HAX_VCPU_IOCTL_SET_FPU,
+ fl, sizeof(*fl), NULL, 0, &dSize,
+ (LPOVERLAPPED) NULL);
+ } else {
+ ret = DeviceIoControl(hDeviceVCPU,
+ HAX_VCPU_IOCTL_GET_FPU,
+ NULL, 0, fl, sizeof(*fl), &dSize,
+ (LPOVERLAPPED) NULL);
+ }
+ if (!ret) {
+ return -EFAULT;
+ } else {
+ return 0;
+ }
+}
+
+int hax_sync_msr(CPUArchState *env, struct hax_msr_data *msrs, int set)
+{
+ int ret;
+ hax_fd fd;
+ HANDLE hDeviceVCPU;
+ DWORD dSize = 0;
+
+ fd = hax_vcpu_get_fd(env);
+ if (hax_invalid_fd(fd)) {
+ return -1;
+ }
+ hDeviceVCPU = fd;
+
+ if (set) {
+ ret = DeviceIoControl(hDeviceVCPU,
+ HAX_VCPU_IOCTL_SET_MSRS,
+ msrs, sizeof(*msrs),
+ msrs, sizeof(*msrs), &dSize, (LPOVERLAPPED) NULL);
+ } else {
+ ret = DeviceIoControl(hDeviceVCPU,
+ HAX_VCPU_IOCTL_GET_MSRS,
+ msrs, sizeof(*msrs),
+ msrs, sizeof(*msrs), &dSize, (LPOVERLAPPED) NULL);
+ }
+ if (!ret) {
+ return -EFAULT;
+ } else {
+ return 0;
+ }
+}
+
+int hax_sync_vcpu_state(CPUArchState *env, struct vcpu_state_t *state, int set)
+{
+ int ret;
+ hax_fd fd;
+ HANDLE hDeviceVCPU;
+ DWORD dSize;
+
+ fd = hax_vcpu_get_fd(env);
+ if (hax_invalid_fd(fd)) {
+ return -1;
+ }
+
+ hDeviceVCPU = fd;
+
+ if (set) {
+ ret = DeviceIoControl(hDeviceVCPU,
+ HAX_VCPU_SET_REGS,
+ state, sizeof(*state),
+ NULL, 0, &dSize, (LPOVERLAPPED) NULL);
+ } else {
+ ret = DeviceIoControl(hDeviceVCPU,
+ HAX_VCPU_GET_REGS,
+ NULL, 0,
+ state, sizeof(*state), &dSize,
+ (LPOVERLAPPED) NULL);
+ }
+ if (!ret) {
+ return -EFAULT;
+ } else {
+ return 0;
+ }
+}
+
+int hax_inject_interrupt(CPUArchState *env, int vector)
+{
+ int ret;
+ hax_fd fd;
+ HANDLE hDeviceVCPU;
+ DWORD dSize;
+
+ fd = hax_vcpu_get_fd(env);
+ if (hax_invalid_fd(fd)) {
+ return -1;
+ }
+
+ hDeviceVCPU = fd;
+
+ ret = DeviceIoControl(hDeviceVCPU,
+ HAX_VCPU_IOCTL_INTERRUPT,
+ &vector, sizeof(vector), NULL, 0, &dSize,
+ (LPOVERLAPPED) NULL);
+ if (!ret) {
+ return -EFAULT;
+ } else {
+ return 0;
+ }
+}
diff --git a/target-i386/hax-windows.h b/target-i386/hax-windows.h
new file mode 100644
index 0000000..1d8f68d
--- /dev/null
+++ b/target-i386/hax-windows.h
@@ -0,0 +1,89 @@
+/*
+ * QEMU HAXM support
+ *
+ * Copyright IBM, Corp. 2008
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * Copyright (c) 2011 Intel Corporation
+ * Written by:
+ * Jiang Yunhong<yunhong.jiang@intel.com>
+ * Xin Xiaohui<xiaohui.xin@intel.com>
+ * Zhang Xiantao<xiantao.zhang@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 TARGET_I386_HAX_WINDOWS_H
+#define TARGET_I386_HAX_WINDOWS_H
+
+#include <windows.h>
+#include <memory.h>
+#include <malloc.h>
+#include <winioctl.h>
+#include <string.h>
+#include <stdio.h>
+#include <windef.h>
+
+#define HAX_INVALID_FD INVALID_HANDLE_VALUE
+
+static inline void hax_mod_close(struct hax_state *hax)
+{
+ CloseHandle(hax->fd);
+}
+
+static inline void hax_close_fd(hax_fd fd)
+{
+ CloseHandle(fd);
+}
+
+static inline int hax_invalid_fd(hax_fd fd)
+{
+ return (fd == INVALID_HANDLE_VALUE);
+}
+
+#define HAX_DEVICE_TYPE 0x4000
+
+#define HAX_IOCTL_VERSION CTL_CODE(HAX_DEVICE_TYPE, 0x900, \
+ METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define HAX_IOCTL_CREATE_VM CTL_CODE(HAX_DEVICE_TYPE, 0x901, \
+ METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define HAX_IOCTL_CAPABILITY CTL_CODE(HAX_DEVICE_TYPE, 0x910, \
+ METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define HAX_VM_IOCTL_VCPU_CREATE CTL_CODE(HAX_DEVICE_TYPE, 0x902, \
+ METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define HAX_VM_IOCTL_ALLOC_RAM CTL_CODE(HAX_DEVICE_TYPE, 0x903, \
+ METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define HAX_VM_IOCTL_SET_RAM CTL_CODE(HAX_DEVICE_TYPE, 0x904, \
+ METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define HAX_VM_IOCTL_VCPU_DESTROY CTL_CODE(HAX_DEVICE_TYPE, 0x905, \
+ METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define HAX_VCPU_IOCTL_RUN CTL_CODE(HAX_DEVICE_TYPE, 0x906, \
+ METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define HAX_VCPU_IOCTL_SET_MSRS CTL_CODE(HAX_DEVICE_TYPE, 0x907, \
+ METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define HAX_VCPU_IOCTL_GET_MSRS CTL_CODE(HAX_DEVICE_TYPE, 0x908, \
+ METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define HAX_VCPU_IOCTL_SET_FPU CTL_CODE(HAX_DEVICE_TYPE, 0x909, \
+ METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define HAX_VCPU_IOCTL_GET_FPU CTL_CODE(HAX_DEVICE_TYPE, 0x90a, \
+ METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define HAX_VCPU_IOCTL_SETUP_TUNNEL CTL_CODE(HAX_DEVICE_TYPE, 0x90b, \
+ METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define HAX_VCPU_IOCTL_INTERRUPT CTL_CODE(HAX_DEVICE_TYPE, 0x90c, \
+ METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define HAX_VCPU_SET_REGS CTL_CODE(HAX_DEVICE_TYPE, 0x90d, \
+ METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define HAX_VCPU_GET_REGS CTL_CODE(HAX_DEVICE_TYPE, 0x90e, \
+ METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define HAX_VM_IOCTL_NOTIFY_QEMU_VERSION CTL_CODE(HAX_DEVICE_TYPE, 0x910, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+#endif /* TARGET_I386_HAX_WINDOWS_H */
--
2.8.0.rc3.226.g39d4020
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [Qemu-devel] [PATCH v4 3/4] Plumb the HAXM-based hardware acceleration support
2016-12-19 10:29 [Qemu-devel] [PATCH v4 0/4] Add HAX support Vincent Palatin
2016-12-19 10:29 ` [Qemu-devel] [PATCH v4 1/4] kvm: move cpu synchronization code Vincent Palatin
2016-12-19 10:29 ` [Qemu-devel] [PATCH v4 2/4] target-i386: Add Intel HAX files Vincent Palatin
@ 2016-12-19 10:29 ` Vincent Palatin
2016-12-19 10:29 ` [Qemu-devel] [PATCH v4 4/4] hax: add Darwin support Vincent Palatin
3 siblings, 0 replies; 6+ messages in thread
From: Vincent Palatin @ 2016-12-19 10:29 UTC (permalink / raw)
To: qemu-devel
Cc: Yu Ning, Stefan Weil, Paolo Bonzini, Michael S . Tsirkin,
Eduardo Habkost, Marcelo Tosatti, Vincent Palatin
Use the Intel HAX is kernel-based hardware acceleration module for
Windows (similar to KVM on Linux).
Based on the "target-i386: Add Intel HAX to android emulator" patch
from David Chou <david.j.chou@intel.com>
Signed-off-by: Vincent Palatin <vpalatin@chromium.org>
---
Makefile.target | 1 +
configure | 18 ++++++++++
cpus.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++-
exec.c | 16 +++++++++
hw/intc/apic_common.c | 3 +-
include/qom/cpu.h | 5 +++
include/sysemu/hw_accel.h | 9 +++++
qemu-options.hx | 11 ++++++
target-i386/Makefile.objs | 4 +++
vl.c | 15 ++++++--
10 files changed, 164 insertions(+), 5 deletions(-)
diff --git a/Makefile.target b/Makefile.target
index 7a5080e..dab81e7 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -96,6 +96,7 @@ obj-y += target-$(TARGET_BASE_ARCH)/
obj-y += disas.o
obj-y += tcg-runtime.o
obj-$(call notempty,$(TARGET_XML_FILES)) += gdbstub-xml.o
+obj-$(call lnot,$(CONFIG_HAX)) += hax-stub.o
obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o
obj-$(CONFIG_LIBDECNUMBER) += libdecnumber/decContext.o
diff --git a/configure b/configure
index 3770d7c..ba32bea 100755
--- a/configure
+++ b/configure
@@ -230,6 +230,7 @@ vhost_net="no"
vhost_scsi="no"
vhost_vsock="no"
kvm="no"
+hax="no"
colo="yes"
rdma=""
gprof="no"
@@ -563,6 +564,7 @@ CYGWIN*)
;;
MINGW32*)
mingw32="yes"
+ hax="yes"
audio_possible_drivers="dsound sdl"
if check_include dsound.h; then
audio_drv_list="dsound"
@@ -612,6 +614,7 @@ OpenBSD)
Darwin)
bsd="yes"
darwin="yes"
+ hax="yes"
LDFLAGS_SHARED="-bundle -undefined dynamic_lookup"
if [ "$cpu" = "x86_64" ] ; then
QEMU_CFLAGS="-arch x86_64 $QEMU_CFLAGS"
@@ -921,6 +924,10 @@ for opt do
;;
--enable-kvm) kvm="yes"
;;
+ --disable-hax) hax="no"
+ ;;
+ --enable-hax) hax="yes"
+ ;;
--disable-colo) colo="no"
;;
--enable-colo) colo="yes"
@@ -1373,6 +1380,7 @@ disabled with --disable-FEATURE, default is enabled if available:
fdt fdt device tree
bluez bluez stack connectivity
kvm KVM acceleration support
+ hax HAX acceleration support
colo COarse-grain LOck-stepping VM for Non-stop Service
rdma RDMA-based migration support
vde support for vde network
@@ -5051,6 +5059,7 @@ echo "ATTR/XATTR support $attr"
echo "Install blobs $blobs"
echo "KVM support $kvm"
echo "COLO support $colo"
+echo "HAX support $hax"
echo "RDMA support $rdma"
echo "TCG interpreter $tcg_interpreter"
echo "fdt support $fdt"
@@ -6035,6 +6044,15 @@ case "$target_name" in
fi
fi
esac
+if test "$hax" = "yes" ; then
+ if test "$target_softmmu" = "yes" ; then
+ case "$target_name" in
+ i386|x86_64)
+ echo "CONFIG_HAX=y" >> $config_target_mak
+ ;;
+ esac
+ fi
+fi
if test "$target_bigendian" = "yes" ; then
echo "TARGET_WORDS_BIGENDIAN=y" >> $config_target_mak
fi
diff --git a/cpus.c b/cpus.c
index fc78502..0e01791 100644
--- a/cpus.c
+++ b/cpus.c
@@ -35,6 +35,7 @@
#include "sysemu/dma.h"
#include "sysemu/hw_accel.h"
#include "sysemu/kvm.h"
+#include "sysemu/hax.h"
#include "qmp-commands.h"
#include "exec/exec-all.h"
@@ -1221,6 +1222,39 @@ static void *qemu_tcg_cpu_thread_fn(void *arg)
return NULL;
}
+static void *qemu_hax_cpu_thread_fn(void *arg)
+{
+ CPUState *cpu = arg;
+ int r;
+ qemu_thread_get_self(cpu->thread);
+ qemu_mutex_lock(&qemu_global_mutex);
+
+ cpu->thread_id = qemu_get_thread_id();
+ cpu->created = true;
+ cpu->halted = 0;
+ current_cpu = cpu;
+
+ hax_init_vcpu(cpu);
+ qemu_cond_signal(&qemu_cpu_cond);
+
+ while (1) {
+ if (cpu_can_run(cpu)) {
+ r = hax_smp_cpu_exec(cpu);
+ if (r == EXCP_DEBUG) {
+ cpu_handle_guest_debug(cpu);
+ }
+ }
+
+ while (cpu_thread_is_idle(cpu)) {
+ qemu_cond_wait(cpu->halt_cond, &qemu_global_mutex);
+ }
+
+ qemu_wait_io_event_common(cpu);
+ }
+ return NULL;
+}
+
+
static void qemu_cpu_kick_thread(CPUState *cpu)
{
#ifndef _WIN32
@@ -1236,7 +1270,33 @@ static void qemu_cpu_kick_thread(CPUState *cpu)
exit(1);
}
#else /* _WIN32 */
- abort();
+ if (!qemu_cpu_is_self(cpu)) {
+ CONTEXT context;
+
+ if (SuspendThread(cpu->hThread) == (DWORD)(-1)) {
+ fprintf(stderr, "qemu:%s: GetLastError:%lu\n", __func__,
+ GetLastError());
+ exit(1);
+ }
+
+ /* On multi-core systems, we are not sure that the thread is actually
+ * suspended until we can get the context.
+ */
+ context.ContextFlags = CONTEXT_CONTROL;
+ while (GetThreadContext(cpu->hThread, &context) != 0) {
+ continue;
+ }
+
+ if (hax_enabled()) {
+ cpu->exit_request = 1;
+ }
+
+ if (ResumeThread(cpu->hThread) == (DWORD)(-1)) {
+ fprintf(stderr, "qemu:%s: GetLastError:%lu\n", __func__,
+ GetLastError());
+ exit(1);
+ }
+ }
#endif
}
@@ -1396,6 +1456,9 @@ static void qemu_tcg_init_vcpu(CPUState *cpu)
static QemuCond *tcg_halt_cond;
static QemuThread *tcg_cpu_thread;
+ if (hax_enabled())
+ hax_init_vcpu(cpu);
+
/* share a single thread for all cpus with TCG */
if (!tcg_cpu_thread) {
cpu->thread = g_malloc0(sizeof(QemuThread));
@@ -1419,6 +1482,26 @@ static void qemu_tcg_init_vcpu(CPUState *cpu)
}
}
+static void qemu_hax_start_vcpu(CPUState *cpu)
+{
+ char thread_name[VCPU_THREAD_NAME_SIZE];
+
+ 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/HAX",
+ cpu->cpu_index);
+ qemu_thread_create(cpu->thread, thread_name, qemu_hax_cpu_thread_fn,
+ cpu, QEMU_THREAD_JOINABLE);
+#ifdef _WIN32
+ cpu->hThread = qemu_thread_get_handle(cpu->thread);
+#endif
+ while (!cpu->created) {
+ qemu_cond_wait(&qemu_cpu_cond, &qemu_global_mutex);
+ }
+}
+
static void qemu_kvm_start_vcpu(CPUState *cpu)
{
char thread_name[VCPU_THREAD_NAME_SIZE];
@@ -1469,6 +1552,8 @@ void qemu_init_vcpu(CPUState *cpu)
if (kvm_enabled()) {
qemu_kvm_start_vcpu(cpu);
+ } else if (hax_enabled()) {
+ qemu_hax_start_vcpu(cpu);
} else if (tcg_enabled()) {
qemu_tcg_init_vcpu(cpu);
} else {
diff --git a/exec.c b/exec.c
index 08c558e..25e393d 100644
--- a/exec.c
+++ b/exec.c
@@ -31,6 +31,7 @@
#include "hw/xen/xen.h"
#endif
#include "sysemu/kvm.h"
+#include "sysemu/hax.h"
#include "sysemu/sysemu.h"
#include "qemu/timer.h"
#include "qemu/config-file.h"
@@ -1611,6 +1612,21 @@ static void ram_block_add(RAMBlock *new_block, Error **errp)
qemu_mutex_unlock_ramlist();
return;
}
+ /*
+ * In Hax, the qemu allocate the virtual address, and HAX kernel
+ * populate the memory with physical memory. Currently we have no
+ * paging, so user should make sure enough free memory in advance
+ */
+ if (hax_enabled()) {
+ int ret;
+ ret = hax_populate_ram((uint64_t)(uintptr_t)new_block->host,
+ new_block->max_length);
+ if (ret < 0) {
+ error_setg(errp, "Hax failed to populate ram");
+ return;
+ }
+ }
+
memory_try_enable_merging(new_block->host, new_block->max_length);
}
}
diff --git a/hw/intc/apic_common.c b/hw/intc/apic_common.c
index d78c885..3945dfd 100644
--- a/hw/intc/apic_common.c
+++ b/hw/intc/apic_common.c
@@ -26,6 +26,7 @@
#include "hw/i386/apic.h"
#include "hw/i386/apic_internal.h"
#include "trace.h"
+#include "sysemu/hax.h"
#include "sysemu/kvm.h"
#include "hw/qdev.h"
#include "hw/sysbus.h"
@@ -316,7 +317,7 @@ static void apic_common_realize(DeviceState *dev, Error **errp)
/* Note: We need at least 1M to map the VAPIC option ROM */
if (!vapic && s->vapic_control & VAPIC_ENABLE_MASK &&
- ram_size >= 1024 * 1024) {
+ !hax_enabled() && ram_size >= 1024 * 1024) {
vapic = sysbus_create_simple("kvmvapic", -1, NULL);
}
s->vapic = vapic;
diff --git a/include/qom/cpu.h b/include/qom/cpu.h
index 3f79a8e..ca4d0fb 100644
--- a/include/qom/cpu.h
+++ b/include/qom/cpu.h
@@ -227,6 +227,8 @@ struct CPUWatchpoint {
struct KVMState;
struct kvm_run;
+struct hax_vcpu_state;
+
#define TB_JMP_CACHE_BITS 12
#define TB_JMP_CACHE_SIZE (1 << TB_JMP_CACHE_BITS)
@@ -392,6 +394,9 @@ struct CPUState {
(absolute value) offset as small as possible. This reduces code
size, especially for hosts without large memory offsets. */
uint32_t tcg_exit_req;
+
+ bool hax_vcpu_dirty;
+ struct hax_vcpu_state *hax_vcpu;
};
QTAILQ_HEAD(CPUTailQ, CPUState);
diff --git a/include/sysemu/hw_accel.h b/include/sysemu/hw_accel.h
index 03812cf..c9b3105 100644
--- a/include/sysemu/hw_accel.h
+++ b/include/sysemu/hw_accel.h
@@ -20,6 +20,9 @@ static inline void cpu_synchronize_state(CPUState *cpu)
if (kvm_enabled()) {
kvm_cpu_synchronize_state(cpu);
}
+ if (hax_enabled()) {
+ hax_cpu_synchronize_state(cpu);
+ }
}
static inline void cpu_synchronize_post_reset(CPUState *cpu)
@@ -27,6 +30,9 @@ static inline void cpu_synchronize_post_reset(CPUState *cpu)
if (kvm_enabled()) {
kvm_cpu_synchronize_post_reset(cpu);
}
+ if (hax_enabled()) {
+ hax_cpu_synchronize_post_reset(cpu);
+ }
}
static inline void cpu_synchronize_post_init(CPUState *cpu)
@@ -34,6 +40,9 @@ static inline void cpu_synchronize_post_init(CPUState *cpu)
if (kvm_enabled()) {
kvm_cpu_synchronize_post_init(cpu);
}
+ if (hax_enabled()) {
+ hax_cpu_synchronize_post_init(cpu);
+ }
}
#endif /* QEMU_HW_ACCEL_H */
diff --git a/qemu-options.hx b/qemu-options.hx
index c534a2f..7fc2807 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -3280,6 +3280,17 @@ Enable KVM full virtualization support. This option is only available
if KVM support is enabled when compiling.
ETEXI
+DEF("enable-hax", 0, QEMU_OPTION_enable_hax, \
+ "-enable-hax enable HAX virtualization support\n", QEMU_ARCH_I386)
+STEXI
+@item -enable-hax
+@findex -enable-hax
+Enable HAX (Hardware-based Acceleration eXecution) support. This option
+is only available if HAX support is enabled when compiling. HAX is only
+applicable to MAC and Windows platform, and thus does not conflict with
+KVM.
+ETEXI
+
DEF("xen-domid", HAS_ARG, QEMU_OPTION_xen_domid,
"-xen-domid id specify xen guest domain id\n", QEMU_ARCH_ALL)
DEF("xen-create", 0, QEMU_OPTION_xen_create,
diff --git a/target-i386/Makefile.objs b/target-i386/Makefile.objs
index b223d79..acbe7b0 100644
--- a/target-i386/Makefile.objs
+++ b/target-i386/Makefile.objs
@@ -5,3 +5,7 @@ obj-y += gdbstub.o
obj-$(CONFIG_SOFTMMU) += machine.o arch_memory_mapping.o arch_dump.o monitor.o
obj-$(CONFIG_KVM) += kvm.o hyperv.o
obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o
+# HAX support
+ifdef CONFIG_WIN32
+obj-$(CONFIG_HAX) += hax-all.o hax-mem.o hax-windows.o
+endif
diff --git a/vl.c b/vl.c
index d77dd86..47bbfe7 100644
--- a/vl.c
+++ b/vl.c
@@ -92,6 +92,7 @@ int main(int argc, char **argv)
#include "sysemu/cpus.h"
#include "migration/colo.h"
#include "sysemu/kvm.h"
+#include "sysemu/hax.h"
#include "qapi/qmp/qjson.h"
#include "qemu/option.h"
#include "qemu/config-file.h"
@@ -1959,7 +1960,7 @@ static void main_loop(void)
int64_t ti;
#endif
do {
- nonblocking = !kvm_enabled() && !xen_enabled() && last_io > 0;
+ nonblocking = tcg_enabled() && last_io > 0;
#ifdef CONFIG_PROFILER
ti = profile_getclock();
#endif
@@ -3724,6 +3725,10 @@ int main(int argc, char **argv, char **envp)
olist = qemu_find_opts("machine");
qemu_opts_parse_noisily(olist, "accel=kvm", false);
break;
+ case QEMU_OPTION_enable_hax:
+ olist = qemu_find_opts("machine");
+ qemu_opts_parse_noisily(olist, "accel=hax", false);
+ break;
case QEMU_OPTION_M:
case QEMU_OPTION_machine:
olist = qemu_find_opts("machine");
@@ -4418,8 +4423,8 @@ int main(int argc, char **argv, char **envp)
cpu_ticks_init();
if (icount_opts) {
- if (kvm_enabled() || xen_enabled()) {
- error_report("-icount is not allowed with kvm or xen");
+ if (!tcg_enabled()) {
+ error_report("-icount is not allowed with hardware virtualization");
exit(1);
}
configure_icount(icount_opts, &error_abort);
@@ -4555,6 +4560,10 @@ int main(int argc, char **argv, char **envp)
numa_post_machine_init();
+ if (hax_enabled()) {
+ hax_sync_vcpus();
+ }
+
if (qemu_opts_foreach(qemu_find_opts("fw_cfg"),
parse_fw_cfg, fw_cfg_find(), NULL) != 0) {
exit(1);
--
2.8.0.rc3.226.g39d4020
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [Qemu-devel] [PATCH v4 4/4] hax: add Darwin support
2016-12-19 10:29 [Qemu-devel] [PATCH v4 0/4] Add HAX support Vincent Palatin
` (2 preceding siblings ...)
2016-12-19 10:29 ` [Qemu-devel] [PATCH v4 3/4] Plumb the HAXM-based hardware acceleration support Vincent Palatin
@ 2016-12-19 10:29 ` Vincent Palatin
3 siblings, 0 replies; 6+ messages in thread
From: Vincent Palatin @ 2016-12-19 10:29 UTC (permalink / raw)
To: qemu-devel
Cc: Yu Ning, Stefan Weil, Paolo Bonzini, Michael S . Tsirkin,
Eduardo Habkost, Marcelo Tosatti, Vincent Palatin
Re-add the MacOSX/Darwin support:
Use the Intel HAX is kernel-based hardware acceleration module
(similar to KVM on Linux).
Based on the original "target-i386: Add Intel HAX to android emulator" patch
from David Chou <david.j.chou@intel.com> from emu-2.2-release branch in
the external/qemu-android repository.
Signed-off-by: Vincent Palatin <vpalatin@chromium.org>
---
cpus.c | 5 +
target-i386/Makefile.objs | 3 +
target-i386/hax-darwin.c | 316 ++++++++++++++++++++++++++++++++++++++++++++++
target-i386/hax-darwin.h | 63 +++++++++
target-i386/hax-i386.h | 8 ++
5 files changed, 395 insertions(+)
create mode 100644 target-i386/hax-darwin.c
create mode 100644 target-i386/hax-darwin.h
diff --git a/cpus.c b/cpus.c
index 0e01791..b8db313 100644
--- a/cpus.c
+++ b/cpus.c
@@ -1264,6 +1264,11 @@ static void qemu_cpu_kick_thread(CPUState *cpu)
return;
}
cpu->thread_kicked = true;
+#ifdef CONFIG_DARWIN
+ if (hax_enabled()) {
+ cpu->exit_request = 1;
+ }
+#endif
err = pthread_kill(cpu->thread->thread, SIG_IPI);
if (err) {
fprintf(stderr, "qemu:%s: %s", __func__, strerror(err));
diff --git a/target-i386/Makefile.objs b/target-i386/Makefile.objs
index acbe7b0..4fcb7f3 100644
--- a/target-i386/Makefile.objs
+++ b/target-i386/Makefile.objs
@@ -9,3 +9,6 @@ obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o
ifdef CONFIG_WIN32
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
+endif
diff --git a/target-i386/hax-darwin.c b/target-i386/hax-darwin.c
new file mode 100644
index 0000000..240d8d3
--- /dev/null
+++ b/target-i386/hax-darwin.c
@@ -0,0 +1,316 @@
+/*
+ * QEMU HAXM support
+ *
+ * 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.
+ *
+ */
+
+/* HAX module interface - darwin version */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+
+#include "qemu/osdep.h"
+#include "target-i386/hax-i386.h"
+
+hax_fd hax_mod_open(void)
+{
+ int fd = open("/dev/HAX", O_RDWR);
+ if (fd == -1) {
+ fprintf(stderr, "Failed to open the hax module\n");
+ }
+
+ fcntl(fd, F_SETFD, FD_CLOEXEC);
+
+ return fd;
+}
+
+int hax_populate_ram(uint64_t va, uint32_t size)
+{
+ int ret;
+ struct hax_alloc_ram_info info;
+
+ if (!hax_global.vm || !hax_global.vm->fd) {
+ fprintf(stderr, "Allocate memory before vm create?\n");
+ return -EINVAL;
+ }
+
+ info.size = size;
+ info.va = va;
+ ret = ioctl(hax_global.vm->fd, HAX_VM_IOCTL_ALLOC_RAM, &info);
+ if (ret < 0) {
+ fprintf(stderr, "Failed to allocate %x memory\n", size);
+ return ret;
+ }
+ return 0;
+}
+
+int hax_set_ram(uint64_t start_pa, uint32_t size, uint64_t host_va, int flags)
+{
+ struct hax_set_ram_info info;
+ int ret;
+
+ info.pa_start = start_pa;
+ info.size = size;
+ info.va = host_va;
+ info.flags = (uint8_t) flags;
+
+ ret = ioctl(hax_global.vm->fd, HAX_VM_IOCTL_SET_RAM, &info);
+ if (ret < 0) {
+ return -errno;
+ }
+ return 0;
+}
+
+int hax_capability(struct hax_state *hax, struct hax_capabilityinfo *cap)
+{
+ int ret;
+
+ ret = ioctl(hax->fd, HAX_IOCTL_CAPABILITY, cap);
+ if (ret == -1) {
+ fprintf(stderr, "Failed to get HAX capability\n");
+ return -errno;
+ }
+
+ return 0;
+}
+
+int hax_mod_version(struct hax_state *hax, struct hax_module_version *version)
+{
+ int ret;
+
+ ret = ioctl(hax->fd, HAX_IOCTL_VERSION, version);
+ if (ret == -1) {
+ fprintf(stderr, "Failed to get HAX version\n");
+ return -errno;
+ }
+
+ return 0;
+}
+
+static char *hax_vm_devfs_string(int vm_id)
+{
+ char *name;
+
+ if (vm_id > MAX_VM_ID) {
+ fprintf(stderr, "Too big VM id\n");
+ return NULL;
+ }
+
+#define HAX_VM_DEVFS "/dev/hax_vm/vmxx"
+ name = g_strdup(HAX_VM_DEVFS);
+ if (!name) {
+ return NULL;
+ }
+
+ snprintf(name, sizeof HAX_VM_DEVFS, "/dev/hax_vm/vm%02d", vm_id);
+ return name;
+}
+
+static char *hax_vcpu_devfs_string(int vm_id, int vcpu_id)
+{
+ char *name;
+
+ if (vm_id > MAX_VM_ID || vcpu_id > MAX_VCPU_ID) {
+ fprintf(stderr, "Too big vm id %x or vcpu id %x\n", vm_id, vcpu_id);
+ return NULL;
+ }
+
+#define HAX_VCPU_DEVFS "/dev/hax_vmxx/vcpuxx"
+ name = g_strdup(HAX_VCPU_DEVFS);
+ if (!name) {
+ return NULL;
+ }
+
+ snprintf(name, sizeof HAX_VCPU_DEVFS, "/dev/hax_vm%02d/vcpu%02d",
+ vm_id, vcpu_id);
+ return name;
+}
+
+int hax_host_create_vm(struct hax_state *hax, int *vmid)
+{
+ int ret;
+ int vm_id = 0;
+
+ if (hax_invalid_fd(hax->fd)) {
+ return -EINVAL;
+ }
+
+ if (hax->vm) {
+ return 0;
+ }
+
+ ret = ioctl(hax->fd, HAX_IOCTL_CREATE_VM, &vm_id);
+ *vmid = vm_id;
+ return ret;
+}
+
+hax_fd hax_host_open_vm(struct hax_state *hax, int vm_id)
+{
+ hax_fd fd;
+ char *vm_name = NULL;
+
+ vm_name = hax_vm_devfs_string(vm_id);
+ if (!vm_name) {
+ return -1;
+ }
+
+ fd = open(vm_name, O_RDWR);
+ g_free(vm_name);
+
+ fcntl(fd, F_SETFD, FD_CLOEXEC);
+
+ return fd;
+}
+
+int hax_notify_qemu_version(hax_fd vm_fd, struct hax_qemu_version *qversion)
+{
+ int ret;
+
+ if (hax_invalid_fd(vm_fd)) {
+ return -EINVAL;
+ }
+
+ ret = ioctl(vm_fd, HAX_VM_IOCTL_NOTIFY_QEMU_VERSION, qversion);
+
+ if (ret < 0) {
+ fprintf(stderr, "Failed to notify qemu API version\n");
+ return ret;
+ }
+ return 0;
+}
+
+/* Simply assume the size should be bigger than the hax_tunnel,
+ * since the hax_tunnel can be extended later with compatibility considered
+ */
+int hax_host_create_vcpu(hax_fd vm_fd, int vcpuid)
+{
+ int ret;
+
+ ret = ioctl(vm_fd, HAX_VM_IOCTL_VCPU_CREATE, &vcpuid);
+ if (ret < 0) {
+ fprintf(stderr, "Failed to create vcpu %x\n", vcpuid);
+ }
+
+ return ret;
+}
+
+hax_fd hax_host_open_vcpu(int vmid, int vcpuid)
+{
+ char *devfs_path = NULL;
+ hax_fd fd;
+
+ devfs_path = hax_vcpu_devfs_string(vmid, vcpuid);
+ if (!devfs_path) {
+ fprintf(stderr, "Failed to get the devfs\n");
+ return -EINVAL;
+ }
+
+ fd = open(devfs_path, O_RDWR);
+ g_free(devfs_path);
+ if (fd < 0) {
+ fprintf(stderr, "Failed to open the vcpu devfs\n");
+ }
+ fcntl(fd, F_SETFD, FD_CLOEXEC);
+ return fd;
+}
+
+int hax_host_setup_vcpu_channel(struct hax_vcpu_state *vcpu)
+{
+ int ret;
+ struct hax_tunnel_info info;
+
+ ret = ioctl(vcpu->fd, HAX_VCPU_IOCTL_SETUP_TUNNEL, &info);
+ if (ret) {
+ fprintf(stderr, "Failed to setup the hax tunnel\n");
+ return ret;
+ }
+
+ if (!valid_hax_tunnel_size(info.size)) {
+ fprintf(stderr, "Invalid hax tunnel size %x\n", info.size);
+ ret = -EINVAL;
+ return ret;
+ }
+
+ vcpu->tunnel = (struct hax_tunnel *) (intptr_t) (info.va);
+ vcpu->iobuf = (unsigned char *) (intptr_t) (info.io_va);
+ return 0;
+}
+
+int hax_vcpu_run(struct hax_vcpu_state *vcpu)
+{
+ int ret;
+
+ ret = ioctl(vcpu->fd, HAX_VCPU_IOCTL_RUN, NULL);
+ return ret;
+}
+
+int hax_sync_fpu(CPUArchState *env, struct fx_layout *fl, int set)
+{
+ int ret, fd;
+
+ fd = hax_vcpu_get_fd(env);
+ if (fd <= 0) {
+ return -1;
+ }
+
+ if (set) {
+ ret = ioctl(fd, HAX_VCPU_IOCTL_SET_FPU, fl);
+ } else {
+ ret = ioctl(fd, HAX_VCPU_IOCTL_GET_FPU, fl);
+ }
+ return ret;
+}
+
+int hax_sync_msr(CPUArchState *env, struct hax_msr_data *msrs, int set)
+{
+ int ret, fd;
+
+ fd = hax_vcpu_get_fd(env);
+ if (fd <= 0) {
+ return -1;
+ }
+ if (set) {
+ ret = ioctl(fd, HAX_VCPU_IOCTL_SET_MSRS, msrs);
+ } else {
+ ret = ioctl(fd, HAX_VCPU_IOCTL_GET_MSRS, msrs);
+ }
+ return ret;
+}
+
+int hax_sync_vcpu_state(CPUArchState *env, struct vcpu_state_t *state, int set)
+{
+ int ret, fd;
+
+ fd = hax_vcpu_get_fd(env);
+ if (fd <= 0) {
+ return -1;
+ }
+
+ if (set) {
+ ret = ioctl(fd, HAX_VCPU_SET_REGS, state);
+ } else {
+ ret = ioctl(fd, HAX_VCPU_GET_REGS, state);
+ }
+ return ret;
+}
+
+int hax_inject_interrupt(CPUArchState *env, int vector)
+{
+ int ret, fd;
+
+ fd = hax_vcpu_get_fd(env);
+ if (fd <= 0) {
+ return -1;
+ }
+
+ ret = ioctl(fd, HAX_VCPU_IOCTL_INTERRUPT, &vector);
+ return ret;
+}
diff --git a/target-i386/hax-darwin.h b/target-i386/hax-darwin.h
new file mode 100644
index 0000000..0c0968b
--- /dev/null
+++ b/target-i386/hax-darwin.h
@@ -0,0 +1,63 @@
+/*
+ * QEMU HAXM support
+ *
+ * Copyright (c) 2011 Intel Corporation
+ * Written by:
+ * Jiang Yunhong<yunhong.jiang@intel.com>
+ * Xin Xiaohui<xiaohui.xin@intel.com>
+ * Zhang Xiantao<xiantao.zhang@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 TARGET_I386_HAX_DARWIN_H
+#define TARGET_I386_HAX_DARWIN_H
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <stdarg.h>
+
+#define HAX_INVALID_FD (-1)
+static inline int hax_invalid_fd(hax_fd fd)
+{
+ return fd <= 0;
+}
+
+static inline void hax_mod_close(struct hax_state *hax)
+{
+ close(hax->fd);
+}
+
+static inline void hax_close_fd(hax_fd fd)
+{
+ close(fd);
+}
+
+/* HAX model level ioctl */
+#define HAX_IOCTL_VERSION _IOWR(0, 0x20, struct hax_module_version)
+#define HAX_IOCTL_CREATE_VM _IOWR(0, 0x21, uint32_t)
+#define HAX_IOCTL_DESTROY_VM _IOW(0, 0x22, uint32_t)
+#define HAX_IOCTL_CAPABILITY _IOR(0, 0x23, struct hax_capabilityinfo)
+
+#define HAX_VM_IOCTL_VCPU_CREATE _IOWR(0, 0x80, uint32_t)
+#define HAX_VM_IOCTL_ALLOC_RAM _IOWR(0, 0x81, struct hax_alloc_ram_info)
+#define HAX_VM_IOCTL_SET_RAM _IOWR(0, 0x82, struct hax_set_ram_info)
+#define HAX_VM_IOCTL_VCPU_DESTROY _IOW(0, 0x83, uint32_t)
+#define HAX_VM_IOCTL_NOTIFY_QEMU_VERSION _IOW(0, 0x84, struct hax_qemu_version)
+
+#define HAX_VCPU_IOCTL_RUN _IO(0, 0xc0)
+#define HAX_VCPU_IOCTL_SET_MSRS _IOWR(0, 0xc1, struct hax_msr_data)
+#define HAX_VCPU_IOCTL_GET_MSRS _IOWR(0, 0xc2, struct hax_msr_data)
+
+#define HAX_VCPU_IOCTL_SET_FPU _IOW(0, 0xc3, struct fx_layout)
+#define HAX_VCPU_IOCTL_GET_FPU _IOR(0, 0xc4, struct fx_layout)
+
+#define HAX_VCPU_IOCTL_SETUP_TUNNEL _IOWR(0, 0xc5, struct hax_tunnel_info)
+#define HAX_VCPU_IOCTL_INTERRUPT _IOWR(0, 0xc6, uint32_t)
+#define HAX_VCPU_SET_REGS _IOWR(0, 0xc7, struct vcpu_state_t)
+#define HAX_VCPU_GET_REGS _IOWR(0, 0xc8, struct vcpu_state_t)
+
+#endif /* TARGET_I386_HAX_DARWIN_H */
diff --git a/target-i386/hax-i386.h b/target-i386/hax-i386.h
index 3f50bd9..b6becbd 100644
--- a/target-i386/hax-i386.h
+++ b/target-i386/hax-i386.h
@@ -16,6 +16,10 @@
#include "cpu.h"
#include "sysemu/hax.h"
+#ifdef CONFIG_DARWIN
+typedef int hax_fd;
+#endif
+
#ifdef CONFIG_WIN32
typedef HANDLE hax_fd;
#endif
@@ -77,6 +81,10 @@ hax_fd hax_mod_open(void);
void hax_memory_region_init(void);
+#ifdef CONFIG_DARWIN
+#include "target-i386/hax-darwin.h"
+#endif
+
#ifdef CONFIG_WIN32
#include "target-i386/hax-windows.h"
#endif
--
2.8.0.rc3.226.g39d4020
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [Qemu-devel] [PATCH v4 2/4] target-i386: Add Intel HAX files
2016-12-19 10:29 ` [Qemu-devel] [PATCH v4 2/4] target-i386: Add Intel HAX files Vincent Palatin
@ 2016-12-19 14:09 ` Vincent Palatin
0 siblings, 0 replies; 6+ messages in thread
From: Vincent Palatin @ 2016-12-19 14:09 UTC (permalink / raw)
To: qemu-devel
Cc: Yu Ning, Stefan Weil, Paolo Bonzini, Michael S . Tsirkin,
Eduardo Habkost, Marcelo Tosatti, Vincent Palatin
On Mon, Dec 19, 2016 at 11:29 AM, Vincent Palatin <vpalatin@chromium.org> wrote:
> That's a forward port of the core HAX interface code from the
> emu-2.2-release branch in the external/qemu-android repository as used by
> the Android emulator.
>
> The original commit was "target-i386: Add Intel HAX to android emulator"
> saying:
> """
> Backport of 2b3098ff27bab079caab9b46b58546b5036f5c0c
> from studio-1.4-dev into emu-master-dev
>
> Intel HAX (harware acceleration) will enhance android emulator performance
> in Windows and Mac OS X in the systems powered by Intel processors with
> "Intel Hardware Accelerated Execution Manager" package installed when
> user runs android emulator with Intel target.
>
> Signed-off-by: David Chou <david.j.chou@intel.com>
> """
>
> It has been modified to build and run along with the current code base.
> The formatting has been fixed to go through scripts/checkpatch.pl,
> and the DPRINTF macros have been updated to get the instanciations checked by
> the compiler.
>
> The FPU registers saving/restoring has been updated to match the current
> QEMU registers layout.
>
> The implementation has been simplified by doing the following modifications:
> - removing the code for supporting the hardware without Unrestricted Guest (UG)
> mode (including all the code to fallback on TCG emulation).
> - not including the Darwin support (which is not yet debugged/tested).
> - simplifying the initialization by removing the leftovers from the Android
> specific code, then trimming down the remaining logic.
> - removing the unused MemoryListener callbacks.
>
> Signed-off-by: Vincent Palatin <vpalatin@chromium.org>
> ---
> hax-stub.c | 39 ++
> include/sysemu/hax.h | 56 +++
> target-i386/hax-all.c | 1138 +++++++++++++++++++++++++++++++++++++++++++
> target-i386/hax-i386.h | 86 ++++
> target-i386/hax-interface.h | 358 ++++++++++++++
> target-i386/hax-mem.c | 271 +++++++++++
> target-i386/hax-windows.c | 479 ++++++++++++++++++
> target-i386/hax-windows.h | 89 ++++
> 8 files changed, 2516 insertions(+)
> create mode 100644 hax-stub.c
> create mode 100644 include/sysemu/hax.h
> create mode 100644 target-i386/hax-all.c
> create mode 100644 target-i386/hax-i386.h
> create mode 100644 target-i386/hax-interface.h
> create mode 100644 target-i386/hax-mem.c
> create mode 100644 target-i386/hax-windows.c
> create mode 100644 target-i386/hax-windows.h
>
> diff --git a/hax-stub.c b/hax-stub.c
> new file mode 100644
> index 0000000..a532dba
> --- /dev/null
> +++ b/hax-stub.c
> @@ -0,0 +1,39 @@
> +/*
> + * QEMU HAXM support
> + *
> + * Copyright (c) 2015, Intel Corporation
> + *
> + * Copyright 2016 Google, Inc.
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, 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/hax.h"
> +
> +int hax_sync_vcpus(void)
> +{
> + return 0;
> +}
> +
> +int hax_populate_ram(uint64_t va, uint32_t size)
> +{
> + return -ENOSYS;
> +}
> +
> +int hax_init_vcpu(CPUState *cpu)
> +{
> + return -ENOSYS;
> +}
> +
> +int hax_smp_cpu_exec(CPUState *cpu)
> +{
> + return -ENOSYS;
> +}
> diff --git a/include/sysemu/hax.h b/include/sysemu/hax.h
> new file mode 100644
> index 0000000..51c8fd5
> --- /dev/null
> +++ b/include/sysemu/hax.h
> @@ -0,0 +1,56 @@
> +/*
> + * QEMU HAXM support
> + *
> + * Copyright IBM, Corp. 2008
> + *
> + * Authors:
> + * Anthony Liguori <aliguori@us.ibm.com>
> + *
> + * Copyright (c) 2011 Intel Corporation
> + * Written by:
> + * Jiang Yunhong<yunhong.jiang@intel.com>
> + * Xin Xiaohui<xiaohui.xin@intel.com>
> + * Zhang Xiantao<xiantao.zhang@intel.com>
> + *
> + * Copyright 2016 Google, Inc.
> + *
> + * 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 QEMU_HAX_H
> +#define QEMU_HAX_H
> +
> +#include "config-host.h"
> +#include "qemu-common.h"
> +
> +int hax_sync_vcpus(void);
> +int hax_init_vcpu(CPUState *cpu);
> +int hax_smp_cpu_exec(CPUState *cpu);
> +int hax_populate_ram(uint64_t va, uint32_t size);
> +
> +void hax_cpu_synchronize_state(CPUState *cpu);
> +void hax_cpu_synchronize_post_reset(CPUState *cpu);
> +void hax_cpu_synchronize_post_init(CPUState *cpu);
> +
> +#ifdef CONFIG_HAX
> +
> +int hax_enabled(void);
> +
> +#include "hw/hw.h"
> +#include "qemu/bitops.h"
> +#include "exec/memory.h"
> +int hax_vcpu_destroy(CPUState *cpu);
> +void hax_raise_event(CPUState *cpu);
> +void hax_reset_vcpu_state(void *opaque);
> +#include "target-i386/hax-interface.h"
> +#include "target-i386/hax-i386.h"
> +
> +#else /* CONFIG_HAX */
> +
> +#define hax_enabled() (0)
> +
> +#endif /* CONFIG_HAX */
> +
> +#endif /* QEMU_HAX_H */
> diff --git a/target-i386/hax-all.c b/target-i386/hax-all.c
> new file mode 100644
> index 0000000..1f0ef7c
> --- /dev/null
> +++ b/target-i386/hax-all.c
> @@ -0,0 +1,1138 @@
> +/*
> + * QEMU HAX support
> + *
> + * Copyright IBM, Corp. 2008
> + * Red Hat, Inc. 2008
> + *
> + * Authors:
> + * Anthony Liguori <aliguori@us.ibm.com>
> + * Glauber Costa <gcosta@redhat.com>
> + *
> + * Copyright (c) 2011 Intel Corporation
> + * Written by:
> + * Jiang Yunhong<yunhong.jiang@intel.com>
> + * Xin Xiaohui<xiaohui.xin@intel.com>
> + * Zhang Xiantao<xiantao.zhang@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.
> + *
> + */
> +
> +/*
> + * HAX common code for both windows and darwin
> + */
> +
> +#include "qemu/osdep.h"
> +#include "cpu.h"
> +#include "exec/address-spaces.h"
> +#include "exec/exec-all.h"
> +#include "exec/ioport.h"
> +
> +#include "qemu-common.h"
> +#include "strings.h"
> +#include "hax-i386.h"
> +#include "sysemu/accel.h"
> +#include "sysemu/sysemu.h"
> +#include "qemu/main-loop.h"
> +#include "hw/boards.h"
> +
> +#define DEBUG_HAX 0
> +
> +#define DPRINTF(fmt, ...) \
> + do { \
> + if (DEBUG_HAX) { \
> + fprintf(stdout, fmt, ## __VA_ARGS__); \
> + } \
> + } while (0)
> +
> +/* Current version */
> +const uint32_t hax_cur_version = 0x3; /* ver 2.0: support fast mmio */
> +/* Minimum HAX kernel version */
> +const uint32_t hax_min_version = 0x4; /* ver 4.0 supports unmapping */
> +
> +static bool hax_allowed;
> +
> +struct hax_state hax_global;
> +
> +static void hax_vcpu_sync_state(CPUArchState *env, int modified);
> +static int hax_arch_get_registers(CPUArchState *env);
> +
> +int hax_enabled(void)
> +{
> + return hax_allowed;
> +}
> +
> +int valid_hax_tunnel_size(uint16_t size)
> +{
> + return size >= sizeof(struct hax_tunnel);
> +}
> +
> +hax_fd hax_vcpu_get_fd(CPUArchState *env)
> +{
> + struct hax_vcpu_state *vcpu = ENV_GET_CPU(env)->hax_vcpu;
> + if (!vcpu) {
> + return HAX_INVALID_FD;
> + }
> + return vcpu->fd;
> +}
> +
> +static int hax_get_capability(struct hax_state *hax)
> +{
> + int ret;
> + struct hax_capabilityinfo capinfo, *cap = &capinfo;
> +
> + ret = hax_capability(hax, cap);
> + if (ret) {
> + return ret;
> + }
> +
> + if ((cap->wstatus & HAX_CAP_WORKSTATUS_MASK) == HAX_CAP_STATUS_NOTWORKING) {
> + if (cap->winfo & HAX_CAP_FAILREASON_VT) {
> + DPRINTF
> + ("VTX feature is not enabled, HAX driver will not work.\n");
> + } else if (cap->winfo & HAX_CAP_FAILREASON_NX) {
> + DPRINTF
> + ("NX feature is not enabled, HAX driver will not work.\n");
> + }
> + return -ENXIO;
> +
> + }
> +
> + if (!(cap->winfo & HAX_CAP_UG)) {
> + fprintf(stderr, "UG mode is not supported by the hardware.\n");
> + return -ENOTSUP;
> + }
> +
> + if (cap->wstatus & HAX_CAP_MEMQUOTA) {
> + if (cap->mem_quota < hax->mem_quota) {
> + fprintf(stderr, "The VM memory needed exceeds the driver limit.\n");
> + return -ENOSPC;
> + }
> + }
> + return 0;
> +}
> +
> +static int hax_version_support(struct hax_state *hax)
> +{
> + int ret;
> + struct hax_module_version version;
> +
> + ret = hax_mod_version(hax, &version);
> + if (ret < 0) {
> + return 0;
> + }
> +
> + if ((hax_min_version > version.cur_version) ||
> + (hax_cur_version < version.compat_version)) {
> + return 0;
> + }
> +
> + return 1;
> +}
> +
> +int hax_vcpu_create(int id)
> +{
> + struct hax_vcpu_state *vcpu = NULL;
> + int ret;
> +
> + if (!hax_global.vm) {
> + fprintf(stderr, "vcpu %x created failed, vm is null\n", id);
> + return -1;
> + }
> +
> + if (hax_global.vm->vcpus[id]) {
> + fprintf(stderr, "vcpu %x allocated already\n", id);
> + return 0;
> + }
> +
> + vcpu = g_malloc(sizeof(struct hax_vcpu_state));
> + if (!vcpu) {
> + fprintf(stderr, "Failed to alloc vcpu state\n");
> + return -ENOMEM;
> + }
> +
> + memset(vcpu, 0, sizeof(struct hax_vcpu_state));
> +
> + ret = hax_host_create_vcpu(hax_global.vm->fd, id);
> + if (ret) {
> + fprintf(stderr, "Failed to create vcpu %x\n", id);
> + goto error;
> + }
> +
> + vcpu->vcpu_id = id;
> + vcpu->fd = hax_host_open_vcpu(hax_global.vm->id, id);
> + if (hax_invalid_fd(vcpu->fd)) {
> + fprintf(stderr, "Failed to open the vcpu\n");
> + ret = -ENODEV;
> + goto error;
> + }
> +
> + hax_global.vm->vcpus[id] = vcpu;
> +
> + ret = hax_host_setup_vcpu_channel(vcpu);
> + if (ret) {
> + fprintf(stderr, "Invalid hax tunnel size\n");
> + ret = -EINVAL;
> + goto error;
> + }
> + return 0;
> +
> + error:
> + /* vcpu and tunnel will be closed automatically */
> + if (vcpu && !hax_invalid_fd(vcpu->fd)) {
> + hax_close_fd(vcpu->fd);
> + }
> +
> + hax_global.vm->vcpus[id] = NULL;
> + g_free(vcpu);
> + return -1;
> +}
> +
> +int hax_vcpu_destroy(CPUState *cpu)
> +{
> + struct hax_vcpu_state *vcpu = cpu->hax_vcpu;
> +
> + if (!hax_global.vm) {
> + fprintf(stderr, "vcpu %x destroy failed, vm is null\n", vcpu->vcpu_id);
> + return -1;
> + }
> +
> + if (!vcpu) {
> + return 0;
> + }
> +
> + /*
> + * 1. The hax_tunnel is also destroied when vcpu destroy
> + * 2. close fd will cause hax module vcpu be cleaned
> + */
> + hax_close_fd(vcpu->fd);
> + hax_global.vm->vcpus[vcpu->vcpu_id] = NULL;
> + g_free(vcpu);
> + return 0;
> +}
> +
> +int hax_init_vcpu(CPUState *cpu)
> +{
> + int ret;
> +
> + ret = hax_vcpu_create(cpu->cpu_index);
> + if (ret < 0) {
> + fprintf(stderr, "Failed to create HAX vcpu\n");
> + exit(-1);
> + }
> +
> + cpu->hax_vcpu = hax_global.vm->vcpus[cpu->cpu_index];
> + cpu->hax_vcpu_dirty = true;
> + qemu_register_reset(hax_reset_vcpu_state, (CPUArchState *) (cpu->env_ptr));
> +
> + return ret;
> +}
> +
> +struct hax_vm *hax_vm_create(struct hax_state *hax)
> +{
> + struct hax_vm *vm;
> + int vm_id = 0, ret;
> +
> + if (hax_invalid_fd(hax->fd)) {
> + return NULL;
> + }
> +
> + if (hax->vm) {
> + return hax->vm;
> + }
> +
> + vm = g_malloc(sizeof(struct hax_vm));
> + if (!vm) {
> + return NULL;
> + }
> + memset(vm, 0, sizeof(struct hax_vm));
> + ret = hax_host_create_vm(hax, &vm_id);
> + if (ret) {
> + fprintf(stderr, "Failed to create vm %x\n", ret);
> + goto error;
> + }
> + vm->id = vm_id;
> + vm->fd = hax_host_open_vm(hax, vm_id);
> + if (hax_invalid_fd(vm->fd)) {
> + fprintf(stderr, "Failed to open vm %d\n", vm_id);
> + goto error;
> + }
> +
> + hax->vm = vm;
> + return vm;
> +
> + error:
> + g_free(vm);
> + hax->vm = NULL;
> + return NULL;
> +}
> +
> +int hax_vm_destroy(struct hax_vm *vm)
> +{
> + int i;
> +
> + for (i = 0; i < HAX_MAX_VCPU; i++)
> + if (vm->vcpus[i]) {
> + fprintf(stderr, "VCPU should be cleaned before vm clean\n");
> + return -1;
> + }
> + hax_close_fd(vm->fd);
> + g_free(vm);
> + hax_global.vm = NULL;
> + return 0;
> +}
> +
> +static void hax_handle_interrupt(CPUState *cpu, int mask)
> +{
> + cpu->interrupt_request |= mask;
> +
> + if (!qemu_cpu_is_self(cpu)) {
> + qemu_cpu_kick(cpu);
> + }
> +}
> +
> +static int hax_init(ram_addr_t ram_size)
> +{
> + struct hax_state *hax = NULL;
> + struct hax_qemu_version qversion;
> + int ret;
> +
> + hax = &hax_global;
> +
> + memset(hax, 0, sizeof(struct hax_state));
> + hax->mem_quota = ram_size;
> +
> + hax->fd = hax_mod_open();
> + if (hax_invalid_fd(hax->fd)) {
> + hax->fd = 0;
> + ret = -ENODEV;
> + goto error;
> + }
> +
> + ret = hax_get_capability(hax);
> +
> + if (ret) {
> + if (ret != -ENOSPC) {
> + ret = -EINVAL;
> + }
> + goto error;
> + }
> +
> + if (!hax_version_support(hax)) {
> + fprintf(stderr, "Incompat HAX version. QEMU current version %x ",
> + hax_cur_version);
> + fprintf(stderr, "requires minimum HAX version %x\n", hax_min_version);
> + ret = -EINVAL;
> + goto error;
> + }
> +
> + hax->vm = hax_vm_create(hax);
> + if (!hax->vm) {
> + fprintf(stderr, "Failed to create HAX VM\n");
> + ret = -EINVAL;
> + goto error;
> + }
> +
> + hax_memory_region_init();
> +
> + qversion.cur_version = hax_cur_version;
> + qversion.min_version = hax_min_version;
> + hax_notify_qemu_version(hax->vm->fd, &qversion);
> + cpu_interrupt_handler = hax_handle_interrupt;
> +
> + return ret;
> + error:
> + if (hax->vm) {
> + hax_vm_destroy(hax->vm);
> + }
> + if (hax->fd) {
> + hax_mod_close(hax);
> + }
> +
> + return ret;
> +}
> +
> +static int hax_accel_init(MachineState *ms)
> +{
> + int ret = hax_init(ms->ram_size);
> +
> + if (ret && (ret != -ENOSPC)) {
> + fprintf(stderr, "No accelerator found.\n");
> + } else {
> + fprintf(stdout, "HAX is %s and emulator runs in %s mode.\n",
> + !ret ? "working" : "not working",
> + !ret ? "fast virt" : "emulation");
> + }
> + return ret;
> +}
> +
> +static int hax_handle_fastmmio(CPUArchState *env, struct hax_fastmmio *hft)
> +{
> + cpu_physical_memory_rw(hft->gpa, (uint8_t *) &hft->value, hft->size,
> + hft->direction);
> +
I got gently notified that I forgot a hunk here to handle the new
MMIO-to-MMIO fastmmio request in the last HAX API,
I will send an updated v5 patch to fix my oversight.
> + return 0;
> +}
> +
> +static int hax_handle_io(CPUArchState *env, uint32_t df, uint16_t port,
> + int direction, int size, int count, void *buffer)
> +{
> + uint8_t *ptr;
> + int i;
> + MemTxAttrs attrs = { 0 };
> +
> + if (!df) {
> + ptr = (uint8_t *) buffer;
> + } else {
> + ptr = buffer + size * count - size;
> + }
> + for (i = 0; i < count; i++) {
> + address_space_rw(&address_space_io, port, attrs,
> + ptr, size, direction == HAX_EXIT_IO_OUT);
> + if (!df) {
> + ptr += size;
> + } else {
> + ptr -= size;
> + }
> + }
> +
> + return 0;
> +}
> +
> +static int hax_vcpu_interrupt(CPUArchState *env)
> +{
> + CPUState *cpu = ENV_GET_CPU(env);
> + struct hax_vcpu_state *vcpu = cpu->hax_vcpu;
> + struct hax_tunnel *ht = vcpu->tunnel;
> +
> + /*
> + * Try to inject an interrupt if the guest can accept it
> + * Unlike KVM, HAX kernel check for the eflags, instead of qemu
> + */
> + if (ht->ready_for_interrupt_injection &&
> + (cpu->interrupt_request & CPU_INTERRUPT_HARD)) {
> + int irq;
> +
> + irq = cpu_get_pic_interrupt(env);
> + if (irq >= 0) {
> + hax_inject_interrupt(env, irq);
> + cpu->interrupt_request &= ~CPU_INTERRUPT_HARD;
> + }
> + }
> +
> + /* If we have an interrupt but the guest is not ready to receive an
> + * interrupt, request an interrupt window exit. This will
> + * cause a return to userspace as soon as the guest is ready to
> + * receive interrupts. */
> + if ((cpu->interrupt_request & CPU_INTERRUPT_HARD)) {
> + ht->request_interrupt_window = 1;
> + } else {
> + ht->request_interrupt_window = 0;
> + }
> + return 0;
> +}
> +
> +void hax_raise_event(CPUState *cpu)
> +{
> + struct hax_vcpu_state *vcpu = cpu->hax_vcpu;
> +
> + if (!vcpu) {
> + return;
> + }
> + vcpu->tunnel->user_event_pending = 1;
> +}
> +
> +/*
> + * Ask hax kernel module to run the CPU for us till:
> + * 1. Guest crash or shutdown
> + * 2. Need QEMU's emulation like guest execute MMIO instruction
> + * 3. Guest execute HLT
> + * 4. QEMU have Signal/event pending
> + * 5. An unknown VMX exit happens
> + */
> +static int hax_vcpu_hax_exec(CPUArchState *env)
> +{
> + int ret = 0;
> + CPUState *cpu = ENV_GET_CPU(env);
> + X86CPU *x86_cpu = X86_CPU(cpu);
> + struct hax_vcpu_state *vcpu = cpu->hax_vcpu;
> + struct hax_tunnel *ht = vcpu->tunnel;
> +
> + if (!hax_enabled()) {
> + DPRINTF("Trying to vcpu execute at eip:" TARGET_FMT_lx "\n", env->eip);
> + return 0;
> + }
> +
> + cpu->halted = 0;
> +
> + if (cpu->interrupt_request & CPU_INTERRUPT_POLL) {
> + cpu->interrupt_request &= ~CPU_INTERRUPT_POLL;
> + apic_poll_irq(x86_cpu->apic_state);
> + }
> +
> + if (cpu->interrupt_request & CPU_INTERRUPT_INIT) {
> + DPRINTF("\nhax_vcpu_hax_exec: handling INIT for %d\n",
> + cpu->cpu_index);
> + do_cpu_init(x86_cpu);
> + hax_vcpu_sync_state(env, 1);
> + }
> +
> + if (cpu->interrupt_request & CPU_INTERRUPT_SIPI) {
> + DPRINTF("hax_vcpu_hax_exec: handling SIPI for %d\n",
> + cpu->cpu_index);
> + hax_vcpu_sync_state(env, 0);
> + do_cpu_sipi(x86_cpu);
> + hax_vcpu_sync_state(env, 1);
> + }
> +
> + do {
> + int hax_ret;
> +
> + if (cpu->exit_request) {
> + ret = 1;
> + break;
> + }
> +
> + hax_vcpu_interrupt(env);
> +
> + qemu_mutex_unlock_iothread();
> + hax_ret = hax_vcpu_run(vcpu);
> + qemu_mutex_lock_iothread();
> + current_cpu = cpu;
> +
> + /* Simply continue the vcpu_run if system call interrupted */
> + if (hax_ret == -EINTR || hax_ret == -EAGAIN) {
> + DPRINTF("io window interrupted\n");
> + continue;
> + }
> +
> + if (hax_ret < 0) {
> + fprintf(stderr, "vcpu run failed for vcpu %x\n", vcpu->vcpu_id);
> + abort();
> + }
> + switch (ht->_exit_status) {
> + case HAX_EXIT_IO:
> + ret = hax_handle_io(env, ht->pio._df, ht->pio._port,
> + ht->pio._direction,
> + ht->pio._size, ht->pio._count, vcpu->iobuf);
> + break;
> + case HAX_EXIT_FAST_MMIO:
> + ret = hax_handle_fastmmio(env, (struct hax_fastmmio *) vcpu->iobuf);
> + break;
> + /* Guest state changed, currently only for shutdown */
> + case HAX_EXIT_STATECHANGE:
> + fprintf(stdout, "VCPU shutdown request\n");
> + qemu_system_shutdown_request();
> + hax_vcpu_sync_state(env, 0);
> + ret = 1;
> + break;
> + case HAX_EXIT_UNKNOWN_VMEXIT:
> + fprintf(stderr, "Unknown VMX exit %x from guest\n",
> + ht->_exit_reason);
> + qemu_system_reset_request();
> + hax_vcpu_sync_state(env, 0);
> + cpu_dump_state(cpu, stderr, fprintf, 0);
> + ret = -1;
> + break;
> + case HAX_EXIT_HLT:
> + if (!(cpu->interrupt_request & CPU_INTERRUPT_HARD) &&
> + !(cpu->interrupt_request & CPU_INTERRUPT_NMI)) {
> + /* hlt instruction with interrupt disabled is shutdown */
> + env->eflags |= IF_MASK;
> + cpu->halted = 1;
> + cpu->exception_index = EXCP_HLT;
> + ret = 1;
> + }
> + break;
> + /* these situations will continue to hax module */
> + case HAX_EXIT_INTERRUPT:
> + case HAX_EXIT_PAUSED:
> + break;
> + case HAX_EXIT_MMIO:
> + /* Should not happen on UG system */
> + fprintf(stderr, "HAX: unsupported MMIO emulation\n");
> + ret = -1;
> + break;
> + case HAX_EXIT_REAL:
> + /* Should not happen on UG system */
> + fprintf(stderr, "HAX: unimplemented real mode emulation\n");
> + ret = -1;
> + break;
> + default:
> + fprintf(stderr, "Unknown exit %x from HAX\n", ht->_exit_status);
> + qemu_system_reset_request();
> + hax_vcpu_sync_state(env, 0);
> + cpu_dump_state(cpu, stderr, fprintf, 0);
> + ret = 1;
> + break;
> + }
> + } while (!ret);
> +
> + if (cpu->exit_request) {
> + cpu->exit_request = 0;
> + cpu->exception_index = EXCP_INTERRUPT;
> + }
> + return ret < 0;
> +}
> +
> +static void do_hax_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg)
> +{
> + CPUArchState *env = cpu->env_ptr;
> +
> + hax_arch_get_registers(env);
> + cpu->hax_vcpu_dirty = true;
> +}
> +
> +void hax_cpu_synchronize_state(CPUState *cpu)
> +{
> + if (!cpu->hax_vcpu_dirty) {
> + run_on_cpu(cpu, do_hax_cpu_synchronize_state, RUN_ON_CPU_NULL);
> + }
> +}
> +
> +static void do_hax_cpu_synchronize_post_reset(CPUState *cpu,
> + run_on_cpu_data arg)
> +{
> + CPUArchState *env = cpu->env_ptr;
> +
> + hax_vcpu_sync_state(env, 1);
> + cpu->hax_vcpu_dirty = false;
> +}
> +
> +void hax_cpu_synchronize_post_reset(CPUState *cpu)
> +{
> + run_on_cpu(cpu, do_hax_cpu_synchronize_post_reset, RUN_ON_CPU_NULL);
> +}
> +
> +static void do_hax_cpu_synchronize_post_init(CPUState *cpu, run_on_cpu_data arg)
> +{
> + CPUArchState *env = cpu->env_ptr;
> +
> + hax_vcpu_sync_state(env, 1);
> + cpu->hax_vcpu_dirty = false;
> +}
> +
> +void hax_cpu_synchronize_post_init(CPUState *cpu)
> +{
> + run_on_cpu(cpu, do_hax_cpu_synchronize_post_init, RUN_ON_CPU_NULL);
> +}
> +
> +int hax_smp_cpu_exec(CPUState *cpu)
> +{
> + CPUArchState *env = (CPUArchState *) (cpu->env_ptr);
> + int fatal;
> + int ret;
> +
> + while (1) {
> + if (cpu->exception_index >= EXCP_INTERRUPT) {
> + ret = cpu->exception_index;
> + cpu->exception_index = -1;
> + break;
> + }
> +
> + fatal = hax_vcpu_hax_exec(env);
> +
> + if (fatal) {
> + fprintf(stderr, "Unsupported HAX vcpu return\n");
> + abort();
> + }
> + }
> +
> + return ret;
> +}
> +
> +static void set_v8086_seg(struct segment_desc_t *lhs, const SegmentCache *rhs)
> +{
> + memset(lhs, 0, sizeof(struct segment_desc_t));
> + lhs->selector = rhs->selector;
> + lhs->base = rhs->base;
> + lhs->limit = rhs->limit;
> + lhs->type = 3;
> + lhs->present = 1;
> + lhs->dpl = 3;
> + lhs->operand_size = 0;
> + lhs->desc = 1;
> + lhs->long_mode = 0;
> + lhs->granularity = 0;
> + lhs->available = 0;
> +}
> +
> +static void get_seg(SegmentCache *lhs, const struct segment_desc_t *rhs)
> +{
> + lhs->selector = rhs->selector;
> + lhs->base = rhs->base;
> + lhs->limit = rhs->limit;
> + lhs->flags = (rhs->type << DESC_TYPE_SHIFT)
> + | (rhs->present * DESC_P_MASK)
> + | (rhs->dpl << DESC_DPL_SHIFT)
> + | (rhs->operand_size << DESC_B_SHIFT)
> + | (rhs->desc * DESC_S_MASK)
> + | (rhs->long_mode << DESC_L_SHIFT)
> + | (rhs->granularity * DESC_G_MASK) | (rhs->available * DESC_AVL_MASK);
> +}
> +
> +static void set_seg(struct segment_desc_t *lhs, const SegmentCache *rhs)
> +{
> + unsigned flags = rhs->flags;
> +
> + memset(lhs, 0, sizeof(struct segment_desc_t));
> + lhs->selector = rhs->selector;
> + lhs->base = rhs->base;
> + lhs->limit = rhs->limit;
> + lhs->type = (flags >> DESC_TYPE_SHIFT) & 15;
> + lhs->present = (flags & DESC_P_MASK) != 0;
> + lhs->dpl = rhs->selector & 3;
> + lhs->operand_size = (flags >> DESC_B_SHIFT) & 1;
> + lhs->desc = (flags & DESC_S_MASK) != 0;
> + lhs->long_mode = (flags >> DESC_L_SHIFT) & 1;
> + lhs->granularity = (flags & DESC_G_MASK) != 0;
> + lhs->available = (flags & DESC_AVL_MASK) != 0;
> +}
> +
> +static void hax_getput_reg(uint64_t *hax_reg, target_ulong *qemu_reg, int set)
> +{
> + target_ulong reg = *hax_reg;
> +
> + if (set) {
> + *hax_reg = *qemu_reg;
> + } else {
> + *qemu_reg = reg;
> + }
> +}
> +
> +/* The sregs has been synced with HAX kernel already before this call */
> +static int hax_get_segments(CPUArchState *env, struct vcpu_state_t *sregs)
> +{
> + get_seg(&env->segs[R_CS], &sregs->_cs);
> + get_seg(&env->segs[R_DS], &sregs->_ds);
> + get_seg(&env->segs[R_ES], &sregs->_es);
> + get_seg(&env->segs[R_FS], &sregs->_fs);
> + get_seg(&env->segs[R_GS], &sregs->_gs);
> + get_seg(&env->segs[R_SS], &sregs->_ss);
> +
> + get_seg(&env->tr, &sregs->_tr);
> + get_seg(&env->ldt, &sregs->_ldt);
> + env->idt.limit = sregs->_idt.limit;
> + env->idt.base = sregs->_idt.base;
> + env->gdt.limit = sregs->_gdt.limit;
> + env->gdt.base = sregs->_gdt.base;
> + return 0;
> +}
> +
> +static int hax_set_segments(CPUArchState *env, struct vcpu_state_t *sregs)
> +{
> + if ((env->eflags & VM_MASK)) {
> + set_v8086_seg(&sregs->_cs, &env->segs[R_CS]);
> + set_v8086_seg(&sregs->_ds, &env->segs[R_DS]);
> + set_v8086_seg(&sregs->_es, &env->segs[R_ES]);
> + set_v8086_seg(&sregs->_fs, &env->segs[R_FS]);
> + set_v8086_seg(&sregs->_gs, &env->segs[R_GS]);
> + set_v8086_seg(&sregs->_ss, &env->segs[R_SS]);
> + } else {
> + set_seg(&sregs->_cs, &env->segs[R_CS]);
> + set_seg(&sregs->_ds, &env->segs[R_DS]);
> + set_seg(&sregs->_es, &env->segs[R_ES]);
> + set_seg(&sregs->_fs, &env->segs[R_FS]);
> + set_seg(&sregs->_gs, &env->segs[R_GS]);
> + set_seg(&sregs->_ss, &env->segs[R_SS]);
> +
> + if (env->cr[0] & CR0_PE_MASK) {
> + /* force ss cpl to cs cpl */
> + sregs->_ss.selector = (sregs->_ss.selector & ~3) |
> + (sregs->_cs.selector & 3);
> + sregs->_ss.dpl = sregs->_ss.selector & 3;
> + }
> + }
> +
> + set_seg(&sregs->_tr, &env->tr);
> + set_seg(&sregs->_ldt, &env->ldt);
> + sregs->_idt.limit = env->idt.limit;
> + sregs->_idt.base = env->idt.base;
> + sregs->_gdt.limit = env->gdt.limit;
> + sregs->_gdt.base = env->gdt.base;
> + return 0;
> +}
> +
> +/*
> + * After get the state from the kernel module, some
> + * qemu emulator state need be updated also
> + */
> +static int hax_setup_qemu_emulator(CPUArchState *env)
> +{
> +
> +#define HFLAG_COPY_MASK (~( \
> + HF_CPL_MASK | HF_PE_MASK | HF_MP_MASK | HF_EM_MASK | \
> + HF_TS_MASK | HF_TF_MASK | HF_VM_MASK | HF_IOPL_MASK | \
> + HF_OSFXSR_MASK | HF_LMA_MASK | HF_CS32_MASK | \
> + HF_SS32_MASK | HF_CS64_MASK | HF_ADDSEG_MASK))
> +
> + uint32_t hflags;
> +
> + hflags = (env->segs[R_CS].flags >> DESC_DPL_SHIFT) & HF_CPL_MASK;
> + hflags |= (env->cr[0] & CR0_PE_MASK) << (HF_PE_SHIFT - CR0_PE_SHIFT);
> + hflags |= (env->cr[0] << (HF_MP_SHIFT - CR0_MP_SHIFT)) &
> + (HF_MP_MASK | HF_EM_MASK | HF_TS_MASK);
> + hflags |= (env->eflags & (HF_TF_MASK | HF_VM_MASK | HF_IOPL_MASK));
> + hflags |= (env->cr[4] & CR4_OSFXSR_MASK) <<
> + (HF_OSFXSR_SHIFT - CR4_OSFXSR_SHIFT);
> +
> + if (env->efer & MSR_EFER_LMA) {
> + hflags |= HF_LMA_MASK;
> + }
> +
> + if ((hflags & HF_LMA_MASK) && (env->segs[R_CS].flags & DESC_L_MASK)) {
> + hflags |= HF_CS32_MASK | HF_SS32_MASK | HF_CS64_MASK;
> + } else {
> + hflags |= (env->segs[R_CS].flags & DESC_B_MASK) >>
> + (DESC_B_SHIFT - HF_CS32_SHIFT);
> + hflags |= (env->segs[R_SS].flags & DESC_B_MASK) >>
> + (DESC_B_SHIFT - HF_SS32_SHIFT);
> + if (!(env->cr[0] & CR0_PE_MASK) ||
> + (env->eflags & VM_MASK) || !(hflags & HF_CS32_MASK)) {
> + hflags |= HF_ADDSEG_MASK;
> + } else {
> + hflags |= ((env->segs[R_DS].base |
> + env->segs[R_ES].base |
> + env->segs[R_SS].base) != 0) << HF_ADDSEG_SHIFT;
> + }
> + }
> +
> + hflags &= ~HF_SMM_MASK;
> +
> + env->hflags = (env->hflags & HFLAG_COPY_MASK) | hflags;
> + return 0;
> +}
> +
> +static int hax_sync_vcpu_register(CPUArchState *env, int set)
> +{
> + struct vcpu_state_t regs;
> + int ret;
> + memset(®s, 0, sizeof(struct vcpu_state_t));
> +
> + if (!set) {
> + ret = hax_sync_vcpu_state(env, ®s, 0);
> + if (ret < 0) {
> + return -1;
> + }
> + }
> +
> + /* generic register */
> + hax_getput_reg(®s._rax, &env->regs[R_EAX], set);
> + hax_getput_reg(®s._rbx, &env->regs[R_EBX], set);
> + hax_getput_reg(®s._rcx, &env->regs[R_ECX], set);
> + hax_getput_reg(®s._rdx, &env->regs[R_EDX], set);
> + hax_getput_reg(®s._rsi, &env->regs[R_ESI], set);
> + hax_getput_reg(®s._rdi, &env->regs[R_EDI], set);
> + hax_getput_reg(®s._rsp, &env->regs[R_ESP], set);
> + hax_getput_reg(®s._rbp, &env->regs[R_EBP], set);
> +#ifdef TARGET_X86_64
> + hax_getput_reg(®s._r8, &env->regs[8], set);
> + hax_getput_reg(®s._r9, &env->regs[9], set);
> + hax_getput_reg(®s._r10, &env->regs[10], set);
> + hax_getput_reg(®s._r11, &env->regs[11], set);
> + hax_getput_reg(®s._r12, &env->regs[12], set);
> + hax_getput_reg(®s._r13, &env->regs[13], set);
> + hax_getput_reg(®s._r14, &env->regs[14], set);
> + hax_getput_reg(®s._r15, &env->regs[15], set);
> +#endif
> + hax_getput_reg(®s._rflags, &env->eflags, set);
> + hax_getput_reg(®s._rip, &env->eip, set);
> +
> + if (set) {
> + regs._cr0 = env->cr[0];
> + regs._cr2 = env->cr[2];
> + regs._cr3 = env->cr[3];
> + regs._cr4 = env->cr[4];
> + hax_set_segments(env, ®s);
> + } else {
> + env->cr[0] = regs._cr0;
> + env->cr[2] = regs._cr2;
> + env->cr[3] = regs._cr3;
> + env->cr[4] = regs._cr4;
> + hax_get_segments(env, ®s);
> + }
> +
> + if (set) {
> + ret = hax_sync_vcpu_state(env, ®s, 1);
> + if (ret < 0) {
> + return -1;
> + }
> + }
> + if (!set) {
> + hax_setup_qemu_emulator(env);
> + }
> + return 0;
> +}
> +
> +static void hax_msr_entry_set(struct vmx_msr *item, uint32_t index,
> + uint64_t value)
> +{
> + item->entry = index;
> + item->value = value;
> +}
> +
> +static int hax_get_msrs(CPUArchState *env)
> +{
> + struct hax_msr_data md;
> + struct vmx_msr *msrs = md.entries;
> + int ret, i, n;
> +
> + n = 0;
> + msrs[n++].entry = MSR_IA32_SYSENTER_CS;
> + msrs[n++].entry = MSR_IA32_SYSENTER_ESP;
> + msrs[n++].entry = MSR_IA32_SYSENTER_EIP;
> + msrs[n++].entry = MSR_IA32_TSC;
> +#ifdef TARGET_X86_64
> + msrs[n++].entry = MSR_EFER;
> + msrs[n++].entry = MSR_STAR;
> + msrs[n++].entry = MSR_LSTAR;
> + msrs[n++].entry = MSR_CSTAR;
> + msrs[n++].entry = MSR_FMASK;
> + msrs[n++].entry = MSR_KERNELGSBASE;
> +#endif
> + md.nr_msr = n;
> + ret = hax_sync_msr(env, &md, 0);
> + if (ret < 0) {
> + return ret;
> + }
> +
> + for (i = 0; i < md.done; i++) {
> + switch (msrs[i].entry) {
> + case MSR_IA32_SYSENTER_CS:
> + env->sysenter_cs = msrs[i].value;
> + break;
> + case MSR_IA32_SYSENTER_ESP:
> + env->sysenter_esp = msrs[i].value;
> + break;
> + case MSR_IA32_SYSENTER_EIP:
> + env->sysenter_eip = msrs[i].value;
> + break;
> + case MSR_IA32_TSC:
> + env->tsc = msrs[i].value;
> + break;
> +#ifdef TARGET_X86_64
> + case MSR_EFER:
> + env->efer = msrs[i].value;
> + break;
> + case MSR_STAR:
> + env->star = msrs[i].value;
> + break;
> + case MSR_LSTAR:
> + env->lstar = msrs[i].value;
> + break;
> + case MSR_CSTAR:
> + env->cstar = msrs[i].value;
> + break;
> + case MSR_FMASK:
> + env->fmask = msrs[i].value;
> + break;
> + case MSR_KERNELGSBASE:
> + env->kernelgsbase = msrs[i].value;
> + break;
> +#endif
> + }
> + }
> +
> + return 0;
> +}
> +
> +static int hax_set_msrs(CPUArchState *env)
> +{
> + struct hax_msr_data md;
> + struct vmx_msr *msrs;
> + msrs = md.entries;
> + int n = 0;
> +
> + memset(&md, 0, sizeof(struct hax_msr_data));
> + hax_msr_entry_set(&msrs[n++], MSR_IA32_SYSENTER_CS, env->sysenter_cs);
> + hax_msr_entry_set(&msrs[n++], MSR_IA32_SYSENTER_ESP, env->sysenter_esp);
> + hax_msr_entry_set(&msrs[n++], MSR_IA32_SYSENTER_EIP, env->sysenter_eip);
> + hax_msr_entry_set(&msrs[n++], MSR_IA32_TSC, env->tsc);
> +#ifdef TARGET_X86_64
> + hax_msr_entry_set(&msrs[n++], MSR_EFER, env->efer);
> + hax_msr_entry_set(&msrs[n++], MSR_STAR, env->star);
> + hax_msr_entry_set(&msrs[n++], MSR_LSTAR, env->lstar);
> + hax_msr_entry_set(&msrs[n++], MSR_CSTAR, env->cstar);
> + hax_msr_entry_set(&msrs[n++], MSR_FMASK, env->fmask);
> + hax_msr_entry_set(&msrs[n++], MSR_KERNELGSBASE, env->kernelgsbase);
> +#endif
> + md.nr_msr = n;
> + md.done = 0;
> +
> + return hax_sync_msr(env, &md, 1);
> +}
> +
> +static int hax_get_fpu(CPUArchState *env)
> +{
> + struct fx_layout fpu;
> + int i, ret;
> +
> + ret = hax_sync_fpu(env, &fpu, 0);
> + if (ret < 0) {
> + return ret;
> + }
> +
> + env->fpstt = (fpu.fsw >> 11) & 7;
> + env->fpus = fpu.fsw;
> + env->fpuc = fpu.fcw;
> + for (i = 0; i < 8; ++i) {
> + env->fptags[i] = !((fpu.ftw >> i) & 1);
> + }
> + memcpy(env->fpregs, fpu.st_mm, sizeof(env->fpregs));
> +
> + for (i = 0; i < 8; i++) {
> + env->xmm_regs[i].ZMM_Q(0) = ldq_p(&fpu.mmx_1[i][0]);
> + env->xmm_regs[i].ZMM_Q(1) = ldq_p(&fpu.mmx_1[i][8]);
> + if (CPU_NB_REGS > 8) {
> + env->xmm_regs[i + 8].ZMM_Q(0) = ldq_p(&fpu.mmx_2[i][0]);
> + env->xmm_regs[i + 8].ZMM_Q(1) = ldq_p(&fpu.mmx_2[i][8]);
> + }
> + }
> + env->mxcsr = fpu.mxcsr;
> +
> + return 0;
> +}
> +
> +static int hax_set_fpu(CPUArchState *env)
> +{
> + struct fx_layout fpu;
> + int i;
> +
> + memset(&fpu, 0, sizeof(fpu));
> + fpu.fsw = env->fpus & ~(7 << 11);
> + fpu.fsw |= (env->fpstt & 7) << 11;
> + fpu.fcw = env->fpuc;
> +
> + for (i = 0; i < 8; ++i) {
> + fpu.ftw |= (!env->fptags[i]) << i;
> + }
> +
> + memcpy(fpu.st_mm, env->fpregs, sizeof(env->fpregs));
> + for (i = 0; i < 8; i++) {
> + stq_p(&fpu.mmx_1[i][0], env->xmm_regs[i].ZMM_Q(0));
> + stq_p(&fpu.mmx_1[i][8], env->xmm_regs[i].ZMM_Q(1));
> + if (CPU_NB_REGS > 8) {
> + stq_p(&fpu.mmx_2[i][0], env->xmm_regs[i + 8].ZMM_Q(0));
> + stq_p(&fpu.mmx_2[i][8], env->xmm_regs[i + 8].ZMM_Q(1));
> + }
> + }
> +
> + fpu.mxcsr = env->mxcsr;
> +
> + return hax_sync_fpu(env, &fpu, 1);
> +}
> +
> +static int hax_arch_get_registers(CPUArchState *env)
> +{
> + int ret;
> +
> + ret = hax_sync_vcpu_register(env, 0);
> + if (ret < 0) {
> + return ret;
> + }
> +
> + ret = hax_get_fpu(env);
> + if (ret < 0) {
> + return ret;
> + }
> +
> + ret = hax_get_msrs(env);
> + if (ret < 0) {
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int hax_arch_set_registers(CPUArchState *env)
> +{
> + int ret;
> + ret = hax_sync_vcpu_register(env, 1);
> +
> + if (ret < 0) {
> + fprintf(stderr, "Failed to sync vcpu reg\n");
> + return ret;
> + }
> + ret = hax_set_fpu(env);
> + if (ret < 0) {
> + fprintf(stderr, "FPU failed\n");
> + return ret;
> + }
> + ret = hax_set_msrs(env);
> + if (ret < 0) {
> + fprintf(stderr, "MSR failed\n");
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static void hax_vcpu_sync_state(CPUArchState *env, int modified)
> +{
> + if (hax_enabled()) {
> + if (modified) {
> + hax_arch_set_registers(env);
> + } else {
> + hax_arch_get_registers(env);
> + }
> + }
> +}
> +
> +/*
> + * much simpler than kvm, at least in first stage because:
> + * We don't need consider the device pass-through, we don't need
> + * consider the framebuffer, and we may even remove the bios at all
> + */
> +int hax_sync_vcpus(void)
> +{
> + if (hax_enabled()) {
> + CPUState *cpu;
> +
> + cpu = first_cpu;
> + if (!cpu) {
> + return 0;
> + }
> +
> + for (; cpu != NULL; cpu = CPU_NEXT(cpu)) {
> + int ret;
> +
> + ret = hax_arch_set_registers(cpu->env_ptr);
> + if (ret < 0) {
> + return ret;
> + }
> + }
> + }
> +
> + return 0;
> +}
> +
> +void hax_reset_vcpu_state(void *opaque)
> +{
> + CPUState *cpu;
> + for (cpu = first_cpu; cpu != NULL; cpu = CPU_NEXT(cpu)) {
> + cpu->hax_vcpu->tunnel->user_event_pending = 0;
> + cpu->hax_vcpu->tunnel->ready_for_interrupt_injection = 0;
> + }
> +}
> +
> +static void hax_accel_class_init(ObjectClass *oc, void *data)
> +{
> + AccelClass *ac = ACCEL_CLASS(oc);
> + ac->name = "HAX";
> + ac->init_machine = hax_accel_init;
> + ac->allowed = &hax_allowed;
> +}
> +
> +static const TypeInfo hax_accel_type = {
> + .name = ACCEL_CLASS_NAME("hax"),
> + .parent = TYPE_ACCEL,
> + .class_init = hax_accel_class_init,
> +};
> +
> +static void hax_type_init(void)
> +{
> + type_register_static(&hax_accel_type);
> +}
> +
> +type_init(hax_type_init);
> diff --git a/target-i386/hax-i386.h b/target-i386/hax-i386.h
> new file mode 100644
> index 0000000..3f50bd9
> --- /dev/null
> +++ b/target-i386/hax-i386.h
> @@ -0,0 +1,86 @@
> +/*
> + * QEMU HAXM support
> + *
> + * 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 _HAX_I386_H
> +#define _HAX_I386_H
> +
> +#include "cpu.h"
> +#include "sysemu/hax.h"
> +
> +#ifdef CONFIG_WIN32
> +typedef HANDLE hax_fd;
> +#endif
> +
> +extern struct hax_state hax_global;
> +struct hax_vcpu_state {
> + hax_fd fd;
> + int vcpu_id;
> + struct hax_tunnel *tunnel;
> + unsigned char *iobuf;
> +};
> +
> +struct hax_state {
> + hax_fd fd; /* the global hax device interface */
> + uint32_t version;
> + struct hax_vm *vm;
> + uint64_t mem_quota;
> +};
> +
> +#define HAX_MAX_VCPU 0x10
> +#define MAX_VM_ID 0x40
> +#define MAX_VCPU_ID 0x40
> +
> +struct hax_vm {
> + hax_fd fd;
> + int id;
> + struct hax_vcpu_state *vcpus[HAX_MAX_VCPU];
> +};
> +
> +#ifdef NEED_CPU_H
> +/* Functions exported to host specific mode */
> +hax_fd hax_vcpu_get_fd(CPUArchState *env);
> +int valid_hax_tunnel_size(uint16_t size);
> +
> +/* Host specific functions */
> +int hax_mod_version(struct hax_state *hax, struct hax_module_version *version);
> +int hax_inject_interrupt(CPUArchState *env, int vector);
> +struct hax_vm *hax_vm_create(struct hax_state *hax);
> +int hax_vcpu_run(struct hax_vcpu_state *vcpu);
> +int hax_vcpu_create(int id);
> +int hax_sync_vcpu_state(CPUArchState *env, struct vcpu_state_t *state,
> + int set);
> +int hax_sync_msr(CPUArchState *env, struct hax_msr_data *msrs, int set);
> +int hax_sync_fpu(CPUArchState *env, struct fx_layout *fl, int set);
> +#endif
> +
> +int hax_vm_destroy(struct hax_vm *vm);
> +int hax_capability(struct hax_state *hax, struct hax_capabilityinfo *cap);
> +int hax_notify_qemu_version(hax_fd vm_fd, struct hax_qemu_version *qversion);
> +int hax_set_ram(uint64_t start_pa, uint32_t size, uint64_t host_va, int flags);
> +
> +/* Common host function */
> +int hax_host_create_vm(struct hax_state *hax, int *vm_id);
> +hax_fd hax_host_open_vm(struct hax_state *hax, int vm_id);
> +int hax_host_create_vcpu(hax_fd vm_fd, int vcpuid);
> +hax_fd hax_host_open_vcpu(int vmid, int vcpuid);
> +int hax_host_setup_vcpu_channel(struct hax_vcpu_state *vcpu);
> +hax_fd hax_mod_open(void);
> +void hax_memory_region_init(void);
> +
> +
> +#ifdef CONFIG_WIN32
> +#include "target-i386/hax-windows.h"
> +#endif
> +
> +#include "target-i386/hax-interface.h"
> +
> +#endif
> diff --git a/target-i386/hax-interface.h b/target-i386/hax-interface.h
> new file mode 100644
> index 0000000..add6b82
> --- /dev/null
> +++ b/target-i386/hax-interface.h
> @@ -0,0 +1,358 @@
> +/*
> + * QEMU HAXM support
> + *
> + * Copyright (c) 2011 Intel Corporation
> + * Written by:
> + * Jiang Yunhong<yunhong.jiang@intel.com>
> + * Xin Xiaohui<xiaohui.xin@intel.com>
> + * Zhang Xiantao<xiantao.zhang@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.
> + *
> + */
> +
> +/* Interface with HAX kernel module */
> +
> +#ifndef _HAX_INTERFACE_H
> +#define _HAX_INTERFACE_H
> +
> +/* fx_layout has 3 formats table 3-56, 512bytes */
> +struct fx_layout {
> + uint16_t fcw;
> + uint16_t fsw;
> + uint8_t ftw;
> + uint8_t res1;
> + uint16_t fop;
> + union {
> + struct {
> + uint32_t fip;
> + uint16_t fcs;
> + uint16_t res2;
> + };
> + uint64_t fpu_ip;
> + };
> + union {
> + struct {
> + uint32_t fdp;
> + uint16_t fds;
> + uint16_t res3;
> + };
> + uint64_t fpu_dp;
> + };
> + uint32_t mxcsr;
> + uint32_t mxcsr_mask;
> + uint8_t st_mm[8][16];
> + uint8_t mmx_1[8][16];
> + uint8_t mmx_2[8][16];
> + uint8_t pad[96];
> +} __attribute__ ((aligned(8)));
> +
> +struct vmx_msr {
> + uint64_t entry;
> + uint64_t value;
> +} __attribute__ ((__packed__));
> +
> +/*
> + * Fixed array is not good, but it makes Mac support a bit easier by avoiding
> + * memory map or copyin staff.
> + */
> +#define HAX_MAX_MSR_ARRAY 0x20
> +struct hax_msr_data {
> + uint16_t nr_msr;
> + uint16_t done;
> + uint16_t pad[2];
> + struct vmx_msr entries[HAX_MAX_MSR_ARRAY];
> +} __attribute__ ((__packed__));
> +
> +union interruptibility_state_t {
> + uint32_t raw;
> + struct {
> + uint32_t sti_blocking:1;
> + uint32_t movss_blocking:1;
> + uint32_t smi_blocking:1;
> + uint32_t nmi_blocking:1;
> + uint32_t reserved:28;
> + };
> + uint64_t pad;
> +};
> +
> +typedef union interruptibility_state_t interruptibility_state_t;
> +
> +/* Segment descriptor */
> +struct segment_desc_t {
> + uint16_t selector;
> + uint16_t _dummy;
> + uint32_t limit;
> + uint64_t base;
> + union {
> + struct {
> + uint32_t type:4;
> + uint32_t desc:1;
> + uint32_t dpl:2;
> + uint32_t present:1;
> + uint32_t:4;
> + uint32_t available:1;
> + uint32_t long_mode:1;
> + uint32_t operand_size:1;
> + uint32_t granularity:1;
> + uint32_t null:1;
> + uint32_t:15;
> + };
> + uint32_t ar;
> + };
> + uint32_t ipad;
> +};
> +
> +typedef struct segment_desc_t segment_desc_t;
> +
> +struct vcpu_state_t {
> + union {
> + uint64_t _regs[16];
> + struct {
> + union {
> + struct {
> + uint8_t _al, _ah;
> + };
> + uint16_t _ax;
> + uint32_t _eax;
> + uint64_t _rax;
> + };
> + union {
> + struct {
> + uint8_t _cl, _ch;
> + };
> + uint16_t _cx;
> + uint32_t _ecx;
> + uint64_t _rcx;
> + };
> + union {
> + struct {
> + uint8_t _dl, _dh;
> + };
> + uint16_t _dx;
> + uint32_t _edx;
> + uint64_t _rdx;
> + };
> + union {
> + struct {
> + uint8_t _bl, _bh;
> + };
> + uint16_t _bx;
> + uint32_t _ebx;
> + uint64_t _rbx;
> + };
> + union {
> + uint16_t _sp;
> + uint32_t _esp;
> + uint64_t _rsp;
> + };
> + union {
> + uint16_t _bp;
> + uint32_t _ebp;
> + uint64_t _rbp;
> + };
> + union {
> + uint16_t _si;
> + uint32_t _esi;
> + uint64_t _rsi;
> + };
> + union {
> + uint16_t _di;
> + uint32_t _edi;
> + uint64_t _rdi;
> + };
> +
> + uint64_t _r8;
> + uint64_t _r9;
> + uint64_t _r10;
> + uint64_t _r11;
> + uint64_t _r12;
> + uint64_t _r13;
> + uint64_t _r14;
> + uint64_t _r15;
> + };
> + };
> +
> + union {
> + uint32_t _eip;
> + uint64_t _rip;
> + };
> +
> + union {
> + uint32_t _eflags;
> + uint64_t _rflags;
> + };
> +
> + segment_desc_t _cs;
> + segment_desc_t _ss;
> + segment_desc_t _ds;
> + segment_desc_t _es;
> + segment_desc_t _fs;
> + segment_desc_t _gs;
> + segment_desc_t _ldt;
> + segment_desc_t _tr;
> +
> + segment_desc_t _gdt;
> + segment_desc_t _idt;
> +
> + uint64_t _cr0;
> + uint64_t _cr2;
> + uint64_t _cr3;
> + uint64_t _cr4;
> +
> + uint64_t _dr0;
> + uint64_t _dr1;
> + uint64_t _dr2;
> + uint64_t _dr3;
> + uint64_t _dr6;
> + uint64_t _dr7;
> + uint64_t _pde;
> +
> + uint32_t _efer;
> +
> + uint32_t _sysenter_cs;
> + uint64_t _sysenter_eip;
> + uint64_t _sysenter_esp;
> +
> + uint32_t _activity_state;
> + uint32_t pad;
> + interruptibility_state_t _interruptibility_state;
> +};
> +
> +/* HAX exit status */
> +enum exit_status {
> + /* IO port request */
> + HAX_EXIT_IO = 1,
> + /* MMIO instruction emulation */
> + HAX_EXIT_MMIO,
> + /* QEMU emulation mode request, currently means guest enter non-PG mode */
> + HAX_EXIT_REAL,
> + /*
> + * Interrupt window open, qemu can inject interrupt now
> + * Also used when signal pending since at that time qemu usually need
> + * check interrupt
> + */
> + HAX_EXIT_INTERRUPT,
> + /* Unknown vmexit, mostly trigger reboot */
> + HAX_EXIT_UNKNOWN_VMEXIT,
> + /* HALT from guest */
> + HAX_EXIT_HLT,
> + /* Reboot request, like because of tripple fault in guest */
> + HAX_EXIT_STATECHANGE,
> + /* the vcpu is now only paused when destroy, so simply return to hax */
> + HAX_EXIT_PAUSED,
> + HAX_EXIT_FAST_MMIO,
> +};
> +
> +/*
> + * The interface definition:
> + * 1. vcpu_run execute will return 0 on success, otherwise mean failed
> + * 2. exit_status return the exit reason, as stated in enum exit_status
> + * 3. exit_reason is the vmx exit reason
> + */
> +struct hax_tunnel {
> + uint32_t _exit_reason;
> + uint32_t _exit_flag;
> + uint32_t _exit_status;
> + uint32_t user_event_pending;
> + int ready_for_interrupt_injection;
> + int request_interrupt_window;
> + union {
> + struct {
> + /* 0: read, 1: write */
> +#define HAX_EXIT_IO_IN 1
> +#define HAX_EXIT_IO_OUT 0
> + uint8_t _direction;
> + uint8_t _df;
> + uint16_t _size;
> + uint16_t _port;
> + uint16_t _count;
> + uint8_t _flags;
> + uint8_t _pad0;
> + uint16_t _pad1;
> + uint32_t _pad2;
> + uint64_t _vaddr;
> + } pio;
> + struct {
> + uint64_t gla;
> + } mmio;
> + struct {
> + } state;
> + };
> +} __attribute__ ((__packed__));
> +
> +struct hax_module_version {
> + uint32_t compat_version;
> + uint32_t cur_version;
> +} __attribute__ ((__packed__));
> +
> +/* This interface is support only after API version 2 */
> +struct hax_qemu_version {
> + /* Current API version in QEMU */
> + uint32_t cur_version;
> + /* The minimum API version supported by QEMU */
> + uint32_t min_version;
> +} __attribute__ ((__packed__));
> +
> +/* The mac specfic interface to qemu, mostly is ioctl related */
> +struct hax_tunnel_info {
> + uint64_t va;
> + uint64_t io_va;
> + uint16_t size;
> + uint16_t pad[3];
> +} __attribute__ ((__packed__));
> +
> +struct hax_alloc_ram_info {
> + uint32_t size;
> + uint32_t pad;
> + uint64_t va;
> +} __attribute__ ((__packed__));
> +#define HAX_RAM_INFO_ROM 0x01 /* Read-Only */
> +#define HAX_RAM_INFO_INVALID 0x80 /* Unmapped, usually used for MMIO */
> +struct hax_set_ram_info {
> + uint64_t pa_start;
> + uint32_t size;
> + uint8_t flags;
> + uint8_t pad[3];
> + uint64_t va;
> +} __attribute__ ((__packed__));
> +
> +#define HAX_CAP_STATUS_WORKING 0x1
> +#define HAX_CAP_STATUS_NOTWORKING 0x0
> +#define HAX_CAP_WORKSTATUS_MASK 0x1
> +
> +#define HAX_CAP_FAILREASON_VT 0x1
> +#define HAX_CAP_FAILREASON_NX 0x2
> +
> +#define HAX_CAP_MEMQUOTA 0x2
> +#define HAX_CAP_UG 0x4
> +
> +struct hax_capabilityinfo {
> + /* bit 0: 1 - working
> + * 0 - not working, possibly because NT/NX disabled
> + * bit 1: 1 - memory limitation working
> + * 0 - no memory limitation
> + */
> + uint16_t wstatus;
> + /* valid when not working
> + * bit 0: VT not enabeld
> + * bit 1: NX not enabled*/
> + uint16_t winfo;
> + uint32_t pad;
> + uint64_t mem_quota;
> +} __attribute__ ((__packed__));
> +
> +struct hax_fastmmio {
> + uint64_t gpa;
> + uint64_t value;
> + uint8_t size;
> + uint8_t direction;
> + uint16_t reg_index;
> + uint32_t pad0;
> + uint64_t _cr0;
> + uint64_t _cr2;
> + uint64_t _cr3;
> + uint64_t _cr4;
> +} __attribute__ ((__packed__));
> +#endif
> diff --git a/target-i386/hax-mem.c b/target-i386/hax-mem.c
> new file mode 100644
> index 0000000..dc8c553
> --- /dev/null
> +++ b/target-i386/hax-mem.c
> @@ -0,0 +1,271 @@
> +/*
> + * HAX memory mapping operations
> + *
> + * Copyright (c) 2015-16 Intel Corporation
> + * Copyright 2016 Google, Inc.
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2. See
> + * the COPYING file in the top-level directory.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "cpu.h"
> +#include "exec/address-spaces.h"
> +#include "exec/exec-all.h"
> +
> +#include "target-i386/hax-i386.h"
> +#include "qemu/queue.h"
> +
> +#define DEBUG_HAX_MEM 0
> +
> +#define DPRINTF(fmt, ...) \
> + do { \
> + if (DEBUG_HAX_MEM) { \
> + fprintf(stdout, fmt, ## __VA_ARGS__); \
> + } \
> + } while (0)
> +
> +/**
> + * HAXMapping: describes a pending guest physical memory mapping
> + *
> + * @start_pa: a guest physical address marking the start of the region; must be
> + * page-aligned
> + * @size: a guest physical address marking the end of the region; must be
> + * page-aligned
> + * @host_va: the host virtual address of the start of the mapping
> + * @flags: mapping parameters e.g. HAX_RAM_INFO_ROM or HAX_RAM_INFO_INVALID
> + * @entry: additional fields for linking #HAXMapping instances together
> + */
> +typedef struct HAXMapping {
> + uint64_t start_pa;
> + uint32_t size;
> + uint64_t host_va;
> + int flags;
> + QTAILQ_ENTRY(HAXMapping) entry;
> +} HAXMapping;
> +
> +/*
> + * A doubly-linked list (actually a tail queue) of the pending page mappings
> + * for the ongoing memory transaction.
> + *
> + * It is used to optimize the number of page mapping updates done through the
> + * kernel module. For example, it's effective when a driver is digging an MMIO
> + * hole inside an existing memory mapping. It will get a deletion of the whole
> + * region, then the addition of the 2 remaining RAM areas around the hole and
> + * finally the memory transaction commit. During the commit, it will effectively
> + * send to the kernel only the removal of the pages from the MMIO hole after
> + * having computed locally the result of the deletion and additions.
> + */
> +static QTAILQ_HEAD(HAXMappingListHead, HAXMapping) mappings =
> + QTAILQ_HEAD_INITIALIZER(mappings);
> +
> +/**
> + * hax_mapping_dump_list: dumps @mappings to stdout (for debugging)
> + */
> +static void hax_mapping_dump_list(void)
> +{
> + HAXMapping *entry;
> +
> + DPRINTF("%s updates:\n", __func__);
> + QTAILQ_FOREACH(entry, &mappings, entry) {
> + DPRINTF("\t%c 0x%016" PRIx64 "->0x%016" PRIx64 " VA 0x%016" PRIx64
> + "%s\n", entry->flags & HAX_RAM_INFO_INVALID ? '-' : '+',
> + entry->start_pa, entry->start_pa + entry->size, entry->host_va,
> + entry->flags & HAX_RAM_INFO_ROM ? " ROM" : "");
> + }
> +}
> +
> +static void hax_insert_mapping_before(HAXMapping *next, uint64_t start_pa,
> + uint32_t size, uint64_t host_va,
> + uint8_t flags)
> +{
> + HAXMapping *entry;
> +
> + entry = g_malloc0(sizeof(*entry));
> + entry->start_pa = start_pa;
> + entry->size = size;
> + entry->host_va = host_va;
> + entry->flags = flags;
> + if (!next) {
> + QTAILQ_INSERT_TAIL(&mappings, entry, entry);
> + } else {
> + QTAILQ_INSERT_BEFORE(next, entry, entry);
> + }
> +}
> +
> +static bool hax_mapping_is_opposite(HAXMapping *entry, uint64_t host_va,
> + uint8_t flags)
> +{
> + /* removed then added without change for the read-only flag */
> + bool nop_flags = (entry->flags ^ flags) == HAX_RAM_INFO_INVALID;
> +
> + return (entry->host_va == host_va) && nop_flags;
> +}
> +
> +static void hax_update_mapping(uint64_t start_pa, uint32_t size,
> + uint64_t host_va, uint8_t flags)
> +{
> + uint64_t end_pa = start_pa + size;
> + uint32_t chunk_sz;
> + HAXMapping *entry, *next;
> +
> + QTAILQ_FOREACH_SAFE(entry, &mappings, entry, next) {
> + if (start_pa >= entry->start_pa + entry->size) {
> + continue;
> + }
> + if (start_pa < entry->start_pa) {
> + chunk_sz = end_pa <= entry->start_pa ? size
> + : entry->start_pa - start_pa;
> + hax_insert_mapping_before(entry, start_pa, chunk_sz,
> + host_va, flags);
> + start_pa += chunk_sz;
> + host_va += chunk_sz;
> + size -= chunk_sz;
> + }
> + chunk_sz = MIN(size, entry->size);
> + if (chunk_sz) {
> + bool nop = hax_mapping_is_opposite(entry, host_va, flags);
> + bool partial = chunk_sz < entry->size;
> + if (partial) {
> + /* remove the beginning of the existing chunk */
> + entry->start_pa += chunk_sz;
> + entry->host_va += chunk_sz;
> + entry->size -= chunk_sz;
> + if (!nop) {
> + hax_insert_mapping_before(entry, start_pa, chunk_sz,
> + host_va, flags);
> + }
> + } else { /* affects the full mapping entry */
> + if (nop) { /* no change to this mapping, remove it */
> + QTAILQ_REMOVE(&mappings, entry, entry);
> + g_free(entry);
> + } else { /* update mapping properties */
> + entry->host_va = host_va;
> + entry->flags = flags;
> + }
> + }
> + start_pa += chunk_sz;
> + host_va += chunk_sz;
> + size -= chunk_sz;
> + }
> + if (!size) { /* we are done */
> + break;
> + }
> + }
> + if (size) { /* add the leftover */
> + hax_insert_mapping_before(NULL, start_pa, size, host_va, flags);
> + }
> +}
> +
> +static void hax_process_section(MemoryRegionSection *section, uint8_t flags)
> +{
> + MemoryRegion *mr = section->mr;
> + hwaddr start_pa = section->offset_within_address_space;
> + ram_addr_t size = int128_get64(section->size);
> + unsigned int delta;
> + uint64_t host_va;
> +
> + /* We only care about RAM pages */
> + if (!memory_region_is_ram(mr)) {
> + return;
> + }
> +
> + /* Adjust start_pa and size so that they are page-aligned. (Cf
> + * kvm_set_phys_mem() in kvm-all.c).
> + */
> + delta = qemu_real_host_page_size - (start_pa & ~qemu_real_host_page_mask);
> + delta &= ~qemu_real_host_page_mask;
> + if (delta > size) {
> + return;
> + }
> + start_pa += delta;
> + size -= delta;
> + size &= qemu_real_host_page_mask;
> + if (!size || (start_pa & ~qemu_real_host_page_mask)) {
> + return;
> + }
> +
> + host_va = (uintptr_t)memory_region_get_ram_ptr(mr)
> + + section->offset_within_region + delta;
> + if (memory_region_is_rom(section->mr)) {
> + flags |= HAX_RAM_INFO_ROM;
> + }
> +
> + /* the kernel module interface uses 32-bit sizes (but we could split...) */
> + g_assert(size <= UINT32_MAX);
> +
> + hax_update_mapping(start_pa, size, host_va, flags);
> +}
> +
> +static void hax_region_add(MemoryListener *listener,
> + MemoryRegionSection *section)
> +{
> + memory_region_ref(section->mr);
> + hax_process_section(section, 0);
> +}
> +
> +static void hax_region_del(MemoryListener *listener,
> + MemoryRegionSection *section)
> +{
> + hax_process_section(section, HAX_RAM_INFO_INVALID);
> + memory_region_unref(section->mr);
> +}
> +
> +static void hax_transaction_begin(MemoryListener *listener)
> +{
> + g_assert(QTAILQ_EMPTY(&mappings));
> +}
> +
> +static void hax_transaction_commit(MemoryListener *listener)
> +{
> + if (!QTAILQ_EMPTY(&mappings)) {
> + HAXMapping *entry, *next;
> +
> + if (DEBUG_HAX_MEM) {
> + hax_mapping_dump_list();
> + }
> + QTAILQ_FOREACH_SAFE(entry, &mappings, entry, next) {
> + if (entry->flags & HAX_RAM_INFO_INVALID) {
> + /* for unmapping, put the values expected by the kernel */
> + entry->flags = HAX_RAM_INFO_INVALID;
> + entry->host_va = 0;
> + }
> + if (hax_set_ram(entry->start_pa, entry->size,
> + entry->host_va, entry->flags)) {
> + fprintf(stderr, "%s: Failed mapping @0x%016" PRIx64 "+0x%"
> + PRIx32 " flags %02x\n", __func__, entry->start_pa,
> + entry->size, entry->flags);
> + }
> + QTAILQ_REMOVE(&mappings, entry, entry);
> + g_free(entry);
> + }
> + }
> +}
> +
> +/* currently we fake the dirty bitmap sync, always dirty */
> +static void hax_log_sync(MemoryListener *listener,
> + MemoryRegionSection *section)
> +{
> + MemoryRegion *mr = section->mr;
> +
> + if (!memory_region_is_ram(mr)) {
> + /* Skip MMIO regions */
> + return;
> + }
> +
> + memory_region_set_dirty(mr, 0, int128_get64(section->size));
> +}
> +
> +static MemoryListener hax_memory_listener = {
> + .begin = hax_transaction_begin,
> + .commit = hax_transaction_commit,
> + .region_add = hax_region_add,
> + .region_del = hax_region_del,
> + .log_sync = hax_log_sync,
> + .priority = 10,
> +};
> +
> +void hax_memory_region_init(void)
> +{
> + memory_listener_register(&hax_memory_listener, &address_space_memory);
> +}
> diff --git a/target-i386/hax-windows.c b/target-i386/hax-windows.c
> new file mode 100644
> index 0000000..15a180b
> --- /dev/null
> +++ b/target-i386/hax-windows.c
> @@ -0,0 +1,479 @@
> +/*
> + * QEMU HAXM support
> + *
> + * 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.
> + *
> + */
> +
> +#include "qemu/osdep.h"
> +#include "cpu.h"
> +#include "exec/exec-all.h"
> +#include "hax-i386.h"
> +
> +/*
> + * return 0 when success, -1 when driver not loaded,
> + * other negative value for other failure
> + */
> +static int hax_open_device(hax_fd *fd)
> +{
> + uint32_t errNum = 0;
> + HANDLE hDevice;
> +
> + if (!fd) {
> + return -2;
> + }
> +
> + hDevice = CreateFile("\\\\.\\HAX",
> + GENERIC_READ | GENERIC_WRITE,
> + 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
> +
> + if (hDevice == INVALID_HANDLE_VALUE) {
> + fprintf(stderr, "Failed to open the HAX device!\n");
> + errNum = GetLastError();
> + if (errNum == ERROR_FILE_NOT_FOUND) {
> + return -1;
> + }
> + return -2;
> + }
> + *fd = hDevice;
> + return 0;
> +}
> +
> +/* hax_fd hax_mod_open */
> + hax_fd hax_mod_open(void)
> +{
> + int ret;
> + hax_fd fd = NULL;
> +
> + ret = hax_open_device(&fd);
> + if (ret != 0) {
> + fprintf(stderr, "Open HAX device failed\n");
> + }
> +
> + return fd;
> +}
> +
> +int hax_populate_ram(uint64_t va, uint32_t size)
> +{
> + int ret;
> + struct hax_alloc_ram_info info;
> + HANDLE hDeviceVM;
> + DWORD dSize = 0;
> +
> + if (!hax_global.vm || !hax_global.vm->fd) {
> + fprintf(stderr, "Allocate memory before vm create?\n");
> + return -EINVAL;
> + }
> +
> + info.size = size;
> + info.va = va;
> +
> + hDeviceVM = hax_global.vm->fd;
> +
> + ret = DeviceIoControl(hDeviceVM,
> + HAX_VM_IOCTL_ALLOC_RAM,
> + &info, sizeof(info), NULL, 0, &dSize,
> + (LPOVERLAPPED) NULL);
> +
> + if (!ret) {
> + fprintf(stderr, "Failed to allocate %x memory\n", size);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +int hax_set_ram(uint64_t start_pa, uint32_t size, uint64_t host_va, int flags)
> +{
> + struct hax_set_ram_info info;
> + HANDLE hDeviceVM = hax_global.vm->fd;
> + DWORD dSize = 0;
> + int ret;
> +
> + info.pa_start = start_pa;
> + info.size = size;
> + info.va = host_va;
> + info.flags = (uint8_t) flags;
> +
> + ret = DeviceIoControl(hDeviceVM, HAX_VM_IOCTL_SET_RAM,
> + &info, sizeof(info), NULL, 0, &dSize,
> + (LPOVERLAPPED) NULL);
> +
> + if (!ret) {
> + return -EFAULT;
> + } else {
> + return 0;
> + }
> +}
> +
> +int hax_capability(struct hax_state *hax, struct hax_capabilityinfo *cap)
> +{
> + int ret;
> + HANDLE hDevice = hax->fd; /* handle to hax module */
> + DWORD dSize = 0;
> + DWORD err = 0;
> +
> + if (hax_invalid_fd(hDevice)) {
> + fprintf(stderr, "Invalid fd for hax device!\n");
> + return -ENODEV;
> + }
> +
> + ret = DeviceIoControl(hDevice, HAX_IOCTL_CAPABILITY, NULL, 0, cap,
> + sizeof(*cap), &dSize, (LPOVERLAPPED) NULL);
> +
> + if (!ret) {
> + err = GetLastError();
> + if (err == ERROR_INSUFFICIENT_BUFFER || err == ERROR_MORE_DATA) {
> + fprintf(stderr, "hax capability is too long to hold.\n");
> + }
> + fprintf(stderr, "Failed to get Hax capability:%luu\n", err);
> + return -EFAULT;
> + } else {
> + return 0;
> + }
> +}
> +
> +int hax_mod_version(struct hax_state *hax, struct hax_module_version *version)
> +{
> + int ret;
> + HANDLE hDevice = hax->fd; /* handle to hax module */
> + DWORD dSize = 0;
> + DWORD err = 0;
> +
> + if (hax_invalid_fd(hDevice)) {
> + fprintf(stderr, "Invalid fd for hax device!\n");
> + return -ENODEV;
> + }
> +
> + ret = DeviceIoControl(hDevice,
> + HAX_IOCTL_VERSION,
> + NULL, 0,
> + version, sizeof(*version), &dSize,
> + (LPOVERLAPPED) NULL);
> +
> + if (!ret) {
> + err = GetLastError();
> + if (err == ERROR_INSUFFICIENT_BUFFER || err == ERROR_MORE_DATA) {
> + fprintf(stderr, "hax module verion is too long to hold.\n");
> + }
> + fprintf(stderr, "Failed to get Hax module version:%lu\n", err);
> + return -EFAULT;
> + } else {
> + return 0;
> + }
> +}
> +
> +static char *hax_vm_devfs_string(int vm_id)
> +{
> + char *name;
> +
> + if (vm_id > MAX_VM_ID) {
> + fprintf(stderr, "Too big VM id\n");
> + return NULL;
> + }
> +
> +#define HAX_VM_DEVFS "\\\\.\\hax_vmxx"
> + name = g_strdup(HAX_VM_DEVFS);
> + if (!name) {
> + return NULL;
> + }
> +
> + snprintf(name, sizeof HAX_VM_DEVFS, "\\\\.\\hax_vm%02d", vm_id);
> + return name;
> +}
> +
> +static char *hax_vcpu_devfs_string(int vm_id, int vcpu_id)
> +{
> + char *name;
> +
> + if (vm_id > MAX_VM_ID || vcpu_id > MAX_VCPU_ID) {
> + fprintf(stderr, "Too big vm id %x or vcpu id %x\n", vm_id, vcpu_id);
> + return NULL;
> + }
> +
> +#define HAX_VCPU_DEVFS "\\\\.\\hax_vmxx_vcpuxx"
> + name = g_strdup(HAX_VCPU_DEVFS);
> + if (!name) {
> + return NULL;
> + }
> +
> + snprintf(name, sizeof HAX_VCPU_DEVFS, "\\\\.\\hax_vm%02d_vcpu%02d",
> + vm_id, vcpu_id);
> + return name;
> +}
> +
> +int hax_host_create_vm(struct hax_state *hax, int *vmid)
> +{
> + int ret;
> + int vm_id = 0;
> + DWORD dSize = 0;
> +
> + if (hax_invalid_fd(hax->fd)) {
> + return -EINVAL;
> + }
> +
> + if (hax->vm) {
> + return 0;
> + }
> +
> + ret = DeviceIoControl(hax->fd,
> + HAX_IOCTL_CREATE_VM,
> + NULL, 0, &vm_id, sizeof(vm_id), &dSize,
> + (LPOVERLAPPED) NULL);
> + if (!ret) {
> + fprintf(stderr, "Failed to create VM. Error code: %lu\n",
> + GetLastError());
> + return -1;
> + }
> + *vmid = vm_id;
> + return 0;
> +}
> +
> +hax_fd hax_host_open_vm(struct hax_state *hax, int vm_id)
> +{
> + char *vm_name = NULL;
> + hax_fd hDeviceVM;
> +
> + vm_name = hax_vm_devfs_string(vm_id);
> + if (!vm_name) {
> + fprintf(stderr, "Failed to open VM. VM name is null\n");
> + return INVALID_HANDLE_VALUE;
> + }
> +
> + hDeviceVM = CreateFile(vm_name,
> + GENERIC_READ | GENERIC_WRITE,
> + 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
> + if (hDeviceVM == INVALID_HANDLE_VALUE) {
> + fprintf(stderr, "Open the vm device error:%s, ec:%lu\n",
> + vm_name, GetLastError());
> + }
> +
> + g_free(vm_name);
> + return hDeviceVM;
> +}
> +
> +int hax_notify_qemu_version(hax_fd vm_fd, struct hax_qemu_version *qversion)
> +{
> + int ret;
> + DWORD dSize = 0;
> + if (hax_invalid_fd(vm_fd)) {
> + return -EINVAL;
> + }
> + ret = DeviceIoControl(vm_fd,
> + HAX_VM_IOCTL_NOTIFY_QEMU_VERSION,
> + qversion, sizeof(struct hax_qemu_version),
> + NULL, 0, &dSize, (LPOVERLAPPED) NULL);
> + if (!ret) {
> + fprintf(stderr, "Failed to notify qemu API version\n");
> + return -1;
> + }
> + return 0;
> +}
> +
> +int hax_host_create_vcpu(hax_fd vm_fd, int vcpuid)
> +{
> + int ret;
> + DWORD dSize = 0;
> +
> + ret = DeviceIoControl(vm_fd,
> + HAX_VM_IOCTL_VCPU_CREATE,
> + &vcpuid, sizeof(vcpuid), NULL, 0, &dSize,
> + (LPOVERLAPPED) NULL);
> + if (!ret) {
> + fprintf(stderr, "Failed to create vcpu %x\n", vcpuid);
> + return -1;
> + }
> +
> + return 0;
> +}
> +
> +hax_fd hax_host_open_vcpu(int vmid, int vcpuid)
> +{
> + char *devfs_path = NULL;
> + hax_fd hDeviceVCPU;
> +
> + devfs_path = hax_vcpu_devfs_string(vmid, vcpuid);
> + if (!devfs_path) {
> + fprintf(stderr, "Failed to get the devfs\n");
> + return INVALID_HANDLE_VALUE;
> + }
> +
> + hDeviceVCPU = CreateFile(devfs_path,
> + GENERIC_READ | GENERIC_WRITE,
> + 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
> + NULL);
> +
> + if (hDeviceVCPU == INVALID_HANDLE_VALUE) {
> + fprintf(stderr, "Failed to open the vcpu devfs\n");
> + }
> + g_free(devfs_path);
> + return hDeviceVCPU;
> +}
> +
> +int hax_host_setup_vcpu_channel(struct hax_vcpu_state *vcpu)
> +{
> + hax_fd hDeviceVCPU = vcpu->fd;
> + int ret;
> + struct hax_tunnel_info info;
> + DWORD dSize = 0;
> +
> + ret = DeviceIoControl(hDeviceVCPU,
> + HAX_VCPU_IOCTL_SETUP_TUNNEL,
> + NULL, 0, &info, sizeof(info), &dSize,
> + (LPOVERLAPPED) NULL);
> + if (!ret) {
> + fprintf(stderr, "Failed to setup the hax tunnel\n");
> + return -1;
> + }
> +
> + if (!valid_hax_tunnel_size(info.size)) {
> + fprintf(stderr, "Invalid hax tunnel size %x\n", info.size);
> + ret = -EINVAL;
> + return ret;
> + }
> + vcpu->tunnel = (struct hax_tunnel *) (intptr_t) (info.va);
> + vcpu->iobuf = (unsigned char *) (intptr_t) (info.io_va);
> + return 0;
> +}
> +
> +int hax_vcpu_run(struct hax_vcpu_state *vcpu)
> +{
> + int ret;
> + HANDLE hDeviceVCPU = vcpu->fd;
> + DWORD dSize = 0;
> +
> + ret = DeviceIoControl(hDeviceVCPU,
> + HAX_VCPU_IOCTL_RUN,
> + NULL, 0, NULL, 0, &dSize, (LPOVERLAPPED) NULL);
> + if (!ret) {
> + return -EFAULT;
> + } else {
> + return 0;
> + }
> +}
> +
> +int hax_sync_fpu(CPUArchState *env, struct fx_layout *fl, int set)
> +{
> + int ret;
> + hax_fd fd;
> + HANDLE hDeviceVCPU;
> + DWORD dSize = 0;
> +
> + fd = hax_vcpu_get_fd(env);
> + if (hax_invalid_fd(fd)) {
> + return -1;
> + }
> +
> + hDeviceVCPU = fd;
> +
> + if (set) {
> + ret = DeviceIoControl(hDeviceVCPU,
> + HAX_VCPU_IOCTL_SET_FPU,
> + fl, sizeof(*fl), NULL, 0, &dSize,
> + (LPOVERLAPPED) NULL);
> + } else {
> + ret = DeviceIoControl(hDeviceVCPU,
> + HAX_VCPU_IOCTL_GET_FPU,
> + NULL, 0, fl, sizeof(*fl), &dSize,
> + (LPOVERLAPPED) NULL);
> + }
> + if (!ret) {
> + return -EFAULT;
> + } else {
> + return 0;
> + }
> +}
> +
> +int hax_sync_msr(CPUArchState *env, struct hax_msr_data *msrs, int set)
> +{
> + int ret;
> + hax_fd fd;
> + HANDLE hDeviceVCPU;
> + DWORD dSize = 0;
> +
> + fd = hax_vcpu_get_fd(env);
> + if (hax_invalid_fd(fd)) {
> + return -1;
> + }
> + hDeviceVCPU = fd;
> +
> + if (set) {
> + ret = DeviceIoControl(hDeviceVCPU,
> + HAX_VCPU_IOCTL_SET_MSRS,
> + msrs, sizeof(*msrs),
> + msrs, sizeof(*msrs), &dSize, (LPOVERLAPPED) NULL);
> + } else {
> + ret = DeviceIoControl(hDeviceVCPU,
> + HAX_VCPU_IOCTL_GET_MSRS,
> + msrs, sizeof(*msrs),
> + msrs, sizeof(*msrs), &dSize, (LPOVERLAPPED) NULL);
> + }
> + if (!ret) {
> + return -EFAULT;
> + } else {
> + return 0;
> + }
> +}
> +
> +int hax_sync_vcpu_state(CPUArchState *env, struct vcpu_state_t *state, int set)
> +{
> + int ret;
> + hax_fd fd;
> + HANDLE hDeviceVCPU;
> + DWORD dSize;
> +
> + fd = hax_vcpu_get_fd(env);
> + if (hax_invalid_fd(fd)) {
> + return -1;
> + }
> +
> + hDeviceVCPU = fd;
> +
> + if (set) {
> + ret = DeviceIoControl(hDeviceVCPU,
> + HAX_VCPU_SET_REGS,
> + state, sizeof(*state),
> + NULL, 0, &dSize, (LPOVERLAPPED) NULL);
> + } else {
> + ret = DeviceIoControl(hDeviceVCPU,
> + HAX_VCPU_GET_REGS,
> + NULL, 0,
> + state, sizeof(*state), &dSize,
> + (LPOVERLAPPED) NULL);
> + }
> + if (!ret) {
> + return -EFAULT;
> + } else {
> + return 0;
> + }
> +}
> +
> +int hax_inject_interrupt(CPUArchState *env, int vector)
> +{
> + int ret;
> + hax_fd fd;
> + HANDLE hDeviceVCPU;
> + DWORD dSize;
> +
> + fd = hax_vcpu_get_fd(env);
> + if (hax_invalid_fd(fd)) {
> + return -1;
> + }
> +
> + hDeviceVCPU = fd;
> +
> + ret = DeviceIoControl(hDeviceVCPU,
> + HAX_VCPU_IOCTL_INTERRUPT,
> + &vector, sizeof(vector), NULL, 0, &dSize,
> + (LPOVERLAPPED) NULL);
> + if (!ret) {
> + return -EFAULT;
> + } else {
> + return 0;
> + }
> +}
> diff --git a/target-i386/hax-windows.h b/target-i386/hax-windows.h
> new file mode 100644
> index 0000000..1d8f68d
> --- /dev/null
> +++ b/target-i386/hax-windows.h
> @@ -0,0 +1,89 @@
> +/*
> + * QEMU HAXM support
> + *
> + * Copyright IBM, Corp. 2008
> + *
> + * Authors:
> + * Anthony Liguori <aliguori@us.ibm.com>
> + *
> + * Copyright (c) 2011 Intel Corporation
> + * Written by:
> + * Jiang Yunhong<yunhong.jiang@intel.com>
> + * Xin Xiaohui<xiaohui.xin@intel.com>
> + * Zhang Xiantao<xiantao.zhang@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 TARGET_I386_HAX_WINDOWS_H
> +#define TARGET_I386_HAX_WINDOWS_H
> +
> +#include <windows.h>
> +#include <memory.h>
> +#include <malloc.h>
> +#include <winioctl.h>
> +#include <string.h>
> +#include <stdio.h>
> +#include <windef.h>
> +
> +#define HAX_INVALID_FD INVALID_HANDLE_VALUE
> +
> +static inline void hax_mod_close(struct hax_state *hax)
> +{
> + CloseHandle(hax->fd);
> +}
> +
> +static inline void hax_close_fd(hax_fd fd)
> +{
> + CloseHandle(fd);
> +}
> +
> +static inline int hax_invalid_fd(hax_fd fd)
> +{
> + return (fd == INVALID_HANDLE_VALUE);
> +}
> +
> +#define HAX_DEVICE_TYPE 0x4000
> +
> +#define HAX_IOCTL_VERSION CTL_CODE(HAX_DEVICE_TYPE, 0x900, \
> + METHOD_BUFFERED, FILE_ANY_ACCESS)
> +#define HAX_IOCTL_CREATE_VM CTL_CODE(HAX_DEVICE_TYPE, 0x901, \
> + METHOD_BUFFERED, FILE_ANY_ACCESS)
> +#define HAX_IOCTL_CAPABILITY CTL_CODE(HAX_DEVICE_TYPE, 0x910, \
> + METHOD_BUFFERED, FILE_ANY_ACCESS)
> +
> +#define HAX_VM_IOCTL_VCPU_CREATE CTL_CODE(HAX_DEVICE_TYPE, 0x902, \
> + METHOD_BUFFERED, FILE_ANY_ACCESS)
> +#define HAX_VM_IOCTL_ALLOC_RAM CTL_CODE(HAX_DEVICE_TYPE, 0x903, \
> + METHOD_BUFFERED, FILE_ANY_ACCESS)
> +#define HAX_VM_IOCTL_SET_RAM CTL_CODE(HAX_DEVICE_TYPE, 0x904, \
> + METHOD_BUFFERED, FILE_ANY_ACCESS)
> +#define HAX_VM_IOCTL_VCPU_DESTROY CTL_CODE(HAX_DEVICE_TYPE, 0x905, \
> + METHOD_BUFFERED, FILE_ANY_ACCESS)
> +
> +#define HAX_VCPU_IOCTL_RUN CTL_CODE(HAX_DEVICE_TYPE, 0x906, \
> + METHOD_BUFFERED, FILE_ANY_ACCESS)
> +#define HAX_VCPU_IOCTL_SET_MSRS CTL_CODE(HAX_DEVICE_TYPE, 0x907, \
> + METHOD_BUFFERED, FILE_ANY_ACCESS)
> +#define HAX_VCPU_IOCTL_GET_MSRS CTL_CODE(HAX_DEVICE_TYPE, 0x908, \
> + METHOD_BUFFERED, FILE_ANY_ACCESS)
> +#define HAX_VCPU_IOCTL_SET_FPU CTL_CODE(HAX_DEVICE_TYPE, 0x909, \
> + METHOD_BUFFERED, FILE_ANY_ACCESS)
> +#define HAX_VCPU_IOCTL_GET_FPU CTL_CODE(HAX_DEVICE_TYPE, 0x90a, \
> + METHOD_BUFFERED, FILE_ANY_ACCESS)
> +
> +#define HAX_VCPU_IOCTL_SETUP_TUNNEL CTL_CODE(HAX_DEVICE_TYPE, 0x90b, \
> + METHOD_BUFFERED, FILE_ANY_ACCESS)
> +#define HAX_VCPU_IOCTL_INTERRUPT CTL_CODE(HAX_DEVICE_TYPE, 0x90c, \
> + METHOD_BUFFERED, FILE_ANY_ACCESS)
> +#define HAX_VCPU_SET_REGS CTL_CODE(HAX_DEVICE_TYPE, 0x90d, \
> + METHOD_BUFFERED, FILE_ANY_ACCESS)
> +#define HAX_VCPU_GET_REGS CTL_CODE(HAX_DEVICE_TYPE, 0x90e, \
> + METHOD_BUFFERED, FILE_ANY_ACCESS)
> +
> +#define HAX_VM_IOCTL_NOTIFY_QEMU_VERSION CTL_CODE(HAX_DEVICE_TYPE, 0x910, \
> + METHOD_BUFFERED, \
> + FILE_ANY_ACCESS)
> +#endif /* TARGET_I386_HAX_WINDOWS_H */
> --
> 2.8.0.rc3.226.g39d4020
>
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2016-12-19 14:09 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-12-19 10:29 [Qemu-devel] [PATCH v4 0/4] Add HAX support Vincent Palatin
2016-12-19 10:29 ` [Qemu-devel] [PATCH v4 1/4] kvm: move cpu synchronization code Vincent Palatin
2016-12-19 10:29 ` [Qemu-devel] [PATCH v4 2/4] target-i386: Add Intel HAX files Vincent Palatin
2016-12-19 14:09 ` Vincent Palatin
2016-12-19 10:29 ` [Qemu-devel] [PATCH v4 3/4] Plumb the HAXM-based hardware acceleration support Vincent Palatin
2016-12-19 10:29 ` [Qemu-devel] [PATCH v4 4/4] hax: add Darwin support Vincent Palatin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).