diff --git a/.travis.yml b/.travis.yml index 708c8860177e332af38891aff946643c70753a0c..01a57399b59dc66d28f34b65ee7a9c449ba8cb07 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,12 +13,13 @@ addons: - libattr1-dev - libbrlapi-dev - libcap-ng-dev + - libgcc-6-dev - libgnutls-dev - libgtk-3-dev - libiscsi-dev - liblttng-ust-dev - - libnfs-dev - libncurses5-dev + - libnfs-dev - libnss3-dev - libpixman-1-dev - libpng12-dev diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index f290f487a573adc8632165a3d8cef3a80e77c5c5..b91fcb7160d327897bea03c2c84b65a0d6248d1f 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -235,6 +235,7 @@ static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot) { KVMState *s = kvm_state; struct kvm_userspace_memory_region mem; + int ret; mem.slot = slot->slot | (kml->as_id << 16); mem.guest_phys_addr = slot->start_addr; @@ -248,7 +249,10 @@ static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot) kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem); } mem.memory_size = slot->memory_size; - return kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem); + ret = kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem); + trace_kvm_set_user_memory(mem.slot, mem.flags, mem.guest_phys_addr, + mem.memory_size, mem.userspace_addr, ret); + return ret; } int kvm_destroy_vcpu(CPUState *cpu) diff --git a/accel/kvm/trace-events b/accel/kvm/trace-events index f89ba5578dc1be2d04e4890b704200da66796d37..58e98efe5deae6b01380c77c3f8d9dc62c6bd5d7 100644 --- a/accel/kvm/trace-events +++ b/accel/kvm/trace-events @@ -12,4 +12,5 @@ kvm_irqchip_commit_routes(void) "" kvm_irqchip_add_msi_route(char *name, int vector, int virq) "dev %s vector %d virq %d" kvm_irqchip_update_msi_route(int virq) "Updating MSI route virq=%d" kvm_irqchip_release_virq(int virq) "virq %d" +kvm_set_user_memory(uint32_t slot, uint32_t flags, uint64_t guest_phys_addr, uint64_t memory_size, uint64_t userspace_addr, int ret) "Slot#%d flags=0x%x gpa=0x%"PRIx64 " size=0x%"PRIx64 " ua=0x%"PRIx64 " ret=%d" diff --git a/accel/stubs/Makefile.objs b/accel/stubs/Makefile.objs index 779343b0c02d7afed5c6a9bf037c34d8bed1810b..3894caf95da64d525c9cb6fabeab64e27e0a0bd2 100644 --- a/accel/stubs/Makefile.objs +++ b/accel/stubs/Makefile.objs @@ -1,4 +1,5 @@ -obj-$(call lnot,$(CONFIG_HAX)) += hax-stub.o -obj-$(call lnot,$(CONFIG_HVF)) += hvf-stub.o -obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o -obj-$(call lnot,$(CONFIG_TCG)) += tcg-stub.o +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_KVM)) += kvm-stub.o +obj-$(call lnot,$(CONFIG_TCG)) += tcg-stub.o diff --git a/accel/stubs/whpx-stub.c b/accel/stubs/whpx-stub.c new file mode 100644 index 0000000000000000000000000000000000000000..5fb049c28172088d23ec3ce93a1f5b4311f10c6b --- /dev/null +++ b/accel/stubs/whpx-stub.c @@ -0,0 +1,48 @@ +/* + * QEMU Windows Hypervisor Platform accelerator (WHPX) stub + * + * Copyright Microsoft Corp. 2017 + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "cpu.h" +#include "sysemu/whpx.h" + +int whpx_init_vcpu(CPUState *cpu) +{ + return -1; +} + +int whpx_vcpu_exec(CPUState *cpu) +{ + return -1; +} + +void whpx_destroy_vcpu(CPUState *cpu) +{ +} + +void whpx_vcpu_kick(CPUState *cpu) +{ +} + +void whpx_cpu_synchronize_state(CPUState *cpu) +{ +} + +void whpx_cpu_synchronize_post_reset(CPUState *cpu) +{ +} + +void whpx_cpu_synchronize_post_init(CPUState *cpu) +{ +} + +void whpx_cpu_synchronize_pre_loadvm(CPUState *cpu) +{ +} diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index a0a4a1924ea6e956c6f7ff6537001c99a762adb7..77899584f27f21a837b9fad5769cb280c51bbb31 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -503,39 +503,6 @@ int cpu_signal_handler(int host_signum, void *pinfo, void *puc) return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask); } -#elif defined(__ia64) - -#ifndef __ISR_VALID - /* This ought to be in <bits/siginfo.h>... */ -# define __ISR_VALID 1 -#endif - -int cpu_signal_handler(int host_signum, void *pinfo, void *puc) -{ - siginfo_t *info = pinfo; - ucontext_t *uc = puc; - unsigned long ip; - int is_write = 0; - - ip = uc->uc_mcontext.sc_ip; - switch (host_signum) { - case SIGILL: - case SIGFPE: - case SIGSEGV: - case SIGBUS: - case SIGTRAP: - if (info->si_code && (info->si_segvflags & __ISR_VALID)) { - /* ISR.W (write-access) is bit 33: */ - is_write = (info->si_isr >> 33) & 1; - } - break; - - default: - break; - } - return handle_cpu_signal(ip, info, is_write, (sigset_t *)&uc->uc_sigmask); -} - #elif defined(__s390__) int cpu_signal_handler(int host_signum, void *pinfo, diff --git a/backends/Makefile.objs b/backends/Makefile.objs index 0400799efdc8dfd36e82745860e3daab5f08f056..67eeeba5fce5b6c7fdc8c07a7b510ffb43f5b846 100644 --- a/backends/Makefile.objs +++ b/backends/Makefile.objs @@ -8,3 +8,5 @@ common-obj-$(CONFIG_LINUX) += hostmem-file.o common-obj-y += cryptodev.o common-obj-y += cryptodev-builtin.o + +common-obj-$(CONFIG_LINUX) += hostmem-memfd.o diff --git a/backends/hostmem-memfd.c b/backends/hostmem-memfd.c new file mode 100644 index 0000000000000000000000000000000000000000..1e20fe0ba8c9c51c05a8f7f8124e626306f830d5 --- /dev/null +++ b/backends/hostmem-memfd.c @@ -0,0 +1,170 @@ +/* + * QEMU host memfd memory backend + * + * Copyright (C) 2018 Red Hat Inc + * + * Authors: + * Marc-André Lureau <marcandre.lureau@redhat.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 "qemu-common.h" +#include "sysemu/hostmem.h" +#include "sysemu/sysemu.h" +#include "qom/object_interfaces.h" +#include "qemu/memfd.h" +#include "qapi/error.h" + +#define TYPE_MEMORY_BACKEND_MEMFD "memory-backend-memfd" + +#define MEMORY_BACKEND_MEMFD(obj) \ + OBJECT_CHECK(HostMemoryBackendMemfd, (obj), TYPE_MEMORY_BACKEND_MEMFD) + +typedef struct HostMemoryBackendMemfd HostMemoryBackendMemfd; + +struct HostMemoryBackendMemfd { + HostMemoryBackend parent_obj; + + bool hugetlb; + uint64_t hugetlbsize; + bool seal; +}; + +static void +memfd_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) +{ + HostMemoryBackendMemfd *m = MEMORY_BACKEND_MEMFD(backend); + char *name; + int fd; + + if (!backend->size) { + error_setg(errp, "can't create backend with size 0"); + return; + } + + if (host_memory_backend_mr_inited(backend)) { + return; + } + + backend->force_prealloc = mem_prealloc; + fd = qemu_memfd_create(TYPE_MEMORY_BACKEND_MEMFD, backend->size, + m->hugetlb, m->hugetlbsize, m->seal ? + F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL : 0, + errp); + if (fd == -1) { + return; + } + + name = object_get_canonical_path(OBJECT(backend)); + memory_region_init_ram_from_fd(&backend->mr, OBJECT(backend), + name, backend->size, true, fd, errp); + g_free(name); +} + +static bool +memfd_backend_get_hugetlb(Object *o, Error **errp) +{ + return MEMORY_BACKEND_MEMFD(o)->hugetlb; +} + +static void +memfd_backend_set_hugetlb(Object *o, bool value, Error **errp) +{ + MEMORY_BACKEND_MEMFD(o)->hugetlb = value; +} + +static void +memfd_backend_set_hugetlbsize(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + HostMemoryBackendMemfd *m = MEMORY_BACKEND_MEMFD(obj); + Error *local_err = NULL; + uint64_t value; + + if (host_memory_backend_mr_inited(MEMORY_BACKEND(obj))) { + error_setg(&local_err, "cannot change property value"); + goto out; + } + + visit_type_size(v, name, &value, &local_err); + if (local_err) { + goto out; + } + if (!value) { + error_setg(&local_err, "Property '%s.%s' doesn't take value '%" + PRIu64 "'", object_get_typename(obj), name, value); + goto out; + } + m->hugetlbsize = value; +out: + error_propagate(errp, local_err); +} + +static void +memfd_backend_get_hugetlbsize(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + HostMemoryBackendMemfd *m = MEMORY_BACKEND_MEMFD(obj); + uint64_t value = m->hugetlbsize; + + visit_type_size(v, name, &value, errp); +} + +static bool +memfd_backend_get_seal(Object *o, Error **errp) +{ + return MEMORY_BACKEND_MEMFD(o)->seal; +} + +static void +memfd_backend_set_seal(Object *o, bool value, Error **errp) +{ + MEMORY_BACKEND_MEMFD(o)->seal = value; +} + +static void +memfd_backend_instance_init(Object *obj) +{ + HostMemoryBackendMemfd *m = MEMORY_BACKEND_MEMFD(obj); + + /* default to sealed file */ + m->seal = true; +} + +static void +memfd_backend_class_init(ObjectClass *oc, void *data) +{ + HostMemoryBackendClass *bc = MEMORY_BACKEND_CLASS(oc); + + bc->alloc = memfd_backend_memory_alloc; + + object_class_property_add_bool(oc, "hugetlb", + memfd_backend_get_hugetlb, + memfd_backend_set_hugetlb, + &error_abort); + object_class_property_add(oc, "hugetlbsize", "int", + memfd_backend_get_hugetlbsize, + memfd_backend_set_hugetlbsize, + NULL, NULL, &error_abort); + object_class_property_add_bool(oc, "seal", + memfd_backend_get_seal, + memfd_backend_set_seal, + &error_abort); +} + +static const TypeInfo memfd_backend_info = { + .name = TYPE_MEMORY_BACKEND_MEMFD, + .parent = TYPE_MEMORY_BACKEND, + .instance_init = memfd_backend_instance_init, + .class_init = memfd_backend_class_init, + .instance_size = sizeof(HostMemoryBackendMemfd), +}; + +static void register_types(void) +{ + type_register_static(&memfd_backend_info); +} + +type_init(register_types); diff --git a/chardev/char-pty.c b/chardev/char-pty.c index 89315e680713ec61032b850cd0f5b7bc15780243..68fd4e20c358747c87c22669847fcd762064eed9 100644 --- a/chardev/char-pty.c +++ b/chardev/char-pty.c @@ -51,15 +51,32 @@ typedef struct { static void pty_chr_update_read_handler_locked(Chardev *chr); static void pty_chr_state(Chardev *chr, int connected); +static void pty_chr_timer_cancel(PtyChardev *s) +{ + if (s->timer_src) { + g_source_destroy(s->timer_src); + g_source_unref(s->timer_src); + s->timer_src = NULL; + } +} + +static void pty_chr_open_src_cancel(PtyChardev *s) +{ + if (s->open_source) { + g_source_destroy(s->open_source); + g_source_unref(s->open_source); + s->open_source = NULL; + } +} + static gboolean pty_chr_timer(gpointer opaque) { struct Chardev *chr = CHARDEV(opaque); PtyChardev *s = PTY_CHARDEV(opaque); qemu_mutex_lock(&chr->chr_write_lock); - s->timer_src = NULL; - g_source_unref(s->open_source); - s->open_source = NULL; + pty_chr_timer_cancel(s); + pty_chr_open_src_cancel(s); if (!s->connected) { /* Next poll ... */ pty_chr_update_read_handler_locked(chr); @@ -68,15 +85,6 @@ static gboolean pty_chr_timer(gpointer opaque) return FALSE; } -static void pty_chr_timer_cancel(PtyChardev *s) -{ - if (s->timer_src) { - g_source_destroy(s->timer_src); - g_source_unref(s->timer_src); - s->timer_src = NULL; - } -} - /* Called with chr_write_lock held. */ static void pty_chr_rearm_timer(Chardev *chr, int ms) { @@ -195,11 +203,7 @@ static void pty_chr_state(Chardev *chr, int connected) PtyChardev *s = PTY_CHARDEV(chr); if (!connected) { - if (s->open_source) { - g_source_destroy(s->open_source); - g_source_unref(s->open_source); - s->open_source = NULL; - } + pty_chr_open_src_cancel(s); remove_fd_in_watch(chr); s->connected = 0; /* (re-)connect poll interval for idle guests: once per second. diff --git a/chardev/char-socket.c b/chardev/char-socket.c index 77cdf487ebe07ef688eccd6fd7d40249d5186f3a..a340af6cd3a053345fd2e47f58091cf00b4e4ac6 100644 --- a/chardev/char-socket.c +++ b/chardev/char-socket.c @@ -42,6 +42,7 @@ typedef struct { QIOChannel *ioc; /* Client I/O channel */ QIOChannelSocket *sioc; /* Client master channel */ QIONetListener *listener; + GSource *hup_source; QCryptoTLSCreds *tls_creds; int connected; int max_size; @@ -352,6 +353,12 @@ static void tcp_chr_free_connection(Chardev *chr) s->read_msgfds_num = 0; } + if (s->hup_source != NULL) { + g_source_destroy(s->hup_source); + g_source_unref(s->hup_source); + s->hup_source = NULL; + } + tcp_set_msgfds(chr, NULL, 0); remove_fd_in_watch(chr); object_unref(OBJECT(s->sioc)); @@ -455,6 +462,15 @@ static gboolean tcp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque) return TRUE; } +static gboolean tcp_chr_hup(QIOChannel *channel, + GIOCondition cond, + void *opaque) +{ + Chardev *chr = CHARDEV(opaque); + tcp_chr_disconnect(chr); + return G_SOURCE_REMOVE; +} + static int tcp_chr_sync_read(Chardev *chr, const uint8_t *buf, int len) { SocketChardev *s = SOCKET_CHARDEV(chr); @@ -528,6 +544,12 @@ static void tcp_chr_connect(void *opaque) tcp_chr_read, chr, chr->gcontext); } + + s->hup_source = qio_channel_create_watch(s->ioc, G_IO_HUP); + g_source_set_callback(s->hup_source, (GSourceFunc)tcp_chr_hup, + chr, NULL); + g_source_attach(s->hup_source, chr->gcontext); + qemu_chr_be_event(chr, CHR_EVENT_OPENED); } diff --git a/configure b/configure index 886abe6a39c03f0222eb7ac3d9a51e55bda6544d..831ebf248fce7e3e0fe7218a5f6b156e7532be69 100755 --- a/configure +++ b/configure @@ -222,6 +222,17 @@ supported_hvf_target() { return 1 } +supported_whpx_target() { + test "$whpx" = "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) @@ -248,6 +259,7 @@ supported_target() { supported_xen_target "$1" && return 0 supported_hax_target "$1" && return 0 supported_hvf_target "$1" && return 0 + supported_whpx_target "$1" && return 0 print_error "TCG disabled, but hardware accelerator not available for '$target'" return 1 } @@ -338,10 +350,12 @@ vhost_user="" kvm="no" hax="no" hvf="no" +whpx="no" rdma="" gprof="no" debug_tcg="no" debug="no" +sanitizers="no" fortify_source="" strip_opt="yes" tcg_interpreter="no" @@ -636,8 +650,6 @@ elif check_define _ARCH_PPC ; then fi elif check_define __mips__ ; then cpu="mips" -elif check_define __ia64__ ; then - cpu="ia64" elif check_define __s390__ ; then if check_define __s390x__ ; then cpu="s390x" @@ -995,6 +1007,10 @@ for opt do strip_opt="no" fortify_source="no" ;; + --enable-sanitizers) sanitizers="yes" + ;; + --disable-sanitizers) sanitizers="no" + ;; --enable-sparse) sparse="yes" ;; --disable-sparse) sparse="no" @@ -1055,6 +1071,10 @@ for opt do ;; --enable-hvf) hvf="yes" ;; + --disable-whpx) whpx="no" + ;; + --enable-whpx) whpx="yes" + ;; --disable-tcg-interpreter) tcg_interpreter="no" ;; --enable-tcg-interpreter) tcg_interpreter="yes" @@ -1476,6 +1496,7 @@ Advanced options (experts only): --firmwarepath=PATH search PATH for firmware files --with-confsuffix=SUFFIX suffix for QEMU data inside datadir/libdir/sysconfdir [$confsuffix] --enable-debug enable common debug build options + --enable-sanitizers enable default sanitizers --disable-strip disable stripping binaries --disable-werror disable compilation abort on warning --disable-stack-protector disable compiler-provided stack protection @@ -1553,6 +1574,7 @@ disabled with --disable-FEATURE, default is enabled if available: kvm KVM acceleration support hax HAX acceleration support hvf Hypervisor.framework acceleration support + whpx Windows Hypervisor Platform acceleration support rdma RDMA-based migration support vde support for vde network netmap support for netmap network @@ -2450,6 +2472,30 @@ if test "$xen_pv_domain_build" = "yes" && "which requires Xen support." fi +########################################## +# Windows Hypervisor Platform accelerator (WHPX) check +if test "$whpx" != "no" ; then + cat > $TMPC << EOF +#include <windows.h> +#include <winhvplatform.h> +#include <winhvemulation.h> +int main(void) { + WHV_CAPABILITY whpx_cap; + WHvGetCapability(WHvCapabilityCodeFeatures, &whpx_cap, sizeof(whpx_cap)); + return 0; +} +EOF + if compile_prog "" "-lwinhvplatform -lwinhvemulation" ; then + libs_softmmu="$libs_softmmu -lwinhvplatform -lwinhvemulation" + whpx="yes" + else + if test "$whpx" = "yes"; then + feature_not_found "winhvplatform" "winhvemulation is not installed" + fi + whpx="no" + fi +fi + ########################################## # Sparse probe if test "$sparse" != "no" ; then @@ -4743,9 +4789,6 @@ if test "$coroutine_pool" = ""; then fi if test "$debug_stack_usage" = "yes"; then - if test "$cpu" = "ia64" -o "$cpu" = "hppa"; then - error_exit "stack usage debugging is not supported for $cpu" - fi if test "$coroutine_pool" = "yes"; then echo "WARN: disabling coroutine pool for stack usage debugging" coroutine_pool=no @@ -5204,10 +5247,46 @@ if compile_prog "" "" ; then have_utmpx=yes fi +########################################## +# checks for sanitizers + +write_c_skeleton + +have_asan=no +have_ubsan=no +have_asan_iface_h=no +have_asan_iface_fiber=no + +if test "$sanitizers" = "yes" ; then + if compile_prog "$CPU_CFLAGS -Werror -fsanitize=address" ""; then + have_asan=yes + fi + if compile_prog "$CPU_CFLAGS -Werror -fsanitize=undefined" ""; then + have_ubsan=yes + fi + + if check_include "sanitizer/asan_interface.h" ; then + have_asan_iface_h=yes + fi + + cat > $TMPC << EOF +#include <sanitizer/asan_interface.h> +int main(void) { + __sanitizer_start_switch_fiber(0, 0, 0); + return 0; +} +EOF + if compile_prog "$CPU_CFLAGS -Werror -fsanitize=address" "" ; then + have_asan_iface_fiber=yes + fi +fi + ########################################## # End of CC checks # After here, no more $cc or $ld runs +write_c_skeleton + if test "$gcov" = "yes" ; then CFLAGS="-fprofile-arcs -ftest-coverage -g $CFLAGS" LDFLAGS="-fprofile-arcs -ftest-coverage $LDFLAGS" @@ -5228,6 +5307,20 @@ else CFLAGS="-O2 $CFLAGS" fi +if test "$have_asan" = "yes"; then + CFLAGS="-fsanitize=address $CFLAGS" + if test "$have_asan_iface_h" = "no" ; then + echo "ASAN build enabled, but ASAN header missing." \ + "Without code annotation, the report may be inferior." + elif test "$have_asan_iface_fiber" = "no" ; then + echo "ASAN build enabled, but ASAN header is too old." \ + "Without code annotation, the report may be inferior." + fi +fi +if test "$have_ubsan" = "yes"; then + CFLAGS="-fsanitize=undefined $CFLAGS" +fi + ########################################## # Do we have libnfs if test "$libnfs" != "no" ; then @@ -5596,6 +5689,7 @@ echo "Install blobs $blobs" echo "KVM support $kvm" echo "HAX support $hax" echo "HVF support $hvf" +echo "WHPX support $whpx" echo "TCG support $tcg" if test "$tcg" = "yes" ; then echo "TCG debug enabled $debug_tcg" @@ -5763,7 +5857,7 @@ if test "$mingw32" = "yes" ; then echo "CONFIG_QGA_NTDDDISK=y" >> $config_host_mak fi if test "$guest_agent_msi" = "yes"; then - echo "QEMU_GA_MSI_ENABLED=yes" >> $config_host_mak + echo "QEMU_GA_MSI_ENABLED=yes" >> $config_host_mak echo "QEMU_GA_MSI_MINGW_DLL_PATH=${QEMU_GA_MSI_MINGW_DLL_PATH}" >> $config_host_mak echo "QEMU_GA_MSI_WITH_VSS=${QEMU_GA_MSI_WITH_VSS}" >> $config_host_mak echo "QEMU_GA_MSI_ARCH=${QEMU_GA_MSI_ARCH}" >> $config_host_mak @@ -6211,6 +6305,10 @@ if test "$valgrind_h" = "yes" ; then echo "CONFIG_VALGRIND_H=y" >> $config_host_mak fi +if test "$have_asan_iface_fiber" = "yes" ; then + echo "CONFIG_ASAN_IFACE_FIBER=y" >> $config_host_mak +fi + if test "$has_environ" = "yes" ; then echo "CONFIG_HAS_ENVIRON=y" >> $config_host_mak fi @@ -6692,6 +6790,9 @@ fi if supported_hvf_target $target; then echo "CONFIG_HVF=y" >> $config_target_mak fi +if supported_whpx_target $target; then + echo "CONFIG_WHPX=y" >> $config_target_mak +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 5d3a165960d54c87aa7cefde3ea32a16fdeb80cf..182caf764ec19f0e3c60fc9d3722bc8c722a9653 100644 --- a/cpus.c +++ b/cpus.c @@ -38,6 +38,7 @@ #include "sysemu/kvm.h" #include "sysemu/hax.h" #include "sysemu/hvf.h" +#include "sysemu/whpx.h" #include "qmp-commands.h" #include "exec/exec-all.h" @@ -1205,6 +1206,7 @@ static void *qemu_kvm_cpu_thread_fn(void *arg) cpu->created = false; qemu_cond_signal(&qemu_cpu_cond); qemu_mutex_unlock_iothread(); + rcu_unregister_thread(); return NULL; } @@ -1233,7 +1235,7 @@ static void *qemu_dummy_cpu_thread_fn(void *arg) cpu->created = true; qemu_cond_signal(&qemu_cpu_cond); - while (1) { + do { qemu_mutex_unlock_iothread(); do { int sig; @@ -1245,8 +1247,9 @@ static void *qemu_dummy_cpu_thread_fn(void *arg) } qemu_mutex_lock_iothread(); qemu_wait_io_event(cpu); - } + } while (!cpu->unplug); + rcu_unregister_thread(); return NULL; #endif } @@ -1465,6 +1468,7 @@ static void *qemu_tcg_rr_cpu_thread_fn(void *arg) deal_with_unplugged_cpus(); } + rcu_unregister_thread(); return NULL; } @@ -1473,6 +1477,7 @@ static void *qemu_hax_cpu_thread_fn(void *arg) CPUState *cpu = arg; int r; + rcu_register_thread(); qemu_mutex_lock_iothread(); qemu_thread_get_self(cpu->thread); @@ -1484,7 +1489,7 @@ static void *qemu_hax_cpu_thread_fn(void *arg) hax_init_vcpu(cpu); qemu_cond_signal(&qemu_cpu_cond); - while (1) { + do { if (cpu_can_run(cpu)) { r = hax_smp_cpu_exec(cpu); if (r == EXCP_DEBUG) { @@ -1493,7 +1498,8 @@ static void *qemu_hax_cpu_thread_fn(void *arg) } qemu_wait_io_event(cpu); - } + } while (!cpu->unplug || cpu_can_run(cpu)); + rcu_unregister_thread(); return NULL; } @@ -1536,6 +1542,50 @@ static void *qemu_hvf_cpu_thread_fn(void *arg) cpu->created = false; qemu_cond_signal(&qemu_cpu_cond); qemu_mutex_unlock_iothread(); + rcu_unregister_thread(); + return NULL; +} + +static void *qemu_whpx_cpu_thread_fn(void *arg) +{ + CPUState *cpu = arg; + int r; + + rcu_register_thread(); + + qemu_mutex_lock_iothread(); + qemu_thread_get_self(cpu->thread); + cpu->thread_id = qemu_get_thread_id(); + current_cpu = cpu; + + r = whpx_init_vcpu(cpu); + if (r < 0) { + fprintf(stderr, "whpx_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 = whpx_vcpu_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); + } while (!cpu->unplug || cpu_can_run(cpu)); + + whpx_destroy_vcpu(cpu); + cpu->created = false; + qemu_cond_signal(&qemu_cpu_cond); + qemu_mutex_unlock_iothread(); + rcu_unregister_thread(); return NULL; } @@ -1599,18 +1649,17 @@ static void *qemu_tcg_cpu_thread_fn(void *arg) /* Ignore everything else? */ break; } - } else if (cpu->unplug) { - qemu_tcg_destroy_vcpu(cpu); - cpu->created = false; - qemu_cond_signal(&qemu_cpu_cond); - qemu_mutex_unlock_iothread(); - return NULL; } atomic_mb_set(&cpu->exit_request, 0); qemu_wait_io_event(cpu); - } + } while (!cpu->unplug || cpu_can_run(cpu)); + qemu_tcg_destroy_vcpu(cpu); + cpu->created = false; + qemu_cond_signal(&qemu_cpu_cond); + qemu_mutex_unlock_iothread(); + rcu_unregister_thread(); return NULL; } @@ -1630,9 +1679,11 @@ static void qemu_cpu_kick_thread(CPUState *cpu) } #else /* _WIN32 */ if (!qemu_cpu_is_self(cpu)) { - if (!QueueUserAPC(dummy_apc_func, cpu->hThread, 0)) { - error_report("%s: QueueUserAPC failed with error %lu", __func__, - GetLastError()); + if (whpx_enabled()) { + whpx_vcpu_kick(cpu); + } else if (!QueueUserAPC(dummy_apc_func, cpu->hThread, 0)) { + fprintf(stderr, "%s: QueueUserAPC failed with error %lu\n", + __func__, GetLastError()); exit(1); } } @@ -1747,19 +1798,14 @@ void resume_all_vcpus(void) } } -void cpu_remove(CPUState *cpu) +void cpu_remove_sync(CPUState *cpu) { cpu->stop = true; cpu->unplug = true; qemu_cpu_kick(cpu); -} - -void cpu_remove_sync(CPUState *cpu) -{ - cpu_remove(cpu); - while (cpu->created) { - qemu_cond_wait(&qemu_cpu_cond, &qemu_global_mutex); - } + qemu_mutex_unlock_iothread(); + qemu_thread_join(cpu->thread); + qemu_mutex_lock_iothread(); } /* For temporary buffers for forming a name */ @@ -1877,6 +1923,25 @@ static void qemu_hvf_start_vcpu(CPUState *cpu) } } +static void qemu_whpx_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/WHPX", + cpu->cpu_index); + qemu_thread_create(cpu->thread, thread_name, qemu_whpx_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_dummy_start_vcpu(CPUState *cpu) { char thread_name[VCPU_THREAD_NAME_SIZE]; @@ -1915,6 +1980,8 @@ void qemu_init_vcpu(CPUState *cpu) qemu_hvf_start_vcpu(cpu); } else if (tcg_enabled()) { qemu_tcg_init_vcpu(cpu); + } else if (whpx_enabled()) { + qemu_whpx_start_vcpu(cpu); } else { qemu_dummy_start_vcpu(cpu); } diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index b0d6e650382b688b74164de8c50f5169ca8fa5c1..ca34cf446242084381abba33dc7d0bf08e28801c 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -67,6 +67,7 @@ CONFIG_CADENCE=y CONFIG_XGMAC=y CONFIG_EXYNOS4=y CONFIG_PXA2XX=y +CONFIG_I2C=y CONFIG_BITBANG_I2C=y CONFIG_FRAMEBUFFER=y CONFIG_XILINX_SPIPS=y diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak index ac27700e7967214a83f6ab423b96f906a3f3ab70..3326e3e0bbb3d7b981c7fc8426a74422c2e7427b 100644 --- a/default-configs/i386-softmmu.mak +++ b/default-configs/i386-softmmu.mak @@ -62,3 +62,4 @@ CONFIG_HYPERV_TESTDEV=$(CONFIG_KVM) CONFIG_PXB=y CONFIG_ACPI_VMGENID=y CONFIG_FW_CFG_DMA=y +CONFIG_I2C=y diff --git a/default-configs/mips-softmmu-common.mak b/default-configs/mips-softmmu-common.mak index 7d8f5db98364b4ecff029b04e061a1fd41372877..e31f046b3b47728fc54e9da9b686cb7ffc88e347 100644 --- a/default-configs/mips-softmmu-common.mak +++ b/default-configs/mips-softmmu-common.mak @@ -34,3 +34,4 @@ CONFIG_ISA_TESTDEV=y CONFIG_EMPTY_SLOT=y CONFIG_MIPS_CPS=y CONFIG_MIPS_ITU=y +CONFIG_I2C=y diff --git a/default-configs/ppc-softmmu.mak b/default-configs/ppc-softmmu.mak index 3baed6a8fdbcbaa0e780d74cf7225f125a2b37e7..65680d85bc1b169a485fcfd918bf6b88d7569f0d 100644 --- a/default-configs/ppc-softmmu.mak +++ b/default-configs/ppc-softmmu.mak @@ -23,6 +23,7 @@ CONFIG_PLATFORM_BUS=y CONFIG_ETSEC=y CONFIG_SM501=y CONFIG_IDE_SII3112=y +CONFIG_I2C=y # For Macs CONFIG_MAC=y diff --git a/default-configs/ppcemb-softmmu.mak b/default-configs/ppcemb-softmmu.mak index 5db4618a5a4de1c92fca1a5ce56929ced00d0046..bc5e1b3ffed9e47c82ee38a432d20b2034812009 100644 --- a/default-configs/ppcemb-softmmu.mak +++ b/default-configs/ppcemb-softmmu.mak @@ -17,3 +17,4 @@ CONFIG_XILINX=y CONFIG_XILINX_ETHLITE=y CONFIG_SM501=y CONFIG_IDE_SII3112=y +CONFIG_I2C=y diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak index b2104ade19d5e59998f6697088dea3ea4d66c576..1c6cda1d9a9e6ae1b79fc659a55ac069301574db 100644 --- a/default-configs/x86_64-softmmu.mak +++ b/default-configs/x86_64-softmmu.mak @@ -62,3 +62,4 @@ CONFIG_HYPERV_TESTDEV=$(CONFIG_KVM) CONFIG_PXB=y CONFIG_ACPI_VMGENID=y CONFIG_FW_CFG_DMA=y +CONFIG_I2C=y diff --git a/hw/core/qdev.c b/hw/core/qdev.c index 2456035d1a8ebae35b6411872732bfc60c880212..11f8a27a6981266ab19556d60b650cfa8e97be08 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -1075,6 +1075,30 @@ static void device_class_init(ObjectClass *class, void *data) dc->user_creatable = true; } +void device_class_set_parent_reset(DeviceClass *dc, + DeviceReset dev_reset, + DeviceReset *parent_reset) +{ + *parent_reset = dc->reset; + dc->reset = dev_reset; +} + +void device_class_set_parent_realize(DeviceClass *dc, + DeviceRealize dev_realize, + DeviceRealize *parent_realize) +{ + *parent_realize = dc->realize; + dc->realize = dev_realize; +} + +void device_class_set_parent_unrealize(DeviceClass *dc, + DeviceUnrealize dev_unrealize, + DeviceUnrealize *parent_unrealize) +{ + *parent_unrealize = dc->unrealize; + dc->unrealize = dev_unrealize; +} + void device_reset(DeviceState *dev) { DeviceClass *klass = DEVICE_GET_CLASS(dev); diff --git a/hw/display/exynos4210_fimd.c b/hw/display/exynos4210_fimd.c index fd0b2bec65eaffc8b2f6d6debb1bc666b45f05b8..86e37e93e9ee68cb62484e00889f09607991c557 100644 --- a/hw/display/exynos4210_fimd.c +++ b/hw/display/exynos4210_fimd.c @@ -98,7 +98,7 @@ #define FIMD_WINCON_BUFSTATUS ((1 << 21) | (1 << 31)) #define FIMD_WINCON_BUF0_STAT ((0 << 21) | (0 << 31)) #define FIMD_WINCON_BUF1_STAT ((1 << 21) | (0 << 31)) -#define FIMD_WINCON_BUF2_STAT ((0 << 21) | (1 << 31)) +#define FIMD_WINCON_BUF2_STAT ((0 << 21) | (1U << 31)) #define FIMD_WINCON_BUFSELECT ((1 << 20) | (1 << 30)) #define FIMD_WINCON_BUF0_SEL ((0 << 20) | (0 << 30)) #define FIMD_WINCON_BUF1_SEL ((1 << 20) | (0 << 30)) diff --git a/hw/i2c/Makefile.objs b/hw/i2c/Makefile.objs index 0594dea3ae08f1fe40d33c694d4808d5de3d4a4b..37cacde97885ce8793cfb0a515aab447c04c9eaa 100644 --- a/hw/i2c/Makefile.objs +++ b/hw/i2c/Makefile.objs @@ -1,4 +1,4 @@ -common-obj-y += core.o smbus.o smbus_eeprom.o +common-obj-$(CONFIG_I2C) += core.o smbus.o smbus_eeprom.o common-obj-$(CONFIG_DDC) += i2c-ddc.o common-obj-$(CONFIG_VERSATILE_I2C) += versatile_i2c.o common-obj-$(CONFIG_ACPI_X86) += smbus_ich9.o diff --git a/hw/i386/kvm/i8254.c b/hw/i386/kvm/i8254.c index 521a58498a29b74a158a1e2ea22d012012952cde..13f20f47d92abbe7e8c973bbfc3942e1f057107f 100644 --- a/hw/i386/kvm/i8254.c +++ b/hw/i386/kvm/i8254.c @@ -315,8 +315,8 @@ static void kvm_pit_class_init(ObjectClass *klass, void *data) PITCommonClass *k = PIT_COMMON_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - kpc->parent_realize = dc->realize; - dc->realize = kvm_pit_realizefn; + device_class_set_parent_realize(dc, kvm_pit_realizefn, + &kpc->parent_realize); k->set_channel_gate = kvm_pit_set_gate; k->get_channel_info = kvm_pit_get_channel_info; dc->reset = kvm_pit_reset; diff --git a/hw/i386/kvm/i8259.c b/hw/i386/kvm/i8259.c index b91e98074ef6b1ffba566c7fc47a1a0c29f56796..05394cdb7b07f77472eb9630d61884a42ece1b7f 100644 --- a/hw/i386/kvm/i8259.c +++ b/hw/i386/kvm/i8259.c @@ -142,8 +142,7 @@ static void kvm_i8259_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->reset = kvm_pic_reset; - kpc->parent_realize = dc->realize; - dc->realize = kvm_pic_realize; + device_class_set_parent_realize(dc, kvm_pic_realize, &kpc->parent_realize); k->pre_save = kvm_pic_get; k->post_load = kvm_pic_put; } diff --git a/hw/input/adb-kbd.c b/hw/input/adb-kbd.c index 354f56e41e04b7bfd3274308ca62e53f464fc720..266aed1b7bc818b203ad3e70fc70c9412fb64284 100644 --- a/hw/input/adb-kbd.c +++ b/hw/input/adb-kbd.c @@ -374,8 +374,8 @@ static void adb_kbd_class_init(ObjectClass *oc, void *data) ADBDeviceClass *adc = ADB_DEVICE_CLASS(oc); ADBKeyboardClass *akc = ADB_KEYBOARD_CLASS(oc); - akc->parent_realize = dc->realize; - dc->realize = adb_kbd_realizefn; + device_class_set_parent_realize(dc, adb_kbd_realizefn, + &akc->parent_realize); set_bit(DEVICE_CATEGORY_INPUT, dc->categories); adc->devreq = adb_kbd_request; diff --git a/hw/input/adb-mouse.c b/hw/input/adb-mouse.c index c9004233b86b5bfd770cead1c0fed9f962661f03..47e88faf259fd18d09a622591736bdf44e8721e4 100644 --- a/hw/input/adb-mouse.c +++ b/hw/input/adb-mouse.c @@ -228,8 +228,8 @@ static void adb_mouse_class_init(ObjectClass *oc, void *data) ADBDeviceClass *adc = ADB_DEVICE_CLASS(oc); ADBMouseClass *amc = ADB_MOUSE_CLASS(oc); - amc->parent_realize = dc->realize; - dc->realize = adb_mouse_realizefn; + device_class_set_parent_realize(dc, adb_mouse_realizefn, + &amc->parent_realize); set_bit(DEVICE_CATEGORY_INPUT, dc->categories); adc->devreq = adb_mouse_request; diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index 724bc9fa61a69b868002a0fd2ecaba57bf5e308b..ea0323f9691318f29bd3b80b7baf00c79a28e95e 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -1461,8 +1461,7 @@ static void arm_gic_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); ARMGICClass *agc = ARM_GIC_CLASS(klass); - agc->parent_realize = dc->realize; - dc->realize = arm_gic_realize; + device_class_set_parent_realize(dc, arm_gic_realize, &agc->parent_realize); } static const TypeInfo arm_gic_info = { diff --git a/hw/intc/arm_gic_kvm.c b/hw/intc/arm_gic_kvm.c index ae095d08a3649a8027cee9f22b0ec745286b90c5..6f467e68a83188b06ac95cbd11cb14f9af4bc387 100644 --- a/hw/intc/arm_gic_kvm.c +++ b/hw/intc/arm_gic_kvm.c @@ -591,10 +591,9 @@ static void kvm_arm_gic_class_init(ObjectClass *klass, void *data) agcc->pre_save = kvm_arm_gic_get; agcc->post_load = kvm_arm_gic_put; - kgc->parent_realize = dc->realize; - kgc->parent_reset = dc->reset; - dc->realize = kvm_arm_gic_realize; - dc->reset = kvm_arm_gic_reset; + device_class_set_parent_realize(dc, kvm_arm_gic_realize, + &kgc->parent_realize); + device_class_set_parent_reset(dc, kvm_arm_gic_reset, &kgc->parent_reset); } static const TypeInfo kvm_arm_gic_info = { diff --git a/hw/intc/arm_gicv3.c b/hw/intc/arm_gicv3.c index f0c967b304d1ecfeea3f32e403f379c0418b3978..479c66733c71aaaf33ad16e90a6bcf8f6d8a9219 100644 --- a/hw/intc/arm_gicv3.c +++ b/hw/intc/arm_gicv3.c @@ -385,8 +385,7 @@ static void arm_gicv3_class_init(ObjectClass *klass, void *data) ARMGICv3Class *agc = ARM_GICV3_CLASS(klass); agcc->post_load = arm_gicv3_post_load; - agc->parent_realize = dc->realize; - dc->realize = arm_gic_realize; + device_class_set_parent_realize(dc, arm_gic_realize, &agc->parent_realize); } static const TypeInfo arm_gicv3_info = { diff --git a/hw/intc/arm_gicv3_its_kvm.c b/hw/intc/arm_gicv3_its_kvm.c index bf290b8bff8bdc21ec629991e9d0788bda7b4052..eea6a73df2ce14dfeb6e707ee58ffdfa0ddb76e4 100644 --- a/hw/intc/arm_gicv3_its_kvm.c +++ b/hw/intc/arm_gicv3_its_kvm.c @@ -245,11 +245,10 @@ static void kvm_arm_its_class_init(ObjectClass *klass, void *data) dc->realize = kvm_arm_its_realize; dc->props = kvm_arm_its_props; - ic->parent_reset = dc->reset; + device_class_set_parent_reset(dc, kvm_arm_its_reset, &ic->parent_reset); icc->send_msi = kvm_its_send_msi; icc->pre_save = kvm_arm_its_pre_save; icc->post_load = kvm_arm_its_post_load; - dc->reset = kvm_arm_its_reset; } static const TypeInfo kvm_arm_its_info = { diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c index 481fe5405a6115e5ddab8a580afd3a861ef2e462..ec371772b3e4a6e042b2049cc9cb5aa9df93d14a 100644 --- a/hw/intc/arm_gicv3_kvm.c +++ b/hw/intc/arm_gicv3_kvm.c @@ -795,10 +795,9 @@ static void kvm_arm_gicv3_class_init(ObjectClass *klass, void *data) agcc->pre_save = kvm_arm_gicv3_get; agcc->post_load = kvm_arm_gicv3_put; - kgc->parent_realize = dc->realize; - kgc->parent_reset = dc->reset; - dc->realize = kvm_arm_gicv3_realize; - dc->reset = kvm_arm_gicv3_reset; + device_class_set_parent_realize(dc, kvm_arm_gicv3_realize, + &kgc->parent_realize); + device_class_set_parent_reset(dc, kvm_arm_gicv3_reset, &kgc->parent_reset); } static const TypeInfo kvm_arm_gicv3_info = { diff --git a/hw/intc/i8259.c b/hw/intc/i8259.c index 1602255a87023e0fd407b028970272d50770788a..76f3d873b84f37c2f81f5096bc6039c54e55d94e 100644 --- a/hw/intc/i8259.c +++ b/hw/intc/i8259.c @@ -443,8 +443,7 @@ static void i8259_class_init(ObjectClass *klass, void *data) PICClass *k = PIC_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - k->parent_realize = dc->realize; - dc->realize = pic_realize; + device_class_set_parent_realize(dc, pic_realize, &k->parent_realize); dc->reset = pic_reset; } diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c index 4919011f385f8298769069abae7a704594d3a70c..16f03701b7ab21478f1bbdd02d3cdce65625bfa3 100644 --- a/hw/misc/ivshmem.c +++ b/hw/misc/ivshmem.c @@ -76,6 +76,7 @@ typedef struct Peer { typedef struct MSIVector { PCIDevice *pdev; int virq; + bool unmasked; } MSIVector; typedef struct IVShmemState { @@ -316,6 +317,11 @@ static int ivshmem_vector_unmask(PCIDevice *dev, unsigned vector, int ret; IVSHMEM_DPRINTF("vector unmask %p %d\n", dev, vector); + if (!v->pdev) { + error_report("ivshmem: vector %d route does not exist", vector); + return -EINVAL; + } + assert(!v->unmasked); ret = kvm_irqchip_update_msi_route(kvm_state, v->virq, msg, dev); if (ret < 0) { @@ -323,22 +329,35 @@ static int ivshmem_vector_unmask(PCIDevice *dev, unsigned vector, } kvm_irqchip_commit_routes(kvm_state); - return kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, n, NULL, v->virq); + ret = kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, n, NULL, v->virq); + if (ret < 0) { + return ret; + } + v->unmasked = true; + + return 0; } static void ivshmem_vector_mask(PCIDevice *dev, unsigned vector) { IVShmemState *s = IVSHMEM_COMMON(dev); EventNotifier *n = &s->peers[s->vm_id].eventfds[vector]; + MSIVector *v = &s->msi_vectors[vector]; int ret; IVSHMEM_DPRINTF("vector mask %p %d\n", dev, vector); + if (!v->pdev) { + error_report("ivshmem: vector %d route does not exist", vector); + return; + } + assert(v->unmasked); - ret = kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, n, - s->msi_vectors[vector].virq); - if (ret != 0) { + ret = kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, n, v->virq); + if (ret < 0) { error_report("remove_irqfd_notifier_gsi failed"); + return; } + v->unmasked = false; } static void ivshmem_vector_poll(PCIDevice *dev, @@ -738,10 +757,14 @@ static void ivshmem_msix_vector_use(IVShmemState *s) } } +static void ivshmem_disable_irqfd(IVShmemState *s); + static void ivshmem_reset(DeviceState *d) { IVShmemState *s = IVSHMEM_COMMON(d); + ivshmem_disable_irqfd(s); + s->intrstatus = 0; s->intrmask = 0; if (ivshmem_has_feature(s, IVSHMEM_MSI)) { @@ -766,6 +789,20 @@ static int ivshmem_setup_interrupts(IVShmemState *s, Error **errp) return 0; } +static void ivshmem_remove_kvm_msi_virq(IVShmemState *s, int vector) +{ + IVSHMEM_DPRINTF("ivshmem_remove_kvm_msi_virq vector:%d\n", vector); + + if (s->msi_vectors[vector].pdev == NULL) { + return; + } + + /* it was cleaned when masked in the frontend. */ + kvm_irqchip_release_virq(kvm_state, s->msi_vectors[vector].virq); + + s->msi_vectors[vector].pdev = NULL; +} + static void ivshmem_enable_irqfd(IVShmemState *s) { PCIDevice *pdev = PCI_DEVICE(s); @@ -777,7 +814,7 @@ static void ivshmem_enable_irqfd(IVShmemState *s) ivshmem_add_kvm_msi_virq(s, i, &err); if (err) { error_report_err(err); - /* TODO do we need to handle the error? */ + goto undo; } } @@ -786,21 +823,14 @@ static void ivshmem_enable_irqfd(IVShmemState *s) ivshmem_vector_mask, ivshmem_vector_poll)) { error_report("ivshmem: msix_set_vector_notifiers failed"); + goto undo; } -} + return; -static void ivshmem_remove_kvm_msi_virq(IVShmemState *s, int vector) -{ - IVSHMEM_DPRINTF("ivshmem_remove_kvm_msi_virq vector:%d\n", vector); - - if (s->msi_vectors[vector].pdev == NULL) { - return; +undo: + while (--i >= 0) { + ivshmem_remove_kvm_msi_virq(s, i); } - - /* it was cleaned when masked in the frontend. */ - kvm_irqchip_release_virq(kvm_state, s->msi_vectors[vector].virq); - - s->msi_vectors[vector].pdev = NULL; } static void ivshmem_disable_irqfd(IVShmemState *s) @@ -808,11 +838,24 @@ static void ivshmem_disable_irqfd(IVShmemState *s) PCIDevice *pdev = PCI_DEVICE(s); int i; + if (!pdev->msix_vector_use_notifier) { + return; + } + + msix_unset_vector_notifiers(pdev); + for (i = 0; i < s->peers[s->vm_id].nb_eventfds; i++) { + /* + * MSI-X is already disabled here so msix_unset_vector_notifiers() + * didn't call our release notifier. Do it now to keep our masks and + * unmasks balanced. + */ + if (s->msi_vectors[i].unmasked) { + ivshmem_vector_mask(pdev, i); + } ivshmem_remove_kvm_msi_virq(s, i); } - msix_unset_vector_notifiers(pdev); } static void ivshmem_write_config(PCIDevice *pdev, uint32_t address, diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c index 0654d594c10c95dc0e45adc2dbdcf5751465a7b5..364863038637d0a6579284b8ed066582d65bb8ad 100644 --- a/hw/net/vmxnet3.c +++ b/hw/net/vmxnet3.c @@ -2664,8 +2664,8 @@ static void vmxnet3_class_init(ObjectClass *class, void *data) c->class_id = PCI_CLASS_NETWORK_ETHERNET; c->subsystem_vendor_id = PCI_VENDOR_ID_VMWARE; c->subsystem_id = PCI_DEVICE_ID_VMWARE_VMXNET3; - vc->parent_dc_realize = dc->realize; - dc->realize = vmxnet3_realize; + device_class_set_parent_realize(dc, vmxnet3_realize, + &vc->parent_dc_realize); dc->desc = "VMWare Paravirtualized Ethernet v3"; dc->reset = vmxnet3_qdev_reset; dc->vmsd = &vmstate_vmxnet3; diff --git a/hw/nvram/Makefile.objs b/hw/nvram/Makefile.objs index 0f4ee71dcb55f9801ae73f1f60db3e937bb05781..a912d25391c516461d559470fdfeaafe084b1156 100644 --- a/hw/nvram/Makefile.objs +++ b/hw/nvram/Makefile.objs @@ -1,6 +1,6 @@ common-obj-$(CONFIG_DS1225Y) += ds1225y.o common-obj-y += eeprom93xx.o -common-obj-y += eeprom_at24c.o +common-obj-$(CONFIG_I2C) += eeprom_at24c.o common-obj-y += fw_cfg.o common-obj-y += chrp_nvram.o common-obj-$(CONFIG_MAC_NVRAM) += mac_nvram.o diff --git a/hw/pci-bridge/gen_pcie_root_port.c b/hw/pci-bridge/gen_pcie_root_port.c index 0e2f2e8bf1a9a289f068fbd4f3b5664218602a00..3dbacc6cea746d8df7ea56aefbd0e88f1a1a54a5 100644 --- a/hw/pci-bridge/gen_pcie_root_port.c +++ b/hw/pci-bridge/gen_pcie_root_port.c @@ -137,8 +137,7 @@ static void gen_rp_dev_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_rp_dev; dc->props = gen_rp_props; - rpc->parent_realize = dc->realize; - dc->realize = gen_rp_realize; + device_class_set_parent_realize(dc, gen_rp_realize, &rpc->parent_realize); rpc->aer_vector = gen_rp_aer_vector; rpc->interrupts_init = gen_rp_interrupts_init; diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c index ba70c0dc1926e303c28646a960b8fc7ef1cf5c7d..7414fe2d675ccf61985207317ae816319cf61158 100644 --- a/hw/scsi/scsi-generic.c +++ b/hw/scsi/scsi-generic.c @@ -482,7 +482,6 @@ static void scsi_generic_realize(SCSIDevice *s, Error **errp) int rc; int sg_version; struct sg_scsi_id scsiid; - Error *local_err = NULL; if (!s->conf.blk) { error_setg(errp, "drive property not set"); @@ -516,11 +515,9 @@ static void scsi_generic_realize(SCSIDevice *s, Error **errp) error_setg(errp, "SG_GET_SCSI_ID ioctl failed"); return; } - blkconf_apply_backend_options(&s->conf, - blk_is_read_only(s->conf.blk), - true, &local_err); - if (local_err) { - error_propagate(errp, local_err); + if (!blkconf_apply_backend_options(&s->conf, + blk_is_read_only(s->conf.blk), + true, errp)) { return; } diff --git a/hw/scsi/vmw_pvscsi.c b/hw/scsi/vmw_pvscsi.c index 27749c0e421a7e40faf0dd07226a28909d10ad13..a3a019e30a745a024d5718404941fccc55c0fb56 100644 --- a/hw/scsi/vmw_pvscsi.c +++ b/hw/scsi/vmw_pvscsi.c @@ -1284,8 +1284,8 @@ static void pvscsi_class_init(ObjectClass *klass, void *data) k->device_id = PCI_DEVICE_ID_VMWARE_PVSCSI; k->class_id = PCI_CLASS_STORAGE_SCSI; k->subsystem_id = 0x1000; - pvs_k->parent_dc_realize = dc->realize; - dc->realize = pvscsi_realize; + device_class_set_parent_realize(dc, pvscsi_realize, + &pvs_k->parent_dc_realize); dc->reset = pvscsi_reset; dc->vmsd = &vmstate_pvscsi; dc->props = pvscsi_properties; diff --git a/hw/timer/i8254.c b/hw/timer/i8254.c index dbc4a0baecbcdc5edb27896580525646cd6df5b7..10578508083e58d76f090dbd7eda907567886f2a 100644 --- a/hw/timer/i8254.c +++ b/hw/timer/i8254.c @@ -358,8 +358,7 @@ static void pit_class_initfn(ObjectClass *klass, void *data) PITCommonClass *k = PIT_COMMON_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - pc->parent_realize = dc->realize; - dc->realize = pit_realizefn; + device_class_set_parent_realize(dc, pit_realizefn, &pc->parent_realize); k->set_channel_gate = pit_set_channel_gate; k->get_channel_info = pit_get_channel_info_common; k->post_load = pit_post_load; diff --git a/hw/vfio/amd-xgbe.c b/hw/vfio/amd-xgbe.c index fab196cebff4d1643aa0d87136dd03ce4c8bf8e1..0c4ec4ba25170366d585b9c2f8fe73ebe2e2a25b 100644 --- a/hw/vfio/amd-xgbe.c +++ b/hw/vfio/amd-xgbe.c @@ -34,8 +34,8 @@ static void vfio_amd_xgbe_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); VFIOAmdXgbeDeviceClass *vcxc = VFIO_AMD_XGBE_DEVICE_CLASS(klass); - vcxc->parent_realize = dc->realize; - dc->realize = amd_xgbe_realize; + device_class_set_parent_realize(dc, amd_xgbe_realize, + &vcxc->parent_realize); dc->desc = "VFIO AMD XGBE"; dc->vmsd = &vfio_platform_amd_xgbe_vmstate; /* Supported by TYPE_VIRT_MACHINE */ diff --git a/hw/vfio/calxeda-xgmac.c b/hw/vfio/calxeda-xgmac.c index 7bb17af7addc4a425f77943ded4dc67b70fd52f4..24cee6d06512c1b65b29508282df8dcda09fd570 100644 --- a/hw/vfio/calxeda-xgmac.c +++ b/hw/vfio/calxeda-xgmac.c @@ -34,8 +34,8 @@ static void vfio_calxeda_xgmac_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); VFIOCalxedaXgmacDeviceClass *vcxc = VFIO_CALXEDA_XGMAC_DEVICE_CLASS(klass); - vcxc->parent_realize = dc->realize; - dc->realize = calxeda_xgmac_realize; + device_class_set_parent_realize(dc, calxeda_xgmac_realize, + &vcxc->parent_realize); dc->desc = "VFIO Calxeda XGMAC"; dc->vmsd = &vfio_platform_calxeda_xgmac_vmstate; /* Supported by TYPE_VIRT_MACHINE */ diff --git a/hw/vfio/common.c b/hw/vfio/common.c index ee9240daeb5d24be0808b0084378f9f6e226fb2e..f895e3c3359af779602c4eb77016fc4424ec3b9e 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -1187,19 +1187,27 @@ static void vfio_disconnect_container(VFIOGroup *group) { VFIOContainer *container = group->container; + QLIST_REMOVE(group, container_next); + group->container = NULL; + + /* + * Explicitly release the listener first before unset container, + * since unset may destroy the backend container if it's the last + * group. + */ + if (QLIST_EMPTY(&container->group_list)) { + vfio_listener_release(container); + } + if (ioctl(group->fd, VFIO_GROUP_UNSET_CONTAINER, &container->fd)) { error_report("vfio: error disconnecting group %d from container", group->groupid); } - QLIST_REMOVE(group, container_next); - group->container = NULL; - if (QLIST_EMPTY(&container->group_list)) { VFIOAddressSpace *space = container->space; VFIOGuestIOMMU *giommu, *tmp; - vfio_listener_release(container); QLIST_REMOVE(container, next); QLIST_FOREACH_SAFE(giommu, &container->giommu_list, giommu_next, tmp) { diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events index 775461ae98cbfe51f482b621315c8929f4e8d330..2b8f81eb25ac077cb01f72bf1a52cde37745a761 100644 --- a/hw/virtio/trace-events +++ b/hw/virtio/trace-events @@ -25,3 +25,9 @@ virtio_balloon_handle_output(const char *name, uint64_t gpa) "section name: %s g virtio_balloon_get_config(uint32_t num_pages, uint32_t actual) "num_pages: %d actual: %d" virtio_balloon_set_config(uint32_t actual, uint32_t oldactual) "actual: %d oldactual: %d" virtio_balloon_to_target(uint64_t target, uint32_t num_pages) "balloon target: 0x%"PRIx64" num_pages: %d" + +# hw/virtio/vhost.c +vhost_region_add(void *p, const char *mr) "dev %p mr %s" +vhost_region_del(void *p, const char *mr) "dev %p mr %s" +vhost_iommu_region_add(void *p, const char *mr) "dev %p mr %s" +vhost_iommu_region_del(void *p, const char *mr) "dev %p mr %s" diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index 386aef85be9cf41ea3a11793668568a0e22157ab..338e4395b7ba076387cc15cf3b283ed7935a320a 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -27,6 +27,7 @@ #include "hw/virtio/virtio-access.h" #include "migration/blocker.h" #include "sysemu/dma.h" +#include "trace.h" /* enabled until disconnected backend stabilizes */ #define _VHOST_DEBUG 1 @@ -329,6 +330,7 @@ static uint64_t vhost_get_log_size(struct vhost_dev *dev) static struct vhost_log *vhost_log_alloc(uint64_t size, bool share) { + Error *err = NULL; struct vhost_log *log; uint64_t logsize = size * sizeof(*(log->log)); int fd = -1; @@ -337,7 +339,12 @@ static struct vhost_log *vhost_log_alloc(uint64_t size, bool share) if (share) { log->log = qemu_memfd_alloc("vhost-log", logsize, F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL, - &fd); + &fd, &err); + if (err) { + error_report_err(err); + g_free(log); + return NULL; + } memset(log->log, 0, logsize); } else { log->log = g_malloc0(logsize); @@ -687,6 +694,7 @@ static void vhost_region_add(MemoryListener *listener, return; } + trace_vhost_region_add(dev, section->mr->name ?: NULL); ++dev->n_mem_sections; dev->mem_sections = g_renew(MemoryRegionSection, dev->mem_sections, dev->n_mem_sections); @@ -706,6 +714,7 @@ static void vhost_region_del(MemoryListener *listener, return; } + trace_vhost_region_del(dev, section->mr->name ?: NULL); vhost_set_memory(listener, section, false); memory_region_unref(section->mr); for (i = 0; i < dev->n_mem_sections; ++i) { @@ -743,6 +752,8 @@ static void vhost_iommu_region_add(MemoryListener *listener, return; } + trace_vhost_iommu_region_add(dev, section->mr->name ?: NULL); + iommu = g_malloc0(sizeof(*iommu)); end = int128_add(int128_make64(section->offset_within_region), section->size); @@ -771,6 +782,8 @@ static void vhost_iommu_region_del(MemoryListener *listener, return; } + trace_vhost_iommu_region_del(dev, section->mr->name ?: NULL); + QLIST_FOREACH(iommu, &dev->iommu_list, iommu_next) { if (iommu->mr == section->mr && iommu->n.start == section->offset_within_region) { @@ -1361,10 +1374,6 @@ void vhost_dev_cleanup(struct vhost_dev *hdev) if (hdev->mem) { /* those are only safe after successful init */ memory_listener_unregister(&hdev->memory_listener); - for (i = 0; i < hdev->n_mem_sections; ++i) { - MemoryRegionSection *section = &hdev->mem_sections[i]; - memory_region_unref(section->mr); - } QLIST_REMOVE(hdev, entry); } if (hdev->migration_blocker) { diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 9ae10f0cdd3d2209e3bd7899ffd928144755f2ae..c20537f31de7c1b2e5195f51a68d30f16872d628 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -1907,8 +1907,8 @@ static void virtio_pci_class_init(ObjectClass *klass, void *data) k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; k->revision = VIRTIO_PCI_ABI_VERSION; k->class_id = PCI_CLASS_OTHERS; - vpciklass->parent_dc_realize = dc->realize; - dc->realize = virtio_pci_dc_realize; + device_class_set_parent_realize(dc, virtio_pci_dc_realize, + &vpciklass->parent_dc_realize); dc->reset = virtio_pci_reset; } diff --git a/include/exec/memory-internal.h b/include/exec/memory-internal.h index 98d82964cc9759e51b7ca1e9e94e683a1dacc4a3..4162474fd52e3698514aca04f10a321684aa688c 100644 --- a/include/exec/memory-internal.h +++ b/include/exec/memory-internal.h @@ -1,5 +1,5 @@ /* - * Declarations for obsolete exec.c functions + * Declarations for functions which are internal to the memory subsystem. * * Copyright 2011 Red Hat, Inc. and/or its affiliates * @@ -12,8 +12,9 @@ */ /* - * This header is for use by exec.c and memory.c ONLY. Do not include it. - * The functions declared here will be removed soon. + * This header is for use by exec.c, memory.c and accel/tcg/cputlb.c ONLY, + * for declarations which are shared between the memory subsystem's + * internals and the TCG TLB code. Do not include it from elsewhere. */ #ifndef MEMORY_INTERNAL_H diff --git a/include/exec/memory.h b/include/exec/memory.h index 3ef83991e99a467fc48f2143c08c55da7c63214e..783ef6457003d7d4ef77e1fd0d71b0aacf780284 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -332,7 +332,7 @@ FlatView *address_space_to_flatview(AddressSpace *as); * MemoryRegionSection: describes a fragment of a #MemoryRegion * * @mr: the region, or %NULL if empty - * @address_space: the address space the region is mapped in + * @fv: the flat view of the address space the region is mapped in * @offset_within_region: the beginning of the section, relative to @mr's start * @size: the size of the section; will not exceed @mr's boundaries * @offset_within_address_space: the address of the first byte of the section @@ -618,6 +618,7 @@ void memory_region_init_rom_nomigrate(MemoryRegion *mr, * @mr: the #MemoryRegion to be initialized. * @owner: the object that tracks the region's reference count * @ops: callbacks for write access handling (must not be NULL). + * @opaque: passed to the read and write callbacks of the @ops structure. * @name: Region name, becomes part of RAMBlock name used in migration stream * must be unique within any device * @size: size of the region. @@ -661,11 +662,10 @@ static inline void memory_region_init_reservation(MemoryRegion *mr, * An IOMMU region translates addresses and forwards accesses to a target * memory region. * - * @typename: QOM class name * @_iommu_mr: the #IOMMUMemoryRegion to be initialized * @instance_size: the IOMMUMemoryRegion subclass instance size + * @mrtypename: the type name of the #IOMMUMemoryRegion * @owner: the object that tracks the region's reference count - * @ops: a function that translates addresses into the @target region * @name: used for debugging; not visible to the user or ABI * @size: size of the region. */ @@ -835,8 +835,8 @@ static inline IOMMUMemoryRegion *memory_region_get_iommu(MemoryRegion *mr) * memory_region_get_iommu_class_nocheck: returns iommu memory region class * if an iommu or NULL if not * - * Returns pointer to IOMMUMemoryRegioniClass if a memory region is an iommu, - * otherwise NULL. This is fast path avoinding QOM checking, use with caution. + * Returns pointer to IOMMUMemoryRegionClass if a memory region is an iommu, + * otherwise NULL. This is fast path avoiding QOM checking, use with caution. * * @mr: the memory region being queried */ @@ -1015,7 +1015,8 @@ int memory_region_get_fd(MemoryRegion *mr); * protecting the pointer, such as a reference to the region that includes * the incoming ram_addr_t. * - * @mr: the memory region being queried. + * @ptr: the host pointer to be converted + * @offset: the offset within memory region */ MemoryRegion *memory_region_from_host(void *ptr, ram_addr_t *offset); @@ -1292,7 +1293,7 @@ void memory_region_clear_global_locking(MemoryRegion *mr); * @size: the size of the access to trigger the eventfd * @match_data: whether to match against @data, instead of just @addr * @data: the data to match against the guest write - * @fd: the eventfd to be triggered when @addr, @size, and @data all match. + * @e: event notifier to be triggered when @addr, @size, and @data all match. **/ void memory_region_add_eventfd(MemoryRegion *mr, hwaddr addr, @@ -1312,7 +1313,7 @@ void memory_region_add_eventfd(MemoryRegion *mr, * @size: the size of the access to trigger the eventfd * @match_data: whether to match against @data, instead of just @addr * @data: the data to match against the guest write - * @fd: the eventfd to be triggered when @addr, @size, and @data all match. + * @e: event notifier to be triggered when @addr, @size, and @data all match. */ void memory_region_del_eventfd(MemoryRegion *mr, hwaddr addr, @@ -1548,7 +1549,7 @@ bool memory_region_request_mmio_ptr(MemoryRegion *mr, hwaddr addr); * will need to request the pointer again. * * @mr: #MemoryRegion associated to the pointer. - * @addr: address within that region + * @offset: offset within the memory region * @size: size of that area. */ void memory_region_invalidate_mmio_ptr(MemoryRegion *mr, hwaddr offset, @@ -1617,6 +1618,7 @@ void address_space_destroy(AddressSpace *as); * @addr: address within that address space * @attrs: memory transaction attributes * @buf: buffer with the data transferred + * @len: the number of bytes to read or write * @is_write: indicates the transfer direction */ MemTxResult address_space_rw(AddressSpace *as, hwaddr addr, @@ -1634,6 +1636,7 @@ MemTxResult address_space_rw(AddressSpace *as, hwaddr addr, * @addr: address within that address space * @attrs: memory transaction attributes * @buf: buffer with the data transferred + * @len: the number of bytes to write */ MemTxResult address_space_write(AddressSpace *as, hwaddr addr, MemTxAttrs attrs, @@ -1832,7 +1835,7 @@ IOMMUTLBEntry address_space_get_iotlb_entry(AddressSpace *as, hwaddr addr, * called from an RCU critical section, to avoid that the last reference * to the returned region disappears after address_space_translate returns. * - * @as: #AddressSpace to be accessed + * @fv: #FlatView to be accessed * @addr: address within that address space * @xlat: pointer to address within the returned memory region section's * #MemoryRegion. @@ -1890,7 +1893,7 @@ void *address_space_map(AddressSpace *as, hwaddr addr, * the amount of memory that was actually read or written by the caller. * * @as: #AddressSpace used - * @addr: address within that address space + * @buffer: host pointer as returned by address_space_map() * @len: buffer length as returned by address_space_map() * @access_len: amount of data actually transferred * @is_write: indicates the transfer direction @@ -1927,7 +1930,7 @@ static inline bool memory_access_is_direct(MemoryRegion *mr, bool is_write) * or failed (eg unassigned memory, device rejected the transaction, * IOMMU fault). * - * @as: #AddressSpace to be accessed + * @fv: #FlatView to be accessed * @addr: address within that address space * @attrs: memory transaction attributes * @buf: buffer with the data transferred diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index 51473eee7ba3b66e98882e0c0a3bc9fc0eb10ded..18c0251b40ad2cfdd72f0dc2531f175d74ec004a 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -32,9 +32,9 @@ typedef enum DeviceCategory { typedef int (*qdev_initfn)(DeviceState *dev); typedef int (*qdev_event)(DeviceState *dev); -typedef void (*qdev_resetfn)(DeviceState *dev); typedef void (*DeviceRealize)(DeviceState *dev, Error **errp); typedef void (*DeviceUnrealize)(DeviceState *dev, Error **errp); +typedef void (*DeviceReset)(DeviceState *dev); typedef void (*BusRealize)(BusState *bus, Error **errp); typedef void (*BusUnrealize)(BusState *bus, Error **errp); @@ -117,7 +117,7 @@ typedef struct DeviceClass { bool hotpluggable; /* callbacks */ - void (*reset)(DeviceState *dev); + DeviceReset reset; DeviceRealize realize; DeviceUnrealize unrealize; @@ -382,6 +382,16 @@ void qdev_machine_init(void); */ void device_reset(DeviceState *dev); +void device_class_set_parent_reset(DeviceClass *dc, + DeviceReset dev_reset, + DeviceReset *parent_reset); +void device_class_set_parent_realize(DeviceClass *dc, + DeviceRealize dev_realize, + DeviceRealize *parent_realize); +void device_class_set_parent_unrealize(DeviceClass *dc, + DeviceUnrealize dev_unrealize, + DeviceUnrealize *parent_unrealize); + const struct VMStateDescription *qdev_get_vmsd(DeviceState *dev); const char *qdev_fw_name(DeviceState *dev); diff --git a/include/qemu/compiler.h b/include/qemu/compiler.h index 340e5fdc0906016fdd3c4e232415d095caa1c24b..5fcc4f7ec72886716c620c87a8e2f4efee0b099a 100644 --- a/include/qemu/compiler.h +++ b/include/qemu/compiler.h @@ -111,4 +111,8 @@ #define GCC_FMT_ATTR(n, m) #endif +#ifndef __has_feature +#define __has_feature(x) 0 /* compatibility with non-clang compilers */ +#endif + #endif /* COMPILER_H */ diff --git a/include/qemu/memfd.h b/include/qemu/memfd.h index 41c24d807c450d5e8c045137f2955a5319f78573..de10198ed660c473c81aee6a2b6ea0365e2b78f2 100644 --- a/include/qemu/memfd.h +++ b/include/qemu/memfd.h @@ -16,9 +16,10 @@ #define F_SEAL_WRITE 0x0008 /* prevent writes */ #endif -int qemu_memfd_create(const char *name, size_t size, unsigned int seals); +int qemu_memfd_create(const char *name, size_t size, bool hugetlb, + uint64_t hugetlbsize, unsigned int seals, Error **errp); void *qemu_memfd_alloc(const char *name, size_t size, unsigned int seals, - int *fd); + int *fd, Error **errp); void qemu_memfd_free(void *ptr, size_t size, int fd); bool qemu_memfd_check(void); diff --git a/include/qemu/processor.h b/include/qemu/processor.h index 8b2570283a027cc9f2eaf1f72623cf83ef235474..8e16c9277dbc7f52391bd69de6551ddb4bff3ed5 100644 --- a/include/qemu/processor.h +++ b/include/qemu/processor.h @@ -12,9 +12,6 @@ #if defined(__i386__) || defined(__x86_64__) # define cpu_relax() asm volatile("rep; nop" ::: "memory") -#elif defined(__ia64__) -# define cpu_relax() asm volatile("hint @pause" ::: "memory") - #elif defined(__aarch64__) # define cpu_relax() asm volatile("yield" ::: "memory") diff --git a/include/qemu/timer.h b/include/qemu/timer.h index 1b518bca302818cbebb4e1c4957d1609be5b2639..3b5a54b014377c51c89595c113016cd5ebb54c3f 100644 --- a/include/qemu/timer.h +++ b/include/qemu/timer.h @@ -931,15 +931,6 @@ static inline int64_t cpu_get_host_ticks(void) return val; } -#elif defined(__ia64) - -static inline int64_t cpu_get_host_ticks(void) -{ - int64_t val; - asm volatile ("mov %0 = ar.itc" : "=r"(val) :: "memory"); - return val; -} - #elif defined(__s390__) static inline int64_t cpu_get_host_ticks(void) diff --git a/include/sysemu/hw_accel.h b/include/sysemu/hw_accel.h index 469ffda4607f74b9550447443d80f9b8f6c5157e..d2ddfb5ad049a9dc6ce11c6c98c0e68df46b3893 100644 --- a/include/sysemu/hw_accel.h +++ b/include/sysemu/hw_accel.h @@ -14,6 +14,7 @@ #include "qom/cpu.h" #include "sysemu/hax.h" #include "sysemu/kvm.h" +#include "sysemu/whpx.h" static inline void cpu_synchronize_state(CPUState *cpu) { @@ -23,6 +24,9 @@ static inline void cpu_synchronize_state(CPUState *cpu) if (hax_enabled()) { hax_cpu_synchronize_state(cpu); } + if (whpx_enabled()) { + whpx_cpu_synchronize_state(cpu); + } } static inline void cpu_synchronize_post_reset(CPUState *cpu) @@ -33,6 +37,9 @@ static inline void cpu_synchronize_post_reset(CPUState *cpu) if (hax_enabled()) { hax_cpu_synchronize_post_reset(cpu); } + if (whpx_enabled()) { + whpx_cpu_synchronize_post_reset(cpu); + } } static inline void cpu_synchronize_post_init(CPUState *cpu) @@ -43,6 +50,9 @@ static inline void cpu_synchronize_post_init(CPUState *cpu) if (hax_enabled()) { hax_cpu_synchronize_post_init(cpu); } + if (whpx_enabled()) { + whpx_cpu_synchronize_post_init(cpu); + } } static inline void cpu_synchronize_pre_loadvm(CPUState *cpu) @@ -53,6 +63,9 @@ static inline void cpu_synchronize_pre_loadvm(CPUState *cpu) if (hax_enabled()) { hax_cpu_synchronize_pre_loadvm(cpu); } + if (whpx_enabled()) { + whpx_cpu_synchronize_pre_loadvm(cpu); + } } #endif /* QEMU_HW_ACCEL_H */ diff --git a/include/sysemu/whpx.h b/include/sysemu/whpx.h new file mode 100644 index 0000000000000000000000000000000000000000..89592ae4fad18e402a21e56b3b64c7095b514493 --- /dev/null +++ b/include/sysemu/whpx.h @@ -0,0 +1,40 @@ +/* + * QEMU Windows Hypervisor Platform accelerator (WHPX) support + * + * Copyright Microsoft, Corp. 2017 + * + * Authors: + * + * 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_WHPX_H +#define QEMU_WHPX_H + +#include "config-host.h" +#include "qemu-common.h" + +int whpx_init_vcpu(CPUState *cpu); +int whpx_vcpu_exec(CPUState *cpu); +void whpx_destroy_vcpu(CPUState *cpu); +void whpx_vcpu_kick(CPUState *cpu); + + +void whpx_cpu_synchronize_state(CPUState *cpu); +void whpx_cpu_synchronize_post_reset(CPUState *cpu); +void whpx_cpu_synchronize_post_init(CPUState *cpu); +void whpx_cpu_synchronize_pre_loadvm(CPUState *cpu); + +#ifdef CONFIG_WHPX + +int whpx_enabled(void); + +#else /* CONFIG_WHPX */ + +#define whpx_enabled() (0) + +#endif /* CONFIG_WHPX */ + +#endif /* QEMU_WHPX_H */ diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 74378947f02a3ad5c742cb50fafff3a2eba30008..df1edf0cd34935aa92cae45899e30f69820be2bb 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -36,10 +36,6 @@ #include <linux/capability.h> #include <sched.h> #include <sys/timex.h> -#ifdef __ia64__ -int __clone2(int (*fn)(void *), void *child_stack_base, - size_t stack_size, int flags, void *arg, ...); -#endif #include <sys/socket.h> #include <sys/un.h> #include <sys/uio.h> @@ -246,8 +242,7 @@ static type name (type1 arg1,type2 arg2,type3 arg3,type4 arg4,type5 arg5, \ #define __NR_sys_inotify_add_watch __NR_inotify_add_watch #define __NR_sys_inotify_rm_watch __NR_inotify_rm_watch -#if defined(__alpha__) || defined (__ia64__) || defined(__x86_64__) || \ - defined(__s390x__) +#if defined(__alpha__) || defined(__x86_64__) || defined(__s390x__) #define __NR__llseek __NR_lseek #endif diff --git a/memory.c b/memory.c index a4c603730a8cb0cda4042d24a2253933d62e6cba..93258a665534d538f0205c6b80bf01a83ebdc13f 100644 --- a/memory.c +++ b/memory.c @@ -1091,6 +1091,7 @@ void memory_region_transaction_commit(void) address_space_update_ioeventfds(as); } memory_region_update_pending = false; + ioeventfd_update_pending = false; MEMORY_LISTENER_CALL_GLOBAL(commit, Forward); } else if (ioeventfd_update_pending) { QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) { @@ -2624,6 +2625,32 @@ static void listener_add_address_space(MemoryListener *listener, flatview_unref(view); } +static void listener_del_address_space(MemoryListener *listener, + AddressSpace *as) +{ + FlatView *view; + FlatRange *fr; + + if (listener->begin) { + listener->begin(listener); + } + view = address_space_get_flatview(as); + FOR_EACH_FLAT_RANGE(fr, view) { + MemoryRegionSection section = section_from_flat_range(fr, view); + + if (fr->dirty_log_mask && listener->log_stop) { + listener->log_stop(listener, §ion, fr->dirty_log_mask, 0); + } + if (listener->region_del) { + listener->region_del(listener, §ion); + } + } + if (listener->commit) { + listener->commit(listener); + } + flatview_unref(view); +} + void memory_listener_register(MemoryListener *listener, AddressSpace *as) { MemoryListener *other = NULL; @@ -2664,6 +2691,7 @@ void memory_listener_unregister(MemoryListener *listener) return; } + listener_del_address_space(listener, listener->address_space); QTAILQ_REMOVE(&memory_listeners, listener, link); QTAILQ_REMOVE(&listener->address_space->listeners, listener, link_as); listener->address_space = NULL; diff --git a/numa.c b/numa.c index 83675a03f3cb65cc945d3bed92951464ae7256d0..a9528aaa7d1a783f218843428d9cc9f6ea40811e 100644 --- a/numa.c +++ b/numa.c @@ -463,6 +463,7 @@ static void allocate_system_memory_nonnuma(MemoryRegion *mr, Object *owner, if (mem_prealloc) { exit(1); } + error_report("falling back to regular RAM allocation."); /* Legacy behavior: if allocation failed, fall back to * regular RAM allocation. diff --git a/qemu-options.hx b/qemu-options.hx index 8ce427da788061a27b800ef6f9d68db6c6d2e071..d15c1713d182dd359336adc6dcbfce683517d8da 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -31,7 +31,7 @@ DEF("machine", HAS_ARG, QEMU_OPTION_machine, \ "-machine [type=]name[,prop[=value][,...]]\n" " selects emulated machine ('-machine help' for list)\n" " property accel=accel1[:accel2[:...]] selects accelerator\n" - " supported accelerators are kvm, xen, hax, hvf or tcg (default: tcg)\n" + " supported accelerators are kvm, xen, hax, hvf, 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,7 +66,7 @@ Supported machine properties are: @table @option @item accel=@var{accels1}[:@var{accels2}[:...]] This is used to enable an accelerator. Depending on the target architecture, -kvm, xen, hax, hvf or tcg can be available. By default, tcg is used. If there is +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. @item kernel_irqchip=on|off @@ -126,13 +126,13 @@ ETEXI DEF("accel", HAS_ARG, QEMU_OPTION_accel, "-accel [accel=]accelerator[,thread=single|multi]\n" - " select accelerator (kvm, xen, hax, hvf or tcg; use 'help' for a list)\n" + " select accelerator (kvm, xen, hax, hvf, whpx or tcg; use 'help' for a list)\n" " thread=single|multi (enable multi-threaded TCG)", QEMU_ARCH_ALL) STEXI @item -accel @var{name}[,prop=@var{value}[,...]] @findex -accel This is used to enable an accelerator. Depending on the target architecture, -kvm, xen, hax, hvf or tcg can be available. By default, tcg is used. If there is +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. @table @option @@ -4024,6 +4024,28 @@ Memory backend objects offer more control than the @option{-m} option that is traditionally used to define guest RAM. Please refer to @option{memory-backend-file} for a description of the options. +@item -object memory-backend-memfd,id=@var{id},merge=@var{on|off},dump=@var{on|off},prealloc=@var{on|off},size=@var{size},host-nodes=@var{host-nodes},policy=@var{default|preferred|bind|interleave},seal=@var{on|off},hugetlb=@var{on|off},hugetlbsize=@var{size} + +Creates an anonymous memory file backend object, which allows QEMU to +share the memory with an external process (e.g. when using +vhost-user). The memory is allocated with memfd and optional +sealing. (Linux only) + +The @option{seal} option creates a sealed-file, that will block +further resizing the memory ('on' by default). + +The @option{hugetlb} option specify the file to be created resides in +the hugetlbfs filesystem (since Linux 4.14). Used in conjunction with +the @option{hugetlb} option, the @option{hugetlbsize} option specify +the hugetlb page size on systems that support multiple hugetlb page +sizes (it must be a power of 2 value supported by the system). + +In some versions of Linux, the @option{hugetlb} option is incompatible +with the @option{seal} option (requires at least Linux 4.16). + +Please refer to @option{memory-backend-file} for a description of the +other options. + @item -object rng-random,id=@var{id},filename=@var{/dev/random} Creates a random number generator backend which obtains entropy from diff --git a/scripts/qemugdb/timers.py b/scripts/qemugdb/timers.py index be71a001e333362cf2b0648071c4082a2bafee17..51ea04b5e2ec1415074553267df7024cc94167aa 100644 --- a/scripts/qemugdb/timers.py +++ b/scripts/qemugdb/timers.py @@ -1,4 +1,5 @@ #!/usr/bin/python +# -*- coding: utf-8 -*- # GDB debugging support # # Copyright 2017 Linaro Ltd diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c index 7d6366bae99d4a97c0ec2306d846a680d04cf54d..55675ce419b0f1c712f1e44c04f04fe4adab7d79 100644 --- a/target/alpha/cpu.c +++ b/target/alpha/cpu.c @@ -233,8 +233,8 @@ static void alpha_cpu_class_init(ObjectClass *oc, void *data) CPUClass *cc = CPU_CLASS(oc); AlphaCPUClass *acc = ALPHA_CPU_CLASS(oc); - acc->parent_realize = dc->realize; - dc->realize = alpha_cpu_realizefn; + device_class_set_parent_realize(dc, alpha_cpu_realizefn, + &acc->parent_realize); cc->class_by_name = alpha_cpu_class_by_name; cc->has_work = alpha_cpu_has_work; diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 9da6ea505c0f287d1ce8a8d12d25db4f28b61ac3..89ccdeae12a359cedeb55c09e2870c7446cba71e 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -1722,8 +1722,8 @@ static void arm_cpu_class_init(ObjectClass *oc, void *data) CPUClass *cc = CPU_CLASS(acc); DeviceClass *dc = DEVICE_CLASS(oc); - acc->parent_realize = dc->realize; - dc->realize = arm_cpu_realizefn; + device_class_set_parent_realize(dc, arm_cpu_realizefn, + &acc->parent_realize); dc->props = arm_cpu_properties; acc->parent_reset = cc->reset; diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 211a7bf7befd9e6988cb3e7c02cadf6de6014a98..1219d0062bdab0e87aaf3f290a0f5ec2eb27087a 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -266,7 +266,6 @@ static void kvm_arm_machine_init_done(Notifier *notifier, void *data) { KVMDevice *kd, *tkd; - memory_listener_unregister(&devlistener); QSLIST_FOREACH_SAFE(kd, &kvm_devices_head, entries, tkd) { if (kd->kda.addr != -1) { kvm_arm_set_device_addr(kd); @@ -274,6 +273,7 @@ static void kvm_arm_machine_init_done(Notifier *notifier, void *data) memory_region_unref(kd->mr); g_free(kd); } + memory_listener_unregister(&devlistener); } static Notifier notify = { diff --git a/target/cris/cpu.c b/target/cris/cpu.c index 949c7a6e2560722b55e3c20b405f345953b58b57..db8d0884a1d8ad67667396c002cf6b57155e7c13 100644 --- a/target/cris/cpu.c +++ b/target/cris/cpu.c @@ -260,8 +260,8 @@ static void cris_cpu_class_init(ObjectClass *oc, void *data) CPUClass *cc = CPU_CLASS(oc); CRISCPUClass *ccc = CRIS_CPU_CLASS(oc); - ccc->parent_realize = dc->realize; - dc->realize = cris_cpu_realizefn; + device_class_set_parent_realize(dc, cris_cpu_realizefn, + &ccc->parent_realize); ccc->parent_reset = cc->reset; cc->reset = cris_cpu_reset; diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index 5213347720cb2081ca7387cb0e0ec7d9ca6242fc..7b635cc4ac29f933d3a83dd5c7492f1f8f8875d7 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -168,8 +168,8 @@ static void hppa_cpu_class_init(ObjectClass *oc, void *data) CPUClass *cc = CPU_CLASS(oc); HPPACPUClass *acc = HPPA_CPU_CLASS(oc); - acc->parent_realize = dc->realize; - dc->realize = hppa_cpu_realizefn; + device_class_set_parent_realize(dc, hppa_cpu_realizefn, + &acc->parent_realize); cc->class_by_name = hppa_cpu_class_by_name; cc->has_work = hppa_cpu_has_work; diff --git a/target/i386/Makefile.objs b/target/i386/Makefile.objs index 44103a693b661bafe6a0e577de88b66645062eb2..f5c6ef20a7bbe10cb12ce86e6c3c45951c178403 100644 --- a/target/i386/Makefile.objs +++ b/target/i386/Makefile.objs @@ -14,3 +14,4 @@ ifdef CONFIG_DARWIN obj-$(CONFIG_HAX) += hax-all.o hax-mem.o hax-darwin.o obj-$(CONFIG_HVF) += hvf/ endif +obj-$(CONFIG_WHPX) += whpx-all.o diff --git a/target/i386/cpu.c b/target/i386/cpu.c index a49d2221adc90eb8815fd14f9520fb88e475a9f3..d70954b8b70b4994a11bf74992669cfde2fe027f 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -4705,10 +4705,10 @@ static void x86_cpu_common_class_init(ObjectClass *oc, void *data) CPUClass *cc = CPU_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); - xcc->parent_realize = dc->realize; - xcc->parent_unrealize = dc->unrealize; - dc->realize = x86_cpu_realizefn; - dc->unrealize = x86_cpu_unrealizefn; + device_class_set_parent_realize(dc, x86_cpu_realizefn, + &xcc->parent_realize); + device_class_set_parent_unrealize(dc, x86_cpu_unrealizefn, + &xcc->parent_unrealize); dc->props = x86_cpu_properties; xcc->parent_reset = cc->reset; diff --git a/target/i386/helper.c b/target/i386/helper.c index f63eb3d3f4fb499b2a69b1f7485b00e573b8bd70..9fba146b7fb0edad2a947b9bcdedc2f0f5a127d9 100644 --- a/target/i386/helper.c +++ b/target/i386/helper.c @@ -986,7 +986,7 @@ void cpu_report_tpr_access(CPUX86State *env, TPRAccess access) X86CPU *cpu = x86_env_get_cpu(env); CPUState *cs = CPU(cpu); - if (kvm_enabled()) { + if (kvm_enabled() || whpx_enabled()) { env->tpr_access_type = access; cpu_interrupt(cs, CPU_INTERRUPT_TPR); diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c index 010866ed22429971a4216f5c35260bad649317af..85e596436514250d6aec3f1646c5324b09ecb791 100644 --- a/target/i386/hvf/hvf.c +++ b/target/i386/hvf/hvf.c @@ -17,6 +17,33 @@ * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see <http://www.gnu.org/licenses/>. + * + * This file contain code under public domain from the hvdos project: + * https://github.com/mist64/hvdos + * + * Parts Copyright (c) 2011 NetApp, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. */ #include "qemu/osdep.h" #include "qemu-common.h" diff --git a/target/i386/hvf/vmx.h b/target/i386/hvf/vmx.h index 9dfcd2f2ebb0b65875d9f3a9b10462a515dfc01e..162a7d51aed501bdc69550a8818c8b5f6cf5d24d 100644 --- a/target/i386/hvf/vmx.h +++ b/target/i386/hvf/vmx.h @@ -17,6 +17,9 @@ * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see <http://www.gnu.org/licenses/>. + * + * This file contain code under public domain from the hvdos project: + * https://github.com/mist64/hvdos */ #ifndef VMX_H diff --git a/target/i386/whpx-all.c b/target/i386/whpx-all.c new file mode 100644 index 0000000000000000000000000000000000000000..0015b27509ad22811ead820486802b683b27e78f --- /dev/null +++ b/target/i386/whpx-all.c @@ -0,0 +1,1366 @@ +/* + * QEMU Windows Hypervisor Platform accelerator (WHPX) + * + * Copyright Microsoft Corp. 2017 + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#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 "sysemu/accel.h" +#include "sysemu/whpx.h" +#include "sysemu/sysemu.h" +#include "sysemu/cpus.h" +#include "qemu/main-loop.h" +#include "hw/boards.h" +#include "qemu/error-report.h" +#include "qemu/queue.h" +#include "qapi/error.h" +#include "migration/blocker.h" + +#include <winhvplatform.h> +#include <winhvemulation.h> + +struct whpx_state { + uint64_t mem_quota; + WHV_PARTITION_HANDLE partition; + uint32_t exit_ctx_size; +}; + +static const WHV_REGISTER_NAME whpx_register_names[] = { + + /* X64 General purpose registers */ + WHvX64RegisterRax, + WHvX64RegisterRcx, + WHvX64RegisterRdx, + WHvX64RegisterRbx, + WHvX64RegisterRsp, + WHvX64RegisterRbp, + WHvX64RegisterRsi, + WHvX64RegisterRdi, + WHvX64RegisterR8, + WHvX64RegisterR9, + WHvX64RegisterR10, + WHvX64RegisterR11, + WHvX64RegisterR12, + WHvX64RegisterR13, + WHvX64RegisterR14, + WHvX64RegisterR15, + WHvX64RegisterRip, + WHvX64RegisterRflags, + + /* X64 Segment registers */ + WHvX64RegisterEs, + WHvX64RegisterCs, + WHvX64RegisterSs, + WHvX64RegisterDs, + WHvX64RegisterFs, + WHvX64RegisterGs, + WHvX64RegisterLdtr, + WHvX64RegisterTr, + + /* X64 Table registers */ + WHvX64RegisterIdtr, + WHvX64RegisterGdtr, + + /* X64 Control Registers */ + WHvX64RegisterCr0, + WHvX64RegisterCr2, + WHvX64RegisterCr3, + WHvX64RegisterCr4, + WHvX64RegisterCr8, + + /* X64 Debug Registers */ + /* + * WHvX64RegisterDr0, + * WHvX64RegisterDr1, + * WHvX64RegisterDr2, + * WHvX64RegisterDr3, + * WHvX64RegisterDr6, + * WHvX64RegisterDr7, + */ + + /* X64 Floating Point and Vector Registers */ + WHvX64RegisterXmm0, + WHvX64RegisterXmm1, + WHvX64RegisterXmm2, + WHvX64RegisterXmm3, + WHvX64RegisterXmm4, + WHvX64RegisterXmm5, + WHvX64RegisterXmm6, + WHvX64RegisterXmm7, + WHvX64RegisterXmm8, + WHvX64RegisterXmm9, + WHvX64RegisterXmm10, + WHvX64RegisterXmm11, + WHvX64RegisterXmm12, + WHvX64RegisterXmm13, + WHvX64RegisterXmm14, + WHvX64RegisterXmm15, + WHvX64RegisterFpMmx0, + WHvX64RegisterFpMmx1, + WHvX64RegisterFpMmx2, + WHvX64RegisterFpMmx3, + WHvX64RegisterFpMmx4, + WHvX64RegisterFpMmx5, + WHvX64RegisterFpMmx6, + WHvX64RegisterFpMmx7, + WHvX64RegisterFpControlStatus, + WHvX64RegisterXmmControlStatus, + + /* X64 MSRs */ + WHvX64RegisterTsc, + WHvX64RegisterEfer, +#ifdef TARGET_X86_64 + WHvX64RegisterKernelGsBase, +#endif + WHvX64RegisterApicBase, + /* WHvX64RegisterPat, */ + WHvX64RegisterSysenterCs, + WHvX64RegisterSysenterEip, + WHvX64RegisterSysenterEsp, + WHvX64RegisterStar, +#ifdef TARGET_X86_64 + WHvX64RegisterLstar, + WHvX64RegisterCstar, + WHvX64RegisterSfmask, +#endif + + /* Interrupt / Event Registers */ + /* + * WHvRegisterPendingInterruption, + * WHvRegisterInterruptState, + * WHvRegisterPendingEvent0, + * WHvRegisterPendingEvent1 + * WHvX64RegisterDeliverabilityNotifications, + */ +}; + +struct whpx_register_set { + WHV_REGISTER_VALUE values[RTL_NUMBER_OF(whpx_register_names)]; +}; + +struct whpx_vcpu { + WHV_EMULATOR_HANDLE emulator; + bool window_registered; + bool interruptable; + uint64_t tpr; + uint64_t apic_base; + WHV_X64_PENDING_INTERRUPTION_REGISTER interrupt_in_flight; + + /* Must be the last field as it may have a tail */ + WHV_RUN_VP_EXIT_CONTEXT exit_ctx; +}; + +static bool whpx_allowed; + +struct whpx_state whpx_global; + + +/* + * VP support + */ + +static struct whpx_vcpu *get_whpx_vcpu(CPUState *cpu) +{ + return (struct whpx_vcpu *)cpu->hax_vcpu; +} + +static WHV_X64_SEGMENT_REGISTER whpx_seg_q2h(const SegmentCache *qs, int v86, + int r86) +{ + WHV_X64_SEGMENT_REGISTER hs; + unsigned flags = qs->flags; + + hs.Base = qs->base; + hs.Limit = qs->limit; + hs.Selector = qs->selector; + + if (v86) { + hs.Attributes = 0; + hs.SegmentType = 3; + hs.Present = 1; + hs.DescriptorPrivilegeLevel = 3; + hs.NonSystemSegment = 1; + + } else { + hs.Attributes = (flags >> DESC_TYPE_SHIFT); + + if (r86) { + /* hs.Base &= 0xfffff; */ + } + } + + return hs; +} + +static SegmentCache whpx_seg_h2q(const WHV_X64_SEGMENT_REGISTER *hs) +{ + SegmentCache qs; + + qs.base = hs->Base; + qs.limit = hs->Limit; + qs.selector = hs->Selector; + + qs.flags = ((uint32_t)hs->Attributes) << DESC_TYPE_SHIFT; + + return qs; +} + +static void whpx_set_registers(CPUState *cpu) +{ + struct whpx_state *whpx = &whpx_global; + struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu); + struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr); + X86CPU *x86_cpu = X86_CPU(cpu); + struct whpx_register_set vcxt = {0}; + HRESULT hr; + int idx = 0; + int i; + int v86, r86; + + assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu)); + + v86 = (env->eflags & VM_MASK); + r86 = !(env->cr[0] & CR0_PE_MASK); + + vcpu->tpr = cpu_get_apic_tpr(x86_cpu->apic_state); + vcpu->apic_base = cpu_get_apic_base(x86_cpu->apic_state); + + /* Indexes for first 16 registers match between HV and QEMU definitions */ + for (idx = 0; idx < CPU_NB_REGS64; idx += 1) { + vcxt.values[idx].Reg64 = env->regs[idx]; + } + + /* Same goes for RIP and RFLAGS */ + assert(whpx_register_names[idx] == WHvX64RegisterRip); + vcxt.values[idx++].Reg64 = env->eip; + + assert(whpx_register_names[idx] == WHvX64RegisterRflags); + vcxt.values[idx++].Reg64 = env->eflags; + + /* Translate 6+4 segment registers. HV and QEMU order matches */ + assert(idx == WHvX64RegisterEs); + for (i = 0; i < 6; i += 1, idx += 1) { + vcxt.values[idx].Segment = whpx_seg_q2h(&env->segs[i], v86, r86); + } + + assert(idx == WHvX64RegisterLdtr); + vcxt.values[idx++].Segment = whpx_seg_q2h(&env->ldt, 0, 0); + + assert(idx == WHvX64RegisterTr); + vcxt.values[idx++].Segment = whpx_seg_q2h(&env->tr, 0, 0); + + assert(idx == WHvX64RegisterIdtr); + vcxt.values[idx].Table.Base = env->idt.base; + vcxt.values[idx].Table.Limit = env->idt.limit; + idx += 1; + + assert(idx == WHvX64RegisterGdtr); + vcxt.values[idx].Table.Base = env->gdt.base; + vcxt.values[idx].Table.Limit = env->gdt.limit; + idx += 1; + + /* CR0, 2, 3, 4, 8 */ + assert(whpx_register_names[idx] == WHvX64RegisterCr0); + vcxt.values[idx++].Reg64 = env->cr[0]; + assert(whpx_register_names[idx] == WHvX64RegisterCr2); + vcxt.values[idx++].Reg64 = env->cr[2]; + assert(whpx_register_names[idx] == WHvX64RegisterCr3); + vcxt.values[idx++].Reg64 = env->cr[3]; + assert(whpx_register_names[idx] == WHvX64RegisterCr4); + vcxt.values[idx++].Reg64 = env->cr[4]; + assert(whpx_register_names[idx] == WHvX64RegisterCr8); + vcxt.values[idx++].Reg64 = vcpu->tpr; + + /* 8 Debug Registers - Skipped */ + + /* 16 XMM registers */ + assert(whpx_register_names[idx] == WHvX64RegisterXmm0); + for (i = 0; i < 16; i += 1, idx += 1) { + vcxt.values[idx].Reg128.Low64 = env->xmm_regs[i].ZMM_Q(0); + vcxt.values[idx].Reg128.High64 = env->xmm_regs[i].ZMM_Q(1); + } + + /* 8 FP registers */ + assert(whpx_register_names[idx] == WHvX64RegisterFpMmx0); + for (i = 0; i < 8; i += 1, idx += 1) { + vcxt.values[idx].Fp.AsUINT128.Low64 = env->fpregs[i].mmx.MMX_Q(0); + /* vcxt.values[idx].Fp.AsUINT128.High64 = + env->fpregs[i].mmx.MMX_Q(1); + */ + } + + /* FP control status register */ + assert(whpx_register_names[idx] == WHvX64RegisterFpControlStatus); + vcxt.values[idx].FpControlStatus.FpControl = env->fpuc; + vcxt.values[idx].FpControlStatus.FpStatus = + (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11; + vcxt.values[idx].FpControlStatus.FpTag = 0; + for (i = 0; i < 8; ++i) { + vcxt.values[idx].FpControlStatus.FpTag |= (!env->fptags[i]) << i; + } + vcxt.values[idx].FpControlStatus.Reserved = 0; + vcxt.values[idx].FpControlStatus.LastFpOp = env->fpop; + vcxt.values[idx].FpControlStatus.LastFpRip = env->fpip; + idx += 1; + + /* XMM control status register */ + assert(whpx_register_names[idx] == WHvX64RegisterXmmControlStatus); + vcxt.values[idx].XmmControlStatus.LastFpRdp = 0; + vcxt.values[idx].XmmControlStatus.XmmStatusControl = env->mxcsr; + vcxt.values[idx].XmmControlStatus.XmmStatusControlMask = 0x0000ffff; + idx += 1; + + /* MSRs */ + assert(whpx_register_names[idx] == WHvX64RegisterTsc); + vcxt.values[idx++].Reg64 = env->tsc; + assert(whpx_register_names[idx] == WHvX64RegisterEfer); + vcxt.values[idx++].Reg64 = env->efer; +#ifdef TARGET_X86_64 + assert(whpx_register_names[idx] == WHvX64RegisterKernelGsBase); + vcxt.values[idx++].Reg64 = env->kernelgsbase; +#endif + + assert(whpx_register_names[idx] == WHvX64RegisterApicBase); + vcxt.values[idx++].Reg64 = vcpu->apic_base; + + /* WHvX64RegisterPat - Skipped */ + + assert(whpx_register_names[idx] == WHvX64RegisterSysenterCs); + vcxt.values[idx++].Reg64 = env->sysenter_cs; + assert(whpx_register_names[idx] == WHvX64RegisterSysenterEip); + vcxt.values[idx++].Reg64 = env->sysenter_eip; + assert(whpx_register_names[idx] == WHvX64RegisterSysenterEsp); + vcxt.values[idx++].Reg64 = env->sysenter_esp; + assert(whpx_register_names[idx] == WHvX64RegisterStar); + vcxt.values[idx++].Reg64 = env->star; +#ifdef TARGET_X86_64 + assert(whpx_register_names[idx] == WHvX64RegisterLstar); + vcxt.values[idx++].Reg64 = env->lstar; + assert(whpx_register_names[idx] == WHvX64RegisterCstar); + vcxt.values[idx++].Reg64 = env->cstar; + assert(whpx_register_names[idx] == WHvX64RegisterSfmask); + vcxt.values[idx++].Reg64 = env->fmask; +#endif + + /* Interrupt / Event Registers - Skipped */ + + assert(idx == RTL_NUMBER_OF(whpx_register_names)); + + hr = WHvSetVirtualProcessorRegisters(whpx->partition, cpu->cpu_index, + whpx_register_names, + RTL_NUMBER_OF(whpx_register_names), + &vcxt.values[0]); + + if (FAILED(hr)) { + error_report("WHPX: Failed to set virtual processor context, hr=%08lx", + hr); + __debugbreak(); + } + + return; +} + +static void whpx_get_registers(CPUState *cpu) +{ + struct whpx_state *whpx = &whpx_global; + struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu); + struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr); + X86CPU *x86_cpu = X86_CPU(cpu); + struct whpx_register_set vcxt; + uint64_t tpr, apic_base; + HRESULT hr; + int idx = 0; + int i; + + assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu)); + + hr = WHvGetVirtualProcessorRegisters(whpx->partition, cpu->cpu_index, + whpx_register_names, + RTL_NUMBER_OF(whpx_register_names), + &vcxt.values[0]); + if (FAILED(hr)) { + error_report("WHPX: Failed to get virtual processor context, hr=%08lx", + hr); + __debugbreak(); + } + + /* Indexes for first 16 registers match between HV and QEMU definitions */ + for (idx = 0; idx < CPU_NB_REGS64; idx += 1) { + env->regs[idx] = vcxt.values[idx].Reg64; + } + + /* Same goes for RIP and RFLAGS */ + assert(whpx_register_names[idx] == WHvX64RegisterRip); + env->eip = vcxt.values[idx++].Reg64; + assert(whpx_register_names[idx] == WHvX64RegisterRflags); + env->eflags = vcxt.values[idx++].Reg64; + + /* Translate 6+4 segment registers. HV and QEMU order matches */ + assert(idx == WHvX64RegisterEs); + for (i = 0; i < 6; i += 1, idx += 1) { + env->segs[i] = whpx_seg_h2q(&vcxt.values[idx].Segment); + } + + assert(idx == WHvX64RegisterLdtr); + env->ldt = whpx_seg_h2q(&vcxt.values[idx++].Segment); + assert(idx == WHvX64RegisterTr); + env->tr = whpx_seg_h2q(&vcxt.values[idx++].Segment); + assert(idx == WHvX64RegisterIdtr); + env->idt.base = vcxt.values[idx].Table.Base; + env->idt.limit = vcxt.values[idx].Table.Limit; + idx += 1; + assert(idx == WHvX64RegisterGdtr); + env->gdt.base = vcxt.values[idx].Table.Base; + env->gdt.limit = vcxt.values[idx].Table.Limit; + idx += 1; + + /* CR0, 2, 3, 4, 8 */ + assert(whpx_register_names[idx] == WHvX64RegisterCr0); + env->cr[0] = vcxt.values[idx++].Reg64; + assert(whpx_register_names[idx] == WHvX64RegisterCr2); + env->cr[2] = vcxt.values[idx++].Reg64; + assert(whpx_register_names[idx] == WHvX64RegisterCr3); + env->cr[3] = vcxt.values[idx++].Reg64; + assert(whpx_register_names[idx] == WHvX64RegisterCr4); + env->cr[4] = vcxt.values[idx++].Reg64; + assert(whpx_register_names[idx] == WHvX64RegisterCr8); + tpr = vcxt.values[idx++].Reg64; + if (tpr != vcpu->tpr) { + vcpu->tpr = tpr; + cpu_set_apic_tpr(x86_cpu->apic_state, tpr); + } + + /* 8 Debug Registers - Skipped */ + + /* 16 XMM registers */ + assert(whpx_register_names[idx] == WHvX64RegisterXmm0); + for (i = 0; i < 16; i += 1, idx += 1) { + env->xmm_regs[i].ZMM_Q(0) = vcxt.values[idx].Reg128.Low64; + env->xmm_regs[i].ZMM_Q(1) = vcxt.values[idx].Reg128.High64; + } + + /* 8 FP registers */ + assert(whpx_register_names[idx] == WHvX64RegisterFpMmx0); + for (i = 0; i < 8; i += 1, idx += 1) { + env->fpregs[i].mmx.MMX_Q(0) = vcxt.values[idx].Fp.AsUINT128.Low64; + /* env->fpregs[i].mmx.MMX_Q(1) = + vcxt.values[idx].Fp.AsUINT128.High64; + */ + } + + /* FP control status register */ + assert(whpx_register_names[idx] == WHvX64RegisterFpControlStatus); + env->fpuc = vcxt.values[idx].FpControlStatus.FpControl; + env->fpstt = (vcxt.values[idx].FpControlStatus.FpStatus >> 11) & 0x7; + env->fpus = vcxt.values[idx].FpControlStatus.FpStatus & ~0x3800; + for (i = 0; i < 8; ++i) { + env->fptags[i] = !((vcxt.values[idx].FpControlStatus.FpTag >> i) & 1); + } + env->fpop = vcxt.values[idx].FpControlStatus.LastFpOp; + env->fpip = vcxt.values[idx].FpControlStatus.LastFpRip; + idx += 1; + + /* XMM control status register */ + assert(whpx_register_names[idx] == WHvX64RegisterXmmControlStatus); + env->mxcsr = vcxt.values[idx].XmmControlStatus.XmmStatusControl; + idx += 1; + + /* MSRs */ + assert(whpx_register_names[idx] == WHvX64RegisterTsc); + env->tsc = vcxt.values[idx++].Reg64; + assert(whpx_register_names[idx] == WHvX64RegisterEfer); + env->efer = vcxt.values[idx++].Reg64; +#ifdef TARGET_X86_64 + assert(whpx_register_names[idx] == WHvX64RegisterKernelGsBase); + env->kernelgsbase = vcxt.values[idx++].Reg64; +#endif + + assert(whpx_register_names[idx] == WHvX64RegisterApicBase); + apic_base = vcxt.values[idx++].Reg64; + if (apic_base != vcpu->apic_base) { + vcpu->apic_base = apic_base; + cpu_set_apic_base(x86_cpu->apic_state, vcpu->apic_base); + } + + /* WHvX64RegisterPat - Skipped */ + + assert(whpx_register_names[idx] == WHvX64RegisterSysenterCs); + env->sysenter_cs = vcxt.values[idx++].Reg64;; + assert(whpx_register_names[idx] == WHvX64RegisterSysenterEip); + env->sysenter_eip = vcxt.values[idx++].Reg64; + assert(whpx_register_names[idx] == WHvX64RegisterSysenterEsp); + env->sysenter_esp = vcxt.values[idx++].Reg64; + assert(whpx_register_names[idx] == WHvX64RegisterStar); + env->star = vcxt.values[idx++].Reg64; +#ifdef TARGET_X86_64 + assert(whpx_register_names[idx] == WHvX64RegisterLstar); + env->lstar = vcxt.values[idx++].Reg64; + assert(whpx_register_names[idx] == WHvX64RegisterCstar); + env->cstar = vcxt.values[idx++].Reg64; + assert(whpx_register_names[idx] == WHvX64RegisterSfmask); + env->fmask = vcxt.values[idx++].Reg64; +#endif + + /* Interrupt / Event Registers - Skipped */ + + assert(idx == RTL_NUMBER_OF(whpx_register_names)); + + return; +} + +static HRESULT CALLBACK whpx_emu_ioport_callback( + void *ctx, + WHV_EMULATOR_IO_ACCESS_INFO *IoAccess) +{ + MemTxAttrs attrs = { 0 }; + address_space_rw(&address_space_io, IoAccess->Port, attrs, + (uint8_t *)&IoAccess->Data, IoAccess->AccessSize, + IoAccess->Direction); + return S_OK; +} + +static HRESULT CALLBACK whpx_emu_memio_callback( + void *ctx, + WHV_EMULATOR_MEMORY_ACCESS_INFO *ma) +{ + cpu_physical_memory_rw(ma->GpaAddress, ma->Data, ma->AccessSize, + ma->Direction); + return S_OK; +} + +static HRESULT CALLBACK whpx_emu_getreg_callback( + void *ctx, + const WHV_REGISTER_NAME *RegisterNames, + UINT32 RegisterCount, + WHV_REGISTER_VALUE *RegisterValues) +{ + HRESULT hr; + struct whpx_state *whpx = &whpx_global; + CPUState *cpu = (CPUState *)ctx; + + hr = WHvGetVirtualProcessorRegisters(whpx->partition, cpu->cpu_index, + RegisterNames, RegisterCount, + RegisterValues); + if (FAILED(hr)) { + error_report("WHPX: Failed to get virtual processor registers," + " hr=%08lx", hr); + __debugbreak(); + } + + return hr; +} + +static HRESULT CALLBACK whpx_emu_setreg_callback( + void *ctx, + const WHV_REGISTER_NAME *RegisterNames, + UINT32 RegisterCount, + const WHV_REGISTER_VALUE *RegisterValues) +{ + HRESULT hr; + struct whpx_state *whpx = &whpx_global; + CPUState *cpu = (CPUState *)ctx; + + hr = WHvSetVirtualProcessorRegisters(whpx->partition, cpu->cpu_index, + RegisterNames, RegisterCount, + RegisterValues); + if (FAILED(hr)) { + error_report("WHPX: Failed to set virtual processor registers," + " hr=%08lx", hr); + __debugbreak(); + } + + /* + * The emulator just successfully wrote the register state. We clear the + * dirty state so we avoid the double write on resume of the VP. + */ + cpu->vcpu_dirty = false; + + return hr; +} + +static HRESULT CALLBACK whpx_emu_translate_callback( + void *ctx, + WHV_GUEST_VIRTUAL_ADDRESS Gva, + WHV_TRANSLATE_GVA_FLAGS TranslateFlags, + WHV_TRANSLATE_GVA_RESULT_CODE *TranslationResult, + WHV_GUEST_PHYSICAL_ADDRESS *Gpa) +{ + HRESULT hr; + struct whpx_state *whpx = &whpx_global; + CPUState *cpu = (CPUState *)ctx; + WHV_TRANSLATE_GVA_RESULT res; + + hr = WHvTranslateGva(whpx->partition, cpu->cpu_index, + Gva, TranslateFlags, &res, Gpa); + if (FAILED(hr)) { + error_report("WHPX: Failed to translate GVA, hr=%08lx", hr); + __debugbreak(); + } else { + *TranslationResult = res.ResultCode; + } + + return hr; +} + +static const WHV_EMULATOR_CALLBACKS whpx_emu_callbacks = { + .WHvEmulatorIoPortCallback = whpx_emu_ioport_callback, + .WHvEmulatorMemoryCallback = whpx_emu_memio_callback, + .WHvEmulatorGetVirtualProcessorRegisters = whpx_emu_getreg_callback, + .WHvEmulatorSetVirtualProcessorRegisters = whpx_emu_setreg_callback, + .WHvEmulatorTranslateGvaPage = whpx_emu_translate_callback, +}; + +static int whpx_handle_mmio(CPUState *cpu, WHV_MEMORY_ACCESS_CONTEXT *ctx) +{ + HRESULT hr; + struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu); + WHV_EMULATOR_STATUS emu_status; + + hr = WHvEmulatorTryMmioEmulation(vcpu->emulator, cpu, ctx, &emu_status); + if (FAILED(hr)) { + __debugbreak(); + error_report("WHPX: Failed to parse MMIO access, hr=%08lx", hr); + return -1; + } + + if (!emu_status.EmulationSuccessful) { + __debugbreak(); + error_report("WHPX: Failed to emulate MMIO access"); + return -1; + } + + return 0; +} + +static int whpx_handle_portio(CPUState *cpu, + WHV_X64_IO_PORT_ACCESS_CONTEXT *ctx) +{ + HRESULT hr; + struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu); + WHV_EMULATOR_STATUS emu_status; + + hr = WHvEmulatorTryIoEmulation(vcpu->emulator, cpu, ctx, &emu_status); + if (FAILED(hr)) { + __debugbreak(); + error_report("WHPX: Failed to parse PortIO access, hr=%08lx", hr); + return -1; + } + + if (!emu_status.EmulationSuccessful) { + __debugbreak(); + error_report("WHPX: Failed to emulate PortMMIO access"); + return -1; + } + + return 0; +} + +static int whpx_handle_halt(CPUState *cpu) +{ + 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 void whpx_vcpu_pre_run(CPUState *cpu) +{ + HRESULT hr; + struct whpx_state *whpx = &whpx_global; + struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu); + struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr); + X86CPU *x86_cpu = X86_CPU(cpu); + int irq; + WHV_X64_PENDING_INTERRUPTION_REGISTER new_int = {0}; + UINT32 reg_count = 0; + WHV_REGISTER_VALUE reg_values[3] = {0}; + WHV_REGISTER_NAME reg_names[3]; + + qemu_mutex_lock_iothread(); + + /* Inject NMI */ + if (!vcpu->interrupt_in_flight.InterruptionPending && + cpu->interrupt_request & (CPU_INTERRUPT_NMI | CPU_INTERRUPT_SMI)) { + if (cpu->interrupt_request & CPU_INTERRUPT_NMI) { + cpu->interrupt_request &= ~CPU_INTERRUPT_NMI; + vcpu->interruptable = false; + new_int.InterruptionType = WHvX64PendingNmi; + new_int.InterruptionPending = 1; + new_int.InterruptionVector = 2; + } + if (cpu->interrupt_request & CPU_INTERRUPT_SMI) { + qemu_mutex_lock_iothread(); + cpu->interrupt_request &= ~CPU_INTERRUPT_SMI; + __debugbreak(); + qemu_mutex_unlock_iothread(); + } + } + + /* + * 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)) { + if ((cpu->interrupt_request & CPU_INTERRUPT_INIT) && + !(env->hflags & HF_SMM_MASK)) { + cpu->exit_request = 1; + } + if (cpu->interrupt_request & CPU_INTERRUPT_TPR) { + cpu->exit_request = 1; + } + } + + /* Get pending hard interruption or replay one that was overwritten */ + if (!vcpu->interrupt_in_flight.InterruptionPending && + vcpu->interruptable && (env->eflags & IF_MASK)) { + assert(!new_int.InterruptionPending); + if (cpu->interrupt_request & CPU_INTERRUPT_HARD) { + cpu->interrupt_request &= ~CPU_INTERRUPT_HARD; + irq = cpu_get_pic_interrupt(env); + if (irq >= 0) { + new_int.InterruptionType = WHvX64PendingInterrupt; + new_int.InterruptionPending = 1; + new_int.InterruptionVector = irq; + } + } + } + + /* Setup interrupt state if new one was prepared */ + if (new_int.InterruptionPending) { + reg_values[reg_count].PendingInterruption = new_int; + reg_names[reg_count] = WHvRegisterPendingInterruption; + reg_count += 1; + } + + /* Sync the TPR to the CR8 if was modified during the intercept */ + reg_values[reg_count].Reg64 = cpu_get_apic_tpr(x86_cpu->apic_state); + if (reg_values[reg_count].Reg64 != vcpu->tpr) { + vcpu->tpr = reg_values[reg_count].Reg64; + cpu->exit_request = 1; + reg_names[reg_count] = WHvX64RegisterCr8; + reg_count += 1; + } + + /* Update the state of the interrupt delivery notification */ + if (cpu->interrupt_request & CPU_INTERRUPT_HARD) { + reg_values[reg_count].DeliverabilityNotifications.InterruptNotification + = 1; + if (vcpu->window_registered != 1) { + vcpu->window_registered = 1; + } + reg_names[reg_count] = WHvX64RegisterDeliverabilityNotifications; + reg_count += 1; + } + + qemu_mutex_unlock_iothread(); + + if (reg_count) { + hr = WHvSetVirtualProcessorRegisters(whpx->partition, cpu->cpu_index, + reg_names, reg_count, reg_values); + if (FAILED(hr)) { + error_report("WHPX: Failed to set interrupt state registers," + " hr=%08lx", hr); + __debugbreak(); + } + } + + return; +} + +static void whpx_vcpu_post_run(CPUState *cpu) +{ + HRESULT hr; + struct whpx_state *whpx = &whpx_global; + struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu); + struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr); + X86CPU *x86_cpu = X86_CPU(cpu); + WHV_REGISTER_VALUE reg_values[4]; + const WHV_REGISTER_NAME reg_names[4] = { + WHvX64RegisterRflags, + WHvX64RegisterCr8, + WHvRegisterPendingInterruption, + WHvRegisterInterruptState, + }; + + hr = WHvGetVirtualProcessorRegisters(whpx->partition, cpu->cpu_index, + reg_names, 4, reg_values); + if (FAILED(hr)) { + error_report("WHPX: Failed to get interrupt state regusters," + " hr=%08lx", hr); + __debugbreak(); + vcpu->interruptable = false; + return; + } + + assert(reg_names[0] == WHvX64RegisterRflags); + env->eflags = reg_values[0].Reg64; + + assert(reg_names[1] == WHvX64RegisterCr8); + if (vcpu->tpr != reg_values[1].Reg64) { + vcpu->tpr = reg_values[1].Reg64; + qemu_mutex_lock_iothread(); + cpu_set_apic_tpr(x86_cpu->apic_state, vcpu->tpr); + qemu_mutex_unlock_iothread(); + } + + assert(reg_names[2] == WHvRegisterPendingInterruption); + vcpu->interrupt_in_flight = reg_values[2].PendingInterruption; + + assert(reg_names[3] == WHvRegisterInterruptState); + vcpu->interruptable = !reg_values[3].InterruptState.InterruptShadow; + + return; +} + +static void whpx_vcpu_process_async_events(CPUState *cpu) +{ + struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr); + X86CPU *x86_cpu = X86_CPU(cpu); + struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu); + + if ((cpu->interrupt_request & CPU_INTERRUPT_INIT) && + !(env->hflags & HF_SMM_MASK)) { + + do_cpu_init(x86_cpu); + cpu->vcpu_dirty = true; + vcpu->interruptable = true; + } + + 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) { + if (!cpu->vcpu_dirty) { + whpx_get_registers(cpu); + } + do_cpu_sipi(x86_cpu); + } + + if (cpu->interrupt_request & CPU_INTERRUPT_TPR) { + cpu->interrupt_request &= ~CPU_INTERRUPT_TPR; + if (!cpu->vcpu_dirty) { + whpx_get_registers(cpu); + } + apic_handle_tpr_access_report(x86_cpu->apic_state, env->eip, + env->tpr_access_type); + } + + return; +} + +static int whpx_vcpu_run(CPUState *cpu) +{ + HRESULT hr; + struct whpx_state *whpx = &whpx_global; + struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu); + int ret; + + whpx_vcpu_process_async_events(cpu); + if (cpu->halted) { + cpu->exception_index = EXCP_HLT; + atomic_set(&cpu->exit_request, false); + return 0; + } + + qemu_mutex_unlock_iothread(); + cpu_exec_start(cpu); + + do { + if (cpu->vcpu_dirty) { + whpx_set_registers(cpu); + cpu->vcpu_dirty = false; + } + + whpx_vcpu_pre_run(cpu); + + if (atomic_read(&cpu->exit_request)) { + whpx_vcpu_kick(cpu); + } + + for (;;) { + hr = WHvRunVirtualProcessor(whpx->partition, cpu->cpu_index, + &vcpu->exit_ctx, whpx->exit_ctx_size); + + if (SUCCEEDED(hr) && (vcpu->exit_ctx.ExitReason == + WHvRunVpExitReasonAlerted)) { + WHvCancelRunVirtualProcessor(whpx->partition, cpu->cpu_index, + 0); + } else { + break; + } + } + + if (FAILED(hr)) { + error_report("WHPX: Failed to exec a virtual processor," + " hr=%08lx", hr); + ret = -1; + break; + } + + whpx_vcpu_post_run(cpu); + + switch (vcpu->exit_ctx.ExitReason) { + case WHvRunVpExitReasonMemoryAccess: + ret = whpx_handle_mmio(cpu, &vcpu->exit_ctx.MemoryAccess); + break; + + case WHvRunVpExitReasonX64IoPortAccess: + ret = whpx_handle_portio(cpu, &vcpu->exit_ctx.IoPortAccess); + break; + + case WHvRunVpExitReasonX64InterruptWindow: + vcpu->window_registered = 0; + break; + + case WHvRunVpExitReasonX64Halt: + ret = whpx_handle_halt(cpu); + break; + + case WHvRunVpExitReasonCanceled: + cpu->exception_index = EXCP_INTERRUPT; + ret = 1; + break; + + case WHvRunVpExitReasonNone: + case WHvRunVpExitReasonUnrecoverableException: + case WHvRunVpExitReasonInvalidVpRegisterValue: + case WHvRunVpExitReasonUnsupportedFeature: + case WHvRunVpExitReasonX64MsrAccess: + case WHvRunVpExitReasonX64Cpuid: + case WHvRunVpExitReasonException: + case WHvRunVpExitReasonAlerted: + default: + error_report("WHPX: Unexpected VP exit code %d", + vcpu->exit_ctx.ExitReason); + whpx_get_registers(cpu); + qemu_mutex_lock_iothread(); + qemu_system_guest_panicked(cpu_get_crash_info(cpu)); + qemu_mutex_unlock_iothread(); + break; + } + + } while (!ret); + + cpu_exec_end(cpu); + qemu_mutex_lock_iothread(); + current_cpu = cpu; + + atomic_set(&cpu->exit_request, false); + + return ret < 0; +} + +static void do_whpx_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg) +{ + whpx_get_registers(cpu); + cpu->vcpu_dirty = true; +} + +static void do_whpx_cpu_synchronize_post_reset(CPUState *cpu, + run_on_cpu_data arg) +{ + whpx_set_registers(cpu); + cpu->vcpu_dirty = false; +} + +static void do_whpx_cpu_synchronize_post_init(CPUState *cpu, + run_on_cpu_data arg) +{ + whpx_set_registers(cpu); + cpu->vcpu_dirty = false; +} + +static void do_whpx_cpu_synchronize_pre_loadvm(CPUState *cpu, + run_on_cpu_data arg) +{ + cpu->vcpu_dirty = true; +} + +/* + * CPU support. + */ + +void whpx_cpu_synchronize_state(CPUState *cpu) +{ + if (!cpu->vcpu_dirty) { + run_on_cpu(cpu, do_whpx_cpu_synchronize_state, RUN_ON_CPU_NULL); + } +} + +void whpx_cpu_synchronize_post_reset(CPUState *cpu) +{ + run_on_cpu(cpu, do_whpx_cpu_synchronize_post_reset, RUN_ON_CPU_NULL); +} + +void whpx_cpu_synchronize_post_init(CPUState *cpu) +{ + run_on_cpu(cpu, do_whpx_cpu_synchronize_post_init, RUN_ON_CPU_NULL); +} + +void whpx_cpu_synchronize_pre_loadvm(CPUState *cpu) +{ + run_on_cpu(cpu, do_whpx_cpu_synchronize_pre_loadvm, RUN_ON_CPU_NULL); +} + +/* + * Vcpu support. + */ + +static Error *whpx_migration_blocker; + +int whpx_init_vcpu(CPUState *cpu) +{ + HRESULT hr; + struct whpx_state *whpx = &whpx_global; + struct whpx_vcpu *vcpu; + Error *local_error = NULL; + + /* Add migration blockers for all unsupported features of the + * Windows Hypervisor Platform + */ + if (whpx_migration_blocker == NULL) { + error_setg(&whpx_migration_blocker, + "State blocked due to non-migratable CPUID feature support," + "dirty memory tracking support, and XSAVE/XRSTOR support"); + + (void)migrate_add_blocker(whpx_migration_blocker, &local_error); + if (local_error) { + error_report_err(local_error); + error_free(whpx_migration_blocker); + migrate_del_blocker(whpx_migration_blocker); + return -EINVAL; + } + } + + vcpu = g_malloc0(FIELD_OFFSET(struct whpx_vcpu, exit_ctx) + + whpx->exit_ctx_size); + + if (!vcpu) { + error_report("WHPX: Failed to allocte VCPU context."); + return -ENOMEM; + } + + hr = WHvEmulatorCreateEmulator(whpx_emu_callbacks, &vcpu->emulator); + if (FAILED(hr)) { + error_report("WHPX: Failed to setup instruction completion support," + " hr=%08lx", hr); + g_free(vcpu); + return -EINVAL; + } + + hr = WHvCreateVirtualProcessor(whpx->partition, cpu->cpu_index, 0); + if (FAILED(hr)) { + error_report("WHPX: Failed to create a virtual processor," + " hr=%08lx", hr); + WHvEmulatorDestroyEmulator(vcpu->emulator); + g_free(vcpu); + return -EINVAL; + } + + vcpu->interruptable = true; + + cpu->vcpu_dirty = true; + cpu->hax_vcpu = (struct hax_vcpu_state *)vcpu; + + return 0; +} + +int whpx_vcpu_exec(CPUState *cpu) +{ + int ret; + int fatal; + + for (;;) { + if (cpu->exception_index >= EXCP_INTERRUPT) { + ret = cpu->exception_index; + cpu->exception_index = -1; + break; + } + + fatal = whpx_vcpu_run(cpu); + + if (fatal) { + error_report("WHPX: Failed to exec a virtual processor"); + abort(); + } + } + + return ret; +} + +void whpx_destroy_vcpu(CPUState *cpu) +{ + struct whpx_state *whpx = &whpx_global; + struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu); + + WHvDeleteVirtualProcessor(whpx->partition, cpu->cpu_index); + WHvEmulatorDestroyEmulator(vcpu->emulator); + g_free(cpu->hax_vcpu); + return; +} + +void whpx_vcpu_kick(CPUState *cpu) +{ + struct whpx_state *whpx = &whpx_global; + WHvCancelRunVirtualProcessor(whpx->partition, cpu->cpu_index, 0); +} + +/* + * Memory support. + */ + +static void whpx_update_mapping(hwaddr start_pa, ram_addr_t size, + void *host_va, int add, int rom, + const char *name) +{ + struct whpx_state *whpx = &whpx_global; + HRESULT hr; + + /* + if (add) { + printf("WHPX: ADD PA:%p Size:%p, Host:%p, %s, '%s'\n", + (void*)start_pa, (void*)size, host_va, + (rom ? "ROM" : "RAM"), name); + } else { + printf("WHPX: DEL PA:%p Size:%p, Host:%p, '%s'\n", + (void*)start_pa, (void*)size, host_va, name); + } + */ + + if (add) { + hr = WHvMapGpaRange(whpx->partition, + host_va, + start_pa, + size, + (WHvMapGpaRangeFlagRead | + WHvMapGpaRangeFlagExecute | + (rom ? 0 : WHvMapGpaRangeFlagWrite))); + } else { + hr = WHvUnmapGpaRange(whpx->partition, + start_pa, + size); + } + + if (FAILED(hr)) { + error_report("WHPX: Failed to %s GPA range '%s' PA:%p, Size:%p bytes," + " Host:%p, hr=%08lx", + (add ? "MAP" : "UNMAP"), name, + (void *)start_pa, (void *)size, host_va, hr); + } +} + +static void whpx_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; + uint64_t host_va; + + if (!memory_region_is_ram(mr)) { + return; + } + + 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; + + whpx_update_mapping(start_pa, size, (void *)host_va, add, + memory_region_is_rom(mr), mr->name); +} + +static void whpx_region_add(MemoryListener *listener, + MemoryRegionSection *section) +{ + memory_region_ref(section->mr); + whpx_process_section(section, 1); +} + +static void whpx_region_del(MemoryListener *listener, + MemoryRegionSection *section) +{ + whpx_process_section(section, 0); + memory_region_unref(section->mr); +} + +static void whpx_transaction_begin(MemoryListener *listener) +{ +} + +static void whpx_transaction_commit(MemoryListener *listener) +{ +} + +static void whpx_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 whpx_memory_listener = { + .begin = whpx_transaction_begin, + .commit = whpx_transaction_commit, + .region_add = whpx_region_add, + .region_del = whpx_region_del, + .log_sync = whpx_log_sync, + .priority = 10, +}; + +static void whpx_memory_init(void) +{ + memory_listener_register(&whpx_memory_listener, &address_space_memory); +} + +static void whpx_handle_interrupt(CPUState *cpu, int mask) +{ + cpu->interrupt_request |= mask; + + if (!qemu_cpu_is_self(cpu)) { + qemu_cpu_kick(cpu); + } +} + +/* + * Partition support + */ + +static int whpx_accel_init(MachineState *ms) +{ + struct whpx_state *whpx; + int ret; + HRESULT hr; + WHV_CAPABILITY whpx_cap; + WHV_PARTITION_PROPERTY prop; + + whpx = &whpx_global; + + memset(whpx, 0, sizeof(struct whpx_state)); + whpx->mem_quota = ms->ram_size; + + hr = WHvGetCapability(WHvCapabilityCodeHypervisorPresent, &whpx_cap, + sizeof(whpx_cap)); + if (FAILED(hr) || !whpx_cap.HypervisorPresent) { + error_report("WHPX: No accelerator found, hr=%08lx", hr); + ret = -ENOSPC; + goto error; + } + + hr = WHvCreatePartition(&whpx->partition); + if (FAILED(hr)) { + error_report("WHPX: Failed to create partition, hr=%08lx", hr); + ret = -EINVAL; + goto error; + } + + memset(&prop, 0, sizeof(WHV_PARTITION_PROPERTY)); + prop.PropertyCode = WHvPartitionPropertyCodeProcessorCount; + prop.ProcessorCount = smp_cpus; + hr = WHvSetPartitionProperty(whpx->partition, + &prop, + sizeof(WHV_PARTITION_PROPERTY)); + + if (FAILED(hr)) { + error_report("WHPX: Failed to set partition core count to %d," + " hr=%08lx", smp_cores, hr); + ret = -EINVAL; + goto error; + } + + hr = WHvSetupPartition(whpx->partition); + if (FAILED(hr)) { + error_report("WHPX: Failed to setup partition, hr=%08lx", hr); + ret = -EINVAL; + goto error; + } + + whpx->exit_ctx_size = WHvGetRunExitContextSize(); + assert(whpx->exit_ctx_size); + + whpx_memory_init(); + + cpu_interrupt_handler = whpx_handle_interrupt; + + printf("Windows Hypervisor Platform accelerator is operational\n"); + return 0; + + error: + + if (NULL != whpx->partition) { + WHvDeletePartition(whpx->partition); + whpx->partition = NULL; + } + + + return ret; +} + +int whpx_enabled(void) +{ + return whpx_allowed; +} + +static void whpx_accel_class_init(ObjectClass *oc, void *data) +{ + AccelClass *ac = ACCEL_CLASS(oc); + ac->name = "WHPX"; + ac->init_machine = whpx_accel_init; + ac->allowed = &whpx_allowed; +} + +static const TypeInfo whpx_accel_type = { + .name = ACCEL_CLASS_NAME("whpx"), + .parent = TYPE_ACCEL, + .class_init = whpx_accel_class_init, +}; + +static void whpx_type_init(void) +{ + type_register_static(&whpx_accel_type); +} + +type_init(whpx_type_init); diff --git a/target/lm32/cpu.c b/target/lm32/cpu.c index 6f5c14767b8f00a2eefb1007fe2cf2e923a5ea31..96c2499d0b24cabc3f7361351c1380839d476aad 100644 --- a/target/lm32/cpu.c +++ b/target/lm32/cpu.c @@ -236,9 +236,8 @@ static void lm32_cpu_class_init(ObjectClass *oc, void *data) CPUClass *cc = CPU_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); - lcc->parent_realize = dc->realize; - dc->realize = lm32_cpu_realizefn; - + device_class_set_parent_realize(dc, lm32_cpu_realizefn, + &lcc->parent_realize); lcc->parent_reset = cc->reset; cc->reset = lm32_cpu_reset; diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c index 98919b358bf55aaa04715181af23e3523d1f3bf4..6a80be009bfe6c1ab33d0780390011da7034b6ee 100644 --- a/target/m68k/cpu.c +++ b/target/m68k/cpu.c @@ -255,9 +255,8 @@ static void m68k_cpu_class_init(ObjectClass *c, void *data) CPUClass *cc = CPU_CLASS(c); DeviceClass *dc = DEVICE_CLASS(c); - mcc->parent_realize = dc->realize; - dc->realize = m68k_cpu_realizefn; - + device_class_set_parent_realize(dc, m68k_cpu_realizefn, + &mcc->parent_realize); mcc->parent_reset = cc->reset; cc->reset = m68k_cpu_reset; diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index 5700652e064b8049d904dd98053f3ee8a1aae9c4..d8df2fb07eb113811e1b38b572512e9f575831f6 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -258,9 +258,8 @@ static void mb_cpu_class_init(ObjectClass *oc, void *data) CPUClass *cc = CPU_CLASS(oc); MicroBlazeCPUClass *mcc = MICROBLAZE_CPU_CLASS(oc); - mcc->parent_realize = dc->realize; - dc->realize = mb_cpu_realizefn; - + device_class_set_parent_realize(dc, mb_cpu_realizefn, + &mcc->parent_realize); mcc->parent_reset = cc->reset; cc->reset = mb_cpu_reset; diff --git a/target/mips/cpu.c b/target/mips/cpu.c index 069f93560ee36dcebb827ba6f89c53b9cdbbd0ab..497706b669e17506e1698a84d2eab4fd09c97b1d 100644 --- a/target/mips/cpu.c +++ b/target/mips/cpu.c @@ -174,9 +174,8 @@ static void mips_cpu_class_init(ObjectClass *c, void *data) CPUClass *cc = CPU_CLASS(c); DeviceClass *dc = DEVICE_CLASS(c); - mcc->parent_realize = dc->realize; - dc->realize = mips_cpu_realizefn; - + device_class_set_parent_realize(dc, mips_cpu_realizefn, + &mcc->parent_realize); mcc->parent_reset = cc->reset; cc->reset = mips_cpu_reset; diff --git a/target/moxie/cpu.c b/target/moxie/cpu.c index f1389e5097d33668574a629037d0b3f8da7a4e87..4170284da62207361af1ea6a0f17c23574f94d89 100644 --- a/target/moxie/cpu.c +++ b/target/moxie/cpu.c @@ -102,9 +102,8 @@ static void moxie_cpu_class_init(ObjectClass *oc, void *data) CPUClass *cc = CPU_CLASS(oc); MoxieCPUClass *mcc = MOXIE_CPU_CLASS(oc); - mcc->parent_realize = dc->realize; - dc->realize = moxie_cpu_realizefn; - + device_class_set_parent_realize(dc, moxie_cpu_realizefn, + &mcc->parent_realize); mcc->parent_reset = cc->reset; cc->reset = moxie_cpu_reset; diff --git a/target/nios2/cpu.c b/target/nios2/cpu.c index 4742e52c78f0cd6d3a45571f0c47934c4016af59..fbfaa2ce2666189190def06319b78b622117232c 100644 --- a/target/nios2/cpu.c +++ b/target/nios2/cpu.c @@ -187,8 +187,8 @@ static void nios2_cpu_class_init(ObjectClass *oc, void *data) CPUClass *cc = CPU_CLASS(oc); Nios2CPUClass *ncc = NIOS2_CPU_CLASS(oc); - ncc->parent_realize = dc->realize; - dc->realize = nios2_cpu_realizefn; + device_class_set_parent_realize(dc, nios2_cpu_realizefn, + &ncc->parent_realize); dc->props = nios2_properties; ncc->parent_reset = cc->reset; cc->reset = nios2_cpu_reset; diff --git a/target/openrisc/cpu.c b/target/openrisc/cpu.c index e0394b8b068ec9bea33a893f75942552da9f0edb..20b115afae8def3ca74f1525bf4f93717de75169 100644 --- a/target/openrisc/cpu.c +++ b/target/openrisc/cpu.c @@ -132,9 +132,8 @@ static void openrisc_cpu_class_init(ObjectClass *oc, void *data) CPUClass *cc = CPU_CLASS(occ); DeviceClass *dc = DEVICE_CLASS(oc); - occ->parent_realize = dc->realize; - dc->realize = openrisc_cpu_realizefn; - + device_class_set_parent_realize(dc, openrisc_cpu_realizefn, + &occ->parent_realize); occ->parent_reset = cc->reset; cc->reset = openrisc_cpu_reset; diff --git a/target/ppc/translate_init.c b/target/ppc/translate_init.c index 55c99c97e3770e70c0123af81040eb3e24a27468..e7b1044944cd68a5d81e0f0cf255e5de2da289a8 100644 --- a/target/ppc/translate_init.c +++ b/target/ppc/translate_init.c @@ -10556,12 +10556,12 @@ static void ppc_cpu_class_init(ObjectClass *oc, void *data) CPUClass *cc = CPU_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); - pcc->parent_realize = dc->realize; - pcc->parent_unrealize = dc->unrealize; + device_class_set_parent_realize(dc, ppc_cpu_realizefn, + &pcc->parent_realize); + device_class_set_parent_unrealize(dc, ppc_cpu_unrealizefn, + &pcc->parent_unrealize); pcc->pvr_match = ppc_pvr_match_default; pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_always; - dc->realize = ppc_cpu_realizefn; - dc->unrealize = ppc_cpu_unrealizefn; dc->props = ppc_cpu_properties; pcc->parent_reset = cc->reset; diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index d2e6b9f5c703dc187b436ab18205fb48a92ce7f7..979469dc3c5451ce7c961ac6a0a72997f1c4003b 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -464,8 +464,8 @@ static void s390_cpu_class_init(ObjectClass *oc, void *data) CPUClass *cc = CPU_CLASS(scc); DeviceClass *dc = DEVICE_CLASS(oc); - scc->parent_realize = dc->realize; - dc->realize = s390_cpu_realizefn; + device_class_set_parent_realize(dc, s390_cpu_realizefn, + &scc->parent_realize); dc->props = s390x_cpu_properties; dc->user_creatable = true; diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c index e0b99fbc891e0b0e30fb4d1eac036c184f5087b5..e37c187ca2e9c8bbe94891741357c08e4bcc62c2 100644 --- a/target/sh4/cpu.c +++ b/target/sh4/cpu.c @@ -236,8 +236,8 @@ static void superh_cpu_class_init(ObjectClass *oc, void *data) CPUClass *cc = CPU_CLASS(oc); SuperHCPUClass *scc = SUPERH_CPU_CLASS(oc); - scc->parent_realize = dc->realize; - dc->realize = superh_cpu_realizefn; + device_class_set_parent_realize(dc, superh_cpu_realizefn, + &scc->parent_realize); scc->parent_reset = cc->reset; cc->reset = superh_cpu_reset; diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index c7adc281dee03e6cddc78ccc5544539bf3248c26..ff6ed91f9aa5994fc32fc0b68580dce1524705ff 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -858,8 +858,8 @@ static void sparc_cpu_class_init(ObjectClass *oc, void *data) CPUClass *cc = CPU_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); - scc->parent_realize = dc->realize; - dc->realize = sparc_cpu_realizefn; + device_class_set_parent_realize(dc, sparc_cpu_realizefn, + &scc->parent_realize); dc->props = sparc_cpu_properties; scc->parent_reset = cc->reset; diff --git a/target/tilegx/cpu.c b/target/tilegx/cpu.c index c140b461acbc14f0bce719ce44c9b080545172dd..b7451bdcf2f2ece065119d07c41de1346ddf9fa9 100644 --- a/target/tilegx/cpu.c +++ b/target/tilegx/cpu.c @@ -141,8 +141,8 @@ static void tilegx_cpu_class_init(ObjectClass *oc, void *data) CPUClass *cc = CPU_CLASS(oc); TileGXCPUClass *tcc = TILEGX_CPU_CLASS(oc); - tcc->parent_realize = dc->realize; - dc->realize = tilegx_cpu_realizefn; + device_class_set_parent_realize(dc, tilegx_cpu_realizefn, + &tcc->parent_realize); tcc->parent_reset = cc->reset; cc->reset = tilegx_cpu_reset; diff --git a/target/tricore/cpu.c b/target/tricore/cpu.c index 179c997aa46b9b244df5f46e9ac235d7270be8f7..2edaef1aef4efefcb0b79ec6003708095de47a36 100644 --- a/target/tricore/cpu.c +++ b/target/tricore/cpu.c @@ -153,8 +153,8 @@ static void tricore_cpu_class_init(ObjectClass *c, void *data) CPUClass *cc = CPU_CLASS(c); DeviceClass *dc = DEVICE_CLASS(c); - mcc->parent_realize = dc->realize; - dc->realize = tricore_cpu_realizefn; + device_class_set_parent_realize(dc, tricore_cpu_realizefn, + &mcc->parent_realize); mcc->parent_reset = cc->reset; cc->reset = tricore_cpu_reset; diff --git a/target/unicore32/cpu.c b/target/unicore32/cpu.c index 17dc1504d76edaeccc2763fa181f61da5a0648b4..fb837aab4c4c6b7d606839615c174f358555c780 100644 --- a/target/unicore32/cpu.c +++ b/target/unicore32/cpu.c @@ -132,8 +132,8 @@ static void uc32_cpu_class_init(ObjectClass *oc, void *data) CPUClass *cc = CPU_CLASS(oc); UniCore32CPUClass *ucc = UNICORE32_CPU_CLASS(oc); - ucc->parent_realize = dc->realize; - dc->realize = uc32_cpu_realizefn; + device_class_set_parent_realize(dc, uc32_cpu_realizefn, + &ucc->parent_realize); cc->class_by_name = uc32_cpu_class_by_name; cc->has_work = uc32_cpu_has_work; diff --git a/target/xtensa/cpu.c b/target/xtensa/cpu.c index 1c982a0b2e31d445b6add8371e46fef0207c4195..4573388a45faa14abec8a7cd946d24616a8a300a 100644 --- a/target/xtensa/cpu.c +++ b/target/xtensa/cpu.c @@ -151,8 +151,8 @@ static void xtensa_cpu_class_init(ObjectClass *oc, void *data) CPUClass *cc = CPU_CLASS(oc); XtensaCPUClass *xcc = XTENSA_CPU_CLASS(cc); - xcc->parent_realize = dc->realize; - dc->realize = xtensa_cpu_realizefn; + device_class_set_parent_realize(dc, xtensa_cpu_realizefn, + &xcc->parent_realize); xcc->parent_reset = cc->reset; cc->reset = xtensa_cpu_reset; diff --git a/tests/test-filter-redirector.c b/tests/test-filter-redirector.c index f2566144cf4980f3b6c465c687686d54aeef4613..fbaf19bbd8e67d2d9a18f92f5a5f1a466e980027 100644 --- a/tests/test-filter-redirector.c +++ b/tests/test-filter-redirector.c @@ -186,7 +186,6 @@ static void test_redirector_rx(void) ret = iov_send(send_sock, iov, 2, 0, sizeof(size) + sizeof(send_buf)); g_assert_cmpint(ret, ==, sizeof(send_buf) + sizeof(size)); - close(send_sock); ret = qemu_recv(backend_sock[0], &len, sizeof(len), 0); g_assert_cmpint(ret, ==, sizeof(len)); @@ -197,6 +196,7 @@ static void test_redirector_rx(void) ret = qemu_recv(backend_sock[0], recv_buf, len, 0); g_assert_cmpstr(recv_buf, ==, send_buf); + close(send_sock); g_free(recv_buf); unlink(sock_path0); unlink(sock_path1); diff --git a/tests/vhost-user-test.c b/tests/vhost-user-test.c index ec6ac9dc9edda9cf3d34c6f1f7a163035674c7a9..95eb449cfc1f1e95417d514b594c853f3d297388 100644 --- a/tests/vhost-user-test.c +++ b/tests/vhost-user-test.c @@ -17,6 +17,7 @@ #include "qemu/range.h" #include "qemu/sockets.h" #include "chardev/char-fe.h" +#include "qemu/memfd.h" #include "sysemu/sysemu.h" #include "libqos/libqos.h" #include "libqos/pci-pc.h" @@ -30,8 +31,6 @@ #include <linux/virtio_net.h> #include <sys/vfs.h> -#define VHOST_USER_NET_TESTS_WORKING 0 /* broken as of 2.10.0 */ - /* GLIB version compatibility flags */ #if !GLIB_CHECK_VERSION(2, 26, 0) #define G_TIME_SPAN_SECOND (G_GINT64_CONSTANT(1000000)) @@ -41,15 +40,14 @@ #define HAVE_MONOTONIC_TIME #endif -#define QEMU_CMD_MEM " -m %d -object memory-backend-file,id=mem,size=%dM,"\ +#define QEMU_CMD_MEM " -m %d -object memory-backend-file,id=mem,size=%dM," \ "mem-path=%s,share=on -numa node,memdev=mem" +#define QEMU_CMD_MEMFD " -m %d -object memory-backend-memfd,id=mem,size=%dM," \ + " -numa node,memdev=mem" #define QEMU_CMD_CHR " -chardev socket,id=%s,path=%s%s" #define QEMU_CMD_NETDEV " -netdev vhost-user,id=net0,chardev=%s,vhostforce" #define QEMU_CMD_NET " -device virtio-net-pci,netdev=net0" -#define QEMU_CMD QEMU_CMD_MEM QEMU_CMD_CHR \ - QEMU_CMD_NETDEV QEMU_CMD_NET - #define HUGETLBFS_MAGIC 0x958458f6 /*********** FROM hw/virtio/vhost-user.c *************************************/ @@ -161,9 +159,40 @@ typedef struct TestServer { QGuestAllocator *alloc; } TestServer; +static TestServer *test_server_new(const gchar *name); +static void test_server_free(TestServer *server); +static void test_server_listen(TestServer *server); + static const char *tmpfs; static const char *root; +enum test_memfd { + TEST_MEMFD_AUTO, + TEST_MEMFD_YES, + TEST_MEMFD_NO, +}; + +static char *get_qemu_cmd(TestServer *s, + int mem, enum test_memfd memfd, const char *mem_path, + const char *chr_opts, const char *extra) +{ + if (memfd == TEST_MEMFD_AUTO && qemu_memfd_check()) { + memfd = TEST_MEMFD_YES; + } + + if (memfd == TEST_MEMFD_YES) { + return g_strdup_printf(QEMU_CMD_MEMFD QEMU_CMD_CHR + QEMU_CMD_NETDEV QEMU_CMD_NET "%s", mem, mem, + s->chr_name, s->socket_path, + chr_opts, s->chr_name, extra); + } else { + return g_strdup_printf(QEMU_CMD_MEM QEMU_CMD_CHR + QEMU_CMD_NETDEV QEMU_CMD_NET "%s", mem, mem, + mem_path, s->chr_name, s->socket_path, + chr_opts, s->chr_name, extra); + } +} + static void init_virtio_dev(TestServer *s, uint32_t features_mask) { uint32_t features; @@ -227,9 +256,8 @@ static void wait_for_fds(TestServer *s) g_mutex_unlock(&s->data_mutex); } -static void read_guest_mem(const void *data) +static void read_guest_mem_server(TestServer *s) { - TestServer *s = (void *)data; uint32_t *guest_mem; int i, j; size_t size; @@ -494,14 +522,6 @@ static void test_server_listen(TestServer *server) test_server_create_chr(server, ",server,nowait"); } -#define GET_QEMU_CMD(s) \ - g_strdup_printf(QEMU_CMD, 512, 512, (root), (s)->chr_name, \ - (s)->socket_path, "", (s)->chr_name) - -#define GET_QEMU_CMDE(s, mem, chr_opts, extra, ...) \ - g_strdup_printf(QEMU_CMD extra, (mem), (mem), (root), (s)->chr_name, \ - (s)->socket_path, (chr_opts), (s)->chr_name, ##__VA_ARGS__) - static gboolean _test_server_free(TestServer *server) { int i; @@ -638,8 +658,9 @@ GSourceFuncs test_migrate_source_funcs = { .check = test_migrate_source_check, }; -static void test_read_guest_mem(void) +static void test_read_guest_mem(const void *arg) { + enum test_memfd memfd = GPOINTER_TO_INT(arg); TestServer *server = NULL; char *qemu_cmd = NULL; QTestState *s = NULL; @@ -647,14 +668,14 @@ static void test_read_guest_mem(void) server = test_server_new("test"); test_server_listen(server); - qemu_cmd = GET_QEMU_CMD(server); + qemu_cmd = get_qemu_cmd(server, 512, memfd, root, "", ""); s = qtest_start(qemu_cmd); g_free(qemu_cmd); init_virtio_dev(server, 1u << VIRTIO_NET_F_MAC); - read_guest_mem(server); + read_guest_mem_server(server); uninit_virtio_dev(server); @@ -669,7 +690,7 @@ static void test_migrate(void) char *uri = g_strdup_printf("%s%s", "unix:", dest->mig_path); QTestState *global = global_qtest, *from, *to; GSource *source; - gchar *cmd; + gchar *cmd, *tmp; QDict *rsp; guint8 *log; guint64 size; @@ -677,7 +698,7 @@ static void test_migrate(void) test_server_listen(s); test_server_listen(dest); - cmd = GET_QEMU_CMDE(s, 2, "", ""); + cmd = get_qemu_cmd(s, 2, TEST_MEMFD_AUTO, root, "", ""); from = qtest_start(cmd); g_free(cmd); @@ -686,7 +707,9 @@ static void test_migrate(void) size = get_log_size(s); g_assert_cmpint(size, ==, (2 * 1024 * 1024) / (VHOST_LOG_PAGE * 8)); - cmd = GET_QEMU_CMDE(dest, 2, "", " -incoming %s", uri); + tmp = g_strdup_printf(" -incoming %s", uri); + cmd = get_qemu_cmd(dest, 2, TEST_MEMFD_AUTO, root, "", tmp); + g_free(tmp); to = qtest_init(cmd); g_free(cmd); @@ -732,7 +755,7 @@ static void test_migrate(void) global_qtest = to; qmp_eventwait("RESUME"); - read_guest_mem(dest); + read_guest_mem_server(dest); uninit_virtio_dev(s); @@ -765,7 +788,7 @@ static void wait_for_rings_started(TestServer *s, size_t count) g_mutex_unlock(&s->data_mutex); } -#if VHOST_USER_NET_TESTS_WORKING && defined(CONFIG_HAS_GLIB_SUBPROCESS_TESTS) +#if defined(CONFIG_HAS_GLIB_SUBPROCESS_TESTS) static inline void test_server_connect(TestServer *server) { test_server_create_chr(server, ",reconnect=1"); @@ -799,7 +822,7 @@ static void test_reconnect_subprocess(void) char *cmd; g_thread_new("connect", connect_thread, s); - cmd = GET_QEMU_CMDE(s, 2, ",server", ""); + cmd = get_qemu_cmd(s, 2, TEST_MEMFD_AUTO, root, ",server", ""); qtest_start(cmd); g_free(cmd); @@ -837,7 +860,7 @@ static void test_connect_fail_subprocess(void) s->test_fail = true; g_thread_new("connect", connect_thread, s); - cmd = GET_QEMU_CMDE(s, 2, ",server", ""); + cmd = get_qemu_cmd(s, 2, TEST_MEMFD_AUTO, root, ",server", ""); qtest_start(cmd); g_free(cmd); @@ -867,7 +890,7 @@ static void test_flags_mismatch_subprocess(void) s->test_flags = TEST_FLAGS_DISCONNECT; g_thread_new("connect", connect_thread, s); - cmd = GET_QEMU_CMDE(s, 2, ",server", ""); + cmd = get_qemu_cmd(s, 2, TEST_MEMFD_AUTO, root, ",server", ""); qtest_start(cmd); g_free(cmd); @@ -902,11 +925,21 @@ static void test_multiqueue(void) s->queues = 2; test_server_listen(s); - cmd = g_strdup_printf(QEMU_CMD_MEM QEMU_CMD_CHR QEMU_CMD_NETDEV ",queues=%d " - "-device virtio-net-pci,netdev=net0,mq=on,vectors=%d", - 512, 512, root, s->chr_name, - s->socket_path, "", s->chr_name, - s->queues, s->queues * 2 + 2); + if (qemu_memfd_check()) { + cmd = g_strdup_printf( + QEMU_CMD_MEMFD QEMU_CMD_CHR QEMU_CMD_NETDEV ",queues=%d " + "-device virtio-net-pci,netdev=net0,mq=on,vectors=%d", + 512, 512, s->chr_name, + s->socket_path, "", s->chr_name, + s->queues, s->queues * 2 + 2); + } else { + cmd = g_strdup_printf( + QEMU_CMD_MEM QEMU_CMD_CHR QEMU_CMD_NETDEV ",queues=%d " + "-device virtio-net-pci,netdev=net0,mq=on,vectors=%d", + 512, 512, root, s->chr_name, + s->socket_path, "", s->chr_name, + s->queues, s->queues * 2 + 2); + } qtest_start(cmd); g_free(cmd); @@ -952,20 +985,29 @@ int main(int argc, char **argv) /* run the main loop thread so the chardev may operate */ thread = g_thread_new(NULL, thread_function, loop); - qtest_add_func("/vhost-user/read-guest-mem", test_read_guest_mem); + if (qemu_memfd_check()) { + qtest_add_data_func("/vhost-user/read-guest-mem/memfd", + GINT_TO_POINTER(TEST_MEMFD_YES), + test_read_guest_mem); + } + qtest_add_data_func("/vhost-user/read-guest-mem/memfile", + GINT_TO_POINTER(TEST_MEMFD_NO), test_read_guest_mem); qtest_add_func("/vhost-user/migrate", test_migrate); qtest_add_func("/vhost-user/multiqueue", test_multiqueue); -#if VHOST_USER_NET_TESTS_WORKING && defined(CONFIG_HAS_GLIB_SUBPROCESS_TESTS) - qtest_add_func("/vhost-user/reconnect/subprocess", - test_reconnect_subprocess); - qtest_add_func("/vhost-user/reconnect", test_reconnect); - qtest_add_func("/vhost-user/connect-fail/subprocess", - test_connect_fail_subprocess); - qtest_add_func("/vhost-user/connect-fail", test_connect_fail); - qtest_add_func("/vhost-user/flags-mismatch/subprocess", - test_flags_mismatch_subprocess); - qtest_add_func("/vhost-user/flags-mismatch", test_flags_mismatch); +#if defined(CONFIG_HAS_GLIB_SUBPROCESS_TESTS) + /* keeps failing on build-system since Aug 15 2017 */ + if (getenv("QTEST_VHOST_USER_FIXME")) { + qtest_add_func("/vhost-user/reconnect/subprocess", + test_reconnect_subprocess); + qtest_add_func("/vhost-user/reconnect", test_reconnect); + qtest_add_func("/vhost-user/connect-fail/subprocess", + test_connect_fail_subprocess); + qtest_add_func("/vhost-user/connect-fail", test_connect_fail); + qtest_add_func("/vhost-user/flags-mismatch/subprocess", + test_flags_mismatch_subprocess); + qtest_add_func("/vhost-user/flags-mismatch", test_flags_mismatch); + } #endif ret = g_test_run(); diff --git a/util/coroutine-ucontext.c b/util/coroutine-ucontext.c index 6621f3f692cc1a9bf7066d8ffaad1dca3439f777..926d3402e3ae48c4605270f14e6ed3bed946feec 100644 --- a/util/coroutine-ucontext.c +++ b/util/coroutine-ucontext.c @@ -31,6 +31,13 @@ #include <valgrind/valgrind.h> #endif +#if defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer) +#ifdef CONFIG_ASAN_IFACE_FIBER +#define CONFIG_ASAN 1 +#include <sanitizer/asan_interface.h> +#endif +#endif + typedef struct { Coroutine base; void *stack; @@ -59,11 +66,37 @@ union cc_arg { int i[2]; }; +static void finish_switch_fiber(void *fake_stack_save) +{ +#ifdef CONFIG_ASAN + const void *bottom_old; + size_t size_old; + + __sanitizer_finish_switch_fiber(fake_stack_save, &bottom_old, &size_old); + + if (!leader.stack) { + leader.stack = (void *)bottom_old; + leader.stack_size = size_old; + } +#endif +} + +static void start_switch_fiber(void **fake_stack_save, + const void *bottom, size_t size) +{ +#ifdef CONFIG_ASAN + __sanitizer_start_switch_fiber(fake_stack_save, bottom, size); +#endif +} + static void coroutine_trampoline(int i0, int i1) { union cc_arg arg; CoroutineUContext *self; Coroutine *co; + void *fake_stack_save = NULL; + + finish_switch_fiber(NULL); arg.i[0] = i0; arg.i[1] = i1; @@ -72,9 +105,13 @@ static void coroutine_trampoline(int i0, int i1) /* Initialize longjmp environment and switch back the caller */ if (!sigsetjmp(self->env, 0)) { + start_switch_fiber(&fake_stack_save, + leader.stack, leader.stack_size); siglongjmp(*(sigjmp_buf *)co->entry_arg, 1); } + finish_switch_fiber(fake_stack_save); + while (true) { co->entry(co->entry_arg); qemu_coroutine_switch(co, co->caller, COROUTINE_TERMINATE); @@ -87,6 +124,7 @@ Coroutine *qemu_coroutine_new(void) ucontext_t old_uc, uc; sigjmp_buf old_env; union cc_arg arg = {0}; + void *fake_stack_save = NULL; /* The ucontext functions preserve signal masks which incurs a * system call overhead. sigsetjmp(buf, 0)/siglongjmp() does not @@ -122,8 +160,12 @@ Coroutine *qemu_coroutine_new(void) /* swapcontext() in, siglongjmp() back out */ if (!sigsetjmp(old_env, 0)) { + start_switch_fiber(&fake_stack_save, co->stack, co->stack_size); swapcontext(&old_uc, &uc); } + + finish_switch_fiber(fake_stack_save); + return &co->base; } @@ -169,13 +211,19 @@ qemu_coroutine_switch(Coroutine *from_, Coroutine *to_, CoroutineUContext *from = DO_UPCAST(CoroutineUContext, base, from_); CoroutineUContext *to = DO_UPCAST(CoroutineUContext, base, to_); int ret; + void *fake_stack_save = NULL; current = to_; ret = sigsetjmp(from->env, 0); if (ret == 0) { + start_switch_fiber(action == COROUTINE_TERMINATE ? + NULL : &fake_stack_save, to->stack, to->stack_size); siglongjmp(to->env, action); } + + finish_switch_fiber(fake_stack_save); + return ret; } diff --git a/util/memfd.c b/util/memfd.c index dce61f9d211b8eef674ffa917df4a0685842f103..07d579ea7d9853a2e7b2a987f2ff390bde42bec2 100644 --- a/util/memfd.c +++ b/util/memfd.c @@ -27,7 +27,9 @@ #include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu/memfd.h" +#include "qemu/host-utils.h" #if defined CONFIG_LINUX && !defined CONFIG_MEMFD #include <sys/syscall.h> @@ -51,36 +53,59 @@ static int memfd_create(const char *name, unsigned int flags) #define MFD_ALLOW_SEALING 0x0002U #endif -int qemu_memfd_create(const char *name, size_t size, unsigned int seals) +#ifndef MFD_HUGETLB +#define MFD_HUGETLB 0x0004U +#endif + +#ifndef MFD_HUGE_SHIFT +#define MFD_HUGE_SHIFT 26 +#endif + +int qemu_memfd_create(const char *name, size_t size, bool hugetlb, + uint64_t hugetlbsize, unsigned int seals, Error **errp) { - int mfd = -1; + int htsize = hugetlbsize ? ctz64(hugetlbsize) : 0; + + if (htsize && 1 << htsize != hugetlbsize) { + error_setg(errp, "Hugepage size must be a power of 2"); + return -1; + } + + htsize = htsize << MFD_HUGE_SHIFT; #ifdef CONFIG_LINUX + int mfd = -1; unsigned int flags = MFD_CLOEXEC; if (seals) { flags |= MFD_ALLOW_SEALING; } - + if (hugetlb) { + flags |= MFD_HUGETLB; + flags |= htsize; + } mfd = memfd_create(name, flags); if (mfd < 0) { - return -1; + goto err; } if (ftruncate(mfd, size) == -1) { - perror("ftruncate"); - close(mfd); - return -1; + goto err; } if (seals && fcntl(mfd, F_ADD_SEALS, seals) == -1) { - perror("fcntl"); - close(mfd); - return -1; + goto err; } -#endif return mfd; + +err: + if (mfd >= 0) { + close(mfd); + } +#endif + error_setg_errno(errp, errno, "failed to create memfd"); + return -1; } /* @@ -90,14 +115,14 @@ int qemu_memfd_create(const char *name, size_t size, unsigned int seals) * sealing. */ void *qemu_memfd_alloc(const char *name, size_t size, unsigned int seals, - int *fd) + int *fd, Error **errp) { void *ptr; - int mfd = qemu_memfd_create(name, size, seals); + int mfd = qemu_memfd_create(name, size, false, 0, seals, NULL); /* some systems have memfd without sealing */ if (mfd == -1) { - mfd = qemu_memfd_create(name, size, 0); + mfd = qemu_memfd_create(name, size, false, 0, 0, NULL); } if (mfd == -1) { @@ -109,27 +134,26 @@ void *qemu_memfd_alloc(const char *name, size_t size, unsigned int seals, unlink(fname); g_free(fname); - if (mfd == -1) { - perror("mkstemp"); - return NULL; - } - - if (ftruncate(mfd, size) == -1) { - perror("ftruncate"); - close(mfd); - return NULL; + if (mfd == -1 || + ftruncate(mfd, size) == -1) { + goto err; } } ptr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, mfd, 0); if (ptr == MAP_FAILED) { - perror("mmap"); - close(mfd); - return NULL; + goto err; } *fd = mfd; return ptr; + +err: + error_setg_errno(errp, errno, "failed to allocate shared memory"); + if (mfd >= 0) { + close(mfd); + } + return NULL; } void qemu_memfd_free(void *ptr, size_t size, int fd) @@ -157,7 +181,7 @@ bool qemu_memfd_check(void) int fd; void *ptr; - ptr = qemu_memfd_alloc("test", 4096, 0, &fd); + ptr = qemu_memfd_alloc("test", 4096, 0, &fd, NULL); memfd_check = ptr ? MEMFD_OK : MEMFD_KO; qemu_memfd_free(ptr, 4096, fd); } diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c index d6a1e1759e8a3b7e16c598a641ecb741d82892fc..fbbef69f62758553b1100b9d2bce57aa763ca414 100644 --- a/util/qemu-sockets.c +++ b/util/qemu-sockets.c @@ -554,6 +554,33 @@ err: } /* compatibility wrapper */ +static int inet_parse_flag(const char *flagname, const char *optstr, bool *val, + Error **errp) +{ + char *end; + size_t len; + + end = strstr(optstr, ","); + if (end) { + if (end[1] == ',') { /* Reject 'ipv6=on,,foo' */ + error_setg(errp, "error parsing '%s' flag '%s'", flagname, optstr); + return -1; + } + len = end - optstr; + } else { + len = strlen(optstr); + } + if (len == 0 || (len == 3 && strncmp(optstr, "=on", len) == 0)) { + *val = true; + } else if (len == 4 && strncmp(optstr, "=off", len) == 0) { + *val = false; + } else { + error_setg(errp, "error parsing '%s' flag '%s'", flagname, optstr); + return -1; + } + return 0; +} + int inet_parse(InetSocketAddress *addr, const char *str, Error **errp) { const char *optstr, *h; @@ -561,6 +588,7 @@ int inet_parse(InetSocketAddress *addr, const char *str, Error **errp) char port[33]; int to; int pos; + char *begin; memset(addr, 0, sizeof(*addr)); @@ -602,11 +630,19 @@ int inet_parse(InetSocketAddress *addr, const char *str, Error **errp) addr->has_to = true; addr->to = to; } - if (strstr(optstr, ",ipv4")) { - addr->ipv4 = addr->has_ipv4 = true; + begin = strstr(optstr, ",ipv4"); + if (begin) { + if (inet_parse_flag("ipv4", begin + 5, &addr->ipv4, errp) < 0) { + return -1; + } + addr->has_ipv4 = true; } - if (strstr(optstr, ",ipv6")) { - addr->ipv6 = addr->has_ipv6 = true; + begin = strstr(optstr, ",ipv6"); + if (begin) { + if (inet_parse_flag("ipv6", begin + 5, &addr->ipv6, errp) < 0) { + return -1; + } + addr->has_ipv6 = true; } return 0; } diff --git a/util/readline.c b/util/readline.c index 24ec839854cf84592948bc33165249b25ea1ee07..ec91ee0fea6f55a81ae0eea1e766fec5c5b50a53 100644 --- a/util/readline.c +++ b/util/readline.c @@ -510,9 +510,6 @@ void readline_free(ReadLineState *rs) for (i = 0; i < READLINE_MAX_CMDS; i++) { g_free(rs->history[i]); } - for (i = 0; i < READLINE_MAX_COMPLETIONS; i++) { - g_free(rs->completions[i]); - } g_free(rs); }