From 67633bb4f7743be2cb2e70b61e482ab92bf1724e Mon Sep 17 00:00:00 2001 From: Pranith Kumar Date: Wed, 10 Jun 2015 10:20:24 -0400 Subject: [PATCH 01/15] qemu-log: Open file for logging when specified qemu-log defaults to stderr when there is no '-D' option mentioned on command line. When '-D' option is specified, we also need to specify '-d' option for it to use the specified logfile. When using monitor to enable logging this is troublesome since there will be no '-d' option because of which monitor dumps the logs to stderr. Fix this by opening the log file when '-D' is specified on the command line. Also fix an ancient comment which does not hold true since changing location and log level has now been streamlined. Signed-off-by: Pranith Kumar CC: Paolo Bonzini CC: Luiz Capitulino CC: Markus Armbruster CC: Peter Maydell Message-Id: <1433946024-18439-1-git-send-email-bobby.prani@gmail.com> Signed-off-by: Paolo Bonzini --- vl.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/vl.c b/vl.c index 2201e27fdc..63ce07f478 100644 --- a/vl.c +++ b/vl.c @@ -3916,17 +3916,14 @@ int main(int argc, char **argv, char **envp) exit(0); } - /* Open the logfile at this point, if necessary. We can't open the logfile - * when encountering either of the logging options (-d or -D) because the - * other one may be encountered later on the command line, changing the - * location or level of logging. + /* Open the logfile at this point and set the log mask if necessary. */ + if (log_file) { + qemu_set_log_filename(log_file); + } + if (log_mask) { int mask; - if (log_file) { - qemu_set_log_filename(log_file); - } - mask = qemu_str_to_log_mask(log_mask); if (!mask) { qemu_print_log_usage(stdout); From 5e031072e71eebab3d7d2ea4609e84bc928d893e Mon Sep 17 00:00:00 2001 From: Yossi Hindin Date: Wed, 6 May 2015 14:57:37 +0300 Subject: [PATCH 02/15] qemu-ga: adding vss-[un]install options Existing command line options include '-s install' and '-s uninstall'. These options install/uninstall both Windows QEMU GA service and optional VSS COM server. The QEMU GA Windows service allows always-on serving guest agent's QMP commands and VSS COM server enables guest agent integration with Volume Shadow Service. This commit introdices new options '-s vss-install' and '-s vss-uninstall', affecting only GA VSS COM server registration. The new options are useful for registering and unregistering the COM server during MSI installation, upgrade and uninstallation. Signed-off-by: Yossi Hindin Message-Id: <1430913460-13174-2-git-send-email-yhindin@redhat.com> Signed-off-by: Paolo Bonzini --- qga/main.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/qga/main.c b/qga/main.c index 9939a2b623..7e1e438f67 100644 --- a/qga/main.c +++ b/qga/main.c @@ -211,7 +211,7 @@ static void usage(const char *cmd) " -V, --version print version information and exit\n" " -d, --daemonize become a daemon\n" #ifdef _WIN32 -" -s, --service service commands: install, uninstall\n" +" -s, --service service commands: install, uninstall, vss-install, vss-uninstall\n" #endif " -b, --blacklist comma-separated list of RPCs to disable (no spaces, \"?\"\n" " to list available RPCs)\n" @@ -1036,6 +1036,14 @@ int main(int argc, char **argv) } else if (strcmp(service, "uninstall") == 0) { ga_uninstall_vss_provider(); return ga_uninstall_service(); + } else if (strcmp(service, "vss-install") == 0) { + if (ga_install_vss_provider()) { + return EXIT_FAILURE; + } + return EXIT_SUCCESS; + } else if (strcmp(service, "vss-uninstall") == 0) { + ga_uninstall_vss_provider(); + return EXIT_SUCCESS; } else { printf("Unknown service command.\n"); return EXIT_FAILURE; From c69403fcd4a0cb89f838a212ab71e4a1a3464c95 Mon Sep 17 00:00:00 2001 From: Yossi Hindin Date: Wed, 6 May 2015 14:57:38 +0300 Subject: [PATCH 03/15] qemu-ga: debug printouts to help troubleshoot installation Debug printouts extended, helps installation troubleshooting Signed-off-by: Yossi Hindin Message-Id: <1430913460-13174-3-git-send-email-yhindin@redhat.com> Signed-off-by: Paolo Bonzini --- qga/channel-win32.c | 2 +- qga/commands-win32.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/qga/channel-win32.c b/qga/channel-win32.c index 0d5e5f511f..04fa5e4d1d 100644 --- a/qga/channel-win32.c +++ b/qga/channel-win32.c @@ -306,7 +306,7 @@ static gboolean ga_channel_open(GAChannel *c, GAChannelMethod method, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, NULL); if (c->handle == INVALID_HANDLE_VALUE) { - g_critical("error opening path"); + g_critical("error opening path %s", newpath); return false; } diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 3ef0549c0f..d0aaec70f3 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -721,6 +721,7 @@ GList *ga_command_blacklist_init(GList *blacklist) } if (!vss_init(true)) { + g_debug("vss_init failed, vss commands are going to be disabled"); const char *list[] = { "guest-get-fsinfo", "guest-fsfreeze-status", "guest-fsfreeze-freeze", "guest-fsfreeze-thaw", NULL}; From 66ae13bb9eb2b16b59698e992bfcea61563b9d78 Mon Sep 17 00:00:00 2001 From: Yossi Hindin Date: Wed, 6 May 2015 14:57:39 +0300 Subject: [PATCH 04/15] qemu-ga: Introduce Windows MSI script The script enables building Windows MSI installation package on Linux with wixl tool. Signed-off-by: Yossi Hindin Message-Id: <1430913460-13174-4-git-send-email-yhindin@redhat.com> Signed-off-by: Paolo Bonzini --- qga/installer/qemu-ga.wxs | 145 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 qga/installer/qemu-ga.wxs diff --git a/qga/installer/qemu-ga.wxs b/qga/installer/qemu-ga.wxs new file mode 100644 index 0000000000..2c43f1b5aa --- /dev/null +++ b/qga/installer/qemu-ga.wxs @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NOT VersionNT64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NOT Installed + Installed + + + + From 9dacf32d2cbd66cbcce7944ebdfd6b2df20e33b8 Mon Sep 17 00:00:00 2001 From: Yossi Hindin Date: Wed, 6 May 2015 14:57:40 +0300 Subject: [PATCH 05/15] qemu-ga: Building Windows MSI installation with configure/Makefile New options were added to enable Windows MSI installation package creation: Option --enable-guest-agent-msi, like the name suggests, enables building Windows MSI package for QEMU guest agent; option --disable-guest-agent-msi disables MSI package creation; by default, no MSI package is created Signed-off-by: Yossi Hindin Message-Id: <1430913460-13174-5-git-send-email-yhindin@redhat.com> Signed-off-by: Paolo Bonzini --- Makefile | 24 +++++++++++++++++++- configure | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 3f97904a3d..e7c5c3af6f 100644 --- a/Makefile +++ b/Makefile @@ -74,7 +74,7 @@ Makefile: ; configure: ; .PHONY: all clean cscope distclean dvi html info install install-doc \ - pdf recurse-all speed test dist + pdf recurse-all speed test dist msi $(call set-vpath, $(SRC_PATH)) @@ -287,10 +287,32 @@ $(qga-obj-y) qemu-ga.o: $(QGALIB_GEN) qemu-ga$(EXESUF): $(qga-obj-y) libqemuutil.a libqemustub.a $(call LINK, $^) +ifdef QEMU_GA_MSI_ENABLED +QEMU_GA_MSI=qemu-ga-$(ARCH).msi + +msi: ${QEMU_GA_MSI} + +$(QEMU_GA_MSI): qemu-ga.exe + +ifdef QEMU_GA_MSI_WITH_VSS +$(QEMU_GA_MSI): qga/vss-win32/qga-vss.dll +endif + +$(QEMU_GA_MSI): config-host.mak + +$(QEMU_GA_MSI): qga/installer/qemu-ga.wxs + $(call quiet-command,QEMU_GA_VERSION="$(QEMU_GA_VERSION)" QEMU_GA_MANUFACTURER="$(QEMU_GA_MANUFACTURER)" QEMU_GA_DISTRO="$(QEMU_GA_DISTRO)" \ + wixl -o $@ $(QEMU_GA_MSI_ARCH) $(QEMU_GA_MSI_WITH_VSS) $(QEMU_GA_MSI_MINGW_DLL_PATH) $<, " WIXL $@") +else +msi: + @echo MSI build not configured or dependency resolution failed (reconfigure with --enable-guest-agent-msi option) +endif + clean: # avoid old build problems by removing potentially incorrect old files rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h rm -f qemu-options.def + rm -f *.msi find . \( -name '*.l[oa]' -o -name '*.so' -o -name '*.dll' -o -name '*.mo' -o -name '*.[oda]' \) -type f -exec rm {} + rm -f $(filter-out %.tlb,$(TOOLS)) $(HELPERS-y) qemu-ga TAGS cscope.* *.pod *~ */*~ rm -f fsdev/*.pod diff --git a/configure b/configure index 222694f34d..6fed07b5e2 100755 --- a/configure +++ b/configure @@ -315,6 +315,7 @@ snappy="" bzip2="" guest_agent="" guest_agent_with_vss="no" +guest_agent_msi="" vss_win32_sdk="" win_sdk="no" want_tools="yes" @@ -1078,6 +1079,10 @@ for opt do ;; --disable-guest-agent) guest_agent="no" ;; + --enable-guest-agent-msi) guest_agent_msi="yes" + ;; + --disable-guest-agent-msi) guest_agent_msi="no" + ;; --with-vss-sdk) vss_win32_sdk="" ;; --with-vss-sdk=*) vss_win32_sdk="$optarg" @@ -1394,6 +1399,8 @@ Advanced options (experts only): reading bzip2-compressed dmg images) --disable-guest-agent disable building of the QEMU Guest Agent --enable-guest-agent enable building of the QEMU Guest Agent + --enable-guest-agent-msi enable building guest agent Windows MSI installation package + --disable-guest-agent-msi disable building guest agent Windows MSI installation --with-vss-sdk=SDK-path enable Windows VSS support in QEMU Guest Agent --with-win-sdk=SDK-path path to Windows Platform SDK (to build VSS .tlb) --disable-seccomp disable seccomp support @@ -3862,6 +3869,56 @@ if test "$mingw32" = "yes" -a "$guest_agent" != "no" -a "$guest_agent_with_vss" fi ########################################## +# Guest agent Window MSI package + +if test "$guest_agent" != yes; then + if test "$guest_agent_msi" = yes; then + error_exit "MSI guest agent package requires guest agent enabled" + fi + guest_agent_msi=no +elif test "$mingw32" != "yes"; then + if test "$guest_agent_msi" = "yes"; then + error_exit "MSI guest agent package is available only for MinGW Windows cross-compilation" + fi + guest_agent_msi=no +elif ! has wixl; then + if test "$guest_agent_msi" = "yes"; then + error_exit "MSI guest agent package requires wixl tool installed ( usually from msitools package )" + fi + guest_agent_msi=no +fi + +if test "$guest_agent_msi" != "no"; then + if test "$guest_agent_with_vss" = "yes"; then + QEMU_GA_MSI_WITH_VSS="-D InstallVss" + fi + + if test "$QEMU_GA_MANUFACTURER" = ""; then + QEMU_GA_MANUFACTURER=QEMU + fi + + if test "$QEMU_GA_DISTRO" = ""; then + QEMU_GA_DISTRO=Linux + fi + + if test "$QEMU_GA_VERSION" = ""; then + QEMU_GA_VERSION=`cat $source_path/VERSION` + fi + + QEMU_GA_MSI_MINGW_DLL_PATH="-D Mingw_dlls=`$pkg_config --variable=prefix glib-2.0`/bin" + + case "$cpu" in + x86_64) + QEMU_GA_MSI_ARCH="-a x64 -D Arch=64" + ;; + i386) + QEMU_GA_MSI_ARCH="-D Arch=32" + ;; + *) + error_exit "CPU $cpu not supported for building installation package" + ;; + esac +fi ########################################## # check if we have fdatasync @@ -4558,6 +4615,15 @@ if test "$mingw32" = "yes" ; then echo "CONFIG_QGA_VSS=y" >> $config_host_mak echo "WIN_SDK=\"$win_sdk\"" >> $config_host_mak fi + if test "$guest_agent_msi" != "no"; then + 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 + echo "QEMU_GA_MANUFACTURER=${QEMU_GA_MANUFACTURER}" >> $config_host_mak + echo "QEMU_GA_DISTRO=${QEMU_GA_DISTRO}" >> $config_host_mak + echo "QEMU_GA_VERSION=${QEMU_GA_VERSION}" >> $config_host_mak + fi else echo "CONFIG_POSIX=y" >> $config_host_mak fi From d4862a87e31a51de9eb260f25c9e99a75efe3235 Mon Sep 17 00:00:00 2001 From: Petr Matousek Date: Wed, 17 Jun 2015 12:46:11 +0200 Subject: [PATCH 06/15] i8254: fix out-of-bounds memory access in pit_ioport_read() Due converting PIO to the new memory read/write api we no longer provide separate I/O region lenghts for read and write operations. As a result, reading from PIT Mode/Command register will end with accessing pit->channels with invalid index. Fix this by ignoring read from the Mode/Command register. This is CVE-2015-3214. Reported-by: Matt Tait Fixes: 0505bcdec8228d8de39ab1a02644e71999e7c052 Cc: qemu-stable@nongnu.org Signed-off-by: Petr Matousek Signed-off-by: Paolo Bonzini --- hw/timer/i8254.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hw/timer/i8254.c b/hw/timer/i8254.c index 3450c98637..9b65a33692 100644 --- a/hw/timer/i8254.c +++ b/hw/timer/i8254.c @@ -196,6 +196,12 @@ static uint64_t pit_ioport_read(void *opaque, hwaddr addr, PITChannelState *s; addr &= 3; + + if (addr == 3) { + /* Mode/Command register is write only, read is ignored */ + return 0; + } + s = &pit->channels[addr]; if (s->status_latched) { s->status_latched = 0; From ebe7d8b166c59b029521f8d95db011e5e0fc649d Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Fri, 24 Apr 2015 19:35:16 +0800 Subject: [PATCH 07/15] tests: Link libqos virtio object to virtio-scsi-test Signed-off-by: Fam Zheng Signed-off-by: Paolo Bonzini --- tests/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Makefile b/tests/Makefile index c5e474455c..9ac7ac2b7d 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -370,7 +370,7 @@ tests/virtio-balloon-test$(EXESUF): tests/virtio-balloon-test.o tests/virtio-blk-test$(EXESUF): tests/virtio-blk-test.o $(libqos-virtio-obj-y) tests/virtio-net-test$(EXESUF): tests/virtio-net-test.o $(libqos-pc-obj-y) tests/virtio-rng-test$(EXESUF): tests/virtio-rng-test.o $(libqos-pc-obj-y) -tests/virtio-scsi-test$(EXESUF): tests/virtio-scsi-test.o +tests/virtio-scsi-test$(EXESUF): tests/virtio-scsi-test.o $(libqos-virtio-obj-y) tests/virtio-9p-test$(EXESUF): tests/virtio-9p-test.o tests/virtio-serial-test$(EXESUF): tests/virtio-serial-test.o tests/virtio-console-test$(EXESUF): tests/virtio-console-test.o From 28452758c405e16d9890c44d6031d44233e8cb38 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Fri, 24 Apr 2015 19:35:17 +0800 Subject: [PATCH 08/15] libqos: Allow calling guest_free on NULL pointer Signed-off-by: Fam Zheng Reviewed-by: John Snow Signed-off-by: Paolo Bonzini --- tests/libqos/malloc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/libqos/malloc.c b/tests/libqos/malloc.c index 827613005a..82b9df537a 100644 --- a/tests/libqos/malloc.c +++ b/tests/libqos/malloc.c @@ -285,6 +285,9 @@ uint64_t guest_alloc(QGuestAllocator *allocator, size_t size) void guest_free(QGuestAllocator *allocator, uint64_t addr) { + if (!addr) { + return; + } mlist_free(allocator, addr); if (allocator->opts & ALLOC_PARANOID) { mlist_check(allocator); From bea2f0982b335c13448dbde8a409107764fa8b59 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Fri, 24 Apr 2015 19:35:18 +0800 Subject: [PATCH 09/15] libqos: Complete virtio device ID definition list Signed-off-by: Fam Zheng Signed-off-by: Paolo Bonzini --- tests/libqos/virtio.h | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/libqos/virtio.h b/tests/libqos/virtio.h index 2449feec59..01012787b8 100644 --- a/tests/libqos/virtio.h +++ b/tests/libqos/virtio.h @@ -19,8 +19,14 @@ #define QVIRTIO_DRIVER 0x2 #define QVIRTIO_DRIVER_OK 0x4 -#define QVIRTIO_NET_DEVICE_ID 0x1 -#define QVIRTIO_BLK_DEVICE_ID 0x2 +#define QVIRTIO_NET_DEVICE_ID 0x1 +#define QVIRTIO_BLK_DEVICE_ID 0x2 +#define QVIRTIO_CONSOLE_DEVICE_ID 0x3 +#define QVIRTIO_RNG_DEVICE_ID 0x4 +#define QVIRTIO_BALLOON_DEVICE_ID 0x5 +#define QVIRTIO_RPMSG_DEVICE_ID 0x7 +#define QVIRTIO_SCSI_DEVICE_ID 0x8 +#define QVIRTIO_9P_DEVICE_ID 0x9 #define QVIRTIO_F_NOTIFY_ON_EMPTY 0x01000000 #define QVIRTIO_F_ANY_LAYOUT 0x08000000 From 06b008d941fd3e9684d38a9b3181a1cf301c78d1 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Fri, 24 Apr 2015 19:35:19 +0800 Subject: [PATCH 10/15] tests: virtio-scsi: Move start/stop to individual test functions Signed-off-by: Fam Zheng Signed-off-by: Paolo Bonzini --- tests/virtio-scsi-test.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/tests/virtio-scsi-test.c b/tests/virtio-scsi-test.c index 989f8251c4..ba119c1dd1 100644 --- a/tests/virtio-scsi-test.c +++ b/tests/virtio-scsi-test.c @@ -12,15 +12,36 @@ #include "libqtest.h" #include "qemu/osdep.h" +static void qvirtio_scsi_start(const char *extra_opts) +{ + char *cmdline; + + cmdline = g_strdup_printf( + "-drive id=drv0,if=none,file=/dev/null,format=raw " + "-device virtio-scsi-pci,id=vs0 " + "-device scsi-hd,bus=vs0.0,drive=drv0 %s", + extra_opts ? : ""); + qtest_start(cmdline); + g_free(cmdline); +} + +static void qvirtio_scsi_stop(void) +{ + qtest_end(); +} + /* Tests only initialization so far. TODO: Replace with functional tests */ static void pci_nop(void) { + qvirtio_scsi_start(NULL); + qvirtio_scsi_stop(); } static void hotplug(void) { QDict *response; + qvirtio_scsi_start("-drive id=drv1,if=none,file=/dev/null,format=raw"); response = qmp("{\"execute\": \"device_add\"," " \"arguments\": {" " \"driver\": \"scsi-hd\"," @@ -42,6 +63,7 @@ static void hotplug(void) g_assert(qdict_haskey(response, "event")); g_assert(!strcmp(qdict_get_str(response, "event"), "DEVICE_DELETED")); QDECREF(response); + qvirtio_scsi_stop(); } int main(int argc, char **argv) @@ -52,13 +74,7 @@ int main(int argc, char **argv) qtest_add_func("/virtio/scsi/pci/nop", pci_nop); qtest_add_func("/virtio/scsi/pci/hotplug", hotplug); - qtest_start("-drive id=drv0,if=none,file=/dev/null,format=raw " - "-drive id=drv1,if=none,file=/dev/null,format=raw " - "-device virtio-scsi-pci,id=vscsi0 " - "-device scsi-hd,bus=vscsi0.0,drive=drv0"); ret = g_test_run(); - qtest_end(); - return ret; } From 397c767b2de5b918a7b890d02aae83d6dcb2a470 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Fri, 24 Apr 2015 19:35:20 +0800 Subject: [PATCH 11/15] tests: virtio-scsi: Add test for unaligned WRITE SAME This is an exercise for virtio-scsi tests using the libqos virtio library. A few common routines are added to facilitate future extensions of the test set. The added test case is a regression test for the bug in d7f4b1999e. Signed-off-by: Fam Zheng Signed-off-by: Paolo Bonzini --- tests/virtio-scsi-test.c | 173 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 173 insertions(+) diff --git a/tests/virtio-scsi-test.c b/tests/virtio-scsi-test.c index ba119c1dd1..11ccdd632e 100644 --- a/tests/virtio-scsi-test.c +++ b/tests/virtio-scsi-test.c @@ -2,6 +2,7 @@ * QTest testcase for VirtIO SCSI * * Copyright (c) 2014 SUSE LINUX Products GmbH + * Copyright (c) 2015 Red Hat Inc. * * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. @@ -11,6 +12,46 @@ #include #include "libqtest.h" #include "qemu/osdep.h" +#include +#include "libqos/virtio.h" +#include "libqos/virtio-pci.h" +#include "libqos/pci-pc.h" +#include "libqos/malloc.h" +#include "libqos/malloc-pc.h" +#include "libqos/malloc-generic.h" + +#define PCI_SLOT 0x02 +#define PCI_FN 0x00 +#define QVIRTIO_SCSI_TIMEOUT_US (1 * 1000 * 1000) +#define CDB_SIZE 32 + +#define MAX_NUM_QUEUES 64 + +typedef struct { + QVirtioDevice *dev; + QGuestAllocator *alloc; + QPCIBus *bus; + int num_queues; + QVirtQueue *vq[MAX_NUM_QUEUES + 2]; +} QVirtIOSCSI; + +typedef struct { + uint8_t lun[8]; + int64_t tag; + uint8_t task_attr; + uint8_t prio; + uint8_t crn; + uint8_t cdb[CDB_SIZE]; +} QEMU_PACKED QVirtIOSCSICmdReq; + +typedef struct { + uint32_t sense_len; + uint32_t resid; + uint16_t status_qualifier; + uint8_t status; + uint8_t response; + uint8_t sense[96]; +} QEMU_PACKED QVirtIOSCSICmdResp; static void qvirtio_scsi_start(const char *extra_opts) { @@ -30,6 +71,116 @@ static void qvirtio_scsi_stop(void) qtest_end(); } +static QVirtIOSCSI *qvirtio_scsi_pci_init(int slot) +{ + QVirtIOSCSI *vs; + QVirtioPCIDevice *dev; + void *addr; + int i; + + vs = g_new0(QVirtIOSCSI, 1); + vs->alloc = pc_alloc_init(); + vs->bus = qpci_init_pc(); + + dev = qvirtio_pci_device_find(vs->bus, QVIRTIO_SCSI_DEVICE_ID); + vs->dev = (QVirtioDevice *)dev; + g_assert(dev != NULL); + g_assert_cmphex(vs->dev->device_type, ==, QVIRTIO_SCSI_DEVICE_ID); + + qvirtio_pci_device_enable(dev); + qvirtio_reset(&qvirtio_pci, vs->dev); + qvirtio_set_acknowledge(&qvirtio_pci, vs->dev); + qvirtio_set_driver(&qvirtio_pci, vs->dev); + + addr = dev->addr + QVIRTIO_PCI_DEVICE_SPECIFIC_NO_MSIX; + vs->num_queues = qvirtio_config_readl(&qvirtio_pci, vs->dev, + (uint64_t)(uintptr_t)addr); + + g_assert_cmpint(vs->num_queues, <, MAX_NUM_QUEUES); + + for (i = 0; i < vs->num_queues + 2; i++) { + vs->vq[i] = qvirtqueue_setup(&qvirtio_pci, vs->dev, vs->alloc, i); + } + + return vs; +} + +static void qvirtio_scsi_pci_free(QVirtIOSCSI *vs) +{ + int i; + + for (i = 0; i < vs->num_queues + 2; i++) { + guest_free(vs->alloc, vs->vq[i]->desc); + } + pc_alloc_uninit(vs->alloc); + qvirtio_pci_device_disable(container_of(vs->dev, QVirtioPCIDevice, vdev)); + g_free(vs->dev); + qpci_free_pc(vs->bus); +} + +static uint64_t qvirtio_scsi_alloc(QVirtIOSCSI *vs, size_t alloc_size, + const void *data) +{ + uint64_t addr; + + addr = guest_alloc(vs->alloc, alloc_size); + if (data) { + memwrite(addr, data, alloc_size); + } + + return addr; +} + +static uint8_t virtio_scsi_do_command(QVirtIOSCSI *vs, const uint8_t *cdb, + const uint8_t *data_in, + size_t data_in_len, + uint8_t *data_out, size_t data_out_len) +{ + QVirtQueue *vq; + QVirtIOSCSICmdReq req = { { 0 } }; + QVirtIOSCSICmdResp resp = { .response = 0xff, .status = 0xff }; + uint64_t req_addr, resp_addr, data_in_addr = 0, data_out_addr = 0; + uint8_t response; + uint32_t free_head; + + vq = vs->vq[2]; + + req.lun[0] = 1; /* Select LUN */ + req.lun[1] = 1; /* Select target 1 */ + memcpy(req.cdb, cdb, CDB_SIZE); + + /* XXX: Fix endian if any multi-byte field in req/resp is used */ + + /* Add request header */ + req_addr = qvirtio_scsi_alloc(vs, sizeof(req), &req); + free_head = qvirtqueue_add(vq, req_addr, sizeof(req), false, true); + + if (data_out_len) { + data_out_addr = qvirtio_scsi_alloc(vs, data_out_len, data_out); + qvirtqueue_add(vq, data_out_addr, data_out_len, false, true); + } + + /* Add response header */ + resp_addr = qvirtio_scsi_alloc(vs, sizeof(resp), &resp); + qvirtqueue_add(vq, resp_addr, sizeof(resp), true, !!data_in_len); + + if (data_in_len) { + data_in_addr = qvirtio_scsi_alloc(vs, data_in_len, data_in); + qvirtqueue_add(vq, data_in_addr, data_in_len, true, false); + } + + qvirtqueue_kick(&qvirtio_pci, vs->dev, vq, free_head); + qvirtio_wait_queue_isr(&qvirtio_pci, vs->dev, vq, QVIRTIO_SCSI_TIMEOUT_US); + + response = readb(resp_addr + offsetof(QVirtIOSCSICmdResp, response)); + + guest_free(vs->alloc, req_addr); + guest_free(vs->alloc, resp_addr); + guest_free(vs->alloc, data_in_addr); + guest_free(vs->alloc, data_out_addr); + return response; +} + /* Tests only initialization so far. TODO: Replace with functional tests */ static void pci_nop(void) { @@ -66,6 +217,26 @@ static void hotplug(void) qvirtio_scsi_stop(); } +/* Test WRITE SAME with the lba not aligned */ +static void test_unaligned_write_same(void) +{ + QVirtIOSCSI *vs; + uint8_t buf[512] = { 0 }; + const uint8_t write_same_cdb[CDB_SIZE] = { 0x41, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x02, 0x00 }; + + qvirtio_scsi_start("-drive file=blkdebug::null-co://,if=none,id=dr1" + ",format=raw,file.align=4k " + "-device scsi-disk,drive=dr1,lun=0,scsi-id=1"); + vs = qvirtio_scsi_pci_init(PCI_SLOT); + + g_assert_cmphex(0, ==, + virtio_scsi_do_command(vs, write_same_cdb, NULL, 0, buf, 512)); + + qvirtio_scsi_pci_free(vs); + qvirtio_scsi_stop(); +} + int main(int argc, char **argv) { int ret; @@ -73,6 +244,8 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); qtest_add_func("/virtio/scsi/pci/nop", pci_nop); qtest_add_func("/virtio/scsi/pci/hotplug", hotplug); + qtest_add_func("/virtio/scsi/pci/scsi-disk/unaligned-write-same", + test_unaligned_write_same); ret = g_test_run(); From fb1a3a051d89975f26296163066bb0745ecca49d Mon Sep 17 00:00:00 2001 From: Paul Donohue Date: Fri, 12 Jun 2015 10:08:45 -0400 Subject: [PATCH 12/15] qemu-timer: Call clock reset notifiers on forward jumps Commit 691a0c9c introduced a mechanism by which QEMU_CLOCK_HOST can notify other parts of the emulator when the host clock has jumped backward. This is used to avoid stalling timers that were scheduled based on the host clock. However, if the host clock jumps forward, then timers that were scheduled based on the host clock may fire rapidly and cause other problems. For example, the mc146818rtc periodic timer will block execution of the VM and consume host CPU while firing every interrupt for the time period that was skipped by the host clock. To correct that problem, this commit fires the reset notification if the host clock jumps forward by more than a hard-coded limit. The limit is currently set to a value of 60 seconds, which should be small enough to prevent excessive timer loops, but large enough to avoid frequent resets in idle VMs. Signed-off-by: Paul Donohue Message-Id: <20150612140845.GD2749@TopQuark.net> Signed-off-by: Paolo Bonzini --- include/qemu/timer.h | 9 +++++++++ qemu-timer.c | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/include/qemu/timer.h b/include/qemu/timer.h index e5bd494c07..9e4f90f4aa 100644 --- a/include/qemu/timer.h +++ b/include/qemu/timer.h @@ -787,6 +787,15 @@ static inline int64_t get_ticks_per_sec(void) return 1000000000LL; } +static inline int64_t get_max_clock_jump(void) +{ + /* This should be small enough to prevent excessive interrupts from being + * generated by the RTC on clock jumps, but large enough to avoid frequent + * unnecessary resets in idle VMs. + */ + return 60 * get_ticks_per_sec(); +} + /* * Low level clock functions */ diff --git a/qemu-timer.c b/qemu-timer.c index 5741f0d0e0..aa6757e359 100644 --- a/qemu-timer.c +++ b/qemu-timer.c @@ -573,7 +573,7 @@ int64_t qemu_clock_get_ns(QEMUClockType type) now = get_clock_realtime(); last = clock->last; clock->last = now; - if (now < last) { + if (now < last || now > (last + get_max_clock_jump())) { notifier_list_notify(&clock->reset_notifiers, &now); } return now; From ae46e23964ad45d5bc72374040e87d8f52ac2178 Mon Sep 17 00:00:00 2001 From: Paul Donohue Date: Fri, 12 Jun 2015 10:10:14 -0400 Subject: [PATCH 13/15] mc146818rtc: Reset the periodic timer on load When loading a VM from a snapshot or migration, clock changes can cause the periodic timer to stall or loop rapidly. qemu-timer has a reset notifier mechanism that is used to avoid timer stalls or loops if the host clock changes while the VM is running when using QEMU_CLOCK_HOST. However, when loading a snapshot or migration, qemu-timer is initialized and fires the reset notifier before mc146818rtc is initialized and has registered its reset handler. In addition, this mechanism isn't used when using QEMU_CLOCK_REALTIME, which might also change when loading a snapshot or migration. To correct that problem, this commit resets the periodic timer after loading from a snapshot or migration if the clock has either jumped backward or has jumped forward by more than the clock jump limit that is used by the reset notifier code in qemu-timer. Signed-off-by: Paul Donohue Message-Id: <20150612141013.GE2749@TopQuark.net> Signed-off-by: Paolo Bonzini --- hw/timer/mc146818rtc.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hw/timer/mc146818rtc.c b/hw/timer/mc146818rtc.c index 32048258c9..2e3ffc8c07 100644 --- a/hw/timer/mc146818rtc.c +++ b/hw/timer/mc146818rtc.c @@ -723,6 +723,12 @@ static int rtc_post_load(void *opaque, int version_id) check_update_timer(s); } + uint64_t now = qemu_clock_get_ns(rtc_clock); + if (now < s->next_periodic_time || + now > (s->next_periodic_time + get_max_clock_jump())) { + periodic_timer_update(s, qemu_clock_get_ns(rtc_clock)); + } + #ifdef TARGET_I386 if (version_id >= 2) { if (s->lost_tick_policy == LOST_TICK_POLICY_SLEW) { From 965eb2fcdfe919ecced6c34803535ad32dc1249c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 17 Jun 2015 10:40:27 +0200 Subject: [PATCH 14/15] exec: do not clamp accesses to MMIO regions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is common for MMIO registers to overlap, for example a 4 byte register at 0xcf8 (totally random choice... :)) and a 1 byte register at 0xcf9. If these registers are implemented via separate MemoryRegions, it is wrong to clamp the accesses as the value written would be truncated. Hence for these regions the effects of commit 23820db (exec: Respect as_translate_internal length clamp, 2015-03-16, previously applied as commit c3c1bb99) must be skipped. Tested-by: Hervé Poussineau Tested-by: Mark Cave-Ayland Signed-off-by: Paolo Bonzini --- exec.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/exec.c b/exec.c index 76bfc4ac4a..d00e017e19 100644 --- a/exec.c +++ b/exec.c @@ -341,6 +341,7 @@ address_space_translate_internal(AddressSpaceDispatch *d, hwaddr addr, hwaddr *x hwaddr *plen, bool resolve_subpage) { MemoryRegionSection *section; + MemoryRegion *mr; Int128 diff; section = address_space_lookup_region(d, addr, resolve_subpage); @@ -350,8 +351,11 @@ address_space_translate_internal(AddressSpaceDispatch *d, hwaddr addr, hwaddr *x /* Compute offset within MemoryRegion */ *xlat = addr + section->offset_within_region; - diff = int128_sub(section->mr->size, int128_make64(addr)); - *plen = int128_get64(int128_min(diff, int128_make64(*plen))); + mr = section->mr; + if (memory_region_is_ram(mr)) { + diff = int128_sub(mr->size, int128_make64(addr)); + *plen = int128_get64(int128_min(diff, int128_make64(*plen))); + } return section; } From e4a511f8cc6f4a46d409fb5c9f72c38ba45f8d83 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 17 Jun 2015 10:36:54 +0200 Subject: [PATCH 15/15] exec: clamp accesses against the MemoryRegionSection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Because the clamping was done against the MemoryRegion, address_space_rw was effectively broken if a write spanned multiple sections that are not linear in underlying memory (with the memory not being under an IOMMU). This is visible with the MIPS rc4030 IOMMU, which is implemented as a series of alias memory regions that point to the actual RAM. Tested-by: Hervé Poussineau Tested-by: Mark Cave-Ayland Signed-off-by: Paolo Bonzini --- exec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exec.c b/exec.c index d00e017e19..f7883d2246 100644 --- a/exec.c +++ b/exec.c @@ -353,7 +353,7 @@ address_space_translate_internal(AddressSpaceDispatch *d, hwaddr addr, hwaddr *x mr = section->mr; if (memory_region_is_ram(mr)) { - diff = int128_sub(mr->size, int128_make64(addr)); + diff = int128_sub(section->size, int128_make64(addr)); *plen = int128_get64(int128_min(diff, int128_make64(*plen))); } return section;