* [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
* 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
* [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
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).