aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2015-06-22 11:50:07 +0100
committerPeter Maydell <peter.maydell@linaro.org>2015-06-22 11:50:08 +0100
commitcb4e0f9ddf7d45de7e4716cbab661ea568bd0b6c (patch)
tree0d733527bb76eede5c0c0dfd8bfbee8bd539c0e5
parentad7020a7e7b27d468ecc2aacb04ba4eb09017074 (diff)
parente4a511f8cc6f4a46d409fb5c9f72c38ba45f8d83 (diff)
Merge remote-tracking branch 'remotes/bonzini/tags/for-upstream' into staging
* i8254 security fix * Avoid long 100% CPU wait after restarting guests that use the periodic timer * Fixes for access clamping (WinXP, MIPS) * wixl/.msi support for qemu-ga on Windows # gpg: Signature made Fri Jun 19 11:30:53 2015 BST using RSA key ID 78C7AE83 # gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>" # gpg: aka "Paolo Bonzini <pbonzini@redhat.com>" # gpg: WARNING: This key is not certified with sufficiently trusted signatures! # gpg: It is not certain that the signature belongs to the owner. # Primary key fingerprint: 46F5 9FBD 57D6 12E7 BFD4 E2F7 7E15 100C CD36 69B1 # Subkey fingerprint: F133 3857 4B66 2389 866C 7682 BFFB D25F 78C7 AE83 * remotes/bonzini/tags/for-upstream: exec: clamp accesses against the MemoryRegionSection exec: do not clamp accesses to MMIO regions mc146818rtc: Reset the periodic timer on load qemu-timer: Call clock reset notifiers on forward jumps tests: virtio-scsi: Add test for unaligned WRITE SAME tests: virtio-scsi: Move start/stop to individual test functions libqos: Complete virtio device ID definition list libqos: Allow calling guest_free on NULL pointer tests: Link libqos virtio object to virtio-scsi-test i8254: fix out-of-bounds memory access in pit_ioport_read() qemu-ga: Building Windows MSI installation with configure/Makefile qemu-ga: Introduce Windows MSI script qemu-ga: debug printouts to help troubleshoot installation qemu-ga: adding vss-[un]install options qemu-log: Open file for logging when specified Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r--Makefile24
-rwxr-xr-xconfigure66
-rw-r--r--exec.c8
-rw-r--r--hw/timer/i8254.c6
-rw-r--r--hw/timer/mc146818rtc.c6
-rw-r--r--include/qemu/timer.h9
-rw-r--r--qemu-timer.c2
-rw-r--r--qga/channel-win32.c2
-rw-r--r--qga/commands-win32.c1
-rw-r--r--qga/installer/qemu-ga.wxs145
-rw-r--r--qga/main.c10
-rw-r--r--tests/Makefile2
-rw-r--r--tests/libqos/malloc.c3
-rw-r--r--tests/libqos/virtio.h10
-rw-r--r--tests/virtio-scsi-test.c201
-rw-r--r--vl.c13
16 files changed, 485 insertions, 23 deletions
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
diff --git a/exec.c b/exec.c
index 76bfc4ac4a..f7883d2246 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(section->size, int128_make64(addr));
+ *plen = int128_get64(int128_min(diff, int128_make64(*plen)));
+ }
return section;
}
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;
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) {
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;
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};
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 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
+ <?ifndef env.QEMU_GA_VERSION ?>
+ <?error Environment variable QEMU_GA_VERSION undefined?>
+ <?endif?>
+
+ <?ifndef env.QEMU_GA_DISTRO ?>
+ <?error Environment variable QEMU_GA_DISTRO undefined?>
+ <?endif?>
+
+ <?ifndef env.QEMU_GA_MANUFACTURER ?>
+ <?error Environment variable QEMU_GA_MANUFACTURER undefined?>
+ <?endif?>
+
+ <?ifndef var.Arch?>
+ <?error Define Arch to 32 or 64?>
+ <?endif?>
+
+ <?ifndef var.Mingw_bin?>
+ <?if $(var.Arch) = "64"?>
+ <?define Mingw_bin=/usr/x86_64-w64-mingw32/sys-root/mingw/bin ?>
+ <?endif?>
+ <?if $(var.Arch) = "32"?>
+ <?define Mingw_bin=/usr/i686-w64-mingw32/sys-root/mingw/bin ?>
+ <?endif?>
+ <?endif?>
+
+ <?if $(var.Arch) = "64"?>
+ <?define ArchLib=libgcc_s_seh-1.dll?>
+ <?define GaProgramFilesFolder="ProgramFiles64Folder" ?>
+ <?endif?>
+
+ <?if $(var.Arch) = "32"?>
+ <?define ArchLib=libgcc_s_sjlj-1.dll?>
+ <?define GaProgramFilesFolder="ProgramFilesFolder" ?>
+ <?endif?>
+
+ <?ifndef var.ArchLib ?>
+ <?error Unexpected Arch value $(var.Arch)?>
+ <?endif?>
+
+ <Product
+ Name="QEMU guest agent"
+ Id="*"
+ UpgradeCode="{EB6B8302-C06E-4bec-ADAC-932C68A3A98D}"
+ Manufacturer="$(env.QEMU_GA_MANUFACTURER)"
+ Version="$(env.QEMU_GA_VERSION)"
+ Language="1033">
+ <?if $(var.Arch) = 32 ?>
+ <Condition Message="Error: 32-bit version of Qemu GA can not be installed on 64-bit Windows.">NOT VersionNT64</Condition>
+ <?endif?>
+ <Package
+ Manufacturer="$(env.QEMU_GA_MANUFACTURER)"
+ InstallerVersion="200"
+ Languages="1033"
+ Compressed="yes"
+ InstallScope="perMachine"
+ />
+ <Media Id="1" Cabinet="qemu_ga.$(env.QEMU_GA_VERSION).cab" EmbedCab="yes" />
+ <Property Id="WHSLogo">1</Property>
+ <Property Id="PREVIOUSVERSIONSINSTALLED" />
+ <Upgrade Id="{EB6B8302-C06E-4bec-ADAC-932C68A3A98D}">
+ <UpgradeVersion
+ Minimum="1.0.0.0" Maximum="$(env.QEMU_GA_VERSION)"
+ Property="PREVIOUSVERSIONSINSTALLED"
+ IncludeMinimum="yes" IncludeMaximum="no" />
+ </Upgrade>
+
+ <Directory Id="TARGETDIR" Name="SourceDir">
+ <Directory Id="$(var.GaProgramFilesFolder)" Name="QEMU Guest Agent">
+ <Directory Id="qemu_ga_directory" Name="Qemu-ga">
+ <Component Id="qemu_ga" Guid="{908B7199-DE2A-4dc6-A8D0-27A5AE444FEA}">
+ <File Id="qemu_ga.exe" Name="qemu-ga.exe" Source="../../qemu-ga.exe" KeyPath="yes" DiskId="1"/>
+ <?ifdef var.InstallVss ?>
+ <File Id="qga_vss.dll" Name="qga-vss.dll" Source="../vss-win32/qga-vss.dll" KeyPath="no" DiskId="1"/>
+ <File Id="qga_vss.tlb" Name="qga-vss.tlb" Source="../vss-win32/qga-vss.tlb" KeyPath="no" DiskId="1"/>
+ <?endif?>
+ <File Id="iconv.dll" Name="iconv.dll" Source="$(var.Mingw_bin)/iconv.dll" KeyPath="no" DiskId="1"/>
+ <File Id="libgcc_arch_lib" Name="$(var.ArchLib)" Source="$(var.Mingw_bin)/$(var.ArchLib)" KeyPath="no" DiskId="1"/>
+ <File Id="libglib_2.0_0.dll" Name="libglib-2.0-0.dll" Source="$(var.Mingw_bin)/libglib-2.0-0.dll" KeyPath="no" DiskId="1"/>
+ <File Id="libintl_8.dll" Name="libintl-8.dll" Source="$(var.Mingw_bin)/libintl-8.dll" KeyPath="no" DiskId="1"/>
+ <File Id="libssp_0.dll" Name="libssp-0.dll" Source="$(var.Mingw_bin)/libssp-0.dll" KeyPath="no" DiskId="1"/>
+ <File Id="libwinpthread_1.dll" Name="libwinpthread-1.dll" Source="$(var.Mingw_bin)/libwinpthread-1.dll" KeyPath="no" DiskId="1"/>
+ <ServiceInstall
+ Id="ServiceInstaller"
+ Type="ownProcess"
+ Vital="yes"
+ Name="QEMU-GA"
+ DisplayName="QEMU Guest Agent"
+ Description="QEMU Guest Agent"
+ Start="auto"
+ Account="LocalSystem"
+ ErrorControl="ignore"
+ Interactive="no"
+ Arguments="-d"
+ >
+ </ServiceInstall>
+ <ServiceControl Id="StartService" Start="install" Stop="both" Remove="uninstall" Name="QEMU-GA" Wait="no" />
+ </Component>
+
+ <Component Id="registry_entries" Guid="d075d109-51ca-11e3-9f8b-000c29858960">
+ <RegistryKey Root="HKLM"
+ Key="Software\$(env.QEMU_GA_MANUFACTURER)\$(env.QEMU_GA_DISTRO)\Tools\QemuGA">
+ <RegistryValue Type="string" Name="ProductID" Value="fb0a0d66-c7fb-4e2e-a16b-c4a3bfe8d13b" />
+ <RegistryValue Type="string" Name="Version" Value="$(env.QEMU_GA_VERSION)" />
+ </RegistryKey>
+ </Component>
+ </Directory>
+ </Directory>
+ </Directory>
+
+ <Property Id="cmd" Value="cmd.exe"/>
+
+ <?ifdef var.InstallVss ?>
+ <CustomAction Id="RegisterCom"
+ ExeCommand='/c "[qemu_ga_directory]qemu-ga.exe" -s vss-install'
+ Execute="deferred"
+ Property="cmd"
+ Impersonate="no"
+ Return="check"
+ >
+ </CustomAction>
+ <CustomAction Id="UnRegisterCom"
+ ExeCommand='/c "[qemu_ga_directory]qemu-ga.exe" -s vss-uninstall'
+ Execute="deferred"
+ Property="cmd"
+ Impersonate="no"
+ Return="check"
+ ></CustomAction>
+ <?endif?>
+
+ <Feature Id="QEMUFeature" Title="QEMU Guest Agent" Level="1">
+ <ComponentRef Id="qemu_ga" />
+ <ComponentRef Id="registry_entries" />
+ </Feature>
+
+ <InstallExecuteSequence>
+ <RemoveExistingProducts Before="InstallInitialize" />
+ <?ifdef var.InstallVss ?>
+ <Custom Action="RegisterCom" After="InstallServices">NOT Installed</Custom>
+ <Custom Action="UnRegisterCom" After="StopServices">Installed</Custom>
+ <?endif?>
+ </InstallExecuteSequence>
+ </Product>
+</Wix>
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;
diff --git a/tests/Makefile b/tests/Makefile
index 4de40deb2f..5dd0c56cbb 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -371,7 +371,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
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);
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
diff --git a/tests/virtio-scsi-test.c b/tests/virtio-scsi-test.c
index 989f8251c4..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,16 +12,187 @@
#include <string.h>
#include "libqtest.h"
#include "qemu/osdep.h"
+#include <stdio.h>
+#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)
+{
+ 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();
+}
+
+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)
{
+ 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 +214,27 @@ 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();
+}
+
+/* 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)
@@ -51,14 +244,10 @@ 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);
- 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;
}
diff --git a/vl.c b/vl.c
index 278cd6af61..1a920e6bcc 100644
--- a/vl.c
+++ b/vl.c
@@ -3997,17 +3997,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);