diff options
author | kamil <kamil@pkgsrc.org> | 2020-02-06 16:46:17 +0000 |
---|---|---|
committer | kamil <kamil@pkgsrc.org> | 2020-02-06 16:46:17 +0000 |
commit | 1402003a0507fac9f5c7cc1561c1305ebfb57547 (patch) | |
tree | ced8dd1dcb87fed44acda898c92c2b12a4edacfe /emulators | |
parent | 78766bad1e0b8f19f9ece8479e9e5ca7c4fc26f9 (diff) | |
download | pkgsrc-1402003a0507fac9f5c7cc1561c1305ebfb57547.tar.gz |
nvmm: Merge NVMM support
NVMM v3 patchset pending upstream.
https://lists.gnu.org/archive/html/qemu-devel/2020-02/msg01405.html
Diffstat (limited to 'emulators')
-rw-r--r-- | emulators/qemu/distinfo | 13 | ||||
-rw-r--r-- | emulators/qemu/patches/patch-accel_stubs_Makefile.objs | 13 | ||||
-rw-r--r-- | emulators/qemu/patches/patch-accel_stubs_nvmm-stub.c | 50 | ||||
-rw-r--r-- | emulators/qemu/patches/patch-configure | 107 | ||||
-rw-r--r-- | emulators/qemu/patches/patch-cpus.c | 92 | ||||
-rw-r--r-- | emulators/qemu/patches/patch-include_sysemu_hw__accel.h | 55 | ||||
-rw-r--r-- | emulators/qemu/patches/patch-include_sysemu_nvmm.h | 42 | ||||
-rw-r--r-- | emulators/qemu/patches/patch-qemu-options.hx | 48 | ||||
-rw-r--r-- | emulators/qemu/patches/patch-target_i386_Makefile.objs | 14 | ||||
-rw-r--r-- | emulators/qemu/patches/patch-target_i386_helper.c | 15 | ||||
-rw-r--r-- | emulators/qemu/patches/patch-target_i386_nvmm-all.c | 1228 |
11 files changed, 1668 insertions, 9 deletions
diff --git a/emulators/qemu/distinfo b/emulators/qemu/distinfo index 7b8dba45a5e..3849bebac16 100644 --- a/emulators/qemu/distinfo +++ b/emulators/qemu/distinfo @@ -1,24 +1,33 @@ -$NetBSD: distinfo,v 1.153 2019/12/15 18:35:25 adam Exp $ +$NetBSD: distinfo,v 1.154 2020/02/06 16:46:17 kamil Exp $ SHA1 (qemu-4.2.0.tar.xz) = b27aa828a8457bd8551ae3c81b80cc365e1f6bfe RMD160 (qemu-4.2.0.tar.xz) = cab8f6d85c0c6e9c01059087ed27d1243a7004a4 SHA512 (qemu-4.2.0.tar.xz) = 2a79973c2b07c53e8c57a808ea8add7b6b2cbca96488ed5d4b669ead8c9318907dec2b6109f180fc8ca8f04c0f73a56e82b3a527b5626b799d7e849f2474ec56 Size (qemu-4.2.0.tar.xz) = 62222068 bytes SHA1 (patch-Makefile) = 85d24d842ad2f7e1e2ec6f0e0e3268c21ef9bf0d +SHA1 (patch-accel_stubs_Makefile.objs) = 92266dc400ac1e97013f7f19ee4b7a63b42c7fc0 +SHA1 (patch-accel_stubs_nvmm-stub.c) = d66d47eabb8bb6728e777da7589b43d491adbcc8 SHA1 (patch-accel_tcg_user-exec.c) = 86ee62f6e5c8cd7942cf0aa9c9f64e4b0879ff33 SHA1 (patch-capstone_Makefile) = f59870031de8c4385a591362749ec82f57fd4c27 -SHA1 (patch-configure) = 14c09363622bcee113b7fda5bd3f031bd9ff6b90 +SHA1 (patch-configure) = c2d68edf08e94ea12c9e57da3dacb0c1326c1213 SHA1 (patch-contrib_ivshmem-client_ivshmem-client.c) = 40c8751607cbf66a37e4c4e08f2664b864e2e984 SHA1 (patch-contrib_ivshmem-server_ivshmem-server.c) = d8f53432b5752f4263dc4ef96108a976a05147a3 +SHA1 (patch-cpus.c) = a319b7533daf3036c44411c4b26270b1fbb96009 SHA1 (patch-hw_core_uboot__image.h) = 17eef02349343c5fcfb7a4069cb6f8fd11efcb59 SHA1 (patch-hw_display_omap__dss.c) = 6b13242f28e32346bc70548c216c578d98fd3420 SHA1 (patch-hw_net_etraxfs__eth.c) = e5dd1661d60dbcd27b332403e0843500ba9544bc SHA1 (patch-hw_net_xilinx__axienet.c) = ebcd2676d64ce6f31e4a8c976d4fdf530ad5e8b7 SHA1 (patch-hw_tpm_tpm__ioctl.h) = a350c1708e1f9d85e07c4c354068703a45174baf SHA1 (patch-hw_usb_dev-mtp.c) = 0f9034fb3904e5d5e3b98d24b94e054181687d95 +SHA1 (patch-include_sysemu_hw__accel.h) = e5f581fa0539e55a6a1d559b94e94d47aedb88d7 SHA1 (patch-include_sysemu_kvm.h) = 9847abe3be70bd708a521310f5d5515e45a1a5a0 +SHA1 (patch-include_sysemu_nvmm.h) = 3bd3da9b42ace0f806fabeb580f90ae19c273869 SHA1 (patch-net_tap-solaris.c) = cc953c9a624dd55ace4e130d0b31bbfb956c17d5 SHA1 (patch-qemu-doc.texi) = 78f926a16494c07248b9a81f39dca5b78ae542bb +SHA1 (patch-qemu-options.hx) = 17fb92e03e32402ea2c04217991a2cd01a335e02 SHA1 (patch-roms_u-boot_tools_imx8m__image.sh) = e4c452062f40569e33aa93eec4a65bd3af2e74fc +SHA1 (patch-target_i386_Makefile.objs) = be8ab2e72521ccd0a71db6b37feb9957b27ac970 +SHA1 (patch-target_i386_helper.c) = 54363fe53688ea4030665b3bbb3ee7aba7ba5348 SHA1 (patch-target_i386_kvm-stub.c) = 4cd2b7a8d8d8a317829f982b5acff7fdf2479d9f +SHA1 (patch-target_i386_nvmm-all.c) = 4b6d29456d3ad6409ce0cd83ef95d2226a7aab33 SHA1 (patch-util_drm.c) = b30a46d623d9431c5af2f3cd101adb89be3f0c2b diff --git a/emulators/qemu/patches/patch-accel_stubs_Makefile.objs b/emulators/qemu/patches/patch-accel_stubs_Makefile.objs new file mode 100644 index 00000000000..d4c8a783f55 --- /dev/null +++ b/emulators/qemu/patches/patch-accel_stubs_Makefile.objs @@ -0,0 +1,13 @@ +$NetBSD: patch-accel_stubs_Makefile.objs,v 1.1 2020/02/06 16:46:17 kamil Exp $ + +Add NVMM support. + +--- accel/stubs/Makefile.objs.orig 2019-12-12 18:20:47.000000000 +0000 ++++ accel/stubs/Makefile.objs +@@ -1,5 +1,6 @@ + obj-$(call lnot,$(CONFIG_HAX)) += hax-stub.o + obj-$(call lnot,$(CONFIG_HVF)) += hvf-stub.o + obj-$(call lnot,$(CONFIG_WHPX)) += whpx-stub.o ++obj-$(call lnot,$(CONFIG_NVMM)) += nvmm-stub.o + obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o + obj-$(call lnot,$(CONFIG_TCG)) += tcg-stub.o diff --git a/emulators/qemu/patches/patch-accel_stubs_nvmm-stub.c b/emulators/qemu/patches/patch-accel_stubs_nvmm-stub.c new file mode 100644 index 00000000000..cd46df737e4 --- /dev/null +++ b/emulators/qemu/patches/patch-accel_stubs_nvmm-stub.c @@ -0,0 +1,50 @@ +$NetBSD: patch-accel_stubs_nvmm-stub.c,v 1.1 2020/02/06 16:46:17 kamil Exp $ + +Add NVMM support. + +--- accel/stubs/nvmm-stub.c.orig 2020-02-06 16:25:13.966864001 +0000 ++++ accel/stubs/nvmm-stub.c +@@ -0,0 +1,43 @@ ++/* ++ * Copyright (c) 2018-2019 Maxime Villard, All rights reserved. ++ * ++ * NetBSD Virtual Machine Monitor (NVMM) accelerator stub. ++ * ++ * 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 "qemu-common.h" ++#include "cpu.h" ++#include "sysemu/nvmm.h" ++ ++int nvmm_init_vcpu(CPUState *cpu) ++{ ++ return -1; ++} ++ ++int nvmm_vcpu_exec(CPUState *cpu) ++{ ++ return -1; ++} ++ ++void nvmm_destroy_vcpu(CPUState *cpu) ++{ ++} ++ ++void nvmm_cpu_synchronize_state(CPUState *cpu) ++{ ++} ++ ++void nvmm_cpu_synchronize_post_reset(CPUState *cpu) ++{ ++} ++ ++void nvmm_cpu_synchronize_post_init(CPUState *cpu) ++{ ++} ++ ++void nvmm_cpu_synchronize_pre_loadvm(CPUState *cpu) ++{ ++} diff --git a/emulators/qemu/patches/patch-configure b/emulators/qemu/patches/patch-configure index 7026848541e..e2900d98e9a 100644 --- a/emulators/qemu/patches/patch-configure +++ b/emulators/qemu/patches/patch-configure @@ -1,14 +1,107 @@ -$NetBSD: patch-configure,v 1.25 2019/04/24 13:59:32 ryoon Exp $ +$NetBSD: patch-configure,v 1.26 2020/02/06 16:46:17 kamil Exp $ configure: Add HAX support in NetBSD +Add NVMM support. ---- configure.orig 2019-04-23 18:14:45.000000000 +0000 +--- configure.orig 2020-02-06 16:24:52.903915848 +0000 +++ configure -@@ -815,6 +815,7 @@ FreeBSD) - ;; - DragonFly) +@@ -241,6 +241,17 @@ supported_whpx_target() { + return 1 + } + ++supported_nvmm_target() { ++ test "$nvmm" = "yes" || return 1 ++ glob "$1" "*-softmmu" || return 1 ++ case "${1%-softmmu}" in ++ i386|x86_64) ++ return 0 ++ ;; ++ esac ++ return 1 ++} ++ + supported_target() { + case "$1" in + *-softmmu) +@@ -268,6 +279,7 @@ supported_target() { + supported_hax_target "$1" && return 0 + supported_hvf_target "$1" && return 0 + supported_whpx_target "$1" && return 0 ++ supported_nvmm_target "$1" && return 0 + print_error "TCG disabled, but hardware accelerator not available for '$target'" + return 1 + } +@@ -387,6 +399,7 @@ kvm="no" + hax="no" + hvf="no" + whpx="no" ++nvmm="no" + rdma="" + pvrdma="" + gprof="no" +@@ -822,6 +835,7 @@ DragonFly) + NetBSD) bsd="yes" -+ hax="yes" + hax="yes" ++ nvmm="yes" make="${MAKE-gmake}" audio_drv_list="oss try-sdl" - audio_possible_drivers="oss sdl pa" + audio_possible_drivers="oss sdl" +@@ -1172,6 +1186,10 @@ for opt do + ;; + --enable-whpx) whpx="yes" + ;; ++ --disable-nvmm) nvmm="no" ++ ;; ++ --enable-nvmm) nvmm="yes" ++ ;; + --disable-tcg-interpreter) tcg_interpreter="no" + ;; + --enable-tcg-interpreter) tcg_interpreter="yes" +@@ -1768,6 +1786,7 @@ disabled with --disable-FEATURE, default + hax HAX acceleration support + hvf Hypervisor.framework acceleration support + whpx Windows Hypervisor Platform acceleration support ++ nvmm NetBSD Virtual Machine Monitor acceleration support + rdma Enable RDMA-based migration + pvrdma Enable PVRDMA support + vde support for vde network +@@ -2758,6 +2777,20 @@ if test "$whpx" != "no" ; then + fi + + ########################################## ++# NetBSD Virtual Machine Monitor (NVMM) accelerator check ++if test "$nvmm" != "no" ; then ++ if check_include "nvmm.h" ; then ++ nvmm="yes" ++ LIBS="-lnvmm $LIBS" ++ else ++ if test "$nvmm" = "yes"; then ++ feature_not_found "NVMM" "NVMM is not available" ++ fi ++ nvmm="no" ++ fi ++fi ++ ++########################################## + # Sparse probe + if test "$sparse" != "no" ; then + if has cgcc; then +@@ -6522,6 +6555,7 @@ echo "KVM support $kvm" + echo "HAX support $hax" + echo "HVF support $hvf" + echo "WHPX support $whpx" ++echo "NVMM support $nvmm" + echo "TCG support $tcg" + if test "$tcg" = "yes" ; then + echo "TCG debug enabled $debug_tcg" +@@ -7811,6 +7845,9 @@ fi + if test "$target_aligned_only" = "yes" ; then + echo "TARGET_ALIGNED_ONLY=y" >> $config_target_mak + fi ++if supported_nvmm_target $target; then ++ echo "CONFIG_NVMM=y" >> $config_target_mak ++fi + if test "$target_bigendian" = "yes" ; then + echo "TARGET_WORDS_BIGENDIAN=y" >> $config_target_mak + fi diff --git a/emulators/qemu/patches/patch-cpus.c b/emulators/qemu/patches/patch-cpus.c new file mode 100644 index 00000000000..3f5e3ebff0b --- /dev/null +++ b/emulators/qemu/patches/patch-cpus.c @@ -0,0 +1,92 @@ +$NetBSD: patch-cpus.c,v 1.1 2020/02/06 16:46:17 kamil Exp $ + +Add NVMM support. + +--- cpus.c.orig 2019-12-12 18:20:47.000000000 +0000 ++++ cpus.c +@@ -42,6 +42,7 @@ + #include "sysemu/hax.h" + #include "sysemu/hvf.h" + #include "sysemu/whpx.h" ++#include "sysemu/nvmm.h" + #include "exec/exec-all.h" + + #include "qemu/thread.h" +@@ -1738,6 +1739,48 @@ static void *qemu_whpx_cpu_thread_fn(voi + return NULL; + } + ++static void *qemu_nvmm_cpu_thread_fn(void *arg) ++{ ++ CPUState *cpu = arg; ++ int r; ++ ++ assert(nvmm_enabled()); ++ ++ rcu_register_thread(); ++ ++ qemu_mutex_lock_iothread(); ++ qemu_thread_get_self(cpu->thread); ++ cpu->thread_id = qemu_get_thread_id(); ++ current_cpu = cpu; ++ ++ r = nvmm_init_vcpu(cpu); ++ if (r < 0) { ++ fprintf(stderr, "nvmm_init_vcpu failed: %s\n", strerror(-r)); ++ exit(1); ++ } ++ ++ /* signal CPU creation */ ++ cpu->created = true; ++ qemu_cond_signal(&qemu_cpu_cond); ++ ++ do { ++ if (cpu_can_run(cpu)) { ++ r = nvmm_vcpu_exec(cpu); ++ if (r == EXCP_DEBUG) { ++ cpu_handle_guest_debug(cpu); ++ } ++ } ++ qemu_wait_io_event(cpu); ++ } while (!cpu->unplug || cpu_can_run(cpu)); ++ ++ nvmm_destroy_vcpu(cpu); ++ cpu->created = false; ++ qemu_cond_signal(&qemu_cpu_cond); ++ qemu_mutex_unlock_iothread(); ++ rcu_unregister_thread(); ++ return NULL; ++} ++ + #ifdef _WIN32 + static void CALLBACK dummy_apc_func(ULONG_PTR unused) + { +@@ -2101,6 +2144,19 @@ static void qemu_whpx_start_vcpu(CPUStat + #endif + } + ++static void qemu_nvmm_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/NVMM", ++ cpu->cpu_index); ++ qemu_thread_create(cpu->thread, thread_name, qemu_nvmm_cpu_thread_fn, ++ cpu, QEMU_THREAD_JOINABLE); ++} ++ + static void qemu_dummy_start_vcpu(CPUState *cpu) + { + char thread_name[VCPU_THREAD_NAME_SIZE]; +@@ -2141,6 +2197,8 @@ void qemu_init_vcpu(CPUState *cpu) + qemu_tcg_init_vcpu(cpu); + } else if (whpx_enabled()) { + qemu_whpx_start_vcpu(cpu); ++ } else if (nvmm_enabled()) { ++ qemu_nvmm_start_vcpu(cpu); + } else { + qemu_dummy_start_vcpu(cpu); + } diff --git a/emulators/qemu/patches/patch-include_sysemu_hw__accel.h b/emulators/qemu/patches/patch-include_sysemu_hw__accel.h new file mode 100644 index 00000000000..94d33e25676 --- /dev/null +++ b/emulators/qemu/patches/patch-include_sysemu_hw__accel.h @@ -0,0 +1,55 @@ +$NetBSD: patch-include_sysemu_hw__accel.h,v 1.1 2020/02/06 16:46:17 kamil Exp $ + +Add NVMM support. + +--- include/sysemu/hw_accel.h.orig 2019-12-12 18:20:48.000000000 +0000 ++++ include/sysemu/hw_accel.h +@@ -15,6 +15,7 @@ + #include "sysemu/hax.h" + #include "sysemu/kvm.h" + #include "sysemu/whpx.h" ++#include "sysemu/nvmm.h" + + static inline void cpu_synchronize_state(CPUState *cpu) + { +@@ -27,6 +28,9 @@ static inline void cpu_synchronize_state + if (whpx_enabled()) { + whpx_cpu_synchronize_state(cpu); + } ++ if (nvmm_enabled()) { ++ nvmm_cpu_synchronize_state(cpu); ++ } + } + + static inline void cpu_synchronize_post_reset(CPUState *cpu) +@@ -40,6 +44,10 @@ static inline void cpu_synchronize_post_ + if (whpx_enabled()) { + whpx_cpu_synchronize_post_reset(cpu); + } ++ if (nvmm_enabled()) { ++ nvmm_cpu_synchronize_post_reset(cpu); ++ } ++ + } + + static inline void cpu_synchronize_post_init(CPUState *cpu) +@@ -53,6 +61,9 @@ static inline void cpu_synchronize_post_ + if (whpx_enabled()) { + whpx_cpu_synchronize_post_init(cpu); + } ++ if (nvmm_enabled()) { ++ nvmm_cpu_synchronize_post_init(cpu); ++ } + } + + static inline void cpu_synchronize_pre_loadvm(CPUState *cpu) +@@ -66,6 +77,9 @@ static inline void cpu_synchronize_pre_l + if (whpx_enabled()) { + whpx_cpu_synchronize_pre_loadvm(cpu); + } ++ if (nvmm_enabled()) { ++ nvmm_cpu_synchronize_pre_loadvm(cpu); ++ } + } + + #endif /* QEMU_HW_ACCEL_H */ diff --git a/emulators/qemu/patches/patch-include_sysemu_nvmm.h b/emulators/qemu/patches/patch-include_sysemu_nvmm.h new file mode 100644 index 00000000000..d504cd56b7d --- /dev/null +++ b/emulators/qemu/patches/patch-include_sysemu_nvmm.h @@ -0,0 +1,42 @@ +$NetBSD: patch-include_sysemu_nvmm.h,v 1.1 2020/02/06 16:46:17 kamil Exp $ + +Add NVMM support. + +--- include/sysemu/nvmm.h.orig 2020-02-06 16:25:13.966985106 +0000 ++++ include/sysemu/nvmm.h +@@ -0,0 +1,35 @@ ++/* ++ * Copyright (c) 2018-2019 Maxime Villard, All rights reserved. ++ * ++ * NetBSD Virtual Machine Monitor (NVMM) accelerator support. ++ * ++ * 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_NVMM_H ++#define QEMU_NVMM_H ++ ++#include "config-host.h" ++#include "qemu-common.h" ++ ++int nvmm_init_vcpu(CPUState *); ++int nvmm_vcpu_exec(CPUState *); ++void nvmm_destroy_vcpu(CPUState *); ++ ++void nvmm_cpu_synchronize_state(CPUState *); ++void nvmm_cpu_synchronize_post_reset(CPUState *); ++void nvmm_cpu_synchronize_post_init(CPUState *); ++void nvmm_cpu_synchronize_pre_loadvm(CPUState *); ++ ++#ifdef CONFIG_NVMM ++ ++int nvmm_enabled(void); ++ ++#else /* CONFIG_NVMM */ ++ ++#define nvmm_enabled() (0) ++ ++#endif /* CONFIG_NVMM */ ++ ++#endif /* CONFIG_NVMM */ diff --git a/emulators/qemu/patches/patch-qemu-options.hx b/emulators/qemu/patches/patch-qemu-options.hx new file mode 100644 index 00000000000..a00da6c663a --- /dev/null +++ b/emulators/qemu/patches/patch-qemu-options.hx @@ -0,0 +1,48 @@ +$NetBSD: patch-qemu-options.hx,v 1.1 2020/02/06 16:46:17 kamil Exp $ + +Add NVMM support. + +--- qemu-options.hx.orig 2019-12-12 18:20:48.000000000 +0000 ++++ qemu-options.hx +@@ -31,7 +31,7 @@ DEF("machine", HAS_ARG, QEMU_OPTION_mach + "-machine [type=]name[,prop[=value][,...]]\n" + " selects emulated machine ('-machine help' for list)\n" + " property accel=accel1[:accel2[:...]] selects accelerator\n" +- " supported accelerators are kvm, xen, hax, hvf, whpx or tcg (default: tcg)\n" ++ " supported accelerators are kvm, xen, hax, hvf, nvmm, whpx or tcg (default: tcg)\n" + " kernel_irqchip=on|off|split controls accelerated irqchip support (default=off)\n" + " vmport=on|off|auto controls emulation of vmport (default: auto)\n" + " kvm_shadow_mem=size of KVM shadow MMU in bytes\n" +@@ -66,9 +66,9 @@ Supported machine properties are: + @table @option + @item accel=@var{accels1}[:@var{accels2}[:...]] + This is used to enable an accelerator. Depending on the target architecture, +-kvm, xen, hax, hvf, whpx or tcg can be available. By default, tcg is used. If there is +-more than one accelerator specified, the next one is used if the previous one +-fails to initialize. ++kvm, xen, hax, hvf, nvmm, whpx or tcg can be available. By default, tcg is used. ++If there is more than one accelerator specified, the next one is used if the ++previous one fails to initialize. + @item kernel_irqchip=on|off + Controls in-kernel irqchip support for the chosen accelerator when available. + @item gfx_passthru=on|off +@@ -119,15 +119,15 @@ ETEXI + + DEF("accel", HAS_ARG, QEMU_OPTION_accel, + "-accel [accel=]accelerator[,thread=single|multi]\n" +- " select accelerator (kvm, xen, hax, hvf, whpx or tcg; use 'help' for a list)\n" ++ " select accelerator (kvm, xen, hax, hvf, nvmm, whpx or tcg; use 'help' for a list)\n" + " thread=single|multi (enable multi-threaded TCG)\n", QEMU_ARCH_ALL) + STEXI + @item -accel @var{name}[,prop=@var{value}[,...]] + @findex -accel + This is used to enable an accelerator. Depending on the target architecture, +-kvm, xen, hax, hvf, whpx or tcg can be available. By default, tcg is used. If there is +-more than one accelerator specified, the next one is used if the previous one +-fails to initialize. ++kvm, xen, hax, hvf, nvmm, whpx or tcg can be available. By default, tcg is used. ++If there is more than one accelerator specified, the next one is used if the ++previous one fails to initialize. + @table @option + @item thread=single|multi + Controls number of TCG threads. When the TCG is multi-threaded there will be one diff --git a/emulators/qemu/patches/patch-target_i386_Makefile.objs b/emulators/qemu/patches/patch-target_i386_Makefile.objs new file mode 100644 index 00000000000..5f2e4e2c1ba --- /dev/null +++ b/emulators/qemu/patches/patch-target_i386_Makefile.objs @@ -0,0 +1,14 @@ +$NetBSD: patch-target_i386_Makefile.objs,v 1.3 2020/02/06 16:46:17 kamil Exp $ + +Add NVMM support. + +--- target/i386/Makefile.objs.orig 2019-12-12 18:20:48.000000000 +0000 ++++ target/i386/Makefile.objs +@@ -17,6 +17,7 @@ obj-$(CONFIG_HAX) += hax-all.o hax-mem.o + endif + obj-$(CONFIG_HVF) += hvf/ + obj-$(CONFIG_WHPX) += whpx-all.o ++obj-$(CONFIG_NVMM) += nvmm-all.o + endif + obj-$(CONFIG_SEV) += sev.o + obj-$(call lnot,$(CONFIG_SEV)) += sev-stub.o diff --git a/emulators/qemu/patches/patch-target_i386_helper.c b/emulators/qemu/patches/patch-target_i386_helper.c new file mode 100644 index 00000000000..6b2823fe7d9 --- /dev/null +++ b/emulators/qemu/patches/patch-target_i386_helper.c @@ -0,0 +1,15 @@ +$NetBSD: patch-target_i386_helper.c,v 1.1 2020/02/06 16:46:17 kamil Exp $ + +Add NVMM support. + +--- target/i386/helper.c.orig 2019-12-12 18:20:48.000000000 +0000 ++++ target/i386/helper.c +@@ -981,7 +981,7 @@ void cpu_report_tpr_access(CPUX86State * + X86CPU *cpu = env_archcpu(env); + CPUState *cs = env_cpu(env); + +- if (kvm_enabled() || whpx_enabled()) { ++ if (kvm_enabled() || whpx_enabled() || nvmm_enabled()) { + env->tpr_access_type = access; + + cpu_interrupt(cs, CPU_INTERRUPT_TPR); diff --git a/emulators/qemu/patches/patch-target_i386_nvmm-all.c b/emulators/qemu/patches/patch-target_i386_nvmm-all.c new file mode 100644 index 00000000000..bf0653bbb99 --- /dev/null +++ b/emulators/qemu/patches/patch-target_i386_nvmm-all.c @@ -0,0 +1,1228 @@ +$NetBSD: patch-target_i386_nvmm-all.c,v 1.1 2020/02/06 16:46:17 kamil Exp $ + +Add NVMM support. + +--- target/i386/nvmm-all.c.orig 2020-02-06 16:25:13.969936459 +0000 ++++ target/i386/nvmm-all.c +@@ -0,0 +1,1221 @@ ++/* ++ * Copyright (c) 2018-2019 Maxime Villard, All rights reserved. ++ * ++ * NetBSD Virtual Machine Monitor (NVMM) accelerator for QEMU. ++ * ++ * 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/address-spaces.h" ++#include "exec/ioport.h" ++#include "qemu-common.h" ++#include "strings.h" ++#include "sysemu/accel.h" ++#include "sysemu/nvmm.h" ++#include "sysemu/sysemu.h" ++#include "sysemu/cpus.h" ++#include "qemu/main-loop.h" ++#include "qemu/error-report.h" ++#include "qemu/queue.h" ++#include "qapi/error.h" ++#include "migration/blocker.h" ++ ++#include <nvmm.h> ++ ++struct qemu_vcpu { ++ struct nvmm_vcpu vcpu; ++ uint8_t tpr; ++ bool stop; ++ ++ /* Window-exiting for INTs/NMIs. */ ++ bool int_window_exit; ++ bool nmi_window_exit; ++ ++ /* The guest is in an interrupt shadow (POP SS, etc). */ ++ bool int_shadow; ++}; ++ ++struct qemu_machine { ++ struct nvmm_capability cap; ++ struct nvmm_machine mach; ++}; ++ ++/* -------------------------------------------------------------------------- */ ++ ++static bool nvmm_allowed; ++static struct qemu_machine qemu_mach; ++ ++static struct qemu_vcpu * ++get_qemu_vcpu(CPUState *cpu) ++{ ++ return (struct qemu_vcpu *)cpu->hax_vcpu; ++} ++ ++static struct nvmm_machine * ++get_nvmm_mach(void) ++{ ++ return &qemu_mach.mach; ++} ++ ++/* -------------------------------------------------------------------------- */ ++ ++static void ++nvmm_set_segment(struct nvmm_x64_state_seg *nseg, const SegmentCache *qseg) ++{ ++ uint32_t attrib = qseg->flags; ++ ++ nseg->selector = qseg->selector; ++ nseg->limit = qseg->limit; ++ nseg->base = qseg->base; ++ nseg->attrib.type = __SHIFTOUT(attrib, DESC_TYPE_MASK); ++ nseg->attrib.s = __SHIFTOUT(attrib, DESC_S_MASK); ++ nseg->attrib.dpl = __SHIFTOUT(attrib, DESC_DPL_MASK); ++ nseg->attrib.p = __SHIFTOUT(attrib, DESC_P_MASK); ++ nseg->attrib.avl = __SHIFTOUT(attrib, DESC_AVL_MASK); ++ nseg->attrib.l = __SHIFTOUT(attrib, DESC_L_MASK); ++ nseg->attrib.def = __SHIFTOUT(attrib, DESC_B_MASK); ++ nseg->attrib.g = __SHIFTOUT(attrib, DESC_G_MASK); ++} ++ ++static void ++nvmm_set_registers(CPUState *cpu) ++{ ++ struct CPUX86State *env = (CPUArchState *)cpu->env_ptr; ++ struct nvmm_machine *mach = get_nvmm_mach(); ++ struct qemu_vcpu *qcpu = get_qemu_vcpu(cpu); ++ struct nvmm_vcpu *vcpu = &qcpu->vcpu; ++ struct nvmm_x64_state *state = vcpu->state; ++ uint64_t bitmap; ++ size_t i; ++ int ret; ++ ++ assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu)); ++ ++ /* GPRs. */ ++ state->gprs[NVMM_X64_GPR_RAX] = env->regs[R_EAX]; ++ state->gprs[NVMM_X64_GPR_RCX] = env->regs[R_ECX]; ++ state->gprs[NVMM_X64_GPR_RDX] = env->regs[R_EDX]; ++ state->gprs[NVMM_X64_GPR_RBX] = env->regs[R_EBX]; ++ state->gprs[NVMM_X64_GPR_RSP] = env->regs[R_ESP]; ++ state->gprs[NVMM_X64_GPR_RBP] = env->regs[R_EBP]; ++ state->gprs[NVMM_X64_GPR_RSI] = env->regs[R_ESI]; ++ state->gprs[NVMM_X64_GPR_RDI] = env->regs[R_EDI]; ++ state->gprs[NVMM_X64_GPR_R8] = env->regs[R_R8]; ++ state->gprs[NVMM_X64_GPR_R9] = env->regs[R_R9]; ++ state->gprs[NVMM_X64_GPR_R10] = env->regs[R_R10]; ++ state->gprs[NVMM_X64_GPR_R11] = env->regs[R_R11]; ++ state->gprs[NVMM_X64_GPR_R12] = env->regs[R_R12]; ++ state->gprs[NVMM_X64_GPR_R13] = env->regs[R_R13]; ++ state->gprs[NVMM_X64_GPR_R14] = env->regs[R_R14]; ++ state->gprs[NVMM_X64_GPR_R15] = env->regs[R_R15]; ++ ++ /* RIP and RFLAGS. */ ++ state->gprs[NVMM_X64_GPR_RIP] = env->eip; ++ state->gprs[NVMM_X64_GPR_RFLAGS] = env->eflags; ++ ++ /* Segments. */ ++ nvmm_set_segment(&state->segs[NVMM_X64_SEG_CS], &env->segs[R_CS]); ++ nvmm_set_segment(&state->segs[NVMM_X64_SEG_DS], &env->segs[R_DS]); ++ nvmm_set_segment(&state->segs[NVMM_X64_SEG_ES], &env->segs[R_ES]); ++ nvmm_set_segment(&state->segs[NVMM_X64_SEG_FS], &env->segs[R_FS]); ++ nvmm_set_segment(&state->segs[NVMM_X64_SEG_GS], &env->segs[R_GS]); ++ nvmm_set_segment(&state->segs[NVMM_X64_SEG_SS], &env->segs[R_SS]); ++ ++ /* Special segments. */ ++ nvmm_set_segment(&state->segs[NVMM_X64_SEG_GDT], &env->gdt); ++ nvmm_set_segment(&state->segs[NVMM_X64_SEG_LDT], &env->ldt); ++ nvmm_set_segment(&state->segs[NVMM_X64_SEG_TR], &env->tr); ++ nvmm_set_segment(&state->segs[NVMM_X64_SEG_IDT], &env->idt); ++ ++ /* Control registers. */ ++ state->crs[NVMM_X64_CR_CR0] = env->cr[0]; ++ state->crs[NVMM_X64_CR_CR2] = env->cr[2]; ++ state->crs[NVMM_X64_CR_CR3] = env->cr[3]; ++ state->crs[NVMM_X64_CR_CR4] = env->cr[4]; ++ state->crs[NVMM_X64_CR_CR8] = qcpu->tpr; ++ state->crs[NVMM_X64_CR_XCR0] = env->xcr0; ++ ++ /* Debug registers. */ ++ state->drs[NVMM_X64_DR_DR0] = env->dr[0]; ++ state->drs[NVMM_X64_DR_DR1] = env->dr[1]; ++ state->drs[NVMM_X64_DR_DR2] = env->dr[2]; ++ state->drs[NVMM_X64_DR_DR3] = env->dr[3]; ++ state->drs[NVMM_X64_DR_DR6] = env->dr[6]; ++ state->drs[NVMM_X64_DR_DR7] = env->dr[7]; ++ ++ /* FPU. */ ++ state->fpu.fx_cw = env->fpuc; ++ state->fpu.fx_sw = (env->fpus & ~0x3800) | ((env->fpstt & 0x7) << 11); ++ state->fpu.fx_tw = 0; ++ for (i = 0; i < 8; i++) { ++ state->fpu.fx_tw |= (!env->fptags[i]) << i; ++ } ++ state->fpu.fx_opcode = env->fpop; ++ state->fpu.fx_ip.fa_64 = env->fpip; ++ state->fpu.fx_dp.fa_64 = env->fpdp; ++ state->fpu.fx_mxcsr = env->mxcsr; ++ state->fpu.fx_mxcsr_mask = 0x0000FFFF; ++ assert(sizeof(state->fpu.fx_87_ac) == sizeof(env->fpregs)); ++ memcpy(state->fpu.fx_87_ac, env->fpregs, sizeof(env->fpregs)); ++ for (i = 0; i < 16; i++) { ++ memcpy(&state->fpu.fx_xmm[i].xmm_bytes[0], ++ &env->xmm_regs[i].ZMM_Q(0), 8); ++ memcpy(&state->fpu.fx_xmm[i].xmm_bytes[8], ++ &env->xmm_regs[i].ZMM_Q(1), 8); ++ } ++ ++ /* MSRs. */ ++ state->msrs[NVMM_X64_MSR_EFER] = env->efer; ++ state->msrs[NVMM_X64_MSR_STAR] = env->star; ++#ifdef TARGET_X86_64 ++ state->msrs[NVMM_X64_MSR_LSTAR] = env->lstar; ++ state->msrs[NVMM_X64_MSR_CSTAR] = env->cstar; ++ state->msrs[NVMM_X64_MSR_SFMASK] = env->fmask; ++ state->msrs[NVMM_X64_MSR_KERNELGSBASE] = env->kernelgsbase; ++#endif ++ state->msrs[NVMM_X64_MSR_SYSENTER_CS] = env->sysenter_cs; ++ state->msrs[NVMM_X64_MSR_SYSENTER_ESP] = env->sysenter_esp; ++ state->msrs[NVMM_X64_MSR_SYSENTER_EIP] = env->sysenter_eip; ++ state->msrs[NVMM_X64_MSR_PAT] = env->pat; ++ state->msrs[NVMM_X64_MSR_TSC] = env->tsc; ++ ++ bitmap = ++ NVMM_X64_STATE_SEGS | ++ NVMM_X64_STATE_GPRS | ++ NVMM_X64_STATE_CRS | ++ NVMM_X64_STATE_DRS | ++ NVMM_X64_STATE_MSRS | ++ NVMM_X64_STATE_FPU; ++ ++ ret = nvmm_vcpu_setstate(mach, vcpu, bitmap); ++ if (ret == -1) { ++ error_report("NVMM: Failed to set virtual processor context," ++ " error=%d", errno); ++ } ++} ++ ++static void ++nvmm_get_segment(SegmentCache *qseg, const struct nvmm_x64_state_seg *nseg) ++{ ++ qseg->selector = nseg->selector; ++ qseg->limit = nseg->limit; ++ qseg->base = nseg->base; ++ ++ qseg->flags = ++ __SHIFTIN((uint32_t)nseg->attrib.type, DESC_TYPE_MASK) | ++ __SHIFTIN((uint32_t)nseg->attrib.s, DESC_S_MASK) | ++ __SHIFTIN((uint32_t)nseg->attrib.dpl, DESC_DPL_MASK) | ++ __SHIFTIN((uint32_t)nseg->attrib.p, DESC_P_MASK) | ++ __SHIFTIN((uint32_t)nseg->attrib.avl, DESC_AVL_MASK) | ++ __SHIFTIN((uint32_t)nseg->attrib.l, DESC_L_MASK) | ++ __SHIFTIN((uint32_t)nseg->attrib.def, DESC_B_MASK) | ++ __SHIFTIN((uint32_t)nseg->attrib.g, DESC_G_MASK); ++} ++ ++static void ++nvmm_get_registers(CPUState *cpu) ++{ ++ struct CPUX86State *env = (CPUArchState *)cpu->env_ptr; ++ struct nvmm_machine *mach = get_nvmm_mach(); ++ struct qemu_vcpu *qcpu = get_qemu_vcpu(cpu); ++ struct nvmm_vcpu *vcpu = &qcpu->vcpu; ++ X86CPU *x86_cpu = X86_CPU(cpu); ++ struct nvmm_x64_state *state = vcpu->state; ++ uint64_t bitmap, tpr; ++ size_t i; ++ int ret; ++ ++ assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu)); ++ ++ bitmap = ++ NVMM_X64_STATE_SEGS | ++ NVMM_X64_STATE_GPRS | ++ NVMM_X64_STATE_CRS | ++ NVMM_X64_STATE_DRS | ++ NVMM_X64_STATE_MSRS | ++ NVMM_X64_STATE_FPU; ++ ++ ret = nvmm_vcpu_getstate(mach, vcpu, bitmap); ++ if (ret == -1) { ++ error_report("NVMM: Failed to get virtual processor context," ++ " error=%d", errno); ++ } ++ ++ /* GPRs. */ ++ env->regs[R_EAX] = state->gprs[NVMM_X64_GPR_RAX]; ++ env->regs[R_ECX] = state->gprs[NVMM_X64_GPR_RCX]; ++ env->regs[R_EDX] = state->gprs[NVMM_X64_GPR_RDX]; ++ env->regs[R_EBX] = state->gprs[NVMM_X64_GPR_RBX]; ++ env->regs[R_ESP] = state->gprs[NVMM_X64_GPR_RSP]; ++ env->regs[R_EBP] = state->gprs[NVMM_X64_GPR_RBP]; ++ env->regs[R_ESI] = state->gprs[NVMM_X64_GPR_RSI]; ++ env->regs[R_EDI] = state->gprs[NVMM_X64_GPR_RDI]; ++ env->regs[R_R8] = state->gprs[NVMM_X64_GPR_R8]; ++ env->regs[R_R9] = state->gprs[NVMM_X64_GPR_R9]; ++ env->regs[R_R10] = state->gprs[NVMM_X64_GPR_R10]; ++ env->regs[R_R11] = state->gprs[NVMM_X64_GPR_R11]; ++ env->regs[R_R12] = state->gprs[NVMM_X64_GPR_R12]; ++ env->regs[R_R13] = state->gprs[NVMM_X64_GPR_R13]; ++ env->regs[R_R14] = state->gprs[NVMM_X64_GPR_R14]; ++ env->regs[R_R15] = state->gprs[NVMM_X64_GPR_R15]; ++ ++ /* RIP and RFLAGS. */ ++ env->eip = state->gprs[NVMM_X64_GPR_RIP]; ++ env->eflags = state->gprs[NVMM_X64_GPR_RFLAGS]; ++ ++ /* Segments. */ ++ nvmm_get_segment(&env->segs[R_ES], &state->segs[NVMM_X64_SEG_ES]); ++ nvmm_get_segment(&env->segs[R_CS], &state->segs[NVMM_X64_SEG_CS]); ++ nvmm_get_segment(&env->segs[R_SS], &state->segs[NVMM_X64_SEG_SS]); ++ nvmm_get_segment(&env->segs[R_DS], &state->segs[NVMM_X64_SEG_DS]); ++ nvmm_get_segment(&env->segs[R_FS], &state->segs[NVMM_X64_SEG_FS]); ++ nvmm_get_segment(&env->segs[R_GS], &state->segs[NVMM_X64_SEG_GS]); ++ ++ /* Special segments. */ ++ nvmm_get_segment(&env->gdt, &state->segs[NVMM_X64_SEG_GDT]); ++ nvmm_get_segment(&env->ldt, &state->segs[NVMM_X64_SEG_LDT]); ++ nvmm_get_segment(&env->tr, &state->segs[NVMM_X64_SEG_TR]); ++ nvmm_get_segment(&env->idt, &state->segs[NVMM_X64_SEG_IDT]); ++ ++ /* Control registers. */ ++ env->cr[0] = state->crs[NVMM_X64_CR_CR0]; ++ env->cr[2] = state->crs[NVMM_X64_CR_CR2]; ++ env->cr[3] = state->crs[NVMM_X64_CR_CR3]; ++ env->cr[4] = state->crs[NVMM_X64_CR_CR4]; ++ tpr = state->crs[NVMM_X64_CR_CR8]; ++ if (tpr != qcpu->tpr) { ++ qcpu->tpr = tpr; ++ cpu_set_apic_tpr(x86_cpu->apic_state, tpr); ++ } ++ env->xcr0 = state->crs[NVMM_X64_CR_XCR0]; ++ ++ /* Debug registers. */ ++ env->dr[0] = state->drs[NVMM_X64_DR_DR0]; ++ env->dr[1] = state->drs[NVMM_X64_DR_DR1]; ++ env->dr[2] = state->drs[NVMM_X64_DR_DR2]; ++ env->dr[3] = state->drs[NVMM_X64_DR_DR3]; ++ env->dr[6] = state->drs[NVMM_X64_DR_DR6]; ++ env->dr[7] = state->drs[NVMM_X64_DR_DR7]; ++ ++ /* FPU. */ ++ env->fpuc = state->fpu.fx_cw; ++ env->fpstt = (state->fpu.fx_sw >> 11) & 0x7; ++ env->fpus = state->fpu.fx_sw & ~0x3800; ++ for (i = 0; i < 8; i++) { ++ env->fptags[i] = !((state->fpu.fx_tw >> i) & 1); ++ } ++ env->fpop = state->fpu.fx_opcode; ++ env->fpip = state->fpu.fx_ip.fa_64; ++ env->fpdp = state->fpu.fx_dp.fa_64; ++ env->mxcsr = state->fpu.fx_mxcsr; ++ assert(sizeof(state->fpu.fx_87_ac) == sizeof(env->fpregs)); ++ memcpy(env->fpregs, state->fpu.fx_87_ac, sizeof(env->fpregs)); ++ for (i = 0; i < 16; i++) { ++ memcpy(&env->xmm_regs[i].ZMM_Q(0), ++ &state->fpu.fx_xmm[i].xmm_bytes[0], 8); ++ memcpy(&env->xmm_regs[i].ZMM_Q(1), ++ &state->fpu.fx_xmm[i].xmm_bytes[8], 8); ++ } ++ ++ /* MSRs. */ ++ env->efer = state->msrs[NVMM_X64_MSR_EFER]; ++ env->star = state->msrs[NVMM_X64_MSR_STAR]; ++#ifdef TARGET_X86_64 ++ env->lstar = state->msrs[NVMM_X64_MSR_LSTAR]; ++ env->cstar = state->msrs[NVMM_X64_MSR_CSTAR]; ++ env->fmask = state->msrs[NVMM_X64_MSR_SFMASK]; ++ env->kernelgsbase = state->msrs[NVMM_X64_MSR_KERNELGSBASE]; ++#endif ++ env->sysenter_cs = state->msrs[NVMM_X64_MSR_SYSENTER_CS]; ++ env->sysenter_esp = state->msrs[NVMM_X64_MSR_SYSENTER_ESP]; ++ env->sysenter_eip = state->msrs[NVMM_X64_MSR_SYSENTER_EIP]; ++ env->pat = state->msrs[NVMM_X64_MSR_PAT]; ++ env->tsc = state->msrs[NVMM_X64_MSR_TSC]; ++ ++ x86_update_hflags(env); ++} ++ ++static bool ++nvmm_can_take_int(CPUState *cpu) ++{ ++ struct CPUX86State *env = (CPUArchState *)cpu->env_ptr; ++ struct qemu_vcpu *qcpu = get_qemu_vcpu(cpu); ++ struct nvmm_vcpu *vcpu = &qcpu->vcpu; ++ struct nvmm_machine *mach = get_nvmm_mach(); ++ ++ if (qcpu->int_window_exit) { ++ return false; ++ } ++ ++ if (qcpu->int_shadow || !(env->eflags & IF_MASK)) { ++ struct nvmm_x64_state *state = vcpu->state; ++ ++ /* Exit on interrupt window. */ ++ nvmm_vcpu_getstate(mach, vcpu, NVMM_X64_STATE_INTR); ++ state->intr.int_window_exiting = 1; ++ nvmm_vcpu_setstate(mach, vcpu, NVMM_X64_STATE_INTR); ++ ++ return false; ++ } ++ ++ return true; ++} ++ ++static bool ++nvmm_can_take_nmi(CPUState *cpu) ++{ ++ struct qemu_vcpu *qcpu = get_qemu_vcpu(cpu); ++ ++ /* ++ * Contrary to INTs, NMIs always schedule an exit when they are ++ * completed. Therefore, if window-exiting is enabled, it means ++ * NMIs are blocked. ++ */ ++ if (qcpu->nmi_window_exit) { ++ return false; ++ } ++ ++ return true; ++} ++ ++/* ++ * Called before the VCPU is run. We inject events generated by the I/O ++ * thread, and synchronize the guest TPR. ++ */ ++static void ++nvmm_vcpu_pre_run(CPUState *cpu) ++{ ++ struct CPUX86State *env = (CPUArchState *)cpu->env_ptr; ++ struct nvmm_machine *mach = get_nvmm_mach(); ++ struct qemu_vcpu *qcpu = get_qemu_vcpu(cpu); ++ struct nvmm_vcpu *vcpu = &qcpu->vcpu; ++ X86CPU *x86_cpu = X86_CPU(cpu); ++ struct nvmm_x64_state *state = vcpu->state; ++ struct nvmm_vcpu_event *event = vcpu->event; ++ bool has_event = false; ++ bool sync_tpr = false; ++ uint8_t tpr; ++ int ret; ++ ++ qemu_mutex_lock_iothread(); ++ ++ tpr = cpu_get_apic_tpr(x86_cpu->apic_state); ++ if (tpr != qcpu->tpr) { ++ qcpu->tpr = tpr; ++ sync_tpr = true; ++ } ++ ++ /* ++ * Force the VCPU out of its inner loop to process any INIT requests ++ * or commit pending TPR access. ++ */ ++ if (cpu->interrupt_request & (CPU_INTERRUPT_INIT | CPU_INTERRUPT_TPR)) { ++ cpu->exit_request = 1; ++ } ++ ++ if (!has_event && (cpu->interrupt_request & CPU_INTERRUPT_NMI)) { ++ if (nvmm_can_take_nmi(cpu)) { ++ cpu->interrupt_request &= ~CPU_INTERRUPT_NMI; ++ event->type = NVMM_VCPU_EVENT_INTR; ++ event->vector = 2; ++ has_event = true; ++ } ++ } ++ ++ if (!has_event && (cpu->interrupt_request & CPU_INTERRUPT_HARD)) { ++ if (nvmm_can_take_int(cpu)) { ++ cpu->interrupt_request &= ~CPU_INTERRUPT_HARD; ++ event->type = NVMM_VCPU_EVENT_INTR; ++ event->vector = cpu_get_pic_interrupt(env); ++ has_event = true; ++ } ++ } ++ ++ /* Don't want SMIs. */ ++ if (cpu->interrupt_request & CPU_INTERRUPT_SMI) { ++ cpu->interrupt_request &= ~CPU_INTERRUPT_SMI; ++ } ++ ++ if (sync_tpr) { ++ ret = nvmm_vcpu_getstate(mach, vcpu, NVMM_X64_STATE_CRS); ++ if (ret == -1) { ++ error_report("NVMM: Failed to get CPU state," ++ " error=%d", errno); ++ } ++ ++ state->crs[NVMM_X64_CR_CR8] = qcpu->tpr; ++ ++ ret = nvmm_vcpu_setstate(mach, vcpu, NVMM_X64_STATE_CRS); ++ if (ret == -1) { ++ error_report("NVMM: Failed to set CPU state," ++ " error=%d", errno); ++ } ++ } ++ ++ if (has_event) { ++ ret = nvmm_vcpu_inject(mach, vcpu); ++ if (ret == -1) { ++ error_report("NVMM: Failed to inject event," ++ " error=%d", errno); ++ } ++ } ++ ++ qemu_mutex_unlock_iothread(); ++} ++ ++/* ++ * Called after the VCPU ran. We synchronize the host view of the TPR and ++ * RFLAGS. ++ */ ++static void ++nvmm_vcpu_post_run(CPUState *cpu, struct nvmm_vcpu_exit *exit) ++{ ++ struct qemu_vcpu *qcpu = get_qemu_vcpu(cpu); ++ struct CPUX86State *env = (CPUArchState *)cpu->env_ptr; ++ X86CPU *x86_cpu = X86_CPU(cpu); ++ uint64_t tpr; ++ ++ env->eflags = exit->exitstate.rflags; ++ qcpu->int_shadow = exit->exitstate.int_shadow; ++ qcpu->int_window_exit = exit->exitstate.int_window_exiting; ++ qcpu->nmi_window_exit = exit->exitstate.nmi_window_exiting; ++ ++ tpr = exit->exitstate.cr8; ++ if (qcpu->tpr != tpr) { ++ qcpu->tpr = tpr; ++ qemu_mutex_lock_iothread(); ++ cpu_set_apic_tpr(x86_cpu->apic_state, qcpu->tpr); ++ qemu_mutex_unlock_iothread(); ++ } ++} ++ ++/* -------------------------------------------------------------------------- */ ++ ++static void ++nvmm_io_callback(struct nvmm_io *io) ++{ ++ MemTxAttrs attrs = { 0 }; ++ int ret; ++ ++ ret = address_space_rw(&address_space_io, io->port, attrs, io->data, ++ io->size, !io->in); ++ if (ret != MEMTX_OK) { ++ error_report("NVMM: I/O Transaction Failed " ++ "[%s, port=%u, size=%zu]", (io->in ? "in" : "out"), ++ io->port, io->size); ++ } ++ ++ /* Needed, otherwise infinite loop. */ ++ current_cpu->vcpu_dirty = false; ++} ++ ++static void ++nvmm_mem_callback(struct nvmm_mem *mem) ++{ ++ cpu_physical_memory_rw(mem->gpa, mem->data, mem->size, mem->write); ++ ++ /* XXX Needed, otherwise infinite loop. */ ++ current_cpu->vcpu_dirty = false; ++} ++ ++static struct nvmm_assist_callbacks nvmm_callbacks = { ++ .io = nvmm_io_callback, ++ .mem = nvmm_mem_callback ++}; ++ ++/* -------------------------------------------------------------------------- */ ++ ++static int ++nvmm_handle_mem(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu) ++{ ++ int ret; ++ ++ ret = nvmm_assist_mem(mach, vcpu); ++ if (ret == -1) { ++ error_report("NVMM: Mem Assist Failed [gpa=%p]", ++ (void *)vcpu->exit->u.mem.gpa); ++ } ++ ++ return ret; ++} ++ ++static int ++nvmm_handle_io(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu) ++{ ++ int ret; ++ ++ ret = nvmm_assist_io(mach, vcpu); ++ if (ret == -1) { ++ error_report("NVMM: I/O Assist Failed [port=%d]", ++ (int)vcpu->exit->u.io.port); ++ } ++ ++ return ret; ++} ++ ++static int ++nvmm_handle_rdmsr(struct nvmm_machine *mach, CPUState *cpu, ++ struct nvmm_vcpu_exit *exit) ++{ ++ struct qemu_vcpu *qcpu = get_qemu_vcpu(cpu); ++ struct nvmm_vcpu *vcpu = &qcpu->vcpu; ++ X86CPU *x86_cpu = X86_CPU(cpu); ++ struct nvmm_x64_state *state = vcpu->state; ++ uint64_t val; ++ int ret; ++ ++ switch (exit->u.rdmsr.msr) { ++ case MSR_IA32_APICBASE: ++ val = cpu_get_apic_base(x86_cpu->apic_state); ++ break; ++ case MSR_MTRRcap: ++ case MSR_MTRRdefType: ++ case MSR_MCG_CAP: ++ case MSR_MCG_STATUS: ++ val = 0; ++ break; ++ default: /* More MSRs to add? */ ++ val = 0; ++ error_report("NVMM: Unexpected RDMSR 0x%x, ignored", ++ exit->u.rdmsr.msr); ++ break; ++ } ++ ++ ret = nvmm_vcpu_getstate(mach, vcpu, NVMM_X64_STATE_GPRS); ++ if (ret == -1) { ++ return -1; ++ } ++ ++ state->gprs[NVMM_X64_GPR_RAX] = (val & 0xFFFFFFFF); ++ state->gprs[NVMM_X64_GPR_RDX] = (val >> 32); ++ state->gprs[NVMM_X64_GPR_RIP] = exit->u.rdmsr.npc; ++ ++ ret = nvmm_vcpu_setstate(mach, vcpu, NVMM_X64_STATE_GPRS); ++ if (ret == -1) { ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static int ++nvmm_handle_wrmsr(struct nvmm_machine *mach, CPUState *cpu, ++ struct nvmm_vcpu_exit *exit) ++{ ++ struct qemu_vcpu *qcpu = get_qemu_vcpu(cpu); ++ struct nvmm_vcpu *vcpu = &qcpu->vcpu; ++ X86CPU *x86_cpu = X86_CPU(cpu); ++ struct nvmm_x64_state *state = vcpu->state; ++ uint64_t val; ++ int ret; ++ ++ val = exit->u.wrmsr.val; ++ ++ switch (exit->u.wrmsr.msr) { ++ case MSR_IA32_APICBASE: ++ cpu_set_apic_base(x86_cpu->apic_state, val); ++ break; ++ case MSR_MTRRdefType: ++ case MSR_MCG_STATUS: ++ break; ++ default: /* More MSRs to add? */ ++ error_report("NVMM: Unexpected WRMSR 0x%x [val=0x%lx], ignored", ++ exit->u.wrmsr.msr, val); ++ break; ++ } ++ ++ ret = nvmm_vcpu_getstate(mach, vcpu, NVMM_X64_STATE_GPRS); ++ if (ret == -1) { ++ return -1; ++ } ++ ++ state->gprs[NVMM_X64_GPR_RIP] = exit->u.wrmsr.npc; ++ ++ ret = nvmm_vcpu_setstate(mach, vcpu, NVMM_X64_STATE_GPRS); ++ if (ret == -1) { ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static int ++nvmm_handle_halted(struct nvmm_machine *mach, CPUState *cpu, ++ struct nvmm_vcpu_exit *exit) ++{ ++ struct CPUX86State *env = (CPUArchState *)cpu->env_ptr; ++ int ret = 0; ++ ++ qemu_mutex_lock_iothread(); ++ ++ if (!((cpu->interrupt_request & CPU_INTERRUPT_HARD) && ++ (env->eflags & IF_MASK)) && ++ !(cpu->interrupt_request & CPU_INTERRUPT_NMI)) { ++ cpu->exception_index = EXCP_HLT; ++ cpu->halted = true; ++ ret = 1; ++ } ++ ++ qemu_mutex_unlock_iothread(); ++ ++ return ret; ++} ++ ++static int ++nvmm_inject_ud(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu) ++{ ++ struct nvmm_vcpu_event *event = vcpu->event; ++ ++ event->type = NVMM_VCPU_EVENT_EXCP; ++ event->vector = 6; ++ event->u.excp.error = 0; ++ ++ return nvmm_vcpu_inject(mach, vcpu); ++} ++ ++static int ++nvmm_vcpu_loop(CPUState *cpu) ++{ ++ struct CPUX86State *env = (CPUArchState *)cpu->env_ptr; ++ struct nvmm_machine *mach = get_nvmm_mach(); ++ struct qemu_vcpu *qcpu = get_qemu_vcpu(cpu); ++ struct nvmm_vcpu *vcpu = &qcpu->vcpu; ++ X86CPU *x86_cpu = X86_CPU(cpu); ++ struct nvmm_vcpu_exit *exit = vcpu->exit; ++ int ret; ++ ++ /* ++ * Some asynchronous events must be handled outside of the inner ++ * VCPU loop. They are handled here. ++ */ ++ if (cpu->interrupt_request & CPU_INTERRUPT_INIT) { ++ nvmm_cpu_synchronize_state(cpu); ++ do_cpu_init(x86_cpu); ++ /* set int/nmi windows back to the reset state */ ++ } ++ 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_HARD) && ++ (env->eflags & IF_MASK)) || ++ (cpu->interrupt_request & CPU_INTERRUPT_NMI)) { ++ cpu->halted = false; ++ } ++ if (cpu->interrupt_request & CPU_INTERRUPT_SIPI) { ++ nvmm_cpu_synchronize_state(cpu); ++ do_cpu_sipi(x86_cpu); ++ } ++ if (cpu->interrupt_request & CPU_INTERRUPT_TPR) { ++ cpu->interrupt_request &= ~CPU_INTERRUPT_TPR; ++ nvmm_cpu_synchronize_state(cpu); ++ apic_handle_tpr_access_report(x86_cpu->apic_state, env->eip, ++ env->tpr_access_type); ++ } ++ ++ if (cpu->halted) { ++ cpu->exception_index = EXCP_HLT; ++ atomic_set(&cpu->exit_request, false); ++ return 0; ++ } ++ ++ qemu_mutex_unlock_iothread(); ++ cpu_exec_start(cpu); ++ ++ /* ++ * Inner VCPU loop. ++ */ ++ do { ++ if (cpu->vcpu_dirty) { ++ nvmm_set_registers(cpu); ++ cpu->vcpu_dirty = false; ++ } ++ ++ if (qcpu->stop) { ++ cpu->exception_index = EXCP_INTERRUPT; ++ qcpu->stop = false; ++ ret = 1; ++ break; ++ } ++ ++ nvmm_vcpu_pre_run(cpu); ++ ++ if (atomic_read(&cpu->exit_request)) { ++ qemu_cpu_kick_self(); ++ } ++ ++ ret = nvmm_vcpu_run(mach, vcpu); ++ if (ret == -1) { ++ error_report("NVMM: Failed to exec a virtual processor," ++ " error=%d", errno); ++ break; ++ } ++ ++ nvmm_vcpu_post_run(cpu, exit); ++ ++ switch (exit->reason) { ++ case NVMM_VCPU_EXIT_NONE: ++ break; ++ case NVMM_VCPU_EXIT_MEMORY: ++ ret = nvmm_handle_mem(mach, vcpu); ++ break; ++ case NVMM_VCPU_EXIT_IO: ++ ret = nvmm_handle_io(mach, vcpu); ++ break; ++ case NVMM_VCPU_EXIT_INT_READY: ++ case NVMM_VCPU_EXIT_NMI_READY: ++ case NVMM_VCPU_EXIT_TPR_CHANGED: ++ break; ++ case NVMM_VCPU_EXIT_HALTED: ++ ret = nvmm_handle_halted(mach, cpu, exit); ++ break; ++ case NVMM_VCPU_EXIT_SHUTDOWN: ++ qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); ++ cpu->exception_index = EXCP_INTERRUPT; ++ ret = 1; ++ break; ++ case NVMM_VCPU_EXIT_RDMSR: ++ ret = nvmm_handle_rdmsr(mach, cpu, exit); ++ break; ++ case NVMM_VCPU_EXIT_WRMSR: ++ ret = nvmm_handle_wrmsr(mach, cpu, exit); ++ break; ++ case NVMM_VCPU_EXIT_MONITOR: ++ case NVMM_VCPU_EXIT_MWAIT: ++ ret = nvmm_inject_ud(mach, vcpu); ++ break; ++ default: ++ error_report("NVMM: Unexpected VM exit code 0x%lx [hw=0x%lx]", ++ exit->reason, exit->u.inv.hwcode); ++ nvmm_get_registers(cpu); ++ qemu_mutex_lock_iothread(); ++ qemu_system_guest_panicked(cpu_get_crash_info(cpu)); ++ qemu_mutex_unlock_iothread(); ++ ret = -1; ++ break; ++ } ++ } while (ret == 0); ++ ++ cpu_exec_end(cpu); ++ qemu_mutex_lock_iothread(); ++ current_cpu = cpu; ++ ++ atomic_set(&cpu->exit_request, false); ++ ++ return ret < 0; ++} ++ ++/* -------------------------------------------------------------------------- */ ++ ++static void ++do_nvmm_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg) ++{ ++ nvmm_get_registers(cpu); ++ cpu->vcpu_dirty = true; ++} ++ ++static void ++do_nvmm_cpu_synchronize_post_reset(CPUState *cpu, run_on_cpu_data arg) ++{ ++ nvmm_set_registers(cpu); ++ cpu->vcpu_dirty = false; ++} ++ ++static void ++do_nvmm_cpu_synchronize_post_init(CPUState *cpu, run_on_cpu_data arg) ++{ ++ nvmm_set_registers(cpu); ++ cpu->vcpu_dirty = false; ++} ++ ++static void ++do_nvmm_cpu_synchronize_pre_loadvm(CPUState *cpu, run_on_cpu_data arg) ++{ ++ cpu->vcpu_dirty = true; ++} ++ ++void nvmm_cpu_synchronize_state(CPUState *cpu) ++{ ++ if (!cpu->vcpu_dirty) { ++ run_on_cpu(cpu, do_nvmm_cpu_synchronize_state, RUN_ON_CPU_NULL); ++ } ++} ++ ++void nvmm_cpu_synchronize_post_reset(CPUState *cpu) ++{ ++ run_on_cpu(cpu, do_nvmm_cpu_synchronize_post_reset, RUN_ON_CPU_NULL); ++} ++ ++void nvmm_cpu_synchronize_post_init(CPUState *cpu) ++{ ++ run_on_cpu(cpu, do_nvmm_cpu_synchronize_post_init, RUN_ON_CPU_NULL); ++} ++ ++void nvmm_cpu_synchronize_pre_loadvm(CPUState *cpu) ++{ ++ run_on_cpu(cpu, do_nvmm_cpu_synchronize_pre_loadvm, RUN_ON_CPU_NULL); ++} ++ ++/* -------------------------------------------------------------------------- */ ++ ++static Error *nvmm_migration_blocker; ++ ++static void ++nvmm_ipi_signal(int sigcpu) ++{ ++ struct qemu_vcpu *qcpu; ++ ++ if (current_cpu) { ++ qcpu = get_qemu_vcpu(current_cpu); ++ qcpu->stop = true; ++ } ++} ++ ++static void ++nvmm_init_cpu_signals(void) ++{ ++ struct sigaction sigact; ++ sigset_t set; ++ ++ /* Install the IPI handler. */ ++ memset(&sigact, 0, sizeof(sigact)); ++ sigact.sa_handler = nvmm_ipi_signal; ++ sigaction(SIG_IPI, &sigact, NULL); ++ ++ /* Allow IPIs on the current thread. */ ++ sigprocmask(SIG_BLOCK, NULL, &set); ++ sigdelset(&set, SIG_IPI); ++ pthread_sigmask(SIG_SETMASK, &set, NULL); ++} ++ ++int ++nvmm_init_vcpu(CPUState *cpu) ++{ ++ struct nvmm_machine *mach = get_nvmm_mach(); ++ struct nvmm_vcpu_conf_cpuid cpuid; ++ struct nvmm_vcpu_conf_tpr tpr; ++ Error *local_error = NULL; ++ struct qemu_vcpu *qcpu; ++ int ret, err; ++ ++ nvmm_init_cpu_signals(); ++ ++ if (nvmm_migration_blocker == NULL) { ++ error_setg(&nvmm_migration_blocker, ++ "NVMM: Migration not supported"); ++ ++ (void)migrate_add_blocker(nvmm_migration_blocker, &local_error); ++ if (local_error) { ++ error_report_err(local_error); ++ migrate_del_blocker(nvmm_migration_blocker); ++ error_free(nvmm_migration_blocker); ++ return -EINVAL; ++ } ++ } ++ ++ qcpu = g_malloc0(sizeof(*qcpu)); ++ if (qcpu == NULL) { ++ error_report("NVMM: Failed to allocate VCPU context."); ++ return -ENOMEM; ++ } ++ ++ ret = nvmm_vcpu_create(mach, cpu->cpu_index, &qcpu->vcpu); ++ if (ret == -1) { ++ err = errno; ++ error_report("NVMM: Failed to create a virtual processor," ++ " error=%d", err); ++ g_free(qcpu); ++ return -err; ++ } ++ ++ memset(&cpuid, 0, sizeof(cpuid)); ++ cpuid.mask = 1; ++ cpuid.leaf = 0x00000001; ++ cpuid.u.mask.set.edx = CPUID_MCE | CPUID_MCA | CPUID_MTRR; ++ ret = nvmm_vcpu_configure(mach, &qcpu->vcpu, NVMM_VCPU_CONF_CPUID, ++ &cpuid); ++ if (ret == -1) { ++ err = errno; ++ error_report("NVMM: Failed to configure a virtual processor," ++ " error=%d", err); ++ g_free(qcpu); ++ return -err; ++ } ++ ++ ret = nvmm_vcpu_configure(mach, &qcpu->vcpu, NVMM_VCPU_CONF_CALLBACKS, ++ &nvmm_callbacks); ++ if (ret == -1) { ++ err = errno; ++ error_report("NVMM: Failed to configure a virtual processor," ++ " error=%d", err); ++ g_free(qcpu); ++ return -err; ++ } ++ ++ if (qemu_mach.cap.arch.vcpu_conf_support & NVMM_CAP_ARCH_VCPU_CONF_TPR) { ++ memset(&tpr, 0, sizeof(tpr)); ++ tpr.exit_changed = 1; ++ ret = nvmm_vcpu_configure(mach, &qcpu->vcpu, NVMM_VCPU_CONF_TPR, &tpr); ++ if (ret == -1) { ++ err = errno; ++ error_report("NVMM: Failed to configure a virtual processor," ++ " error=%d", err); ++ g_free(qcpu); ++ return -err; ++ } ++ } ++ ++ cpu->vcpu_dirty = true; ++ cpu->hax_vcpu = (struct hax_vcpu_state *)qcpu; ++ ++ return 0; ++} ++ ++int ++nvmm_vcpu_exec(CPUState *cpu) ++{ ++ int ret, fatal; ++ ++ while (1) { ++ if (cpu->exception_index >= EXCP_INTERRUPT) { ++ ret = cpu->exception_index; ++ cpu->exception_index = -1; ++ break; ++ } ++ ++ fatal = nvmm_vcpu_loop(cpu); ++ ++ if (fatal) { ++ error_report("NVMM: Failed to execute a VCPU."); ++ abort(); ++ } ++ } ++ ++ return ret; ++} ++ ++void ++nvmm_destroy_vcpu(CPUState *cpu) ++{ ++ struct nvmm_machine *mach = get_nvmm_mach(); ++ struct qemu_vcpu *qcpu = get_qemu_vcpu(cpu); ++ ++ nvmm_vcpu_destroy(mach, &qcpu->vcpu); ++ g_free(cpu->hax_vcpu); ++} ++ ++/* -------------------------------------------------------------------------- */ ++ ++static void ++nvmm_update_mapping(hwaddr start_pa, ram_addr_t size, uintptr_t hva, ++ bool add, bool rom, const char *name) ++{ ++ struct nvmm_machine *mach = get_nvmm_mach(); ++ int ret, prot; ++ ++ if (add) { ++ prot = PROT_READ | PROT_EXEC; ++ if (!rom) { ++ prot |= PROT_WRITE; ++ } ++ ret = nvmm_gpa_map(mach, hva, start_pa, size, prot); ++ } else { ++ ret = nvmm_gpa_unmap(mach, hva, start_pa, size); ++ } ++ ++ if (ret == -1) { ++ error_report("NVMM: Failed to %s GPA range '%s' PA:%p, " ++ "Size:%p bytes, HostVA:%p, error=%d", ++ (add ? "map" : "unmap"), name, (void *)(uintptr_t)start_pa, ++ (void *)size, (void *)hva, errno); ++ } ++} ++ ++static void ++nvmm_process_section(MemoryRegionSection *section, int add) ++{ ++ MemoryRegion *mr = section->mr; ++ hwaddr start_pa = section->offset_within_address_space; ++ ram_addr_t size = int128_get64(section->size); ++ unsigned int delta; ++ uintptr_t hva; ++ ++ if (!memory_region_is_ram(mr)) { ++ return; ++ } ++ ++ /* Adjust start_pa and size so that they are page-aligned. */ ++ 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; ++ } ++ ++ hva = (uintptr_t)memory_region_get_ram_ptr(mr) + ++ section->offset_within_region + delta; ++ ++ nvmm_update_mapping(start_pa, size, hva, add, ++ memory_region_is_rom(mr), mr->name); ++} ++ ++static void ++nvmm_region_add(MemoryListener *listener, MemoryRegionSection *section) ++{ ++ memory_region_ref(section->mr); ++ nvmm_process_section(section, 1); ++} ++ ++static void ++nvmm_region_del(MemoryListener *listener, MemoryRegionSection *section) ++{ ++ nvmm_process_section(section, 0); ++ memory_region_unref(section->mr); ++} ++ ++static void ++nvmm_transaction_begin(MemoryListener *listener) ++{ ++ /* nothing */ ++} ++ ++static void ++nvmm_transaction_commit(MemoryListener *listener) ++{ ++ /* nothing */ ++} ++ ++static void ++nvmm_log_sync(MemoryListener *listener, MemoryRegionSection *section) ++{ ++ MemoryRegion *mr = section->mr; ++ ++ if (!memory_region_is_ram(mr)) { ++ return; ++ } ++ ++ memory_region_set_dirty(mr, 0, int128_get64(section->size)); ++} ++ ++static MemoryListener nvmm_memory_listener = { ++ .begin = nvmm_transaction_begin, ++ .commit = nvmm_transaction_commit, ++ .region_add = nvmm_region_add, ++ .region_del = nvmm_region_del, ++ .log_sync = nvmm_log_sync, ++ .priority = 10, ++}; ++ ++static void ++nvmm_ram_block_added(RAMBlockNotifier *n, void *host, size_t size) ++{ ++ struct nvmm_machine *mach = get_nvmm_mach(); ++ uintptr_t hva = (uintptr_t)host; ++ int ret; ++ ++ ret = nvmm_hva_map(mach, hva, size); ++ ++ if (ret == -1) { ++ error_report("NVMM: Failed to map HVA, HostVA:%p " ++ "Size:%p bytes, error=%d", ++ (void *)hva, (void *)size, errno); ++ } ++} ++ ++static struct RAMBlockNotifier nvmm_ram_notifier = { ++ .ram_block_added = nvmm_ram_block_added ++}; ++ ++/* -------------------------------------------------------------------------- */ ++ ++static void ++nvmm_handle_interrupt(CPUState *cpu, int mask) ++{ ++ cpu->interrupt_request |= mask; ++ ++ if (!qemu_cpu_is_self(cpu)) { ++ qemu_cpu_kick(cpu); ++ } ++} ++ ++/* -------------------------------------------------------------------------- */ ++ ++static int ++nvmm_accel_init(MachineState *ms) ++{ ++ int ret, err; ++ ++ ret = nvmm_init(); ++ if (ret == -1) { ++ err = errno; ++ error_report("NVMM: Initialization failed, error=%d", errno); ++ return -err; ++ } ++ ++ ret = nvmm_capability(&qemu_mach.cap); ++ if (ret == -1) { ++ err = errno; ++ error_report("NVMM: Unable to fetch capability, error=%d", errno); ++ return -err; ++ } ++ if (qemu_mach.cap.version != 1) { ++ error_report("NVMM: Unsupported version %u", qemu_mach.cap.version); ++ return -EPROGMISMATCH; ++ } ++ if (qemu_mach.cap.state_size != sizeof(struct nvmm_x64_state)) { ++ error_report("NVMM: Wrong state size %u", qemu_mach.cap.state_size); ++ return -EPROGMISMATCH; ++ } ++ ++ ret = nvmm_machine_create(&qemu_mach.mach); ++ if (ret == -1) { ++ err = errno; ++ error_report("NVMM: Machine creation failed, error=%d", errno); ++ return -err; ++ } ++ ++ memory_listener_register(&nvmm_memory_listener, &address_space_memory); ++ ram_block_notifier_add(&nvmm_ram_notifier); ++ ++ cpu_interrupt_handler = nvmm_handle_interrupt; ++ ++ printf("NetBSD Virtual Machine Monitor accelerator is operational\n"); ++ return 0; ++} ++ ++int ++nvmm_enabled(void) ++{ ++ return nvmm_allowed; ++} ++ ++static void ++nvmm_accel_class_init(ObjectClass *oc, void *data) ++{ ++ AccelClass *ac = ACCEL_CLASS(oc); ++ ac->name = "NVMM"; ++ ac->init_machine = nvmm_accel_init; ++ ac->allowed = &nvmm_allowed; ++} ++ ++static const TypeInfo nvmm_accel_type = { ++ .name = ACCEL_CLASS_NAME("nvmm"), ++ .parent = TYPE_ACCEL, ++ .class_init = nvmm_accel_class_init, ++}; ++ ++static void ++nvmm_type_init(void) ++{ ++ type_register_static(&nvmm_accel_type); ++} ++ ++type_init(nvmm_type_init); |