[qemu/f14/master] Updates from upstream to support spice 0.6.0
Justin M. Forbes
jforbes at fedoraproject.org
Tue Sep 14 19:27:10 UTC 2010
commit a81953e7105b07201caf3d19d5b17e9089be00e5
Author: Justin M. Forbes <jforbes at redhat.com>
Date: Tue Sep 14 14:26:08 2010 -0500
Updates from upstream to support spice 0.6.0
.gitignore | 1 +
...-add-pflib-PixelFormat-conversion-library.patch | 259 +++
0002-configure-add-logging.patch | 39 +
0003-add-spice-into-the-configure-file.patch | 94 +
0004-spice-core-bits.patch | 333 ++++
0005-spice-add-keyboard.patch | 118 ++
0006-spice-add-mouse.patch | 62 +
0007-spice-simple-display.patch | 532 +++++
0008-spice-add-tablet-support.patch | 160 ++
0009-vgabios-update-to-0.6c-pcibios-patches.patch | 28 +
0010-switch-stdvga-to-pci-vgabios.patch | 31 +
0011-switch-vmware_vga-to-pci-vgabios.patch | 50 +
0012-all-vga-refuse-hotplugging.patch | 61 +
0013-spice-tls-support.patch | 168 ++
0014-spice-add-qxl-device.patch | 2085 ++++++++++++++++++++
0015-spice-add-audio.patch | 405 ++++
...-add-virtio-serial-based-vdi-port-backend.patch | 238 +++
0017-spice-add-pci-vdi-port-backend-obsolete.patch | 592 ++++++
0018-use-memalign-instead-of-posix_memalign.patch | 29 +
0019-spice-live-migration-wip.patch | 179 ++
0020-spice-display-draw.h-is-internal-now.patch | 23 +
0021-spice-display-disable-debug.patch | 25 +
0022-spice-display-pci-rev-fixups.patch | 27 +
0023-qxl-pci-rev-fixups.patch | 52 +
0024-qxl-support-QXL_IO_DESTROY_ALL_SURFACES.patch | 26 +
0025-spice-vmc-two-bugfixes-in-vmc_read.patch | 57 +
...ing-disabling-jpeg-and-zlib-over-glz-via-.patch | 84 +
0027-ifdef-new-config-options.patch | 57 +
...spice-vmc-add-counter-to-debug-statements.patch | 27 +
...plit-vmc_write-to-max-sized-virtio_serial.patch | 53 +
...0x480-resolution-to-qxl_modes-n900-native.patch | 24 +
0031-qxl-savevm-fixes.patch | 259 +++
...e-vmc-split-vmc_write-to-max-sized-virtio.patch | 53 +
...spice-vmc-add-counter-to-debug-statements.patch | 28 +
...Revert-spice-vmc-two-bugfixes-in-vmc_read.patch | 55 +
0035-Revert-spice-live-migration-wip.patch | 181 ++
...t-spice-add-pci-vdi-port-backend-obsolete.patch | 590 ++++++
...e-add-virtio-serial-based-vdi-port-backen.patch | 236 +++
...irtio-serial-based-spice-vmchannel-backen.patch | 297 +++
0039-qxl-fix-release-ring-overrun.patch | 30 +
...edora-13-machine-type-for-backward-compat.patch | 37 +
qemu.spec | 105 +-
sources | 2 +-
43 files changed, 7781 insertions(+), 11 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index f939288..07e3a9e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
qemu-kvm-0.13.0-b81fe95.tar.gz
qemu-kvm-0.13.0-25fdf4a.tar.gz
+/qemu-kvm-0.13.0-rc1.tar.gz
diff --git a/0001-add-pflib-PixelFormat-conversion-library.patch b/0001-add-pflib-PixelFormat-conversion-library.patch
new file mode 100644
index 0000000..de9805e
--- /dev/null
+++ b/0001-add-pflib-PixelFormat-conversion-library.patch
@@ -0,0 +1,259 @@
+From 09992bc6b432987ed3871dd7e4327ab6a589b865 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel at redhat.com>
+Date: Mon, 14 Jun 2010 09:54:27 +0200
+Subject: [PATCH 01/39] add pflib: PixelFormat conversion library.
+
+---
+ Makefile.objs | 1 +
+ pflib.c | 213 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ pflib.h | 6 ++
+ 3 files changed, 220 insertions(+), 0 deletions(-)
+ create mode 100644 pflib.c
+ create mode 100644 pflib.h
+
+diff --git a/Makefile.objs b/Makefile.objs
+index dbee210..147051f 100644
+--- a/Makefile.objs
++++ b/Makefile.objs
+@@ -84,6 +84,7 @@ common-obj-y += qemu-char.o savevm.o #aio.o
+ common-obj-y += msmouse.o ps2.o
+ common-obj-y += qdev.o qdev-properties.o
+ common-obj-y += block-migration.o
++common-obj-y += pflib.o
+
+ common-obj-$(CONFIG_BRLAPI) += baum.o
+ common-obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o
+diff --git a/pflib.c b/pflib.c
+new file mode 100644
+index 0000000..1154d0c
+--- /dev/null
++++ b/pflib.c
+@@ -0,0 +1,213 @@
++/*
++ * PixelFormat conversion library.
++ *
++ * Author: Gerd Hoffmann <kraxel at redhat.com>
++ *
++ * This work is licensed under the terms of the GNU GPL, version 2. See
++ * the COPYING file in the top-level directory.
++ *
++ */
++#include "qemu-common.h"
++#include "console.h"
++#include "pflib.h"
++
++typedef struct QemuPixel QemuPixel;
++
++typedef void (*pf_convert)(QemuPfConv *conv,
++ void *dst, void *src, uint32_t cnt);
++typedef void (*pf_convert_from)(PixelFormat *pf,
++ QemuPixel *dst, void *src, uint32_t cnt);
++typedef void (*pf_convert_to)(PixelFormat *pf,
++ void *dst, QemuPixel *src, uint32_t cnt);
++
++struct QemuPfConv {
++ pf_convert convert;
++ PixelFormat src;
++ PixelFormat dst;
++
++ /* for copy_generic() */
++ pf_convert_from conv_from;
++ pf_convert_to conv_to;
++ QemuPixel *conv_buf;
++ uint32_t conv_cnt;
++};
++
++struct QemuPixel {
++ uint8_t red;
++ uint8_t green;
++ uint8_t blue;
++ uint8_t alpha;
++};
++
++/* ----------------------------------------------------------------------- */
++/* PixelFormat -> QemuPixel conversions */
++
++static void conv_16_to_pixel(PixelFormat *pf,
++ QemuPixel *dst, void *src, uint32_t cnt)
++{
++ uint16_t *src16 = src;
++
++ while (cnt > 0) {
++ dst->red = ((*src16 & pf->rmask) >> pf->rshift) << (8 - pf->rbits);
++ dst->green = ((*src16 & pf->gmask) >> pf->gshift) << (8 - pf->gbits);
++ dst->blue = ((*src16 & pf->bmask) >> pf->bshift) << (8 - pf->bbits);
++ dst->alpha = ((*src16 & pf->amask) >> pf->ashift) << (8 - pf->abits);
++ dst++, src16++, cnt--;
++ }
++}
++
++/* assumes pf->{r,g,b,a}bits == 8 */
++static void conv_32_to_pixel_fast(PixelFormat *pf,
++ QemuPixel *dst, void *src, uint32_t cnt)
++{
++ uint32_t *src32 = src;
++
++ while (cnt > 0) {
++ dst->red = (*src32 & pf->rmask) >> pf->rshift;
++ dst->green = (*src32 & pf->gmask) >> pf->gshift;
++ dst->blue = (*src32 & pf->bmask) >> pf->bshift;
++ dst->alpha = (*src32 & pf->amask) >> pf->ashift;
++ dst++, src32++, cnt--;
++ }
++}
++
++static void conv_32_to_pixel_generic(PixelFormat *pf,
++ QemuPixel *dst, void *src, uint32_t cnt)
++{
++ uint32_t *src32 = src;
++
++ while (cnt > 0) {
++ if (pf->rbits < 8) {
++ dst->red = ((*src32 & pf->rmask) >> pf->rshift) << (8 - pf->rbits);
++ } else {
++ dst->red = ((*src32 & pf->rmask) >> pf->rshift) >> (pf->rbits - 8);
++ }
++ if (pf->gbits < 8) {
++ dst->green = ((*src32 & pf->gmask) >> pf->gshift) << (8 - pf->gbits);
++ } else {
++ dst->green = ((*src32 & pf->gmask) >> pf->gshift) >> (pf->gbits - 8);
++ }
++ if (pf->bbits < 8) {
++ dst->blue = ((*src32 & pf->bmask) >> pf->bshift) << (8 - pf->bbits);
++ } else {
++ dst->blue = ((*src32 & pf->bmask) >> pf->bshift) >> (pf->bbits - 8);
++ }
++ if (pf->abits < 8) {
++ dst->alpha = ((*src32 & pf->amask) >> pf->ashift) << (8 - pf->abits);
++ } else {
++ dst->alpha = ((*src32 & pf->amask) >> pf->ashift) >> (pf->abits - 8);
++ }
++ dst++, src32++, cnt--;
++ }
++}
++
++/* ----------------------------------------------------------------------- */
++/* QemuPixel -> PixelFormat conversions */
++
++static void conv_pixel_to_16(PixelFormat *pf,
++ void *dst, QemuPixel *src, uint32_t cnt)
++{
++ uint16_t *dst16 = dst;
++
++ while (cnt > 0) {
++ *dst16 = ((uint16_t)src->red >> (8 - pf->rbits)) << pf->rshift;
++ *dst16 |= ((uint16_t)src->green >> (8 - pf->gbits)) << pf->gshift;
++ *dst16 |= ((uint16_t)src->blue >> (8 - pf->bbits)) << pf->bshift;
++ *dst16 |= ((uint16_t)src->alpha >> (8 - pf->abits)) << pf->ashift;
++ dst16++, src++, cnt--;
++ }
++}
++
++static void conv_pixel_to_32(PixelFormat *pf,
++ void *dst, QemuPixel *src, uint32_t cnt)
++{
++ uint32_t *dst32 = dst;
++
++ while (cnt > 0) {
++ *dst32 = ((uint32_t)src->red >> (8 - pf->rbits)) << pf->rshift;
++ *dst32 |= ((uint32_t)src->green >> (8 - pf->gbits)) << pf->gshift;
++ *dst32 |= ((uint32_t)src->blue >> (8 - pf->bbits)) << pf->bshift;
++ *dst32 |= ((uint32_t)src->alpha >> (8 - pf->abits)) << pf->ashift;
++ dst32++, src++, cnt--;
++ }
++}
++
++/* ----------------------------------------------------------------------- */
++/* PixelFormat -> PixelFormat conversions */
++
++static void convert_copy(QemuPfConv *conv, void *dst, void *src, uint32_t cnt)
++{
++ uint32_t bytes = cnt * conv->src.bytes_per_pixel;
++ memcpy(dst, src, bytes);
++}
++
++static void convert_generic(QemuPfConv *conv, void *dst, void *src, uint32_t cnt)
++{
++ if (conv->conv_cnt < cnt) {
++ conv->conv_cnt = cnt;
++ conv->conv_buf = qemu_realloc(conv->conv_buf, sizeof(QemuPixel) * conv->conv_cnt);
++ }
++ conv->conv_from(&conv->src, conv->conv_buf, src, cnt);
++ conv->conv_to(&conv->dst, dst, conv->conv_buf, cnt);
++}
++
++/* ----------------------------------------------------------------------- */
++/* public interface */
++
++QemuPfConv *qemu_pf_conv_get(PixelFormat *dst, PixelFormat *src)
++{
++ QemuPfConv *conv = qemu_mallocz(sizeof(QemuPfConv));
++
++ conv->src = *src;
++ conv->dst = *dst;
++
++ if (memcmp(&conv->src, &conv->dst, sizeof(PixelFormat)) == 0) {
++ /* formats identical, can simply copy */
++ conv->convert = convert_copy;
++ } else {
++ /* generic two-step conversion: src -> QemuPixel -> dst */
++ switch (conv->src.bytes_per_pixel) {
++ case 2:
++ conv->conv_from = conv_16_to_pixel;
++ break;
++ case 4:
++ if (conv->src.rbits == 8 && conv->src.gbits == 8 && conv->src.bbits == 8) {
++ conv->conv_from = conv_32_to_pixel_fast;
++ } else {
++ conv->conv_from = conv_32_to_pixel_generic;
++ }
++ break;
++ default:
++ goto err;
++ }
++ switch (conv->dst.bytes_per_pixel) {
++ case 2:
++ conv->conv_to = conv_pixel_to_16;
++ break;
++ case 4:
++ conv->conv_to = conv_pixel_to_32;
++ break;
++ default:
++ goto err;
++ }
++ conv->convert = convert_generic;
++ }
++ return conv;
++
++err:
++ qemu_free(conv);
++ return NULL;
++}
++
++void qemu_pf_conv_run(QemuPfConv *conv, void *dst, void *src, uint32_t cnt)
++{
++ conv->convert(conv, dst, src, cnt);
++}
++
++void qemu_pf_conv_put(QemuPfConv *conv)
++{
++ if (conv) {
++ qemu_free(conv->conv_buf);
++ qemu_free(conv);
++ }
++}
+diff --git a/pflib.h b/pflib.h
+new file mode 100644
+index 0000000..8d73fdd
+--- /dev/null
++++ b/pflib.h
+@@ -0,0 +1,6 @@
++/* public */
++typedef struct QemuPfConv QemuPfConv;
++
++QemuPfConv *qemu_pf_conv_get(PixelFormat *dst, PixelFormat *src);
++void qemu_pf_conv_run(QemuPfConv *conv, void *dst, void *src, uint32_t cnt);
++void qemu_pf_conv_put(QemuPfConv *conv);
+--
+1.7.2.3
+
diff --git a/0002-configure-add-logging.patch b/0002-configure-add-logging.patch
new file mode 100644
index 0000000..34f374c
--- /dev/null
+++ b/0002-configure-add-logging.patch
@@ -0,0 +1,39 @@
+From 89df4f8cf7ecde07e3dc5e2ea3c19cbcd02165d0 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel at redhat.com>
+Date: Fri, 23 Apr 2010 13:44:10 +0200
+Subject: [PATCH 02/39] configure: add logging
+
+Write compile commands and messages to config.log.
+Useful for debugging configure.
+---
+ configure | 7 +++++--
+ 1 files changed, 5 insertions(+), 2 deletions(-)
+
+diff --git a/configure b/configure
+index b85590f..e09c442 100755
+--- a/configure
++++ b/configure
+@@ -16,15 +16,18 @@ TMPO="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.o"
+ TMPE="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.exe"
+
+ trap "rm -f $TMPC $TMPO $TMPE ; exit" EXIT INT QUIT TERM
++rm -f config.log
+
+ compile_object() {
+- $cc $QEMU_CFLAGS -c -o $TMPO $TMPC > /dev/null 2> /dev/null
++ echo $cc $QEMU_CFLAGS -c -o $TMPO $TMPC >> config.log
++ $cc $QEMU_CFLAGS -c -o $TMPO $TMPC >> config.log 2>&1
+ }
+
+ compile_prog() {
+ local_cflags="$1"
+ local_ldflags="$2"
+- $cc $QEMU_CFLAGS $local_cflags -o $TMPE $TMPC $LDFLAGS $local_ldflags > /dev/null 2> /dev/null
++ echo $cc $QEMU_CFLAGS $local_cflags -o $TMPE $TMPC $LDFLAGS $local_ldflags >> config.log
++ $cc $QEMU_CFLAGS $local_cflags -o $TMPE $TMPC $LDFLAGS $local_ldflags >> config.log 2>&1
+ }
+
+ # check whether a command is available to this shell (may be either an
+--
+1.7.2.3
+
diff --git a/0003-add-spice-into-the-configure-file.patch b/0003-add-spice-into-the-configure-file.patch
new file mode 100644
index 0000000..3d4eb0a
--- /dev/null
+++ b/0003-add-spice-into-the-configure-file.patch
@@ -0,0 +1,94 @@
+From 0034da7fb15d1225e0fd725009743d48511a90b7 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel at redhat.com>
+Date: Wed, 24 Mar 2010 10:26:51 +0100
+Subject: [PATCH 03/39] add spice into the configure file
+
+---
+ configure | 36 ++++++++++++++++++++++++++++++++++++
+ 1 files changed, 36 insertions(+), 0 deletions(-)
+
+diff --git a/configure b/configure
+index e09c442..2aaa6d7 100755
+--- a/configure
++++ b/configure
+@@ -331,6 +331,7 @@ cpu_emulation="yes"
+ check_utests="no"
+ user_pie="no"
+ zero_malloc=""
++spice=""
+
+ # OS specific
+ if check_define __linux__ ; then
+@@ -647,6 +648,10 @@ for opt do
+ ;;
+ --enable-kvm-device-assignment) kvm_cap_device_assignment="yes"
+ ;;
++ --disable-spice) spice="no"
++ ;;
++ --enable-spice) spice="yes"
++ ;;
+ --enable-profiler) profiler="yes"
+ ;;
+ --enable-cocoa)
+@@ -933,6 +938,8 @@ echo " --enable-docs enable documentation build"
+ echo " --disable-docs disable documentation build"
+ echo " --disable-vhost-net disable vhost-net acceleration support"
+ echo " --enable-vhost-net enable vhost-net acceleration support"
++echo " --disable-spice disable spice"
++echo " --enable-spice enable spice"
+ echo ""
+ echo "NOTE: The object files are built at the place where configure is launched"
+ exit 1
+@@ -2184,6 +2191,30 @@ if compile_prog "" ""; then
+ gcc_attribute_warn_unused_result=yes
+ fi
+
++# spice probe
++if test "$spice" != "no" ; then
++ cat > $TMPC << EOF
++#include <spice.h>
++int main(void) { spice_server_new(); return 0; }
++EOF
++ spice_proto_ver=$($pkgconfig --modversion spice-protocol 2>/dev/null)
++ spice_server_ver=$($pkgconfig --modversion spice-server 2>/dev/null)
++ spice_cflags=$($pkgconfig --cflags spice-protocol spice-server 2>/dev/null)
++ spice_libs=$($pkgconfig --libs spice-protocol spice-server 2>/dev/null)
++ if compile_prog "$spice_cflags" "$spice_libs" ; then
++ spice="yes"
++ libs_softmmu="$libs_softmmu $spice_libs"
++ QEMU_CFLAGS="$QEMU_CFLAGS $spice_cflags"
++ else
++ if test "$spice" = "yes" ; then
++ feature_not_found "spice"
++ fi
++ spice="no"
++ fi
++fi
++
++##########################################
++
+ ##########################################
+ # check if we have fdatasync
+
+@@ -2329,6 +2360,7 @@ echo "preadv support $preadv"
+ echo "fdatasync $fdatasync"
+ echo "uuid support $uuid"
+ echo "vhost-net support $vhost_net"
++echo "spice support $spice"
+
+ if test $sdl_too_old = "yes"; then
+ echo "-> Your SDL version is too old - please upgrade to have SDL support"
+@@ -2574,6 +2606,10 @@ else
+ echo "CONFIG_NO_CPU_EMULATION=y" >> $config_host_mak
+ fi
+
++if test "$spice" = "yes" ; then
++ echo "CONFIG_SPICE=y" >> $config_host_mak
++fi
++
+ # XXX: suppress that
+ if [ "$bsd" = "yes" ] ; then
+ echo "CONFIG_BSD=y" >> $config_host_mak
+--
+1.7.2.3
+
diff --git a/0004-spice-core-bits.patch b/0004-spice-core-bits.patch
new file mode 100644
index 0000000..a4b156b
--- /dev/null
+++ b/0004-spice-core-bits.patch
@@ -0,0 +1,333 @@
+From 67361a4ad5c99c5dfecdb9d2fc1ba794c38c44ff Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel at redhat.com>
+Date: Thu, 11 Mar 2010 11:13:27 -0300
+Subject: [PATCH 04/39] spice: core bits
+
+Add -spice command line switch. Has support setting passwd and port for
+now. With this patch applied the spice client can successfully connect
+to qemu. You can't do anything useful yet though.
+---
+ Makefile.objs | 2 +
+ qemu-config.c | 23 ++++++++
+ qemu-config.h | 1 +
+ qemu-options.hx | 8 +++
+ qemu-spice.h | 22 ++++++++
+ spice.c | 151 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ vl.c | 15 ++++++
+ 7 files changed, 222 insertions(+), 0 deletions(-)
+ create mode 100644 qemu-spice.h
+ create mode 100644 spice.c
+
+diff --git a/Makefile.objs b/Makefile.objs
+index 147051f..569b458 100644
+--- a/Makefile.objs
++++ b/Makefile.objs
+@@ -89,6 +89,8 @@ common-obj-y += pflib.o
+ common-obj-$(CONFIG_BRLAPI) += baum.o
+ common-obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o
+
++common-obj-$(CONFIG_SPICE) += spice.o
++
+ audio-obj-y = audio.o noaudio.o wavaudio.o mixeng.o
+ audio-obj-$(CONFIG_SDL) += sdlaudio.o
+ audio-obj-$(CONFIG_OSS) += ossaudio.o
+diff --git a/qemu-config.c b/qemu-config.c
+index 08ee553..8a894cf 100644
+--- a/qemu-config.c
++++ b/qemu-config.c
+@@ -346,6 +346,26 @@ QemuOptsList qemu_cpudef_opts = {
+ },
+ };
+
++#ifdef CONFIG_SPICE
++QemuOptsList qemu_spice_opts = {
++ .name = "spice",
++ .head = QTAILQ_HEAD_INITIALIZER(qemu_spice_opts.head),
++ .desc = {
++ {
++ .name = "port",
++ .type = QEMU_OPT_NUMBER,
++ },{
++ .name = "password",
++ .type = QEMU_OPT_STRING,
++ },{
++ .name = "disable-ticketing",
++ .type = QEMU_OPT_BOOL,
++ },
++ { /* end if list */ }
++ },
++};
++#endif
++
+ static QemuOptsList *vm_config_groups[] = {
+ &qemu_drive_opts,
+ &qemu_chardev_opts,
+@@ -356,6 +376,9 @@ static QemuOptsList *vm_config_groups[] = {
+ &qemu_global_opts,
+ &qemu_mon_opts,
+ &qemu_cpudef_opts,
++#ifdef CONFIG_SPICE
++ &qemu_spice_opts,
++#endif
+ NULL,
+ };
+
+diff --git a/qemu-config.h b/qemu-config.h
+index dca69d4..3a90213 100644
+--- a/qemu-config.h
++++ b/qemu-config.h
+@@ -14,6 +14,7 @@ extern QemuOptsList qemu_rtc_opts;
+ extern QemuOptsList qemu_global_opts;
+ extern QemuOptsList qemu_mon_opts;
+ extern QemuOptsList qemu_cpudef_opts;
++extern QemuOptsList qemu_spice_opts;
+
+ QemuOptsList *qemu_find_opts(const char *group);
+ int qemu_set_option(const char *str);
+diff --git a/qemu-options.hx b/qemu-options.hx
+index 66c84a0..85551cc 100644
+--- a/qemu-options.hx
++++ b/qemu-options.hx
+@@ -676,6 +676,14 @@ STEXI
+ Enable SDL.
+ ETEXI
+
++#ifdef CONFIG_SPICE
++DEF("spice", HAS_ARG, QEMU_OPTION_spice,
++ "-spice <args> use spice\n", QEMU_ARCH_ALL)
++STEXI
++Use Spice.
++ETEXI
++#endif
++
+ DEF("portrait", 0, QEMU_OPTION_portrait,
+ "-portrait rotate graphical output 90 deg left (only PXA LCD)\n",
+ QEMU_ARCH_ALL)
+diff --git a/qemu-spice.h b/qemu-spice.h
+new file mode 100644
+index 0000000..5597576
+--- /dev/null
++++ b/qemu-spice.h
+@@ -0,0 +1,22 @@
++#ifndef QEMU_SPICE_H
++#define QEMU_SPICE_H
++
++#ifdef CONFIG_SPICE
++
++#include <spice.h>
++
++#include "qemu-option.h"
++#include "qemu-config.h"
++
++extern SpiceServer *spice_server;
++extern int using_spice;
++
++void qemu_spice_init(void);
++
++#else /* CONFIG_SPICE */
++
++#define using_spice 0
++
++#endif /* CONFIG_SPICE */
++
++#endif /* QEMU_SPICE_H */
+diff --git a/spice.c b/spice.c
+new file mode 100644
+index 0000000..50fa5ca
+--- /dev/null
++++ b/spice.c
+@@ -0,0 +1,151 @@
++#include <stdlib.h>
++#include <stdio.h>
++#include <string.h>
++
++#include <spice.h>
++#include <spice-experimental.h>
++
++#include "qemu-common.h"
++#include "qemu-spice.h"
++#include "qemu-timer.h"
++#include "qemu-queue.h"
++#include "monitor.h"
++
++/* core bits */
++
++SpiceServer *spice_server;
++int using_spice = 0;
++
++struct SpiceTimer {
++ QEMUTimer *timer;
++ QTAILQ_ENTRY(SpiceTimer) next;
++};
++static QTAILQ_HEAD(, SpiceTimer) timers = QTAILQ_HEAD_INITIALIZER(timers);
++
++static SpiceTimer *timer_add(SpiceTimerFunc func, void *opaque)
++{
++ SpiceTimer *timer;
++
++ timer = qemu_mallocz(sizeof(*timer));
++ timer->timer = qemu_new_timer(rt_clock, func, opaque);
++ QTAILQ_INSERT_TAIL(&timers, timer, next);
++ return timer;
++}
++
++static void timer_start(SpiceTimer *timer, uint32_t ms)
++{
++ qemu_mod_timer(timer->timer, qemu_get_clock(rt_clock) + ms);
++}
++
++static void timer_cancel(SpiceTimer *timer)
++{
++ qemu_del_timer(timer->timer);
++}
++
++static void timer_remove(SpiceTimer *timer)
++{
++ qemu_del_timer(timer->timer);
++ qemu_free_timer(timer->timer);
++ QTAILQ_REMOVE(&timers, timer, next);
++ free(timer);
++}
++
++struct SpiceWatch {
++ int fd;
++ int event_mask;
++ SpiceWatchFunc func;
++ void *opaque;
++ QTAILQ_ENTRY(SpiceWatch) next;
++};
++static QTAILQ_HEAD(, SpiceWatch) watches = QTAILQ_HEAD_INITIALIZER(watches);
++
++static void watch_read(void *opaque)
++{
++ SpiceWatch *watch = opaque;
++ watch->func(watch->fd, SPICE_WATCH_EVENT_READ, watch->opaque);
++}
++
++static void watch_write(void *opaque)
++{
++ SpiceWatch *watch = opaque;
++ watch->func(watch->fd, SPICE_WATCH_EVENT_WRITE, watch->opaque);
++}
++
++static void watch_update_mask(SpiceWatch *watch, int event_mask)
++{
++ IOHandler *on_read = NULL;
++ IOHandler *on_write = NULL;
++
++ watch->event_mask = event_mask;
++ if (watch->event_mask & SPICE_WATCH_EVENT_READ)
++ on_read = watch_read;
++ if (watch->event_mask & SPICE_WATCH_EVENT_WRITE)
++ on_read = watch_write;
++ qemu_set_fd_handler(watch->fd, on_read, on_write, watch);
++}
++
++static SpiceWatch *watch_add(int fd, int event_mask, SpiceWatchFunc func, void *opaque)
++{
++ SpiceWatch *watch;
++
++ watch = qemu_mallocz(sizeof(*watch));
++ watch->fd = fd;
++ watch->func = func;
++ watch->opaque = opaque;
++ QTAILQ_INSERT_TAIL(&watches, watch, next);
++
++ watch_update_mask(watch, event_mask);
++ return watch;
++}
++
++static void watch_remove(SpiceWatch *watch)
++{
++ watch_update_mask(watch, 0);
++ QTAILQ_REMOVE(&watches, watch, next);
++ qemu_free(watch);
++}
++
++static SpiceCoreInterface core_interface = {
++ .base.type = SPICE_INTERFACE_CORE,
++ .base.description = "qemu core services",
++ .base.major_version = SPICE_INTERFACE_CORE_MAJOR,
++ .base.minor_version = SPICE_INTERFACE_CORE_MINOR,
++
++ .timer_add = timer_add,
++ .timer_start = timer_start,
++ .timer_cancel = timer_cancel,
++ .timer_remove = timer_remove,
++
++ .watch_add = watch_add,
++ .watch_update_mask = watch_update_mask,
++ .watch_remove = watch_remove,
++};
++
++/* functions for the rest of qemu */
++
++void qemu_spice_init(void)
++{
++ QemuOpts *opts = QTAILQ_FIRST(&qemu_spice_opts.head);
++ const char *password;
++ int port;
++
++ if (!opts)
++ return;
++ port = qemu_opt_get_number(opts, "port", 0);
++ if (!port)
++ return;
++ password = qemu_opt_get(opts, "password");
++
++ spice_server = spice_server_new();
++ spice_server_set_port(spice_server, port);
++ if (password)
++ spice_server_set_ticket(spice_server, password, 0, 0, 0);
++ if (qemu_opt_get_bool(opts, "disable-ticketing", 0))
++ spice_server_set_noauth(spice_server);
++
++ /* TODO: make configurable via cmdline */
++ spice_server_set_image_compression(spice_server, SPICE_IMAGE_COMPRESS_AUTO_GLZ);
++
++ spice_server_init(spice_server, &core_interface);
++ using_spice = 1;
++}
+diff --git a/vl.c b/vl.c
+index de8bad1..97897e0 100644
+--- a/vl.c
++++ b/vl.c
+@@ -162,6 +162,8 @@ int main(int argc, char **argv)
+ #include "cpus.h"
+ #include "arch_init.h"
+
++#include "qemu-spice.h"
++
+ //#define DEBUG_NET
+ //#define DEBUG_SLIRP
+
+@@ -2677,6 +2679,15 @@ int main(int argc, char **argv, char **envp)
+ }
+ break;
+ }
++#ifdef CONFIG_SPICE
++ case QEMU_OPTION_spice:
++ opts = qemu_opts_parse(&qemu_spice_opts, optarg, 0);
++ if (!opts) {
++ fprintf(stderr, "parse error: %s\n", optarg);
++ exit(1);
++ }
++ break;
++#endif
+ case QEMU_OPTION_writeconfig:
+ {
+ FILE *fp;
+@@ -2951,6 +2962,10 @@ int main(int argc, char **argv, char **envp)
+ }
+ qemu_add_globals();
+
++#ifdef CONFIG_SPICE
++ qemu_spice_init();
++#endif
++
+ machine->init(ram_size, boot_devices,
+ kernel_filename, kernel_cmdline, initrd_filename, cpu_model);
+
+--
+1.7.2.3
+
diff --git a/0005-spice-add-keyboard.patch b/0005-spice-add-keyboard.patch
new file mode 100644
index 0000000..6394c15
--- /dev/null
+++ b/0005-spice-add-keyboard.patch
@@ -0,0 +1,118 @@
+From 90f6ec84332857752c252b1c3b89d86eb9714b0e Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel at redhat.com>
+Date: Thu, 11 Mar 2010 11:13:28 -0300
+Subject: [PATCH 05/39] spice: add keyboard
+
+Open keyboard channel. Now you can type into the spice client and the
+keyboard events are sent to your guest. You'll need some other display
+like vnc to actually see the guest responding to them though.
+---
+ Makefile.objs | 2 +-
+ qemu-spice.h | 1 +
+ spice-input.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ spice.c | 2 ++
+ 4 files changed, 61 insertions(+), 1 deletions(-)
+ create mode 100644 spice-input.c
+
+diff --git a/Makefile.objs b/Makefile.objs
+index 569b458..023a0dc 100644
+--- a/Makefile.objs
++++ b/Makefile.objs
+@@ -89,7 +89,7 @@ common-obj-y += pflib.o
+ common-obj-$(CONFIG_BRLAPI) += baum.o
+ common-obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o
+
+-common-obj-$(CONFIG_SPICE) += spice.o
++common-obj-$(CONFIG_SPICE) += spice.o spice-input.o
+
+ audio-obj-y = audio.o noaudio.o wavaudio.o mixeng.o
+ audio-obj-$(CONFIG_SDL) += sdlaudio.o
+diff --git a/qemu-spice.h b/qemu-spice.h
+index 5597576..ceb3db2 100644
+--- a/qemu-spice.h
++++ b/qemu-spice.h
+@@ -12,6 +12,7 @@ extern SpiceServer *spice_server;
+ extern int using_spice;
+
+ void qemu_spice_init(void);
++void qemu_spice_input_init(void);
+
+ #else /* CONFIG_SPICE */
+
+diff --git a/spice-input.c b/spice-input.c
+new file mode 100644
+index 0000000..e1014d7
+--- /dev/null
++++ b/spice-input.c
+@@ -0,0 +1,57 @@
++#include <stdlib.h>
++#include <stdio.h>
++#include <string.h>
++
++#include <spice.h>
++
++#include "qemu-common.h"
++#include "qemu-spice.h"
++#include "console.h"
++
++/* keyboard bits */
++
++typedef struct QemuSpiceKbd {
++ SpiceKbdInstance sin;
++ int ledstate;
++} QemuSpiceKbd;
++
++static void kbd_push_key(SpiceKbdInstance *sin, uint8_t frag);
++static uint8_t kbd_get_leds(SpiceKbdInstance *sin);
++static void kbd_leds(void *opaque, int l);
++
++static const SpiceKbdInterface kbd_interface = {
++ .base.type = SPICE_INTERFACE_KEYBOARD,
++ .base.description = "qemu keyboard",
++ .base.major_version = SPICE_INTERFACE_KEYBOARD_MAJOR,
++ .base.minor_version = SPICE_INTERFACE_KEYBOARD_MINOR,
++ .push_scan_freg = kbd_push_key,
++ .get_leds = kbd_get_leds,
++};
++
++static void kbd_push_key(SpiceKbdInstance *sin, uint8_t frag)
++{
++ kbd_put_keycode(frag);
++}
++
++static uint8_t kbd_get_leds(SpiceKbdInstance *sin)
++{
++ QemuSpiceKbd *kbd = container_of(sin, QemuSpiceKbd, sin);
++ return kbd->ledstate;
++}
++
++static void kbd_leds(void *opaque, int ledstate)
++{
++ QemuSpiceKbd *kbd = opaque;
++ kbd->ledstate = ledstate;
++ spice_server_kbd_leds(&kbd->sin, ledstate);
++}
++
++void qemu_spice_input_init(void)
++{
++ QemuSpiceKbd *kbd;
++
++ kbd = qemu_mallocz(sizeof(*kbd));
++ kbd->sin.base.sif = &kbd_interface.base;
++ spice_server_add_interface(spice_server, &kbd->sin.base);
++ qemu_add_led_event_handler(kbd_leds, kbd);
++}
+diff --git a/spice.c b/spice.c
+index 50fa5ca..c763d52 100644
+--- a/spice.c
++++ b/spice.c
+@@ -148,4 +148,6 @@ void qemu_spice_init(void)
+
+ spice_server_init(spice_server, &core_interface);
+ using_spice = 1;
++
++ qemu_spice_input_init();
+ }
+--
+1.7.2.3
+
diff --git a/0006-spice-add-mouse.patch b/0006-spice-add-mouse.patch
new file mode 100644
index 0000000..ea41880
--- /dev/null
+++ b/0006-spice-add-mouse.patch
@@ -0,0 +1,62 @@
+From e18846175191cbc590ac46fa3820726aeebd6d48 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel at redhat.com>
+Date: Thu, 11 Mar 2010 11:13:29 -0300
+Subject: [PATCH 06/39] spice: add mouse
+
+Open mouse channel. Now you can move the guests mouse pointer.
+No tablet / absolute positioning (yet) though.
+---
+ spice-input.c | 31 +++++++++++++++++++++++++++++++
+ 1 files changed, 31 insertions(+), 0 deletions(-)
+
+diff --git a/spice-input.c b/spice-input.c
+index e1014d7..8f3deb4 100644
+--- a/spice-input.c
++++ b/spice-input.c
+@@ -46,12 +46,43 @@ static void kbd_leds(void *opaque, int ledstate)
+ spice_server_kbd_leds(&kbd->sin, ledstate);
+ }
+
++/* mouse bits */
++
++typedef struct QemuSpiceMouse {
++ SpiceMouseInstance sin;
++} QemuSpiceMouse;
++
++static void mouse_motion(SpiceMouseInstance *sin, int dx, int dy, int dz,
++ uint32_t buttons_state)
++{
++ kbd_mouse_event(dx, dy, dz, buttons_state);
++}
++
++static void mouse_buttons(SpiceMouseInstance *sin, uint32_t buttons_state)
++{
++ kbd_mouse_event(0, 0, 0, buttons_state);
++}
++
++static const SpiceMouseInterface mouse_interface = {
++ .base.type = SPICE_INTERFACE_MOUSE,
++ .base.description = "mouse",
++ .base.major_version = SPICE_INTERFACE_MOUSE_MAJOR,
++ .base.minor_version = SPICE_INTERFACE_MOUSE_MINOR,
++ .motion = mouse_motion,
++ .buttons = mouse_buttons,
++};
++
+ void qemu_spice_input_init(void)
+ {
+ QemuSpiceKbd *kbd;
++ QemuSpiceMouse *mouse;
+
+ kbd = qemu_mallocz(sizeof(*kbd));
+ kbd->sin.base.sif = &kbd_interface.base;
+ spice_server_add_interface(spice_server, &kbd->sin.base);
+ qemu_add_led_event_handler(kbd_leds, kbd);
++
++ mouse = qemu_mallocz(sizeof(*mouse));
++ mouse->sin.base.sif = &mouse_interface.base;
++ spice_server_add_interface(spice_server, &mouse->sin.base);
+ }
+--
+1.7.2.3
+
diff --git a/0007-spice-simple-display.patch b/0007-spice-simple-display.patch
new file mode 100644
index 0000000..e59caa1
--- /dev/null
+++ b/0007-spice-simple-display.patch
@@ -0,0 +1,532 @@
+From 0143117eb5e6233fdeff3b679492b51148cc8f85 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel at redhat.com>
+Date: Wed, 24 Mar 2010 15:47:18 +0100
+Subject: [PATCH 07/39] spice: simple display
+
+With that patch applied you'll actually see the guests screen in the
+spice client. This does *not* bring qxl and full spice support though.
+This is basically the qxl vga mode made more generic, so it plays
+together with any qemu-emulated gfx card. You can display stdvga or
+cirrus via spice client. You can have both vnc and spice enabled and
+clients connected at the same time.
+---
+ Makefile.objs | 2 +-
+ qemu-spice.h | 1 +
+ spice-display.c | 394 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ spice-display.h | 52 ++++++++
+ vl.c | 7 +-
+ 5 files changed, 454 insertions(+), 2 deletions(-)
+ create mode 100644 spice-display.c
+ create mode 100644 spice-display.h
+
+diff --git a/Makefile.objs b/Makefile.objs
+index 023a0dc..d05643f 100644
+--- a/Makefile.objs
++++ b/Makefile.objs
+@@ -89,7 +89,7 @@ common-obj-y += pflib.o
+ common-obj-$(CONFIG_BRLAPI) += baum.o
+ common-obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o
+
+-common-obj-$(CONFIG_SPICE) += spice.o spice-input.o
++common-obj-$(CONFIG_SPICE) += spice.o spice-input.o spice-display.o
+
+ audio-obj-y = audio.o noaudio.o wavaudio.o mixeng.o
+ audio-obj-$(CONFIG_SDL) += sdlaudio.o
+diff --git a/qemu-spice.h b/qemu-spice.h
+index ceb3db2..f061004 100644
+--- a/qemu-spice.h
++++ b/qemu-spice.h
+@@ -13,6 +13,7 @@ extern int using_spice;
+
+ void qemu_spice_init(void);
+ void qemu_spice_input_init(void);
++void qemu_spice_display_init(DisplayState *ds);
+
+ #else /* CONFIG_SPICE */
+
+diff --git a/spice-display.c b/spice-display.c
+new file mode 100644
+index 0000000..13a620e
+--- /dev/null
++++ b/spice-display.c
+@@ -0,0 +1,394 @@
++#include <stdio.h>
++#include <stdlib.h>
++#include <stdbool.h>
++#include <stdint.h>
++#include <string.h>
++#include <pthread.h>
++
++#include "qemu-common.h"
++#include "qemu-spice.h"
++#include "qemu-timer.h"
++#include "qemu-queue.h"
++#include "monitor.h"
++#include "console.h"
++#include "sysemu.h"
++
++#include "spice-display.h"
++
++static int debug = 1;
++
++int qemu_spice_rect_is_empty(const QXLRect* r)
++{
++ return r->top == r->bottom || r->left == r->right;
++}
++
++void qemu_spice_rect_union(QXLRect *dest, const QXLRect *r)
++{
++ if (qemu_spice_rect_is_empty(r)) {
++ return;
++ }
++
++ if (qemu_spice_rect_is_empty(dest)) {
++ *dest = *r;
++ return;
++ }
++
++ dest->top = MIN(dest->top, r->top);
++ dest->left = MIN(dest->left, r->left);
++ dest->bottom = MAX(dest->bottom, r->bottom);
++ dest->right = MAX(dest->right, r->right);
++}
++
++SimpleSpiceUpdate *qemu_spice_create_update(SimpleSpiceDisplay *ssd)
++{
++ SimpleSpiceUpdate *update;
++ QXLDrawable *drawable;
++ QXLImage *image;
++ QXLCommand *cmd;
++ uint8_t *src, *dst;
++ int by, bw, bh;
++
++ if (qemu_spice_rect_is_empty(&ssd->dirty)) {
++ return NULL;
++ };
++
++ pthread_mutex_lock(&ssd->lock);
++ if (debug > 1)
++ fprintf(stderr, "%s: lr %d -> %d, tb -> %d -> %d\n", __FUNCTION__,
++ ssd->dirty.left, ssd->dirty.right,
++ ssd->dirty.top, ssd->dirty.bottom);
++
++ update = qemu_mallocz(sizeof(*update));
++ drawable = &update->drawable;
++ image = &update->image;
++ cmd = &update->ext.cmd;
++
++ bw = ssd->dirty.right - ssd->dirty.left;
++ bh = ssd->dirty.bottom - ssd->dirty.top;
++ update->bitmap = qemu_malloc(bw * bh * 4);
++
++ drawable->bbox = ssd->dirty;
++ drawable->clip.type = SPICE_CLIP_TYPE_NONE;
++ drawable->effect = QXL_EFFECT_OPAQUE;
++ drawable->release_info.id = (intptr_t)update;
++ drawable->type = QXL_DRAW_COPY;
++
++ drawable->u.copy.rop_descriptor = SPICE_ROPD_OP_PUT;
++ drawable->u.copy.src_bitmap = (intptr_t)image;
++ drawable->u.copy.src_area.right = bw;
++ drawable->u.copy.src_area.bottom = bh;
++
++ QXL_SET_IMAGE_ID(image, QXL_IMAGE_GROUP_DEVICE, ssd->unique++);
++ image->descriptor.type = SPICE_IMAGE_TYPE_BITMAP;
++ image->bitmap.flags = QXL_BITMAP_DIRECT | QXL_BITMAP_TOP_DOWN;
++ image->bitmap.stride = bw * 4;
++ image->descriptor.width = image->bitmap.x = bw;
++ image->descriptor.height = image->bitmap.y = bh;
++ image->bitmap.data = (intptr_t)(update->bitmap);
++ image->bitmap.palette = 0;
++ image->bitmap.format = SPICE_BITMAP_FMT_32BIT;
++
++ if (ssd->conv == NULL) {
++ PixelFormat dst = qemu_default_pixelformat(32);
++ ssd->conv = qemu_pf_conv_get(&dst, &ssd->ds->surface->pf);
++ assert(ssd->conv);
++ }
++
++ src = ds_get_data(ssd->ds) +
++ ssd->dirty.top * ds_get_linesize(ssd->ds) +
++ ssd->dirty.left * ds_get_bytes_per_pixel(ssd->ds);
++ dst = update->bitmap;
++ for (by = 0; by < bh; by++) {
++ qemu_pf_conv_run(ssd->conv, dst, src, bw);
++ src += ds_get_linesize(ssd->ds);
++ dst += image->bitmap.stride;
++ }
++
++ cmd->type = QXL_CMD_DRAW;
++ cmd->data = (intptr_t)drawable;
++
++ memset(&ssd->dirty, 0, sizeof(ssd->dirty));
++ pthread_mutex_unlock(&ssd->lock);
++ return update;
++}
++
++void qemu_spice_destroy_update(SimpleSpiceDisplay *sdpy, SimpleSpiceUpdate *update)
++{
++ qemu_free(update->bitmap);
++ qemu_free(update);
++}
++
++void qemu_spice_create_host_memslot(SimpleSpiceDisplay *ssd)
++{
++ QXLDevMemSlot memslot;
++
++ if (debug)
++ fprintf(stderr, "%s:\n", __FUNCTION__);
++
++ memset(&memslot, 0, sizeof(memslot));
++ memslot.slot_group_id = MEMSLOT_GROUP_HOST;
++ memslot.virt_end = ~0;
++ ssd->worker->add_memslot(ssd->worker, &memslot);
++}
++
++void qemu_spice_create_host_primary(SimpleSpiceDisplay *ssd)
++{
++ QXLDevSurfaceCreate surface;
++
++ if (debug)
++ fprintf(stderr, "%s: %dx%d\n", __FUNCTION__,
++ ds_get_width(ssd->ds), ds_get_height(ssd->ds));
++
++ surface.format = SPICE_SURFACE_FMT_32_xRGB;
++ surface.width = ds_get_width(ssd->ds);
++ surface.height = ds_get_height(ssd->ds);
++ surface.stride = -surface.width * 4;
++ surface.mouse_mode = 0;
++ surface.flags = 0;
++ surface.type = 0;
++ surface.mem = (intptr_t)ssd->buf;
++ surface.group_id = MEMSLOT_GROUP_HOST;
++ ssd->worker->create_primary_surface(ssd->worker, 0, &surface);
++}
++
++void qemu_spice_destroy_host_primary(SimpleSpiceDisplay *ssd)
++{
++ if (debug)
++ fprintf(stderr, "%s:\n", __FUNCTION__);
++
++ ssd->worker->destroy_primary_surface(ssd->worker, 0);
++}
++
++void qemu_spice_vm_change_state_handler(void *opaque, int running, int reason)
++{
++ SimpleSpiceDisplay *ssd = opaque;
++
++ if (running) {
++ ssd->worker->start(ssd->worker);
++ } else {
++ ssd->worker->stop(ssd->worker);
++ }
++ ssd->running = running;
++}
++
++/* display listener callbacks */
++
++void qemu_spice_display_update(SimpleSpiceDisplay *ssd,
++ int x, int y, int w, int h)
++{
++ QXLRect update_area;
++
++ if (debug > 1)
++ fprintf(stderr, "%s: x %d y %d w %d h %d\n", __FUNCTION__, x, y, w, h);
++ update_area.left = x,
++ update_area.right = x + w;
++ update_area.top = y;
++ update_area.bottom = y + h;
++
++ pthread_mutex_lock(&ssd->lock);
++ if (qemu_spice_rect_is_empty(&ssd->dirty)) {
++ ssd->notify++;
++ }
++ qemu_spice_rect_union(&ssd->dirty, &update_area);
++ pthread_mutex_unlock(&ssd->lock);
++}
++
++void qemu_spice_display_resize(SimpleSpiceDisplay *ssd)
++{
++ if (debug)
++ fprintf(stderr, "%s:\n", __FUNCTION__);
++
++ pthread_mutex_lock(&ssd->lock);
++ memset(&ssd->dirty, 0, sizeof(ssd->dirty));
++ pthread_mutex_unlock(&ssd->lock);
++
++ qemu_spice_destroy_host_primary(ssd);
++ qemu_spice_create_host_primary(ssd);
++ qemu_pf_conv_put(ssd->conv);
++ ssd->conv = NULL;
++
++ pthread_mutex_lock(&ssd->lock);
++ ssd->dirty.left = 0;
++ ssd->dirty.right = ds_get_width(ssd->ds);
++ ssd->dirty.top = 0;
++ ssd->dirty.bottom = ds_get_height(ssd->ds);
++ ssd->notify++;
++ pthread_mutex_unlock(&ssd->lock);
++}
++
++void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd)
++{
++ if (debug > 2)
++ fprintf(stderr, "%s:\n", __FUNCTION__);
++ vga_hw_update();
++ if (ssd->notify) {
++ ssd->notify = 0;
++ ssd->worker->wakeup(ssd->worker);
++ if (debug > 1)
++ fprintf(stderr, "%s: notify\n", __FUNCTION__);
++ }
++}
++
++/* spice display interface callbacks */
++
++static void interface_attach_worker(QXLInstance *sin, QXLWorker *qxl_worker)
++{
++ SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
++
++ if (debug)
++ fprintf(stderr, "%s:\n", __FUNCTION__);
++ ssd->worker = qxl_worker;
++}
++
++static void interface_set_compression_level(QXLInstance *sin, int level)
++{
++ if (debug)
++ fprintf(stderr, "%s:\n", __FUNCTION__);
++ /* nothing to do */
++}
++
++static void interface_set_mm_time(QXLInstance *sin, uint32_t mm_time)
++{
++ if (debug > 2)
++ fprintf(stderr, "%s:\n", __FUNCTION__);
++ /* nothing to do */
++}
++
++static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info)
++{
++ SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
++
++ info->memslot_gen_bits = MEMSLOT_GENERATION_BITS;
++ info->memslot_id_bits = MEMSLOT_SLOT_BITS;
++ info->num_memslots = NUM_MEMSLOTS;
++ info->num_memslots_groups = NUM_MEMSLOTS_GROUPS;
++ info->internal_groupslot_id = 0;
++ info->qxl_ram_size = ssd->bufsize;
++ info->n_surfaces = NUM_SURFACES;
++}
++
++static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext)
++{
++ SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
++ SimpleSpiceUpdate *update;
++
++ if (debug > 2)
++ fprintf(stderr, "%s:\n", __FUNCTION__);
++ update = qemu_spice_create_update(ssd);
++ if (update == NULL) {
++ return false;
++ }
++ *ext = update->ext;
++ return true;
++}
++
++static int interface_req_cmd_notification(QXLInstance *sin)
++{
++ if (debug)
++ fprintf(stderr, "%s:\n", __FUNCTION__);
++ return 1;
++}
++
++static void interface_release_resource(QXLInstance *sin,
++ struct QXLReleaseInfoExt ext)
++{
++ SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
++ uintptr_t id;
++
++ if (debug > 1)
++ fprintf(stderr, "%s:\n", __FUNCTION__);
++ id = ext.info->id;
++ qemu_spice_destroy_update(ssd, (void*)id);
++}
++
++static int interface_get_cursor_command(QXLInstance *sin, struct QXLCommandExt *ext)
++{
++ if (debug > 2)
++ fprintf(stderr, "%s:\n", __FUNCTION__);
++ return false;
++}
++
++static int interface_req_cursor_notification(QXLInstance *sin)
++{
++ if (debug)
++ fprintf(stderr, "%s:\n", __FUNCTION__);
++ return 1;
++}
++
++static void interface_notify_update(QXLInstance *sin, uint32_t update_id)
++{
++ fprintf(stderr, "%s: abort()\n", __FUNCTION__);
++ abort();
++}
++
++static int interface_flush_resources(QXLInstance *sin)
++{
++ fprintf(stderr, "%s: abort()\n", __FUNCTION__);
++ abort();
++ return 0;
++}
++
++static const QXLInterface dpy_interface = {
++ .base.type = SPICE_INTERFACE_QXL,
++ .base.description = "qemu simple display",
++ .base.major_version = SPICE_INTERFACE_QXL_MAJOR,
++ .base.minor_version = SPICE_INTERFACE_QXL_MINOR,
++
++ .pci_vendor = REDHAT_PCI_VENDOR_ID,
++ .pci_id = QXL_DEVICE_ID,
++ .pci_revision = QXL_REVISION,
++
++ .attache_worker = interface_attach_worker,
++ .set_compression_level = interface_set_compression_level,
++ .set_mm_time = interface_set_mm_time,
++
++ .get_init_info = interface_get_init_info,
++ .get_command = interface_get_command,
++ .req_cmd_notification = interface_req_cmd_notification,
++ .release_resource = interface_release_resource,
++ .get_cursor_command = interface_get_cursor_command,
++ .req_cursor_notification = interface_req_cursor_notification,
++ .notify_update = interface_notify_update,
++ .flush_resources = interface_flush_resources,
++};
++
++static SimpleSpiceDisplay sdpy;
++
++static void display_update(struct DisplayState *ds, int x, int y, int w, int h)
++{
++ qemu_spice_display_update(&sdpy, x, y, w, h);
++}
++
++static void display_resize(struct DisplayState *ds)
++{
++ qemu_spice_display_resize(&sdpy);
++}
++
++static void display_refresh(struct DisplayState *ds)
++{
++ qemu_spice_display_refresh(&sdpy);
++}
++
++static DisplayChangeListener display_listener = {
++ .dpy_update = display_update,
++ .dpy_resize = display_resize,
++ .dpy_refresh = display_refresh,
++};
++
++void qemu_spice_display_init(DisplayState *ds)
++{
++ assert(sdpy.ds == NULL);
++ sdpy.ds = ds;
++ sdpy.bufsize = (16 * 1024 * 1024);
++ sdpy.buf = qemu_malloc(sdpy.bufsize);
++ pthread_mutex_init(&sdpy.lock, NULL);
++ register_displaychangelistener(ds, &display_listener);
++
++ sdpy.qxl.base.sif = &dpy_interface.base;
++ spice_server_add_interface(spice_server, &sdpy.qxl.base);
++ assert(sdpy.worker);
++
++ qemu_add_vm_change_state_handler(qemu_spice_vm_change_state_handler, &sdpy);
++ qemu_spice_create_host_memslot(&sdpy);
++ qemu_spice_create_host_primary(&sdpy);
++}
+diff --git a/spice-display.h b/spice-display.h
+new file mode 100644
+index 0000000..70a7be4
+--- /dev/null
++++ b/spice-display.h
+@@ -0,0 +1,52 @@
++#include <spice/ipc_ring.h>
++#include <spice/draw.h>
++#include <spice/qxl_dev.h>
++
++#include "pflib.h"
++
++#define NUM_MEMSLOTS 8
++#define MEMSLOT_GENERATION_BITS 8
++#define MEMSLOT_SLOT_BITS 8
++
++#define MEMSLOT_GROUP_HOST 0
++#define MEMSLOT_GROUP_GUEST 1
++#define NUM_MEMSLOTS_GROUPS 2
++
++#define NUM_SURFACES 1024
++
++typedef struct SimpleSpiceDisplay {
++ DisplayState *ds;
++ void *buf;
++ int bufsize;
++ QXLWorker *worker;
++ QXLInstance qxl;
++ uint32_t unique;
++ QemuPfConv *conv;
++
++ pthread_mutex_t lock;
++ QXLRect dirty;
++ int notify;
++ int running;
++} SimpleSpiceDisplay;
++
++typedef struct SimpleSpiceUpdate {
++ QXLDrawable drawable;
++ QXLImage image;
++ QXLCommandExt ext;
++ uint8_t *bitmap;
++} SimpleSpiceUpdate;
++
++int qemu_spice_rect_is_empty(const QXLRect* r);
++void qemu_spice_rect_union(QXLRect *dest, const QXLRect *r);
++
++SimpleSpiceUpdate *qemu_spice_create_update(SimpleSpiceDisplay *sdpy);
++void qemu_spice_destroy_update(SimpleSpiceDisplay *sdpy, SimpleSpiceUpdate *update);
++void qemu_spice_create_host_memslot(SimpleSpiceDisplay *ssd);
++void qemu_spice_create_host_primary(SimpleSpiceDisplay *ssd);
++void qemu_spice_destroy_host_primary(SimpleSpiceDisplay *ssd);
++void qemu_spice_vm_change_state_handler(void *opaque, int running, int reason);
++
++void qemu_spice_display_update(SimpleSpiceDisplay *ssd,
++ int x, int y, int w, int h);
++void qemu_spice_display_resize(SimpleSpiceDisplay *ssd);
++void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd);
+diff --git a/vl.c b/vl.c
+index 97897e0..2ccebc8 100644
+--- a/vl.c
++++ b/vl.c
+@@ -2993,7 +2993,7 @@ int main(int argc, char **argv, char **envp)
+ /* just use the first displaystate for the moment */
+ ds = get_displaystate();
+
+- if (display_type == DT_DEFAULT) {
++ if (display_type == DT_DEFAULT && !using_spice) {
+ #if defined(CONFIG_SDL) || defined(CONFIG_COCOA)
+ display_type = DT_SDL;
+ #else
+@@ -3033,6 +3033,11 @@ int main(int argc, char **argv, char **envp)
+ default:
+ break;
+ }
++#ifdef CONFIG_SPICE
++ if (using_spice) {
++ qemu_spice_display_init(ds);
++ }
++#endif
+ dpy_resize(ds);
+
+ dcl = ds->listeners;
+--
+1.7.2.3
+
diff --git a/0008-spice-add-tablet-support.patch b/0008-spice-add-tablet-support.patch
new file mode 100644
index 0000000..6f37c81
--- /dev/null
+++ b/0008-spice-add-tablet-support.patch
@@ -0,0 +1,160 @@
+From e3c6e18e27f0d598b37e9be1795dbcb42f740071 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel at redhat.com>
+Date: Tue, 13 Apr 2010 09:05:03 +0200
+Subject: [PATCH 08/39] spice: add tablet support
+
+Add support for the spice tablet interface. The tablet interface will
+be registered (and then used by the spice client) as soon as a absolute
+pointing device is available and used by the guest, i.e. you'll have to
+configure your guest with '-usbdevice tablet'.
+---
+ spice-display.c | 2 +-
+ spice-input.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++----
+ 2 files changed, 93 insertions(+), 8 deletions(-)
+
+diff --git a/spice-display.c b/spice-display.c
+index 13a620e..a749e64 100644
+--- a/spice-display.c
++++ b/spice-display.c
+@@ -143,7 +143,7 @@ void qemu_spice_create_host_primary(SimpleSpiceDisplay *ssd)
+ surface.width = ds_get_width(ssd->ds);
+ surface.height = ds_get_height(ssd->ds);
+ surface.stride = -surface.width * 4;
+- surface.mouse_mode = 0;
++ surface.mouse_mode = true;
+ surface.flags = 0;
+ surface.type = 0;
+ surface.mem = (intptr_t)ssd->buf;
+diff --git a/spice-input.c b/spice-input.c
+index 8f3deb4..5646ff9 100644
+--- a/spice-input.c
++++ b/spice-input.c
+@@ -1,5 +1,6 @@
+ #include <stdlib.h>
+ #include <stdio.h>
++#include <stdbool.h>
+ #include <string.h>
+
+ #include <spice.h>
+@@ -48,9 +49,13 @@ static void kbd_leds(void *opaque, int ledstate)
+
+ /* mouse bits */
+
+-typedef struct QemuSpiceMouse {
+- SpiceMouseInstance sin;
+-} QemuSpiceMouse;
++typedef struct QemuSpicePointer {
++ SpiceMouseInstance mouse;
++ SpiceTabletInstance tablet;
++ int width, height, x, y;
++ Notifier mouse_mode;
++ bool absolute;
++} QemuSpicePointer;
+
+ static void mouse_motion(SpiceMouseInstance *sin, int dx, int dy, int dz,
+ uint32_t buttons_state)
+@@ -72,17 +77,97 @@ static const SpiceMouseInterface mouse_interface = {
+ .buttons = mouse_buttons,
+ };
+
++static void tablet_set_logical_size(SpiceTabletInstance* sin, int width, int height)
++{
++ QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet);
++
++ fprintf(stderr, "%s: %dx%d\n", __FUNCTION__, width, height);
++ if (height < 16)
++ height = 16;
++ if (width < 16)
++ width = 16;
++ pointer->width = width;
++ pointer->height = height;
++}
++
++static void tablet_position(SpiceTabletInstance* sin, int x, int y,
++ uint32_t buttons_state)
++{
++ QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet);
++
++ pointer->x = x * 0x7FFF / (pointer->width - 1);
++ pointer->y = y * 0x7FFF / (pointer->height - 1);
++ kbd_mouse_event(pointer->x, pointer->y, 0, buttons_state);
++}
++
++
++static void tablet_wheel(SpiceTabletInstance* sin, int wheel,
++ uint32_t buttons_state)
++{
++ QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet);
++
++ kbd_mouse_event(pointer->x, pointer->y, wheel, buttons_state);
++}
++
++static void tablet_buttons(SpiceTabletInstance *sin,
++ uint32_t buttons_state)
++{
++ QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet);
++
++ kbd_mouse_event(pointer->x, pointer->y, 0, buttons_state);
++}
++
++static const SpiceTabletInterface tablet_interface = {
++ .base.type = SPICE_INTERFACE_TABLET,
++ .base.description = "tablet",
++ .base.major_version = SPICE_INTERFACE_TABLET_MAJOR,
++ .base.minor_version = SPICE_INTERFACE_TABLET_MINOR,
++ .set_logical_size = tablet_set_logical_size,
++ .position = tablet_position,
++ .wheel = tablet_wheel,
++ .buttons = tablet_buttons,
++};
++
++static void mouse_mode_notifier(Notifier *notifier)
++{
++ QemuSpicePointer *pointer = container_of(notifier, QemuSpicePointer, mouse_mode);
++ bool is_absolute = kbd_mouse_is_absolute();
++ bool has_absolute = kbd_mouse_has_absolute();
++
++ fprintf(stderr, "%s: absolute pointer: %s%s\n", __FUNCTION__,
++ has_absolute ? "present" : "not available",
++ is_absolute ? "+active" : "");
++
++ if (pointer->absolute == is_absolute)
++ return;
++
++ if (is_absolute) {
++ fprintf(stderr, "%s: using absolute pointer (client mode)\n", __FUNCTION__);
++ spice_server_add_interface(spice_server, &pointer->tablet.base);
++ } else {
++ fprintf(stderr, "%s: using relative pointer (server mode)\n", __FUNCTION__);
++ spice_server_remove_interface(&pointer->tablet.base);
++ }
++ pointer->absolute = is_absolute;
++}
++
+ void qemu_spice_input_init(void)
+ {
+ QemuSpiceKbd *kbd;
+- QemuSpiceMouse *mouse;
++ QemuSpicePointer *pointer;
+
+ kbd = qemu_mallocz(sizeof(*kbd));
+ kbd->sin.base.sif = &kbd_interface.base;
+ spice_server_add_interface(spice_server, &kbd->sin.base);
+ qemu_add_led_event_handler(kbd_leds, kbd);
+
+- mouse = qemu_mallocz(sizeof(*mouse));
+- mouse->sin.base.sif = &mouse_interface.base;
+- spice_server_add_interface(spice_server, &mouse->sin.base);
++ pointer = qemu_mallocz(sizeof(*pointer));
++ pointer->mouse.base.sif = &mouse_interface.base;
++ pointer->tablet.base.sif = &tablet_interface.base;
++ spice_server_add_interface(spice_server, &pointer->mouse.base);
++
++ pointer->absolute = false;
++ pointer->mouse_mode.notify = mouse_mode_notifier;
++ qemu_add_mouse_mode_change_notifier(&pointer->mouse_mode);
++ mouse_mode_notifier(&pointer->mouse_mode);
+ }
+--
+1.7.2.3
+
diff --git a/0009-vgabios-update-to-0.6c-pcibios-patches.patch b/0009-vgabios-update-to-0.6c-pcibios-patches.patch
new file mode 100644
index 0000000..29ff6f4
--- /dev/null
+++ b/0009-vgabios-update-to-0.6c-pcibios-patches.patch
@@ -0,0 +1,28 @@
+From 0920337756cdf82dcd585efb23ae18f8086696c8 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel at redhat.com>
+Date: Wed, 24 Mar 2010 11:16:54 +0100
+Subject: [PATCH 09/39] vgabios update to 0.6c + pcibios patches.
+
+---
+ Makefile | 5 +++--
+ 1 files changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/Makefile b/Makefile
+index 3cd07e0..e40c9a2 100644
+--- a/Makefile
++++ b/Makefile
+@@ -154,8 +154,9 @@ ar de en-us fi fr-be hr it lv nl pl ru th \
+ common de-ch es fo fr-ca hu ja mk nl-be pt sl tr
+
+ ifdef INSTALL_BLOBS
+-BLOBS=bios.bin vgabios.bin vgabios-cirrus.bin ppc_rom.bin \
+-video.x openbios-sparc32 openbios-sparc64 openbios-ppc \
++BLOBS=bios.bin vgabios.bin vgabios-cirrus.bin \
++vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin vgabios-qxldev.bin \
++ppc_rom.bin video.x openbios-sparc32 openbios-sparc64 openbios-ppc \
+ gpxe-eepro100-80861209.rom \
+ gpxe-eepro100-80861229.rom \
+ pxe-e1000.bin \
+--
+1.7.2.3
+
diff --git a/0010-switch-stdvga-to-pci-vgabios.patch b/0010-switch-stdvga-to-pci-vgabios.patch
new file mode 100644
index 0000000..ccb71ac
--- /dev/null
+++ b/0010-switch-stdvga-to-pci-vgabios.patch
@@ -0,0 +1,31 @@
+From 6ac04dff1ee3932d2ef94c1e42f4e8208fbf92bf Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel at redhat.com>
+Date: Thu, 6 May 2010 11:13:11 +0200
+Subject: [PATCH 10/39] switch stdvga to pci vgabios
+
+---
+ hw/vga-pci.c | 7 +++----
+ 1 files changed, 3 insertions(+), 4 deletions(-)
+
+diff --git a/hw/vga-pci.c b/hw/vga-pci.c
+index 3907871..8e1ed35 100644
+--- a/hw/vga-pci.c
++++ b/hw/vga-pci.c
+@@ -105,11 +105,10 @@ static int pci_vga_initfn(PCIDevice *dev)
+ bios_total_size <<= 1;
+ pci_register_bar(&d->dev, PCI_ROM_SLOT, bios_total_size,
+ PCI_BASE_ADDRESS_MEM_PREFETCH, vga_map);
++ } else {
++ if (dev->romfile == NULL)
++ dev->romfile = qemu_strdup("vgabios-stdvga.bin");
+ }
+-
+- vga_init_vbe(s);
+- /* ROM BIOS */
+- rom_add_vga(VGABIOS_FILENAME);
+ return 0;
+ }
+
+--
+1.7.2.3
+
diff --git a/0011-switch-vmware_vga-to-pci-vgabios.patch b/0011-switch-vmware_vga-to-pci-vgabios.patch
new file mode 100644
index 0000000..30bfa9c
--- /dev/null
+++ b/0011-switch-vmware_vga-to-pci-vgabios.patch
@@ -0,0 +1,50 @@
+From 07cdc867f1e12a4e8b4096e7f1f3ffda2f4e7d02 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel at redhat.com>
+Date: Thu, 6 May 2010 11:14:11 +0200
+Subject: [PATCH 11/39] switch vmware_vga to pci vgabios
+
+---
+ hw/vmware_vga.c | 7 +------
+ 1 files changed, 1 insertions(+), 6 deletions(-)
+
+diff --git a/hw/vmware_vga.c b/hw/vmware_vga.c
+index 12bff48..682f287 100644
+--- a/hw/vmware_vga.c
++++ b/hw/vmware_vga.c
+@@ -114,14 +114,12 @@ struct pci_vmsvga_state_s {
+ # define SVGA_IO_BASE SVGA_LEGACY_BASE_PORT
+ # define SVGA_IO_MUL 1
+ # define SVGA_FIFO_SIZE 0x10000
+-# define SVGA_MEM_BASE 0xe0000000
+ # define SVGA_PCI_DEVICE_ID PCI_DEVICE_ID_VMWARE_SVGA2
+ #else
+ # define SVGA_ID SVGA_ID_1
+ # define SVGA_IO_BASE SVGA_LEGACY_BASE_PORT
+ # define SVGA_IO_MUL 4
+ # define SVGA_FIFO_SIZE 0x10000
+-# define SVGA_MEM_BASE 0xe0000000
+ # define SVGA_PCI_DEVICE_ID PCI_DEVICE_ID_VMWARE_SVGA
+ #endif
+
+@@ -1171,10 +1169,6 @@ static void vmsvga_init(struct vmsvga_state_s *s, int vga_ram_size)
+ vga_init(&s->vga);
+ vmstate_register(NULL, 0, &vmstate_vga_common, &s->vga);
+
+- vga_init_vbe(&s->vga);
+-
+- rom_add_vga(VGABIOS_FILENAME);
+-
+ vmsvga_reset(s);
+ }
+
+@@ -1272,6 +1266,7 @@ static PCIDeviceInfo vmsvga_info = {
+ .qdev.size = sizeof(struct pci_vmsvga_state_s),
+ .qdev.vmsd = &vmstate_vmware_vga,
+ .init = pci_vmsvga_initfn,
++ .romfile = "vgabios-vmware.bin",
+ };
+
+ static void vmsvga_register(void)
+--
+1.7.2.3
+
diff --git a/0012-all-vga-refuse-hotplugging.patch b/0012-all-vga-refuse-hotplugging.patch
new file mode 100644
index 0000000..393b1b2
--- /dev/null
+++ b/0012-all-vga-refuse-hotplugging.patch
@@ -0,0 +1,61 @@
+From a659f6b472d95503657ac68a52242ce769006f17 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel at redhat.com>
+Date: Tue, 11 May 2010 22:28:44 +0200
+Subject: [PATCH 12/39] all vga: refuse hotplugging.
+
+Try to pci hotplug a vga card, watch qemu die with hw_error().
+This patch fixes it.
+---
+ hw/cirrus_vga.c | 4 ++++
+ hw/vga-pci.c | 4 ++++
+ hw/vmware_vga.c | 4 ++++
+ 3 files changed, 12 insertions(+), 0 deletions(-)
+
+diff --git a/hw/cirrus_vga.c b/hw/cirrus_vga.c
+index efa7a42..dadaa80 100644
+--- a/hw/cirrus_vga.c
++++ b/hw/cirrus_vga.c
+@@ -3206,6 +3206,10 @@ static int pci_cirrus_vga_initfn(PCIDevice *dev)
+ uint8_t *pci_conf = d->dev.config;
+ int device_id = CIRRUS_ID_CLGD5446;
+
++ if (dev->qdev.hotplugged) {
++ return -1;
++ }
++
+ /* setup VGA */
+ vga_common_init(&s->vga, VGA_RAM_SIZE);
+ cirrus_init_common(s, device_id, 1);
+diff --git a/hw/vga-pci.c b/hw/vga-pci.c
+index 8e1ed35..4e673a5 100644
+--- a/hw/vga-pci.c
++++ b/hw/vga-pci.c
+@@ -81,6 +81,10 @@ static int pci_vga_initfn(PCIDevice *dev)
+ VGACommonState *s = &d->vga;
+ uint8_t *pci_conf = d->dev.config;
+
++ if (dev->qdev.hotplugged) {
++ return -1;
++ }
++
+ // vga + console init
+ vga_common_init(s, VGA_RAM_SIZE);
+ vga_init(s);
+diff --git a/hw/vmware_vga.c b/hw/vmware_vga.c
+index 682f287..7ff89aa 100644
+--- a/hw/vmware_vga.c
++++ b/hw/vmware_vga.c
+@@ -1232,6 +1232,10 @@ static int pci_vmsvga_initfn(PCIDevice *dev)
+ struct pci_vmsvga_state_s *s =
+ DO_UPCAST(struct pci_vmsvga_state_s, card, dev);
+
++ if (dev->qdev.hotplugged) {
++ return -1;
++ }
++
+ pci_config_set_vendor_id(s->card.config, PCI_VENDOR_ID_VMWARE);
+ pci_config_set_device_id(s->card.config, SVGA_PCI_DEVICE_ID);
+ pci_config_set_class(s->card.config, PCI_CLASS_DISPLAY_VGA);
+--
+1.7.2.3
+
diff --git a/0013-spice-tls-support.patch b/0013-spice-tls-support.patch
new file mode 100644
index 0000000..da247af
--- /dev/null
+++ b/0013-spice-tls-support.patch
@@ -0,0 +1,168 @@
+From e0d06d42a83e7796b2c39ad6cab3630c0a8c2845 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel at redhat.com>
+Date: Thu, 11 Mar 2010 11:13:32 -0300
+Subject: [PATCH 13/39] spice: tls support
+
+Add options to the -spice command line switch to setup tls:
+
+tls-port
+ listening port
+
+x509-dir
+ x509 file directory. Expects same filenames as
+ -vnc $display,x509=$dir
+
+x509-key-file
+x509-key-password
+x509-cert-file
+x509-cacert-file
+x509-dh-key-file
+ x509 files can also be set individually.
+
+tls-ciphers
+ which ciphers to use.
+---
+ qemu-config.c | 24 ++++++++++++++++++++
+ spice.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++---
+ 2 files changed, 86 insertions(+), 4 deletions(-)
+
+diff --git a/qemu-config.c b/qemu-config.c
+index 8a894cf..74bfc62 100644
+--- a/qemu-config.c
++++ b/qemu-config.c
+@@ -355,11 +355,35 @@ QemuOptsList qemu_spice_opts = {
+ .name = "port",
+ .type = QEMU_OPT_NUMBER,
+ },{
++ .name = "tls-port",
++ .type = QEMU_OPT_NUMBER,
++ },{
+ .name = "password",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "disable-ticketing",
+ .type = QEMU_OPT_BOOL,
++ },{
++ .name = "x509-dir",
++ .type = QEMU_OPT_STRING,
++ },{
++ .name = "x509-key-file",
++ .type = QEMU_OPT_STRING,
++ },{
++ .name = "x509-key-password",
++ .type = QEMU_OPT_STRING,
++ },{
++ .name = "x509-cert-file",
++ .type = QEMU_OPT_STRING,
++ },{
++ .name = "x509-cacert-file",
++ .type = QEMU_OPT_STRING,
++ },{
++ .name = "x509-dh-key-file",
++ .type = QEMU_OPT_STRING,
++ },{
++ .name = "tls-ciphers",
++ .type = QEMU_OPT_STRING,
+ },
+ { /* end if list */ }
+ },
+diff --git a/spice.c b/spice.c
+index c763d52..3fe76cd 100644
+--- a/spice.c
++++ b/spice.c
+@@ -9,6 +9,7 @@
+ #include "qemu-spice.h"
+ #include "qemu-timer.h"
+ #include "qemu-queue.h"
++#include "qemu-x509.h"
+ #include "monitor.h"
+
+ /* core bits */
+@@ -126,18 +127,71 @@ static SpiceCoreInterface core_interface = {
+ void qemu_spice_init(void)
+ {
+ QemuOpts *opts = QTAILQ_FIRST(&qemu_spice_opts.head);
+- const char *password;
+- int port;
++ const char *password, *str, *x509_dir,
++ *x509_key_password = NULL,
++ *x509_dh_file = NULL,
++ *tls_ciphers = NULL;
++ char *x509_key_file = NULL,
++ *x509_cert_file = NULL,
++ *x509_cacert_file = NULL;
++ int port, tls_port, len;
+
+ if (!opts)
+ return;
+ port = qemu_opt_get_number(opts, "port", 0);
+- if (!port)
++ tls_port = qemu_opt_get_number(opts, "tls-port", 0);
++ if (!port && !tls_port)
+ return;
+ password = qemu_opt_get(opts, "password");
+
++ if (tls_port) {
++ x509_dir = qemu_opt_get(opts, "x509-dir");
++ if (NULL == x509_dir)
++ x509_dir = ".";
++ len = strlen(x509_dir) + 32;
++
++ str = qemu_opt_get(opts, "x509-key-file");
++ if (str) {
++ x509_key_file = qemu_strdup(str);
++ } else {
++ x509_key_file = qemu_malloc(len);
++ snprintf(x509_key_file, len, "%s/%s", x509_dir, X509_SERVER_KEY_FILE);
++ }
++
++ str = qemu_opt_get(opts, "x509-cert-file");
++ if (str) {
++ x509_cert_file = qemu_strdup(str);
++ } else {
++ x509_cert_file = qemu_malloc(len);
++ snprintf(x509_cert_file, len, "%s/%s", x509_dir, X509_SERVER_CERT_FILE);
++ }
++
++ str = qemu_opt_get(opts, "x509-cacert-file");
++ if (str) {
++ x509_cacert_file = qemu_strdup(str);
++ } else {
++ x509_cacert_file = qemu_malloc(len);
++ snprintf(x509_cacert_file, len, "%s/%s", x509_dir, X509_CA_CERT_FILE);
++ }
++
++ x509_key_password = qemu_opt_get(opts, "x509-key-password");
++ x509_dh_file = qemu_opt_get(opts, "x509-dh-file");
++ tls_ciphers = qemu_opt_get(opts, "tls-ciphers");
++ }
++
+ spice_server = spice_server_new();
+- spice_server_set_port(spice_server, port);
++ if (port) {
++ spice_server_set_port(spice_server, port);
++ }
++ if (tls_port) {
++ spice_server_set_tls(spice_server, tls_port,
++ x509_cacert_file,
++ x509_cert_file,
++ x509_key_file,
++ x509_key_password,
++ x509_dh_file,
++ tls_ciphers);
++ }
+ if (password)
+ spice_server_set_ticket(spice_server, password, 0, 0, 0);
+ if (qemu_opt_get_bool(opts, "disable-ticketing", 0))
+@@ -150,4 +204,8 @@ void qemu_spice_init(void)
+ using_spice = 1;
+
+ qemu_spice_input_init();
++
++ qemu_free(x509_key_file);
++ qemu_free(x509_cert_file);
++ qemu_free(x509_cacert_file);
+ }
+--
+1.7.2.3
+
diff --git a/0014-spice-add-qxl-device.patch b/0014-spice-add-qxl-device.patch
new file mode 100644
index 0000000..4ea90ba
--- /dev/null
+++ b/0014-spice-add-qxl-device.patch
@@ -0,0 +1,2085 @@
+From 4cd79eae3a476fda86b67a8075735a03c1254a08 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel at redhat.com>
+Date: Tue, 27 Apr 2010 11:50:11 +0200
+Subject: [PATCH 14/39] spice: add qxl device
+
+qxl is a paravirtual graphics card. The qxl device is the bridge
+between the guest and the spice server (aka libspice-server). The
+spice server will send the rendering commands to the spice client, which
+will actually render them.
+
+The spice server is also able to render locally, which is done in case
+the guest wants read something from video memory. Local rendering is
+also used to support display over vnc and sdl.
+
+qxl is activated using "-vga qxl". qxl supports multihead, additional
+cards can be added via '-device qxl".
+---
+ Makefile.target | 1 +
+ hw/hw.h | 14 +
+ hw/pc.c | 8 +
+ hw/qxl-logger.c | 179 +++++++
+ hw/qxl-render.c | 207 ++++++++
+ hw/qxl.c | 1411 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ hw/qxl.h | 102 ++++
+ hw/vga_int.h | 2 +-
+ sysemu.h | 3 +-
+ vl.c | 4 +-
+ 10 files changed, 1928 insertions(+), 3 deletions(-)
+ create mode 100644 hw/qxl-logger.c
+ create mode 100644 hw/qxl-render.c
+ create mode 100644 hw/qxl.c
+ create mode 100644 hw/qxl.h
+
+diff --git a/Makefile.target b/Makefile.target
+index 9e13d99..4da33b5 100644
+--- a/Makefile.target
++++ b/Makefile.target
+@@ -216,6 +216,7 @@ obj-i386-y += debugcon.o multiboot.o
+ obj-i386-y += pc_piix.o
+ obj-i386-y += testdev.o
+ obj-i386-y += acpi.o acpi_piix4.o
++obj-i386-$(CONFIG_SPICE) += qxl.o qxl-logger.o qxl-render.o
+
+ obj-i386-y += pcspk.o i8254.o
+ obj-i386-$(CONFIG_KVM_PIT) += i8254-kvm.o
+diff --git a/hw/hw.h b/hw/hw.h
+index ec6985d..044ebfb 100644
+--- a/hw/hw.h
++++ b/hw/hw.h
+@@ -528,6 +528,17 @@ extern const VMStateInfo vmstate_info_unused_buffer;
+ .start = (_start), \
+ }
+
++#define VMSTATE_VBUFFER_UINT32(_field, _state, _version, _test, _start, _field_size) { \
++ .name = (stringify(_field)), \
++ .version_id = (_version), \
++ .field_exists = (_test), \
++ .size_offset = vmstate_offset_value(_state, _field_size, uint32_t),\
++ .info = &vmstate_info_buffer, \
++ .flags = VMS_VBUFFER|VMS_POINTER, \
++ .offset = offsetof(_state, _field), \
++ .start = (_start), \
++}
++
+ #define VMSTATE_BUFFER_UNSAFE_INFO(_field, _state, _version, _info, _size) { \
+ .name = (stringify(_field)), \
+ .version_id = (_version), \
+@@ -742,6 +753,9 @@ extern const VMStateDescription vmstate_i2c_slave;
+ #define VMSTATE_PARTIAL_VBUFFER(_f, _s, _size) \
+ VMSTATE_VBUFFER(_f, _s, 0, NULL, 0, _size)
+
++#define VMSTATE_PARTIAL_VBUFFER_UINT32(_f, _s, _size) \
++ VMSTATE_VBUFFER_UINT32(_f, _s, 0, NULL, 0, _size)
++
+ #define VMSTATE_SUB_VBUFFER(_f, _s, _start, _size) \
+ VMSTATE_VBUFFER(_f, _s, 0, NULL, _start, _size)
+
+diff --git a/hw/pc.c b/hw/pc.c
+index 77b1592..1f2df2f 100644
+--- a/hw/pc.c
++++ b/hw/pc.c
+@@ -41,6 +41,7 @@
+ #include "sysemu.h"
+ #include "device-assignment.h"
+ #include "kvm.h"
++#include "qemu-spice.h"
+
+ /* output Bochs bios info messages */
+ //#define DEBUG_BIOS
+@@ -1002,6 +1003,13 @@ void pc_vga_init(PCIBus *pci_bus)
+ pci_vmsvga_init(pci_bus);
+ else
+ fprintf(stderr, "%s: vmware_vga: no PCI bus\n", __FUNCTION__);
++#ifdef CONFIG_SPICE
++ } else if (qxl_enabled) {
++ if (pci_bus)
++ pci_create_simple(pci_bus, -1, "qxl");
++ else
++ fprintf(stderr, "%s: qxl: no PCI bus\n", __FUNCTION__);
++#endif
+ } else if (std_vga_enabled) {
+ if (pci_bus) {
+ pci_vga_init(pci_bus, 0, 0);
+diff --git a/hw/qxl-logger.c b/hw/qxl-logger.c
+new file mode 100644
+index 0000000..d4a935a
+--- /dev/null
++++ b/hw/qxl-logger.c
+@@ -0,0 +1,179 @@
++/*
++ * qxl command logging -- for debug purposes
++ */
++
++#include <stdio.h>
++#include <stdbool.h>
++#include <stdint.h>
++#include <string.h>
++
++#include "qxl.h"
++
++static const char *qxl_type[] = {
++ [ QXL_CMD_NOP ] = "nop",
++ [ QXL_CMD_DRAW ] = "draw",
++ [ QXL_CMD_UPDATE ] = "update",
++ [ QXL_CMD_CURSOR ] = "cursor",
++ [ QXL_CMD_MESSAGE ] = "message",
++ [ QXL_CMD_SURFACE ] = "surface",
++};
++
++static const char *qxl_draw_type[] = {
++ [ QXL_DRAW_NOP ] = "nop",
++ [ QXL_DRAW_FILL ] = "fill",
++ [ QXL_DRAW_OPAQUE ] = "opaque",
++ [ QXL_DRAW_COPY ] = "copy",
++ [ QXL_COPY_BITS ] = "copy-bits",
++ [ QXL_DRAW_BLEND ] = "blend",
++ [ QXL_DRAW_BLACKNESS ] = "blackness",
++ [ QXL_DRAW_WHITENESS ] = "whitemess",
++ [ QXL_DRAW_INVERS ] = "invers",
++ [ QXL_DRAW_ROP3 ] = "rop3",
++ [ QXL_DRAW_STROKE ] = "stroke",
++ [ QXL_DRAW_TEXT ] = "text",
++ [ QXL_DRAW_TRANSPARENT ] = "transparent",
++ [ QXL_DRAW_ALPHA_BLEND ] = "alpha-blend",
++};
++
++static const char *qxl_draw_effect[] = {
++ [ QXL_EFFECT_BLEND ] = "blend",
++ [ QXL_EFFECT_OPAQUE ] = "opaque",
++ [ QXL_EFFECT_REVERT_ON_DUP ] = "revert-on-dup",
++ [ QXL_EFFECT_BLACKNESS_ON_DUP ] = "blackness-on-dup",
++ [ QXL_EFFECT_WHITENESS_ON_DUP ] = "whiteness-on-dup",
++ [ QXL_EFFECT_NOP_ON_DUP ] = "nop-on-dup",
++ [ QXL_EFFECT_NOP ] = "nop",
++ [ QXL_EFFECT_OPAQUE_BRUSH ] = "opaque-brush",
++};
++
++static const char *qxl_surface_cmd[] = {
++ [ QXL_SURFACE_CMD_CREATE ] = "create",
++ [ QXL_SURFACE_CMD_DESTROY ] = "destroy",
++};
++
++static const char *spice_surface_fmt[] = {
++ [ SPICE_SURFACE_FMT_INVALID ] = "invalid",
++ [ SPICE_SURFACE_FMT_1_A ] = "alpha/1",
++ [ SPICE_SURFACE_FMT_8_A ] = "alpha/8",
++ [ SPICE_SURFACE_FMT_16_555 ] = "555/16",
++ [ SPICE_SURFACE_FMT_16_565 ] = "565/16",
++ [ SPICE_SURFACE_FMT_32_xRGB ] = "xRGB/32",
++ [ SPICE_SURFACE_FMT_32_ARGB ] = "ARGB/32",
++};
++
++static const char *qxl_cursor_cmd[] = {
++ [ QXL_CURSOR_SET ] = "set",
++ [ QXL_CURSOR_MOVE ] = "move",
++ [ QXL_CURSOR_HIDE ] = "hide",
++ [ QXL_CURSOR_TRAIL ] = "trail",
++};
++
++static const char *spice_cursor_type[] = {
++ [ SPICE_CURSOR_TYPE_ALPHA ] = "alpha",
++ [ SPICE_CURSOR_TYPE_MONO ] = "mono",
++ [ SPICE_CURSOR_TYPE_COLOR4 ] = "color4",
++ [ SPICE_CURSOR_TYPE_COLOR8 ] = "color8",
++ [ SPICE_CURSOR_TYPE_COLOR16 ] = "color16",
++ [ SPICE_CURSOR_TYPE_COLOR24 ] = "color24",
++ [ SPICE_CURSOR_TYPE_COLOR32 ] = "color32",
++};
++
++static const char *qxl_v2n(const char *n[], size_t l, int v)
++{
++ if (v >= l || !n[v])
++ return "???";
++ return n[v];
++}
++#define qxl_name(_list, _value) qxl_v2n(_list, ARRAY_SIZE(_list), _value)
++
++static void qxl_log_cmd_draw(PCIQXLDevice *qxl, QXLDrawable *draw)
++{
++ fprintf(stderr, ": surface_id %d type %s effect %s",
++ draw->surface_id,
++ qxl_name(qxl_draw_type, draw->type),
++ qxl_name(qxl_draw_effect, draw->effect));
++}
++
++static void qxl_log_cmd_draw_compat(PCIQXLDevice *qxl, QXLCompatDrawable *draw)
++{
++ fprintf(stderr, ": type %s effect %s",
++ qxl_name(qxl_draw_type, draw->type),
++ qxl_name(qxl_draw_effect, draw->effect));
++}
++
++static void qxl_log_cmd_surface(PCIQXLDevice *qxl, QXLSurfaceCmd *cmd)
++{
++ fprintf(stderr, ": %s id %d",
++ qxl_name(qxl_surface_cmd, cmd->type),
++ cmd->surface_id);
++ if (cmd->type == QXL_SURFACE_CMD_CREATE) {
++ fprintf(stderr, " size %dx%d stride %d format %s (count %d, max %d)",
++ cmd->u.surface_create.width,
++ cmd->u.surface_create.height,
++ cmd->u.surface_create.stride,
++ qxl_name(spice_surface_fmt, cmd->u.surface_create.format),
++ qxl->guest_surfaces.count, qxl->guest_surfaces.max);
++ }
++ if (cmd->type == QXL_SURFACE_CMD_DESTROY) {
++ fprintf(stderr, " (count %d)", qxl->guest_surfaces.count);
++ }
++}
++
++void qxl_log_cmd_cursor(PCIQXLDevice *qxl, QXLCursorCmd *cmd, int group_id)
++{
++ QXLCursor *cursor;
++
++ fprintf(stderr, ": %s",
++ qxl_name(qxl_cursor_cmd, cmd->type));
++ switch (cmd->type) {
++ case QXL_CURSOR_SET:
++ fprintf(stderr, " +%d+%d visible %s, shape @ 0x%" PRIx64,
++ cmd->u.set.position.x,
++ cmd->u.set.position.y,
++ cmd->u.set.visible ? "yes" : "no",
++ cmd->u.set.shape);
++ cursor = qxl_phys2virt(qxl, cmd->u.set.shape, group_id);
++ fprintf(stderr, " type %s size %dx%d hot-spot +%d+%d"
++ " unique 0x%" PRIx64 " data-size %d",
++ qxl_name(spice_cursor_type, cursor->header.type),
++ cursor->header.width, cursor->header.height,
++ cursor->header.hot_spot_x, cursor->header.hot_spot_y,
++ cursor->header.unique, cursor->data_size);
++ break;
++ case QXL_CURSOR_MOVE:
++ fprintf(stderr, " +%d+%d", cmd->u.position.x, cmd->u.position.y);
++ break;
++ }
++}
++
++void qxl_log_command(PCIQXLDevice *qxl, const char *ring, QXLCommandExt *ext)
++{
++ bool compat = ext->flags & QXL_COMMAND_FLAG_COMPAT;
++ void *data;
++
++ if (!qxl->cmdlog) {
++ return;
++ }
++ fprintf(stderr, "qxl-%d/%s:", qxl->id, ring);
++ fprintf(stderr, " cmd @ 0x%" PRIx64 " %s%s", ext->cmd.data,
++ qxl_name(qxl_type, ext->cmd.type),
++ compat ? "(compat)" : "");
++
++ data = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id);
++ switch (ext->cmd.type) {
++ case QXL_CMD_DRAW:
++ if (!compat) {
++ qxl_log_cmd_draw(qxl, data);
++ } else {
++ qxl_log_cmd_draw_compat(qxl, data);
++ }
++ break;
++ case QXL_CMD_SURFACE:
++ qxl_log_cmd_surface(qxl, data);
++ break;
++ case QXL_CMD_CURSOR:
++ qxl_log_cmd_cursor(qxl, data, ext->group_id);
++ break;
++ }
++ fprintf(stderr, "\n");
++}
+diff --git a/hw/qxl-render.c b/hw/qxl-render.c
+new file mode 100644
+index 0000000..d9ebca4
+--- /dev/null
++++ b/hw/qxl-render.c
+@@ -0,0 +1,207 @@
++/*
++ * qxl local rendering (aka display on sdl/vnc)
++ */
++#include <stdio.h>
++#include <stdbool.h>
++#include <stdint.h>
++#include <string.h>
++
++#include "qxl.h"
++
++static void qxl_flip(PCIQXLDevice *qxl, QXLRect *rect)
++{
++ uint8_t *src = qxl->guest_primary.data;
++ uint8_t *dst = qxl->guest_primary.flipped;
++ int len, i;
++
++ src += (qxl->guest_primary.surface.height - rect->top - 1) *
++ qxl->guest_primary.stride;
++ dst += rect->top * qxl->guest_primary.stride;
++ src += rect->left * qxl->guest_primary.bytes_pp;
++ dst += rect->left * qxl->guest_primary.bytes_pp;
++ len = (rect->right - rect->left) * qxl->guest_primary.bytes_pp;
++
++ for (i = rect->top; i < rect->bottom; i++) {
++ memcpy(dst, src, len);
++ dst += qxl->guest_primary.stride;
++ src -= qxl->guest_primary.stride;
++ }
++}
++
++void qxl_render_resize(PCIQXLDevice *qxl)
++{
++ QXLSurfaceCreate *sc = &qxl->guest_primary.surface;
++
++ qxl->guest_primary.stride = sc->stride;
++ qxl->guest_primary.resized++;
++ switch (sc->format) {
++ case SPICE_SURFACE_FMT_16_555:
++ qxl->guest_primary.bytes_pp = 2;
++ qxl->guest_primary.bits_pp = 15;
++ break;
++ case SPICE_SURFACE_FMT_16_565:
++ qxl->guest_primary.bytes_pp = 2;
++ qxl->guest_primary.bits_pp = 16;
++ break;
++ case SPICE_SURFACE_FMT_32_xRGB:
++ case SPICE_SURFACE_FMT_32_ARGB:
++ qxl->guest_primary.bytes_pp = 4;
++ qxl->guest_primary.bits_pp = 32;
++ break;
++ default:
++ fprintf(stderr, "%s: unhandled format: %x\n", __FUNCTION__,
++ qxl->guest_primary.surface.format);
++ qxl->guest_primary.bytes_pp = 4;
++ qxl->guest_primary.bits_pp = 32;
++ break;
++ }
++}
++
++void qxl_render_update(PCIQXLDevice *qxl)
++{
++ VGACommonState *vga = &qxl->vga;
++ QXLRect dirty[32], update;
++ void *ptr;
++ int i;
++
++ if (qxl->guest_primary.resized) {
++ qxl->guest_primary.resized = 0;
++
++ if (qxl->guest_primary.flipped) {
++ qemu_free(qxl->guest_primary.flipped);
++ qxl->guest_primary.flipped = NULL;
++ }
++ qemu_free_displaysurface(vga->ds);
++
++ qxl->guest_primary.data = qemu_get_ram_ptr(qxl->vga.vram_offset);
++ if (qxl->guest_primary.stride < 0) {
++ /* spice surface is upside down -> need extra buffer to flip */
++ qxl->guest_primary.stride = -qxl->guest_primary.stride;
++ qxl->guest_primary.flipped = qemu_malloc(qxl->guest_primary.surface.width *
++ qxl->guest_primary.stride);
++ ptr = qxl->guest_primary.flipped;
++ } else {
++ ptr = qxl->guest_primary.data;
++ }
++ fprintf(stderr, "%s: %dx%d, stride %d, bpp %d, depth %d, flip %s\n",
++ __FUNCTION__,
++ qxl->guest_primary.surface.width,
++ qxl->guest_primary.surface.height,
++ qxl->guest_primary.stride,
++ qxl->guest_primary.bytes_pp,
++ qxl->guest_primary.bits_pp,
++ qxl->guest_primary.flipped ? "yes" : "no");
++ vga->ds->surface =
++ qemu_create_displaysurface_from(qxl->guest_primary.surface.width,
++ qxl->guest_primary.surface.height,
++ qxl->guest_primary.bits_pp,
++ qxl->guest_primary.stride,
++ ptr);
++ dpy_resize(vga->ds);
++ }
++
++ if (!qxl->guest_primary.commands)
++ return;
++ qxl->guest_primary.commands = 0;
++
++ update.left = 0;
++ update.right = qxl->guest_primary.surface.width;
++ update.top = 0;
++ update.bottom = qxl->guest_primary.surface.height;
++
++ memset(dirty, 0, sizeof(dirty));
++ qxl->ssd.worker->update_area(qxl->ssd.worker, 0, &update,
++ dirty, ARRAY_SIZE(dirty), 1);
++
++ for (i = 0; i < ARRAY_SIZE(dirty); i++) {
++ if (qemu_spice_rect_is_empty(dirty+i))
++ break;
++ if (qxl->guest_primary.flipped) {
++ qxl_flip(qxl, dirty+i);
++ }
++ dpy_update(vga->ds,
++ dirty[i].left, dirty[i].top,
++ dirty[i].right - dirty[i].left,
++ dirty[i].bottom - dirty[i].top);
++ }
++}
++
++static QEMUCursor *qxl_cursor(PCIQXLDevice *qxl, QXLCursor *cursor)
++{
++ QEMUCursor *c;
++ uint8_t *image, *mask;
++ int size;
++
++ c = cursor_alloc(cursor->header.width, cursor->header.height);
++ c->hot_x = cursor->header.hot_spot_x;
++ c->hot_y = cursor->header.hot_spot_y;
++ switch (cursor->header.type) {
++ case SPICE_CURSOR_TYPE_ALPHA:
++ size = cursor->header.width * cursor->header.height * sizeof(uint32_t);
++ memcpy(c->data, cursor->chunk.data, size);
++ if (qxl->debug > 1)
++ cursor_print_ascii_art(c, "qxl/alpha");
++ break;
++ case SPICE_CURSOR_TYPE_MONO:
++ mask = cursor->chunk.data;
++ image = mask + cursor_get_mono_bpl(c) * c->width;
++ cursor_set_mono(c, 0xffffff, 0x000000, image, 1, mask);
++ if (qxl->debug > 1)
++ cursor_print_ascii_art(c, "qxl/mono");
++ break;
++ default:
++ fprintf(stderr, "%s: not implemented: type %d\n",
++ __FUNCTION__, cursor->header.type);
++ goto fail;
++ }
++ return c;
++
++fail:
++ cursor_put(c);
++ return NULL;
++}
++
++
++void qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext)
++{
++ QXLCursorCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id);
++ QXLCursor *cursor;
++ QEMUCursor *c;
++ int x = -1, y = -1;
++
++ if (!qxl->ssd.ds->mouse_set ||
++ !qxl->ssd.ds->cursor_define)
++ return;
++
++#if 1
++ if (cmd->type != QXL_CURSOR_MOVE) {
++ fprintf(stderr, "%s", __FUNCTION__);
++ qxl_log_cmd_cursor(qxl, cmd, ext->group_id);
++ fprintf(stderr, "\n");
++ }
++#endif
++ switch (cmd->type) {
++ case QXL_CURSOR_SET:
++ x = cmd->u.set.position.x;
++ y = cmd->u.set.position.y;
++ cursor = qxl_phys2virt(qxl, cmd->u.set.shape, ext->group_id);
++ if (cursor->chunk.data_size != cursor->data_size) {
++ fprintf(stderr, "%s: multiple chunks\n", __FUNCTION__);
++ return;
++ }
++ c = qxl_cursor(qxl, cursor);
++ if (c == NULL) {
++ c = cursor_builtin_left_ptr();
++ }
++ qxl->ssd.ds->cursor_define(c);
++ cursor_put(c);
++ break;
++ case QXL_CURSOR_MOVE:
++ x = cmd->u.position.x;
++ y = cmd->u.position.y;
++ break;
++ }
++ if (x != -1 && y != -1) {
++ qxl->ssd.ds->mouse_set(x, y, 1);
++ }
++}
+diff --git a/hw/qxl.c b/hw/qxl.c
+new file mode 100644
+index 0000000..475360c
+--- /dev/null
++++ b/hw/qxl.c
+@@ -0,0 +1,1411 @@
++#include <stdio.h>
++#include <stdlib.h>
++#include <stdbool.h>
++#include <stdint.h>
++#include <string.h>
++#include <pthread.h>
++
++#include "qemu-common.h"
++#include "qemu-timer.h"
++#include "qemu-queue.h"
++#include "monitor.h"
++#include "sysemu.h"
++
++#include "qxl.h"
++
++#undef SPICE_RING_PROD_ITEM
++#define SPICE_RING_PROD_ITEM(r, ret) { \
++ typeof(r) start = r; \
++ typeof(r) end = r + 1; \
++ uint32_t prod = (r)->prod & SPICE_RING_INDEX_MASK(r); \
++ typeof(&(r)->items[prod]) m_item = &(r)->items[prod]; \
++ if (!((uint8_t*)m_item >= (uint8_t*)(start) && (uint8_t*)(m_item + 1) <= (uint8_t*)(end))) { \
++ abort(); \
++ } \
++ ret = &m_item->el; \
++ }
++
++#undef SPICE_RING_CONS_ITEM
++#define SPICE_RING_CONS_ITEM(r, ret) { \
++ typeof(r) start = r; \
++ typeof(r) end = r + 1; \
++ uint32_t cons = (r)->cons & SPICE_RING_INDEX_MASK(r); \
++ typeof(&(r)->items[cons]) m_item = &(r)->items[cons]; \
++ if (!((uint8_t*)m_item >= (uint8_t*)(start) && (uint8_t*)(m_item + 1) <= (uint8_t*)(end))) { \
++ abort(); \
++ } \
++ ret = &m_item->el; \
++ }
++
++#undef ALIGN
++#define ALIGN(a, b) (((a) + ((b) - 1)) & ~((b) - 1))
++
++#define PIXEL_SIZE 0.2936875 //1280x1024 is 14.8" x 11.9"
++
++#define QXL_MODE(_x, _y, _b, _o) \
++ { .x_res = _x, \
++ .y_res = _y, \
++ .bits = _b, \
++ .stride = (_x) * (_b) / 8, \
++ .x_mili = PIXEL_SIZE * (_x), \
++ .y_mili = PIXEL_SIZE * (_y), \
++ .orientation = _o, \
++ }
++
++#define QXL_MODE_16_32(x_res, y_res, orientation) \
++ QXL_MODE(x_res, y_res, 16, orientation), \
++ QXL_MODE(x_res, y_res, 32, orientation)
++
++#define QXL_MODE_EX(x_res, y_res) \
++ QXL_MODE_16_32(x_res, y_res, 0), \
++ QXL_MODE_16_32(y_res, x_res, 1), \
++ QXL_MODE_16_32(x_res, y_res, 2), \
++ QXL_MODE_16_32(y_res, x_res, 3)
++
++static QXLMode qxl_modes[] = {
++ QXL_MODE_EX(640, 480),
++ QXL_MODE_EX(800, 600),
++ QXL_MODE_EX(832, 624),
++ QXL_MODE_EX(1024, 768),
++ QXL_MODE_EX(1152, 864),
++ QXL_MODE_EX(1152, 870),
++ QXL_MODE_EX(1280, 720),
++ QXL_MODE_EX(1280, 768),
++ QXL_MODE_EX(1280, 800),
++ QXL_MODE_EX(1280, 960),
++ QXL_MODE_EX(1280, 1024),
++ QXL_MODE_EX(1360, 768),
++ QXL_MODE_EX(1366, 768),
++ QXL_MODE_EX(1400, 1050),
++ QXL_MODE_EX(1440, 900),
++ QXL_MODE_EX(1600, 900),
++ QXL_MODE_EX(1600, 1200),
++ QXL_MODE_EX(1680, 1050),
++ QXL_MODE_EX(1920, 1080),
++#ifdef QXL_HIRES_MODES
++ QXL_MODE_EX(1920, 1200),
++ QXL_MODE_EX(1920, 1440),
++ QXL_MODE_EX(2048, 1536),
++ QXL_MODE_EX(2560, 1600),
++ QXL_MODE_EX(2560, 2048),
++ QXL_MODE_EX(2800, 2100),
++ QXL_MODE_EX(3200, 2400),
++#endif
++};
++
++static int device_id = 0;
++static PCIQXLDevice *qxl0;
++
++static void qxl_send_events(PCIQXLDevice *d, uint32_t events);
++static void qxl_destroy_primary(PCIQXLDevice *d);
++static void qxl_reset_memslots(PCIQXLDevice *d);
++static void qxl_reset_surfaces(PCIQXLDevice *d);
++static void qxl_ring_set_dirty(PCIQXLDevice *qxl);
++
++static inline uint32_t msb_mask(uint32_t val)
++{
++ uint32_t mask;
++
++ do {
++ mask = ~(val - 1) & val;
++ val &= ~mask;
++ } while (mask < val);
++
++ return mask;
++}
++
++static ram_addr_t qxl_rom_size(void)
++{
++ uint32_t rom_size = sizeof(QXLRom) + sizeof(QXLModes) + sizeof(qxl_modes);
++ rom_size = MAX(rom_size, TARGET_PAGE_SIZE);
++ rom_size = msb_mask(rom_size * 2 - 1);
++ return rom_size;
++}
++
++static void init_qxl_rom(PCIQXLDevice *d)
++{
++ QXLRom *rom = qemu_get_ram_ptr(d->rom_offset);
++ QXLModes *modes = (QXLModes *)(rom + 1);
++ uint32_t ram_header_size;
++ uint32_t surface0_area_size;
++ uint32_t num_pages;
++ uint32_t fb, maxfb = 0;
++ int i;
++
++ memset(rom, 0, d->rom_size);
++
++ rom->magic = cpu_to_le32(QXL_ROM_MAGIC);
++ rom->id = cpu_to_le32(d->id);
++ rom->modes_offset = cpu_to_le32(sizeof(QXLRom));
++
++ rom->slot_gen_bits = MEMSLOT_GENERATION_BITS;
++ rom->slot_id_bits = MEMSLOT_SLOT_BITS;
++ rom->slots_start = 1;
++ rom->slots_end = NUM_MEMSLOTS - 1;
++ rom->n_surfaces = cpu_to_le32(NUM_SURFACES);
++
++ modes->n_modes = cpu_to_le32(ARRAY_SIZE(qxl_modes));
++ for (i = 0; i < modes->n_modes; i++) {
++ fb = qxl_modes[i].y_res * qxl_modes[i].stride;
++ if (maxfb < fb)
++ maxfb = fb;
++ modes->modes[i].id = cpu_to_le32(i);
++ modes->modes[i].x_res = cpu_to_le32(qxl_modes[i].x_res);
++ modes->modes[i].y_res = cpu_to_le32(qxl_modes[i].y_res);
++ modes->modes[i].bits = cpu_to_le32(qxl_modes[i].bits);
++ modes->modes[i].stride = cpu_to_le32(qxl_modes[i].stride);
++ modes->modes[i].x_mili = cpu_to_le32(qxl_modes[i].x_mili);
++ modes->modes[i].y_mili = cpu_to_le32(qxl_modes[i].y_mili);
++ modes->modes[i].orientation = cpu_to_le32(qxl_modes[i].orientation);
++ }
++ if (maxfb < VGA_RAM_SIZE && d->id == 0)
++ maxfb = VGA_RAM_SIZE;
++
++ ram_header_size = ALIGN(sizeof(QXLRam), 4096);
++ surface0_area_size = ALIGN(maxfb, 4096);
++ num_pages = d->vga.vram_size;
++ num_pages -= ram_header_size;
++ num_pages -= surface0_area_size;
++ num_pages = num_pages / TARGET_PAGE_SIZE;
++
++ rom->draw_area_offset = cpu_to_le32(0);
++ rom->surface0_area_size = cpu_to_le32(surface0_area_size);
++ rom->pages_offset = cpu_to_le32(surface0_area_size);
++ rom->num_pages = cpu_to_le32(num_pages);
++ rom->ram_header_offset = cpu_to_le32(d->vga.vram_size - ram_header_size);
++
++ d->shadow_rom = *rom;
++ d->rom = rom;
++ d->modes = modes;
++}
++
++static void init_qxl_ram(PCIQXLDevice *d)
++{
++ uint8_t *buf;
++ uint64_t *item;
++
++ buf = d->vga.vram_ptr;
++ d->ram = (QXLRam *)(buf + le32_to_cpu(d->shadow_rom.ram_header_offset));
++ d->ram->magic = cpu_to_le32(QXL_RAM_MAGIC);
++ d->ram->int_pending = cpu_to_le32(0);
++ d->ram->int_mask = cpu_to_le32(0);
++ SPICE_RING_INIT(&d->ram->cmd_ring);
++ SPICE_RING_INIT(&d->ram->cursor_ring);
++ SPICE_RING_INIT(&d->ram->release_ring);
++ SPICE_RING_PROD_ITEM(&d->ram->release_ring, item);
++ *item = 0;
++ qxl_ring_set_dirty(d);
++}
++
++static void qxl_set_dirty(ram_addr_t addr, ram_addr_t end)
++{
++ while (addr < end) {
++ cpu_physical_memory_set_dirty(addr);
++ addr += TARGET_PAGE_SIZE;
++ }
++}
++
++static void qxl_rom_set_dirty(PCIQXLDevice *qxl)
++{
++ ram_addr_t addr = qxl->rom_offset;
++ qxl_set_dirty(addr, addr + qxl->rom_size);
++}
++
++static void qxl_ram_set_dirty(PCIQXLDevice *qxl, void *ptr)
++{
++ ram_addr_t addr = qxl->vga.vram_offset;
++ void *base = qxl->vga.vram_ptr;
++ intptr_t offset;
++
++ offset = ptr - base;
++ offset &= ~(TARGET_PAGE_SIZE-1);
++ assert(offset < qxl->vga.vram_size);
++ qxl_set_dirty(addr + offset, addr + offset + TARGET_PAGE_SIZE);
++}
++
++static void qxl_ring_set_dirty(PCIQXLDevice *qxl)
++{
++ ram_addr_t addr = qxl->vga.vram_offset + qxl->shadow_rom.ram_header_offset;
++ ram_addr_t end = qxl->vga.vram_offset + qxl->vga.vram_size;
++ qxl_set_dirty(addr, end);
++}
++
++/*
++ * keep track of some command state, for savevm/loadvm.
++ */
++static void qxl_track_command(PCIQXLDevice *qxl, struct QXLCommandExt *ext)
++{
++ switch (le32_to_cpu(ext->cmd.type)) {
++ case QXL_CMD_SURFACE:
++ {
++ QXLSurfaceCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id);
++ uint32_t id = le32_to_cpu(cmd->surface_id);
++ PANIC_ON(id >= NUM_SURFACES);
++ if (cmd->type == QXL_SURFACE_CMD_CREATE) {
++ qxl->guest_surfaces.cmds[id] = ext->cmd.data;
++ qxl->guest_surfaces.count++;
++ if (qxl->guest_surfaces.max < qxl->guest_surfaces.count)
++ qxl->guest_surfaces.max = qxl->guest_surfaces.count;
++ }
++ if (cmd->type == QXL_SURFACE_CMD_DESTROY) {
++ qxl->guest_surfaces.cmds[id] = 0;
++ qxl->guest_surfaces.count--;
++ }
++ break;
++ }
++ case QXL_CMD_CURSOR:
++ {
++ QXLCursorCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id);
++ if (cmd->type == QXL_CURSOR_SET) {
++ qxl->guest_cursor = ext->cmd.data;
++ }
++ break;
++ }
++ }
++}
++
++/* spice display interface callbacks */
++
++static void interface_attach_worker(QXLInstance *sin, QXLWorker *qxl_worker)
++{
++ PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
++
++ dprintf(qxl, 1, "%s:\n", __FUNCTION__);
++ qxl->ssd.worker = qxl_worker;
++}
++
++static void interface_set_compression_level(QXLInstance *sin, int level)
++{
++ PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
++
++ dprintf(qxl, 1, "%s: %d\n", __FUNCTION__, level);
++ qxl->shadow_rom.compression_level = cpu_to_le32(level);
++ qxl->rom->compression_level = cpu_to_le32(level);
++ qxl_rom_set_dirty(qxl);
++}
++
++static void interface_set_mm_time(QXLInstance *sin, uint32_t mm_time)
++{
++ PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
++
++ qxl->shadow_rom.mm_clock = cpu_to_le32(mm_time);
++ qxl->rom->mm_clock = cpu_to_le32(mm_time);
++ qxl_rom_set_dirty(qxl);
++}
++
++static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info)
++{
++ PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
++
++ dprintf(qxl, 1, "%s:\n", __FUNCTION__);
++ info->memslot_gen_bits = MEMSLOT_GENERATION_BITS;
++ info->memslot_id_bits = MEMSLOT_SLOT_BITS;
++ info->num_memslots = NUM_MEMSLOTS;
++ info->num_memslots_groups = NUM_MEMSLOTS_GROUPS;
++ info->internal_groupslot_id = 0;
++ info->qxl_ram_size = le32_to_cpu(qxl->shadow_rom.num_pages) << TARGET_PAGE_BITS;
++ info->n_surfaces = NUM_SURFACES;
++}
++
++static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext)
++{
++ PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
++ SimpleSpiceUpdate *update;
++ QXLCommandRing *ring;
++ QXLCommand *cmd;
++ int notify;
++
++ switch (qxl->mode) {
++ case QXL_MODE_VGA:
++ dprintf(qxl, 2, "%s: vga\n", __FUNCTION__);
++ update = qemu_spice_create_update(&qxl->ssd);
++ if (update == NULL) {
++ return false;
++ }
++ *ext = update->ext;
++ qxl_log_command(qxl, "vga", ext);
++ return true;
++ case QXL_MODE_COMPAT:
++ case QXL_MODE_NATIVE:
++ case QXL_MODE_UNDEFINED:
++ dprintf(qxl, 2, "%s: %s\n", __FUNCTION__,
++ qxl->cmdflags ? "compat" : "native");
++ ring = &qxl->ram->cmd_ring;
++ if (SPICE_RING_IS_EMPTY(ring)) {
++ return false;
++ }
++ SPICE_RING_CONS_ITEM(ring, cmd);
++ ext->cmd = *cmd;
++ ext->group_id = MEMSLOT_GROUP_GUEST;
++ ext->flags = qxl->cmdflags;
++ SPICE_RING_POP(ring, notify);
++ qxl_ring_set_dirty(qxl);
++ if (notify) {
++ qxl_send_events(qxl, QXL_INTERRUPT_DISPLAY);
++ }
++ qxl->guest_primary.commands++;
++ qxl_track_command(qxl, ext);
++ qxl_log_command(qxl, "cmd", ext);
++ return true;
++ default:
++ return false;
++ }
++}
++
++static int interface_req_cmd_notification(QXLInstance *sin)
++{
++ PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
++ int wait = 1;
++
++ switch (qxl->mode) {
++ case QXL_MODE_COMPAT:
++ case QXL_MODE_NATIVE:
++ case QXL_MODE_UNDEFINED:
++ SPICE_RING_CONS_WAIT(&qxl->ram->cmd_ring, wait);
++ qxl_ring_set_dirty(qxl);
++ break;
++ default:
++ /* nothing */
++ break;
++ }
++ return wait;
++}
++
++static inline void qxl_push_free_res(PCIQXLDevice *d)
++{
++ QXLReleaseRing *ring = &d->ram->release_ring;
++ uint64_t *item;
++
++#define QXL_FREE_BUNCH_SIZE 10
++
++ if (SPICE_RING_IS_EMPTY(ring) || (d->num_free_res == QXL_FREE_BUNCH_SIZE &&
++ ring->prod - ring->cons + 1 != ring->num_items)) {
++ int notify;
++
++ SPICE_RING_PUSH(ring, notify);
++ if (notify) {
++ qxl_send_events(d, QXL_INTERRUPT_DISPLAY);
++ }
++ SPICE_RING_PROD_ITEM(ring, item);
++ *item = 0;
++ d->num_free_res = 0;
++ d->last_release = NULL;
++ qxl_ring_set_dirty(d);
++ }
++}
++
++static void interface_release_resource(QXLInstance *sin,
++ struct QXLReleaseInfoExt ext)
++{
++ PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
++ QXLReleaseRing *ring;
++ uint64_t *item, id;
++
++ if (ext.group_id == MEMSLOT_GROUP_HOST) {
++ /* host group -> vga mode update request */
++ qemu_spice_destroy_update(&qxl->ssd, (void*)ext.info->id);
++ return;
++ }
++
++ /*
++ * ext->info points into guest-visible memory
++ * pci bar 0, $command.release_info
++ */
++ ring = &qxl->ram->release_ring;
++ SPICE_RING_PROD_ITEM(ring, item);
++ if (*item == 0) {
++ /* stick head into the ring */
++ id = ext.info->id;
++ ext.info->next = 0;
++ qxl_ram_set_dirty(qxl, &ext.info->next);
++ *item = id;
++ qxl_ring_set_dirty(qxl);
++ } else {
++ /* append item to the list */
++ qxl->last_release->next = ext.info->id;
++ qxl_ram_set_dirty(qxl, &qxl->last_release->next);
++ ext.info->next = 0;
++ qxl_ram_set_dirty(qxl, &ext.info->next);
++ }
++ qxl->last_release = ext.info;
++ qxl->num_free_res++;
++ qxl_push_free_res(qxl);
++}
++
++static int interface_get_cursor_command(QXLInstance *sin, struct QXLCommandExt *ext)
++{
++ PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
++ QXLCursorRing *ring;
++ QXLCommand *cmd;
++ int notify;
++
++ switch (qxl->mode) {
++ case QXL_MODE_COMPAT:
++ case QXL_MODE_NATIVE:
++ case QXL_MODE_UNDEFINED:
++ ring = &qxl->ram->cursor_ring;
++ if (SPICE_RING_IS_EMPTY(ring)) {
++ return false;
++ }
++ SPICE_RING_CONS_ITEM(ring, cmd);
++ ext->cmd = *cmd;
++ ext->group_id = MEMSLOT_GROUP_GUEST;
++ ext->flags = qxl->cmdflags;
++ SPICE_RING_POP(ring, notify);
++ qxl_ring_set_dirty(qxl);
++ if (notify) {
++ qxl_send_events(qxl, QXL_INTERRUPT_CURSOR);
++ }
++ qxl->guest_primary.commands++;
++ qxl_track_command(qxl, ext);
++ qxl_log_command(qxl, "csr", ext);
++ if (qxl->id == 0) {
++ qxl_render_cursor(qxl, ext);
++ }
++ return true;
++ default:
++ return false;
++ }
++}
++
++static int interface_req_cursor_notification(QXLInstance *sin)
++{
++ PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
++ int wait = 1;
++
++ switch (qxl->mode) {
++ case QXL_MODE_COMPAT:
++ case QXL_MODE_NATIVE:
++ case QXL_MODE_UNDEFINED:
++ SPICE_RING_CONS_WAIT(&qxl->ram->cursor_ring, wait);
++ qxl_ring_set_dirty(qxl);
++ break;
++ default:
++ /* nothing */
++ break;
++ }
++ return wait;
++}
++
++static void interface_notify_update(QXLInstance *sin, uint32_t update_id)
++{
++ fprintf(stderr, "%s: abort()\n", __FUNCTION__);
++ abort();
++}
++
++static int interface_flush_resources(QXLInstance *sin)
++{
++ PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
++ int ret;
++
++ ret = qxl->num_free_res;
++ if (ret) {
++ qxl_push_free_res(qxl);
++ }
++ return ret;
++}
++
++static const QXLInterface qxl_interface = {
++ .base.type = SPICE_INTERFACE_QXL,
++ .base.description = "qxl gpu",
++ .base.major_version = SPICE_INTERFACE_QXL_MAJOR,
++ .base.minor_version = SPICE_INTERFACE_QXL_MINOR,
++
++ .pci_vendor = REDHAT_PCI_VENDOR_ID,
++ .pci_id = QXL_DEVICE_ID,
++ .pci_revision = QXL_REVISION,
++
++ .attache_worker = interface_attach_worker,
++ .set_compression_level = interface_set_compression_level,
++ .set_mm_time = interface_set_mm_time,
++
++ .get_init_info = interface_get_init_info,
++ .get_command = interface_get_command,
++ .req_cmd_notification = interface_req_cmd_notification,
++ .release_resource = interface_release_resource,
++ .get_cursor_command = interface_get_cursor_command,
++ .req_cursor_notification = interface_req_cursor_notification,
++ .notify_update = interface_notify_update,
++ .flush_resources = interface_flush_resources,
++};
++
++static void qxl_enter_vga_mode(PCIQXLDevice *d)
++{
++ if (d->mode == QXL_MODE_VGA) {
++ return;
++ }
++ dprintf(d, 1, "%s\n", __FUNCTION__);
++ qemu_spice_create_host_primary(&d->ssd);
++ d->mode = QXL_MODE_VGA;
++ memset(&d->ssd.dirty, 0, sizeof(d->ssd.dirty));
++}
++
++static void qxl_exit_vga_mode(PCIQXLDevice *d)
++{
++ if (d->mode != QXL_MODE_VGA) {
++ return;
++ }
++ dprintf(d, 1, "%s\n", __FUNCTION__);
++ qxl_destroy_primary(d);
++}
++
++static void qxl_set_irq(PCIQXLDevice *d)
++{
++ uint32_t pending = le32_to_cpu(d->ram->int_pending);
++ uint32_t mask = le32_to_cpu(d->ram->int_mask);
++ int level = !!(pending & mask);
++ qemu_set_irq(d->pci.irq[0], level);
++ qxl_ring_set_dirty(d);
++}
++
++static void qxl_write_config(PCIDevice *d, uint32_t address,
++ uint32_t val, int len)
++{
++ PCIQXLDevice *qxl = DO_UPCAST(PCIQXLDevice, pci, d);
++ VGACommonState *vga = &qxl->vga;
++
++ if (qxl->id == 0) {
++ vga_dirty_log_stop(vga);
++ }
++ pci_default_write_config(d, address, val, len);
++ if (qxl->id == 0) {
++ if (vga->map_addr && qxl->pci.io_regions[0].addr == -1)
++ vga->map_addr = 0;
++ vga_dirty_log_start(vga);
++ }
++}
++
++static void qxl_check_state(PCIQXLDevice *d)
++{
++ QXLRam *ram = d->ram;
++
++ assert(SPICE_RING_IS_EMPTY(&ram->cmd_ring));
++ assert(SPICE_RING_IS_EMPTY(&ram->cursor_ring));
++}
++
++static void qxl_reset_state(PCIQXLDevice *d)
++{
++ QXLRam *ram = d->ram;
++ QXLRom *rom = d->rom;
++
++ assert(SPICE_RING_IS_EMPTY(&ram->cmd_ring));
++ assert(SPICE_RING_IS_EMPTY(&ram->cursor_ring));
++ d->shadow_rom.update_id = cpu_to_le32(0);
++ *rom = d->shadow_rom;
++ qxl_rom_set_dirty(d);
++ init_qxl_ram(d);
++ d->num_free_res = 0;
++ d->last_release = NULL;
++ memset(&d->ssd.dirty, 0, sizeof(d->ssd.dirty));
++}
++
++static void qxl_soft_reset(PCIQXLDevice *d)
++{
++ dprintf(d, 1, "%s:\n", __FUNCTION__);
++ qxl_check_state(d);
++
++ if (d->id == 0) {
++ qxl_enter_vga_mode(d);
++ } else {
++ d->mode = QXL_MODE_UNDEFINED;
++ }
++}
++
++static void qxl_hard_reset(PCIQXLDevice *d, int loadvm)
++{
++ dprintf(d, 1, "%s: start%s\n", __FUNCTION__,
++ loadvm ? " (loadvm)" : "");
++
++ d->ssd.worker->reset_cursor(d->ssd.worker);
++ d->ssd.worker->reset_image_cache(d->ssd.worker);
++ qxl_reset_surfaces(d);
++ qxl_reset_memslots(d);
++
++ /* pre loadvm reset must not touch QXLRam. This lives in
++ * device memory, is migrated together with RAM and thus
++ * already loaded at this point */
++ if (!loadvm) {
++ qxl_reset_state(d);
++ }
++ qemu_spice_create_host_memslot(&d->ssd);
++ qxl_soft_reset(d);
++
++ dprintf(d, 1, "%s: done\n", __FUNCTION__);
++}
++
++static void qxl_reset_handler(DeviceState *dev)
++{
++ PCIQXLDevice *d = DO_UPCAST(PCIQXLDevice, pci.qdev, dev);
++ qxl_hard_reset(d, 0);
++}
++
++static void qxl_vga_ioport_write(void *opaque, uint32_t addr, uint32_t val)
++{
++ VGACommonState *vga = opaque;
++ PCIQXLDevice *qxl = container_of(vga, PCIQXLDevice, vga);
++
++ if (qxl->mode != QXL_MODE_VGA) {
++ dprintf(qxl, 1, "%s\n", __FUNCTION__);
++ qxl_destroy_primary(qxl);
++ qxl_soft_reset(qxl);
++ }
++ vga_ioport_write(opaque, addr, val);
++}
++
++static void qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta)
++{
++ static const int regions[] = {
++ QXL_RAM_RANGE_INDEX,
++ QXL_VRAM_RANGE_INDEX,
++ };
++ uint64_t guest_start;
++ uint64_t guest_end;
++ int pci_region;
++ pcibus_t pci_start;
++ pcibus_t pci_end;
++ intptr_t virt_start;
++ QXLDevMemSlot memslot;
++ int i;
++
++ guest_start = le64_to_cpu(d->guest_slots[slot_id].slot.mem_start);
++ guest_end = le64_to_cpu(d->guest_slots[slot_id].slot.mem_end);
++
++ dprintf(d, 1, "%s: slot %d: guest phys 0x%" PRIx64 " - 0x%" PRIx64 "\n",
++ __FUNCTION__, slot_id,
++ guest_start, guest_end);
++
++ PANIC_ON(slot_id >= NUM_MEMSLOTS);
++ PANIC_ON(guest_start > guest_end);
++
++ for (i = 0; i < ARRAY_SIZE(regions); i++) {
++ pci_region = regions[i];
++ pci_start = d->pci.io_regions[pci_region].addr;
++ pci_end = pci_start + d->pci.io_regions[pci_region].size;
++ /* mapped? */
++ if (pci_start == -1) {
++ continue;
++ }
++ /* start address in range ? */
++ if (guest_start < pci_start || guest_start > pci_end) {
++ continue;
++ }
++ /* end address in range ? */
++ if (guest_end > pci_end) {
++ continue;
++ }
++ /* passed */
++ break;
++ }
++ PANIC_ON(i == ARRAY_SIZE(regions)); /* finished loop without match */
++
++ switch (pci_region) {
++ case QXL_RAM_RANGE_INDEX:
++ virt_start = (intptr_t)qemu_get_ram_ptr(d->vga.vram_offset);
++ break;
++ case QXL_VRAM_RANGE_INDEX:
++ virt_start = (intptr_t)qemu_get_ram_ptr(d->vram_offset);
++ break;
++ default:
++ /* should not happen */
++ abort();
++ }
++
++ memslot.slot_id = slot_id;
++ memslot.slot_group_id = MEMSLOT_GROUP_GUEST; /* guest group */
++ memslot.virt_start = virt_start + (guest_start - pci_start);
++ memslot.virt_end = virt_start + (guest_end - pci_start);
++ memslot.addr_delta = memslot.virt_start - delta;
++ memslot.generation = d->rom->slot_generation = 0; // FIXME d->generation++;
++ qxl_rom_set_dirty(d);
++
++ dprintf(d, 1, "%s: slot %d: host virt 0x%" PRIx64 " - 0x%" PRIx64 "\n",
++ __FUNCTION__, memslot.slot_id,
++ memslot.virt_start, memslot.virt_end);
++
++ d->ssd.worker->add_memslot(d->ssd.worker, &memslot);
++ d->guest_slots[slot_id].ptr = (void*)memslot.virt_start;
++ d->guest_slots[slot_id].size = memslot.virt_end - memslot.virt_start;
++ d->guest_slots[slot_id].delta = delta;
++ d->guest_slots[slot_id].active = 1;
++}
++
++static void qxl_del_memslot(PCIQXLDevice *d, uint32_t slot_id)
++{
++ dprintf(d, 1, "%s: slot %d\n", __FUNCTION__, slot_id);
++ d->ssd.worker->del_memslot(d->ssd.worker, MEMSLOT_GROUP_HOST, slot_id);
++ d->guest_slots[slot_id].active = 0;
++}
++
++static void qxl_reset_memslots(PCIQXLDevice *d)
++{
++ dprintf(d, 1, "%s:\n", __FUNCTION__);
++ d->ssd.worker->reset_memslots(d->ssd.worker);
++ memset(&d->guest_slots, 0, sizeof(d->guest_slots));
++}
++
++static void qxl_reset_surfaces(PCIQXLDevice *d)
++{
++ dprintf(d, 1, "%s:\n", __FUNCTION__);
++ d->mode = QXL_MODE_UNDEFINED;
++ d->ssd.worker->destroy_surfaces(d->ssd.worker);
++ memset(&d->guest_surfaces.cmds, 0, sizeof(d->guest_surfaces.cmds));
++}
++
++void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL pqxl, int group_id)
++{
++ uint64_t phys = le64_to_cpu(pqxl);
++ uint32_t slot = (phys >> (64 - 8)) & 0xff;
++ uint64_t offset = phys & 0xffffffffffff;
++
++ switch (group_id) {
++ case MEMSLOT_GROUP_HOST:
++ return (void*)offset;
++ case MEMSLOT_GROUP_GUEST:
++ PANIC_ON(slot > NUM_MEMSLOTS);
++ PANIC_ON(!qxl->guest_slots[slot].active);
++ PANIC_ON(offset < qxl->guest_slots[slot].delta);
++ offset -= qxl->guest_slots[slot].delta;
++ PANIC_ON(offset > qxl->guest_slots[slot].size)
++ return qxl->guest_slots[slot].ptr + offset;
++ default:
++ PANIC_ON(1);
++ }
++}
++
++static void qxl_create_guest_primary(PCIQXLDevice *qxl, int loadvm)
++{
++ QXLDevSurfaceCreate surface;
++ QXLSurfaceCreate *sc = &qxl->guest_primary.surface;
++
++ assert(qxl->mode != QXL_MODE_NATIVE);
++ qxl_exit_vga_mode(qxl);
++
++ dprintf(qxl, 1, "%s: %dx%d\n", __FUNCTION__,
++ le32_to_cpu(sc->width), le32_to_cpu(sc->height));
++
++ surface.format = le32_to_cpu(sc->format);
++ surface.height = le32_to_cpu(sc->height);
++ surface.mem = le64_to_cpu(sc->mem);
++ surface.position = le32_to_cpu(sc->position);
++ surface.stride = le32_to_cpu(sc->stride);
++ surface.width = le32_to_cpu(sc->width);
++ surface.type = le32_to_cpu(sc->type);
++ surface.flags = le32_to_cpu(sc->flags);
++
++ surface.mouse_mode = true;
++ surface.group_id = MEMSLOT_GROUP_GUEST;
++ if (loadvm) {
++ surface.flags |= QXL_SURF_FLAG_KEEP_DATA;
++ }
++
++ qxl->mode = QXL_MODE_NATIVE;
++ qxl->cmdflags = 0;
++ qxl->ssd.worker->create_primary_surface(qxl->ssd.worker, 0, &surface);
++
++ /* for local rendering */
++ qxl_render_resize(qxl);
++}
++
++static void qxl_destroy_primary(PCIQXLDevice *d)
++{
++ if (d->mode == QXL_MODE_UNDEFINED) {
++ return;
++ }
++
++ dprintf(d, 1, "%s\n", __FUNCTION__);
++
++ d->mode = QXL_MODE_UNDEFINED;
++ d->ssd.worker->destroy_primary_surface(d->ssd.worker, 0);
++}
++
++static void qxl_set_mode(PCIQXLDevice *d, int modenr)
++{
++ pcibus_t start = d->pci.io_regions[QXL_RAM_RANGE_INDEX].addr;
++ pcibus_t end = d->pci.io_regions[QXL_RAM_RANGE_INDEX].size + start;
++ QXLMode *mode = d->modes->modes + modenr;
++ uint64_t devmem = d->pci.io_regions[QXL_RAM_RANGE_INDEX].addr;
++ QXLMemSlot slot = {
++ .mem_start = start,
++ .mem_end = end
++ };
++ QXLSurfaceCreate surface = {
++ .width = mode->x_res,
++ .height = mode->y_res,
++ .stride = -mode->x_res * 4,
++ .format = SPICE_SURFACE_FMT_32_xRGB,
++ .mouse_mode = true,
++ .mem = devmem,
++ };
++
++ dprintf(d, 1, "%s: mode %d [ %d x %d @ %d bpp devmem 0x%lx ]\n", __FUNCTION__,
++ modenr, mode->x_res, mode->y_res, mode->bits, devmem);
++ qxl_hard_reset(d, 0);
++
++ d->guest_slots[0].slot = slot;
++ qxl_add_memslot(d, 0, devmem);
++
++ d->guest_primary.surface = surface;
++ qxl_create_guest_primary(d, 0);
++
++ d->mode = QXL_MODE_COMPAT;
++ d->cmdflags = QXL_COMMAND_FLAG_COMPAT;
++ d->shadow_rom.mode = cpu_to_le32(modenr);
++ d->rom->mode = cpu_to_le32(modenr);
++ qxl_rom_set_dirty(d);
++}
++
++static void ioport_write(void *opaque, uint32_t addr, uint32_t val)
++{
++ PCIQXLDevice *d = opaque;
++ uint32_t io_port = addr - d->io_base;
++
++ switch (io_port) {
++ case QXL_IO_RESET:
++ case QXL_IO_SET_MODE:
++ case QXL_IO_MEMSLOT_ADD:
++ case QXL_IO_MEMSLOT_DEL:
++ case QXL_IO_CREATE_PRIMARY:
++ break;
++ default:
++ if (d->mode == QXL_MODE_NATIVE || d->mode == QXL_MODE_COMPAT)
++ break;
++ dprintf(d, 1, "%s: unexpected port 0x%x in vga mode\n", __FUNCTION__, io_port);
++ return;
++ }
++
++ switch (io_port) {
++ case QXL_IO_UPDATE_AREA:
++ {
++ QXLRect update = d->ram->update_area;
++ d->ssd.worker->update_area(d->ssd.worker, d->ram->update_surface,
++ &update, NULL, 0, 0);
++ break;
++ }
++ case QXL_IO_NOTIFY_CMD:
++ d->ssd.worker->wakeup(d->ssd.worker);
++ break;
++ case QXL_IO_NOTIFY_CURSOR:
++ d->ssd.worker->wakeup(d->ssd.worker);
++ break;
++ case QXL_IO_UPDATE_IRQ:
++ qxl_set_irq(d);
++ break;
++ case QXL_IO_NOTIFY_OOM:
++ if (!SPICE_RING_IS_EMPTY(&d->ram->release_ring)) {
++ break;
++ }
++ pthread_yield();
++ if (!SPICE_RING_IS_EMPTY(&d->ram->release_ring)) {
++ break;
++ }
++ d->ssd.worker->oom(d->ssd.worker);
++ break;
++ case QXL_IO_SET_MODE:
++ dprintf(d, 1, "QXL_SET_MODE %d\n", val);
++ qxl_set_mode(d, val);
++ break;
++ case QXL_IO_LOG:
++ dprintf(d, 1, "log %s", d->ram->log_buf);
++ break;
++ case QXL_IO_RESET:
++ dprintf(d, 1, "QXL_IO_RESET\n");
++ qxl_hard_reset(d, 0);
++ break;
++ case QXL_IO_MEMSLOT_ADD:
++ PANIC_ON(val >= NUM_MEMSLOTS);
++ PANIC_ON(d->guest_slots[val].active);
++ d->guest_slots[val].slot = d->ram->mem_slot;
++ qxl_add_memslot(d, val, 0);
++ break;
++ case QXL_IO_MEMSLOT_DEL:
++ qxl_del_memslot(d, val);
++ break;
++ case QXL_IO_CREATE_PRIMARY:
++ PANIC_ON(val != 0);
++ dprintf(d, 1, "QXL_IO_CREATE_PRIMARY\n");
++ d->guest_primary.surface = d->ram->create_surface;
++ qxl_create_guest_primary(d, 0);
++ break;
++ case QXL_IO_DESTROY_PRIMARY:
++ PANIC_ON(val != 0);
++ dprintf(d, 1, "QXL_IO_DESTROY_PRIMARY\n");
++ qxl_destroy_primary(d);
++ break;
++ case QXL_IO_DESTROY_SURFACE_WAIT:
++ d->ssd.worker->destroy_surface_wait(d->ssd.worker, val);
++ break;
++ default:
++ fprintf(stderr, "%s: ioport=0x%x, abort()\n", __FUNCTION__, io_port);
++ abort();
++ }
++}
++
++static uint32_t ioport_read(void *opaque, uint32_t addr)
++{
++ PCIQXLDevice *d = opaque;
++
++ dprintf(d, 1, "%s: unexpected\n", __FUNCTION__);
++ return 0xff;
++}
++
++static void qxl_map(PCIDevice *pci, int region_num,
++ pcibus_t addr, pcibus_t size, int type)
++{
++ static const char *names[] = {
++ [ QXL_IO_RANGE_INDEX ] = "ioports",
++ [ QXL_RAM_RANGE_INDEX ] = "devram",
++ [ QXL_ROM_RANGE_INDEX ] = "rom",
++ [ QXL_VRAM_RANGE_INDEX ] = "vram",
++ };
++ PCIQXLDevice *qxl = DO_UPCAST(PCIQXLDevice, pci, pci);
++
++ dprintf(qxl, 1, "%s: bar %d [%s] addr 0x%lx size 0x%lx\n", __FUNCTION__,
++ region_num, names[region_num], addr, size);
++
++ switch (region_num) {
++ case QXL_IO_RANGE_INDEX:
++ register_ioport_write(addr, size, 1, ioport_write, pci);
++ register_ioport_read(addr, size, 1, ioport_read, pci);
++ qxl->io_base = addr;
++ break;
++ case QXL_RAM_RANGE_INDEX:
++ cpu_register_physical_memory(addr, size, qxl->vga.vram_offset | IO_MEM_RAM);
++ qxl->vga.map_addr = addr;
++ qxl->vga.map_end = addr + size;
++ if (qxl->id == 0) {
++ vga_dirty_log_start(&qxl->vga);
++ }
++ break;
++ case QXL_ROM_RANGE_INDEX:
++ cpu_register_physical_memory(addr, size, qxl->rom_offset | IO_MEM_ROM);
++ break;
++ case QXL_VRAM_RANGE_INDEX:
++ cpu_register_physical_memory(addr, size, qxl->vram_offset | IO_MEM_RAM);
++ break;
++ }
++}
++
++static void pipe_read(void *opaque)
++{
++ PCIQXLDevice *d = opaque;
++ char dummy;
++ int len;
++
++ do {
++ len = read(d->pipe[0], &dummy, sizeof(dummy));
++ } while (len == sizeof(dummy));
++ qxl_set_irq(d);
++}
++
++static void qxl_send_events(PCIQXLDevice *d, uint32_t events)
++{
++ uint32_t old_pending;
++ uint32_t le_events = cpu_to_le32(events);
++
++ assert(d->ssd.running);
++ old_pending = __sync_fetch_and_or(&d->ram->int_pending, le_events);
++ if ((old_pending & le_events) == le_events) {
++ return;
++ }
++ if (pthread_self() == d->main) {
++ qxl_set_irq(d);
++ } else {
++ if (write(d->pipe[1], d, 1) != 1) {
++ dprintf(d, 1, "%s: write to pipe failed\n", __FUNCTION__);
++ }
++ }
++}
++
++static void init_pipe_signaling(PCIQXLDevice *d)
++{
++ if (pipe(d->pipe) < 0) {
++ dprintf(d, 1, "%s: pipe creation failed\n", __FUNCTION__);
++ return;
++ }
++#ifdef CONFIG_IOTHREAD
++ fcntl(d->pipe[0], F_SETFL, O_NONBLOCK);
++#else
++ fcntl(d->pipe[0], F_SETFL, O_NONBLOCK /* | O_ASYNC */);
++#endif
++ fcntl(d->pipe[1], F_SETFL, O_NONBLOCK);
++ fcntl(d->pipe[0], F_SETOWN, getpid());
++
++ d->main = pthread_self();
++ qemu_set_fd_handler(d->pipe[0], pipe_read, NULL, d);
++}
++
++/* graphics console */
++
++static void qxl_hw_update(void *opaque)
++{
++ PCIQXLDevice *qxl = opaque;
++ VGACommonState *vga = &qxl->vga;
++
++ switch (qxl->mode) {
++ case QXL_MODE_VGA:
++ vga->update(vga);
++ break;
++ case QXL_MODE_NATIVE:
++ qxl_render_update(qxl);
++ break;
++ default:
++ break;
++ }
++}
++
++static void qxl_hw_invalidate(void *opaque)
++{
++ PCIQXLDevice *qxl = opaque;
++ VGACommonState *vga = &qxl->vga;
++
++ vga->invalidate(vga);
++}
++
++static void qxl_hw_screen_dump(void *opaque, const char *filename)
++{
++ PCIQXLDevice *qxl = opaque;
++ VGACommonState *vga = &qxl->vga;
++
++ if (qxl->mode == QXL_MODE_VGA) {
++ vga->screen_dump(vga, filename);
++ return;
++ }
++}
++
++static void qxl_hw_text_update(void *opaque, console_ch_t *chardata)
++{
++ PCIQXLDevice *qxl = opaque;
++ VGACommonState *vga = &qxl->vga;
++
++ if (qxl->mode == QXL_MODE_VGA) {
++ vga->text_update(vga, chardata);
++ return;
++ }
++}
++
++static void qxl_vm_change_state_handler(void *opaque, int running, int reason)
++{
++ PCIQXLDevice *qxl = opaque;
++ qemu_spice_vm_change_state_handler(&qxl->ssd, running, reason);
++}
++
++/* display change listener */
++
++static void display_update(struct DisplayState *ds, int x, int y, int w, int h)
++{
++ if (qxl0->mode == QXL_MODE_VGA) {
++ qemu_spice_display_update(&qxl0->ssd, x, y, w, h);
++ }
++}
++
++static void display_resize(struct DisplayState *ds)
++{
++ if (qxl0->mode == QXL_MODE_VGA) {
++ qemu_spice_display_resize(&qxl0->ssd);
++ }
++}
++
++static void display_refresh(struct DisplayState *ds)
++{
++ if (qxl0->mode == QXL_MODE_VGA) {
++ qemu_spice_display_refresh(&qxl0->ssd);
++ }
++}
++
++static DisplayChangeListener display_listener = {
++ .dpy_update = display_update,
++ .dpy_resize = display_resize,
++ .dpy_refresh = display_refresh,
++};
++
++static int qxl_init(PCIDevice *dev)
++{
++ PCIQXLDevice *qxl = DO_UPCAST(PCIQXLDevice, pci, dev);
++ VGACommonState *vga = &qxl->vga;
++ uint8_t* config = qxl->pci.config;
++ ram_addr_t ram_size = msb_mask(qxl->vga.vram_size * 2 - 1);
++ uint32_t pci_device_id;
++ uint32_t pci_device_rev;
++
++ if (device_id == 0 && dev->qdev.hotplugged) {
++ device_id++;
++ }
++
++ qxl->id = device_id;
++ qxl->mode = QXL_MODE_UNDEFINED;
++ qxl->generation = 1;
++
++ switch (qxl->revision) {
++ case 1: /* qxl-1 */
++ pci_device_id = 0x0100;
++ pci_device_rev = 1;
++ break;
++ case 2: /* qxl-2 */
++ pci_device_id = 0x0100;
++ pci_device_rev = 2;
++ break;
++ default: /* unstable */
++ pci_device_id = 0x01ff;
++ pci_device_rev = 1;
++ break;
++ }
++
++ if (!qxl->id) {
++ if (ram_size < 32 * 1024 * 1024)
++ ram_size = 32 * 1024 * 1024;
++ vga_common_init(vga, ram_size);
++ vga_init(vga);
++ register_ioport_write(0x3c0, 16, 1, qxl_vga_ioport_write, vga);
++ register_ioport_write(0x3b4, 2, 1, qxl_vga_ioport_write, vga);
++ register_ioport_write(0x3d4, 2, 1, qxl_vga_ioport_write, vga);
++ register_ioport_write(0x3ba, 1, 1, qxl_vga_ioport_write, vga);
++ register_ioport_write(0x3da, 1, 1, qxl_vga_ioport_write, vga);
++
++ vga->ds = graphic_console_init(qxl_hw_update, qxl_hw_invalidate,
++ qxl_hw_screen_dump, qxl_hw_text_update, qxl);
++ qxl->ssd.ds = vga->ds;
++ qxl->ssd.bufsize = (16 * 1024 * 1024);
++ qxl->ssd.buf = qemu_malloc(qxl->ssd.bufsize);
++ pthread_mutex_init(&qxl->ssd.lock, NULL);
++
++ qxl0 = qxl;
++ register_displaychangelistener(vga->ds, &display_listener);
++
++ if (qxl->pci.romfile == NULL) {
++ if (pci_device_id == 0x01ff) {
++ qxl->pci.romfile = qemu_strdup("vgabios-qxldev.bin");
++ } else {
++ qxl->pci.romfile = qemu_strdup("vgabios-qxl.bin");
++ }
++ }
++ pci_config_set_class(config, PCI_CLASS_DISPLAY_VGA);
++ } else {
++ if (ram_size < 16 * 1024 * 1024)
++ ram_size = 16 * 1024 * 1024;
++ qxl->vga.vram_size = ram_size;
++ qxl->vga.vram_offset = qemu_ram_alloc(&qxl->pci.qdev, "bar0", qxl->vga.vram_size);
++ qxl->vga.vram_ptr = qemu_get_ram_ptr(qxl->vga.vram_offset);
++
++ pci_config_set_class(config, PCI_CLASS_DISPLAY_OTHER);
++ }
++
++ pci_config_set_vendor_id(config, REDHAT_PCI_VENDOR_ID);
++ pci_config_set_device_id(config, pci_device_id);
++ pci_set_byte(&config[PCI_REVISION_ID], pci_device_rev);
++ pci_set_byte(&config[PCI_INTERRUPT_PIN], 1);
++
++ qxl->rom_size = qxl_rom_size();
++ qxl->rom_offset = qemu_ram_alloc(&qxl->pci.qdev, "bar2", qxl->rom_size);
++ init_qxl_rom(qxl);
++ init_qxl_ram(qxl);
++
++ if (qxl->vram_size < 16 * 1024 * 1024)
++ qxl->vram_size = 16 * 1024 * 1024;
++ qxl->vram_size = msb_mask(qxl->vram_size * 2 - 1);
++ qxl->vram_offset = qemu_ram_alloc(&qxl->pci.qdev, "bar1", qxl->vram_size);
++
++ pci_register_bar(&qxl->pci, QXL_IO_RANGE_INDEX,
++ msb_mask(QXL_IO_RANGE_SIZE * 2 - 1),
++ PCI_BASE_ADDRESS_SPACE_IO, qxl_map);
++
++ pci_register_bar(&qxl->pci, QXL_ROM_RANGE_INDEX,
++ qxl->rom_size, PCI_BASE_ADDRESS_SPACE_MEMORY,
++ qxl_map);
++
++ pci_register_bar(&qxl->pci, QXL_RAM_RANGE_INDEX,
++ qxl->vga.vram_size, PCI_BASE_ADDRESS_SPACE_MEMORY,
++ qxl_map);
++
++ pci_register_bar(&qxl->pci, QXL_VRAM_RANGE_INDEX, qxl->vram_size,
++ PCI_BASE_ADDRESS_SPACE_MEMORY, qxl_map);
++
++ qxl->ssd.qxl.base.sif = &qxl_interface.base;
++ qxl->ssd.qxl.id = qxl->id;
++ spice_server_add_interface(spice_server, &qxl->ssd.qxl.base);
++ qemu_add_vm_change_state_handler(qxl_vm_change_state_handler, qxl);
++
++ init_pipe_signaling(qxl);
++ qxl_reset_state(qxl);
++
++ device_id++;
++ return 0;
++}
++
++static void qxl_pre_save(void *opaque)
++{
++ PCIQXLDevice* d = opaque;
++ uint8_t *ram_start = d->vga.vram_ptr;
++
++ dprintf(d, 1, "%s:\n", __FUNCTION__);
++#if 1 /* wanna zap this */
++ if (d->last_release == NULL) {
++ d->last_release_offset = 0;
++ } else {
++ d->last_release_offset = (uint8_t *)d->last_release - ram_start;
++ }
++ assert(d->last_release_offset < d->vga.vram_size);
++#endif
++}
++
++static int qxl_pre_load(void *opaque)
++{
++ PCIQXLDevice* d = opaque;
++
++ dprintf(d, 1, "%s: start\n", __FUNCTION__);
++ qxl_hard_reset(d, 1);
++ qxl_exit_vga_mode(d);
++ dprintf(d, 1, "%s: done\n", __FUNCTION__);
++ return 0;
++}
++
++static int qxl_post_load(void *opaque, int version)
++{
++ PCIQXLDevice* d = opaque;
++ uint8_t *ram_start = d->vga.vram_ptr;
++ QXLCommandExt *cmds;
++ int in, out, i, newmode;
++
++ dprintf(d, 1, "%s: start\n", __FUNCTION__);
++ newmode = d->mode;
++ d->mode = QXL_MODE_UNDEFINED;
++ switch (newmode) {
++ case QXL_MODE_UNDEFINED:
++ break;
++ case QXL_MODE_VGA:
++ qxl_enter_vga_mode(d);
++ break;
++ case QXL_MODE_NATIVE:
++ for (i = 0; i < NUM_MEMSLOTS; i++) {
++ if (!d->guest_slots[i].active)
++ continue;
++ qxl_add_memslot(d, i, 0);
++ }
++ qxl_create_guest_primary(d, 1);
++
++ /* replay surface-create and cursor-set commands */
++ cmds = qemu_mallocz(sizeof(QXLCommandExt) * (NUM_SURFACES + 1));
++ for (in = 0, out = 0; in < NUM_SURFACES; in++) {
++ if (d->guest_surfaces.cmds[in] == 0)
++ continue;
++ cmds[out].cmd.data = d->guest_surfaces.cmds[in];
++ cmds[out].cmd.type = QXL_CMD_SURFACE;
++ cmds[out].group_id = MEMSLOT_GROUP_GUEST;
++ out++;
++ }
++ cmds[out].cmd.data = d->guest_cursor;
++ cmds[out].cmd.type = QXL_CMD_CURSOR;
++ cmds[out].group_id = MEMSLOT_GROUP_GUEST;
++ out++;
++ d->ssd.worker->loadvm_commands(d->ssd.worker, cmds, out);
++ qemu_free(cmds);
++
++ break;
++ case QXL_MODE_COMPAT:
++ qxl_set_mode(d, d->shadow_rom.mode);
++ break;
++ }
++ dprintf(d, 1, "%s: done\n", __FUNCTION__);
++
++#if 1 /* wanna zap this */
++ if (d->last_release_offset >= d->vga.vram_size) {
++ dprintf(d, 1, "%s: invalid last_release_offset %u, ram_size %u\n",
++ __FUNCTION__, d->last_release_offset, d->vga.vram_size);
++ exit(-1);
++ }
++
++ if (d->last_release_offset == 0) {
++ d->last_release = NULL;
++ } else {
++ d->last_release = (QXLReleaseInfo *)(ram_start + d->last_release_offset);
++ }
++#endif
++
++ return 0;
++}
++
++#define QXL_VER 1
++
++static VMStateDescription qxl_memslot = {
++ .name = "qxl-memslot",
++ .version_id = QXL_VER,
++ .minimum_version_id = QXL_VER,
++ .fields = (VMStateField[]) {
++ VMSTATE_UINT64(slot.mem_start, struct guest_slots),
++ VMSTATE_UINT64(slot.mem_end, struct guest_slots),
++ VMSTATE_UINT32(active, struct guest_slots),
++ VMSTATE_END_OF_LIST()
++ }
++};
++
++static VMStateDescription qxl_surface = {
++ .name = "qxl-surface",
++ .version_id = QXL_VER,
++ .minimum_version_id = QXL_VER,
++ .fields = (VMStateField[]) {
++ VMSTATE_UINT32(width, QXLSurfaceCreate),
++ VMSTATE_UINT32(height, QXLSurfaceCreate),
++ VMSTATE_INT32(stride, QXLSurfaceCreate),
++ VMSTATE_UINT32(format, QXLSurfaceCreate),
++ VMSTATE_UINT32(position, QXLSurfaceCreate),
++ VMSTATE_UINT32(mouse_mode, QXLSurfaceCreate),
++ VMSTATE_UINT32(flags, QXLSurfaceCreate),
++ VMSTATE_UINT32(type, QXLSurfaceCreate),
++ VMSTATE_UINT64(mem, QXLSurfaceCreate),
++ VMSTATE_END_OF_LIST()
++ }
++};
++
++static VMStateDescription qxl_vmstate = {
++ .name = "qxl",
++ .version_id = QXL_VER,
++ .minimum_version_id = QXL_VER,
++ .pre_save = qxl_pre_save,
++ .pre_load = qxl_pre_load,
++ .post_load = qxl_post_load,
++ .fields = (VMStateField []) {
++ VMSTATE_PCI_DEVICE(pci, PCIQXLDevice),
++ VMSTATE_STRUCT(vga, PCIQXLDevice, QXL_VER, vmstate_vga_common, VGACommonState),
++ VMSTATE_UINT32(shadow_rom.mode, PCIQXLDevice),
++#if 1 /* wanna zap this */
++ VMSTATE_UINT32(num_free_res, PCIQXLDevice),
++ VMSTATE_UINT32(last_release_offset, PCIQXLDevice),
++#endif
++ VMSTATE_UINT32(mode, PCIQXLDevice),
++ VMSTATE_UINT32(ssd.unique, PCIQXLDevice),
++#if 1 /* new stuff */
++ VMSTATE_STRUCT_ARRAY(guest_slots, PCIQXLDevice, NUM_MEMSLOTS, QXL_VER,
++ qxl_memslot, struct guest_slots),
++ VMSTATE_STRUCT(guest_primary.surface, PCIQXLDevice, QXL_VER,
++ qxl_surface, QXLSurfaceCreate),
++ VMSTATE_ARRAY(guest_surfaces.cmds, PCIQXLDevice, NUM_SURFACES, QXL_VER,
++ vmstate_info_uint64, uint64_t),
++ VMSTATE_UINT64(guest_cursor, PCIQXLDevice),
++#endif
++ VMSTATE_END_OF_LIST()
++ }
++};
++
++static PCIDeviceInfo qxl_info = {
++ .qdev.name = "qxl",
++ .qdev.desc = "Spice QXL GPU",
++ .qdev.size = sizeof(PCIQXLDevice),
++ .qdev.reset = qxl_reset_handler,
++ .qdev.vmsd = &qxl_vmstate,
++ .init = qxl_init,
++ .config_write = qxl_write_config,
++ .qdev.props = (Property[]) {
++ DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size, 64 * 1024 * 1024),
++ DEFINE_PROP_UINT32("vram_size", PCIQXLDevice, vram_size, 64 * 1024 * 1024),
++ DEFINE_PROP_UINT32("revision", PCIQXLDevice, revision, 3),
++ DEFINE_PROP_UINT32("debug", PCIQXLDevice, debug, 0),
++ DEFINE_PROP_UINT32("cmdlog", PCIQXLDevice, cmdlog, 0),
++ DEFINE_PROP_END_OF_LIST(),
++ }
++};
++
++static void qxl_register(void)
++{
++ pci_qdev_register(&qxl_info);
++}
++
++device_init(qxl_register);
+diff --git a/hw/qxl.h b/hw/qxl.h
+new file mode 100644
+index 0000000..1216405
+--- /dev/null
++++ b/hw/qxl.h
+@@ -0,0 +1,102 @@
++#include "console.h"
++#include "hw.h"
++#include "pci.h"
++#include "vga_int.h"
++
++#include "qemu-spice.h"
++#include "spice-display.h"
++
++enum qxl_mode {
++ QXL_MODE_UNDEFINED,
++ QXL_MODE_VGA,
++ QXL_MODE_COMPAT, /* spice 0.4.x */
++ QXL_MODE_NATIVE,
++};
++
++typedef struct PCIQXLDevice {
++ PCIDevice pci;
++ SimpleSpiceDisplay ssd;
++ int id;
++ uint32_t debug;
++ uint32_t cmdlog;
++ enum qxl_mode mode;
++ uint32_t cmdflags;
++ int generation;
++ uint32_t revision;
++
++ struct guest_slots {
++ QXLMemSlot slot;
++ void *ptr;
++ uint64_t size;
++ uint64_t delta;
++ uint32_t active;
++ } guest_slots[NUM_MEMSLOTS];
++
++ struct guest_primary {
++ QXLSurfaceCreate surface;
++ uint32_t commands;
++ uint32_t resized;
++ int32_t stride;
++ uint32_t bits_pp;
++ uint32_t bytes_pp;
++ uint8_t *data, *flipped;
++ } guest_primary;
++
++ struct surfaces {
++ QXLPHYSICAL cmds[NUM_SURFACES];
++ uint32_t count;
++ uint32_t max;
++ } guest_surfaces;
++ QXLPHYSICAL guest_cursor;
++
++ /* thread signaling */
++ pthread_t main;
++ int pipe[2];
++
++ /* ram pci bar */
++ QXLRam *ram;
++ VGACommonState vga;
++ uint32_t num_free_res;
++ QXLReleaseInfo *last_release;
++ uint32_t last_release_offset;
++
++ /* rom pci bar */
++ QXLRom shadow_rom;
++ QXLRom *rom;
++ QXLModes *modes;
++ uint32_t rom_size;
++ uint64_t rom_offset;
++
++ /* vram pci bar */
++ uint32_t vram_size;
++ uint64_t vram_offset;
++
++ /* io bar */
++ uint32_t io_base;
++
++} PCIQXLDevice;
++
++#define PANIC_ON(x) if ((x)) { \
++ printf("%s: PANIC %s failed\n", __FUNCTION__, #x); \
++ exit(-1); \
++}
++
++#define dprintf(_qxl, _level, _fmt, ...) \
++ do { \
++ if (_qxl->debug >= _level) { \
++ fprintf(stderr, "qxl-%d: ", _qxl->id); \
++ fprintf(stderr, _fmt, ## __VA_ARGS__); \
++ } \
++ } while (0)
++
++/* qxl.c */
++void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL phys, int group_id);
++
++/* qxl-logger.c */
++void qxl_log_cmd_cursor(PCIQXLDevice *qxl, QXLCursorCmd *cmd, int group_id);
++void qxl_log_command(PCIQXLDevice *qxl, const char *ring, QXLCommandExt *ext);
++
++/* qxl-render.c */
++void qxl_render_resize(PCIQXLDevice *qxl);
++void qxl_render_update(PCIQXLDevice *qxl);
++void qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext);
+diff --git a/hw/vga_int.h b/hw/vga_int.h
+index 70e0f19..4a82683 100644
+--- a/hw/vga_int.h
++++ b/hw/vga_int.h
+@@ -106,7 +106,7 @@ typedef void (* vga_update_retrace_info_fn)(struct VGACommonState *s);
+ typedef struct VGACommonState {
+ uint8_t *vram_ptr;
+ ram_addr_t vram_offset;
+- unsigned int vram_size;
++ uint32_t vram_size;
+ uint32_t lfb_addr;
+ uint32_t lfb_end;
+ uint32_t map_addr;
+diff --git a/sysemu.h b/sysemu.h
+index bf1d68a..ea3634d 100644
+--- a/sysemu.h
++++ b/sysemu.h
+@@ -103,7 +103,7 @@ extern int autostart;
+ extern int bios_size;
+
+ typedef enum {
+- VGA_NONE, VGA_STD, VGA_CIRRUS, VGA_VMWARE, VGA_XENFB
++ VGA_NONE, VGA_STD, VGA_CIRRUS, VGA_VMWARE, VGA_XENFB, VGA_QXL,
+ } VGAInterfaceType;
+
+ extern int vga_interface_type;
+@@ -111,6 +111,7 @@ extern int vga_interface_type;
+ #define std_vga_enabled (vga_interface_type == VGA_STD)
+ #define xenfb_enabled (vga_interface_type == VGA_XENFB)
+ #define vmsvga_enabled (vga_interface_type == VGA_VMWARE)
++#define qxl_enabled (vga_interface_type == VGA_QXL)
+
+ extern int graphic_width;
+ extern int graphic_height;
+diff --git a/vl.c b/vl.c
+index 2ccebc8..eb630bd 100644
+--- a/vl.c
++++ b/vl.c
+@@ -1459,6 +1459,8 @@ static void select_vgahw (const char *p)
+ vga_interface_type = VGA_VMWARE;
+ } else if (strstart(p, "xenfb", &opts)) {
+ vga_interface_type = VGA_XENFB;
++ } else if (strstart(p, "qxl", &opts)) {
++ vga_interface_type = VGA_QXL;
+ } else if (!strstart(p, "none", &opts)) {
+ invalid_vga:
+ fprintf(stderr, "Unknown vga type: %s\n", p);
+@@ -3034,7 +3036,7 @@ int main(int argc, char **argv, char **envp)
+ break;
+ }
+ #ifdef CONFIG_SPICE
+- if (using_spice) {
++ if (using_spice && !qxl_enabled) {
+ qemu_spice_display_init(ds);
+ }
+ #endif
+--
+1.7.2.3
+
diff --git a/0015-spice-add-audio.patch b/0015-spice-add-audio.patch
new file mode 100644
index 0000000..6725ffe
--- /dev/null
+++ b/0015-spice-add-audio.patch
@@ -0,0 +1,405 @@
+From f3e02bc08c4521dc53d858174612341462d588ce Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel at redhat.com>
+Date: Tue, 13 Apr 2010 10:34:46 +0200
+Subject: [PATCH 15/39] spice: add audio
+
+Add support for the spice audio interface.
+
+The driver is first in the driver list, but can_be_default is set only
+in case spice is active. So if you are using spice the spice audio
+driver is the default one, otherwise whatever comes first after spice in
+the list. Overriding the default using QEMU_AUDIO_DRV works in any
+case.
+---
+ Makefile.objs | 1 +
+ audio/audio.c | 3 +
+ audio/audio_int.h | 1 +
+ audio/spiceaudio.c | 312 ++++++++++++++++++++++++++++++++++++++++++++++++++++
+ qemu-spice.h | 1 +
+ spice.c | 1 +
+ 6 files changed, 319 insertions(+), 0 deletions(-)
+ create mode 100644 audio/spiceaudio.c
+
+diff --git a/Makefile.objs b/Makefile.objs
+index d05643f..9a6b0f3 100644
+--- a/Makefile.objs
++++ b/Makefile.objs
+@@ -94,6 +94,7 @@ common-obj-$(CONFIG_SPICE) += spice.o spice-input.o spice-display.o
+ audio-obj-y = audio.o noaudio.o wavaudio.o mixeng.o
+ audio-obj-$(CONFIG_SDL) += sdlaudio.o
+ audio-obj-$(CONFIG_OSS) += ossaudio.o
++audio-obj-$(CONFIG_SPICE) += spiceaudio.o
+ audio-obj-$(CONFIG_COREAUDIO) += coreaudio.o
+ audio-obj-$(CONFIG_ALSA) += alsaaudio.o
+ audio-obj-$(CONFIG_DSOUND) += dsoundaudio.o
+diff --git a/audio/audio.c b/audio/audio.c
+index ad51077..ade342e 100644
+--- a/audio/audio.c
++++ b/audio/audio.c
+@@ -44,6 +44,9 @@
+ that we generate the list.
+ */
+ static struct audio_driver *drvtab[] = {
++#ifdef CONFIG_SPICE
++ &spice_audio_driver,
++#endif
+ CONFIG_AUDIO_DRIVERS
+ &no_audio_driver,
+ &wav_audio_driver
+diff --git a/audio/audio_int.h b/audio/audio_int.h
+index 06e313f..d1f6c2d 100644
+--- a/audio/audio_int.h
++++ b/audio/audio_int.h
+@@ -209,6 +209,7 @@ extern struct audio_driver coreaudio_audio_driver;
+ extern struct audio_driver dsound_audio_driver;
+ extern struct audio_driver esd_audio_driver;
+ extern struct audio_driver pa_audio_driver;
++extern struct audio_driver spice_audio_driver;
+ extern struct audio_driver winwave_audio_driver;
+ extern struct mixeng_volume nominal_volume;
+
+diff --git a/audio/spiceaudio.c b/audio/spiceaudio.c
+new file mode 100644
+index 0000000..8ae7499
+--- /dev/null
++++ b/audio/spiceaudio.c
+@@ -0,0 +1,312 @@
++#include "hw/hw.h"
++#include "qemu-timer.h"
++#include "qemu-spice.h"
++
++#define AUDIO_CAP "spice"
++#include "audio.h"
++#include "audio_int.h"
++
++#define LINE_IN_SAMPLES 1024
++#define LINE_OUT_SAMPLES 1024
++
++typedef struct SpiceVoiceOut {
++ HWVoiceOut hw;
++ SpicePlaybackInstance sin;
++ int64_t prev_ticks;
++ int active;
++ uint32_t *frame;
++ uint32_t *fpos;
++ uint32_t fsize;
++} SpiceVoiceOut;
++
++typedef struct SpiceVoiceIn {
++ HWVoiceIn hw;
++ SpiceRecordInstance sin;
++ int64_t prev_ticks;
++ int active;
++ uint32_t samples[LINE_IN_SAMPLES];
++} SpiceVoiceIn;
++
++static const SpicePlaybackInterface playback_sif = {
++ .base.type = SPICE_INTERFACE_PLAYBACK,
++ .base.description = "playback",
++ .base.major_version = SPICE_INTERFACE_PLAYBACK_MAJOR,
++ .base.minor_version = SPICE_INTERFACE_PLAYBACK_MINOR,
++};
++
++static const SpiceRecordInterface record_sif = {
++ .base.type = SPICE_INTERFACE_RECORD,
++ .base.description = "record",
++ .base.major_version = SPICE_INTERFACE_RECORD_MAJOR,
++ .base.minor_version = SPICE_INTERFACE_RECORD_MINOR,
++};
++
++static void *spice_audio_init(void)
++{
++ if (!using_spice) {
++ return NULL;
++ }
++ return &spice_audio_init;
++}
++
++static void spice_audio_fini(void *opaque)
++{
++ /* nothing */
++}
++
++static int calculate_samples(struct audio_pcm_info *info, int64_t *old_ticks)
++{
++ int64_t now;
++ int64_t ticks;
++ int64_t bytes;
++ int samples;
++
++ now = qemu_get_clock (vm_clock);
++ ticks = now - *old_ticks;
++ *old_ticks = now;
++ bytes = muldiv64 (ticks, info->bytes_per_second, get_ticks_per_sec ());
++ bytes = audio_MIN (bytes, INT_MAX);
++ samples = bytes >> info->shift;
++ return samples;
++}
++
++/* playback */
++
++static int line_out_init(HWVoiceOut *hw, struct audsettings *as)
++{
++ SpiceVoiceOut *out = container_of(hw, SpiceVoiceOut, hw);
++ struct audsettings settings;
++
++ settings.freq = SPICE_INTERFACE_PLAYBACK_FREQ;
++ settings.nchannels = SPICE_INTERFACE_PLAYBACK_CHAN;
++ settings.fmt = AUD_FMT_S16;
++ settings.endianness = AUDIO_HOST_ENDIANNESS;
++
++ audio_pcm_init_info(&hw->info, &settings);
++ hw->samples = LINE_OUT_SAMPLES;
++ out->active = 0;
++
++ out->sin.base.sif = &playback_sif.base;
++ spice_server_add_interface(spice_server, &out->sin.base);
++ return 0;
++}
++
++static void line_out_fini(HWVoiceOut *hw)
++{
++ SpiceVoiceOut *out = container_of(hw, SpiceVoiceOut, hw);
++
++ spice_server_remove_interface(&out->sin.base);
++}
++
++static int line_out_run(HWVoiceOut *hw, int live)
++{
++ SpiceVoiceOut *out = container_of(hw, SpiceVoiceOut, hw);
++ int rpos, decr;
++ int samples;
++
++ if (!live) {
++ return 0;
++ }
++
++ decr = calculate_samples(&hw->info, &out->prev_ticks);
++ decr = audio_MIN(live, decr);
++
++ samples = decr;
++ rpos = hw->rpos;
++ while (samples) {
++ int left_till_end_samples = hw->samples - rpos;
++ int len = audio_MIN(samples, left_till_end_samples);
++
++ if (!out->frame) {
++ spice_server_playback_get_buffer(&out->sin, &out->frame, &out->fsize);
++ out->fpos = out->frame;
++ }
++ if (out->frame) {
++ len = audio_MIN(len, out->fsize);
++ hw->clip(out->fpos, hw->mix_buf + rpos, len);
++ out->fsize -= len;
++ out->fpos += len;
++ if (out->fsize == 0) {
++ spice_server_playback_put_samples(&out->sin, out->frame);
++ out->frame = out->fpos = NULL;
++ }
++ }
++ rpos = (rpos + len) % hw->samples;
++ samples -= len;
++ }
++ hw->rpos = rpos;
++ return decr;
++}
++
++static int line_out_write(SWVoiceOut *sw, void *buf, int len)
++{
++ return audio_pcm_sw_write(sw, buf, len);
++}
++
++static int line_out_ctl(HWVoiceOut *hw, int cmd, ...)
++{
++ SpiceVoiceOut *out = container_of(hw, SpiceVoiceOut, hw);
++
++ switch (cmd) {
++ case VOICE_ENABLE:
++ if (out->active) {
++ break;
++ }
++ out->active = 1;
++ out->prev_ticks = qemu_get_clock (vm_clock);
++ spice_server_playback_start(&out->sin);
++ break;
++ case VOICE_DISABLE:
++ if (!out->active) {
++ break;
++ }
++ out->active = 0;
++ if (out->frame) {
++ memset(out->fpos, 0, out->fsize << 2);
++ spice_server_playback_put_samples(&out->sin, out->frame);
++ out->frame = out->fpos = NULL;
++ }
++ spice_server_playback_stop(&out->sin);
++ break;
++ }
++ return 0;
++}
++
++/* record */
++
++static int line_in_init(HWVoiceIn *hw, struct audsettings *as)
++{
++ SpiceVoiceIn *in = container_of(hw, SpiceVoiceIn, hw);
++ struct audsettings settings;
++
++ settings.freq = SPICE_INTERFACE_RECORD_FREQ;
++ settings.nchannels = SPICE_INTERFACE_RECORD_CHAN;
++ settings.fmt = AUD_FMT_S16;
++ settings.endianness = AUDIO_HOST_ENDIANNESS;
++
++ audio_pcm_init_info(&hw->info, &settings);
++ hw->samples = LINE_IN_SAMPLES;
++ in->active = 0;
++
++ in->sin.base.sif = &record_sif.base;
++ spice_server_add_interface(spice_server, &in->sin.base);
++ return 0;
++}
++
++static void line_in_fini(HWVoiceIn *hw)
++{
++ SpiceVoiceIn *in = container_of(hw, SpiceVoiceIn, hw);
++
++ spice_server_remove_interface(&in->sin.base);
++}
++
++static int line_in_run(HWVoiceIn *hw)
++{
++ SpiceVoiceIn *in = container_of(hw, SpiceVoiceIn, hw);
++ int num_samples;
++ int ready;
++ int len[2];
++ uint64_t delta_samp;
++ uint32_t *samples;
++
++ if (!(num_samples = hw->samples - audio_pcm_hw_get_live_in(hw))) {
++ return 0;
++ }
++
++ delta_samp = calculate_samples(&hw->info, &in->prev_ticks);
++ num_samples = audio_MIN(num_samples, delta_samp);
++
++ ready = spice_server_record_get_samples(&in->sin, in->samples, num_samples);
++ samples = in->samples;
++ if (ready == 0) {
++ static uint32_t silence[LINE_IN_SAMPLES];
++ samples = silence;
++ ready = LINE_IN_SAMPLES;
++ }
++
++ num_samples = audio_MIN(ready, num_samples);
++
++ if (hw->wpos + num_samples > hw->samples) {
++ len[0] = hw->samples - hw->wpos;
++ len[1] = num_samples - len[0];
++ } else {
++ len[0] = num_samples;
++ len[1] = 0;
++ }
++
++ hw->conv(hw->conv_buf + hw->wpos, samples, len[0], &nominal_volume);
++
++ if (len[1]) {
++ hw->conv(hw->conv_buf, samples + len[0], len[1],
++ &nominal_volume);
++ }
++
++ hw->wpos = (hw->wpos + num_samples) % hw->samples;
++
++ return num_samples;
++}
++
++static int line_in_read(SWVoiceIn *sw, void *buf, int size)
++{
++ return audio_pcm_sw_read(sw, buf, size);
++}
++
++static int line_in_ctl(HWVoiceIn *hw, int cmd, ...)
++{
++ SpiceVoiceIn *in = container_of(hw, SpiceVoiceIn, hw);
++
++ switch (cmd) {
++ case VOICE_ENABLE:
++ if (in->active) {
++ break;
++ }
++ in->active = 1;
++ in->prev_ticks = qemu_get_clock (vm_clock);
++ spice_server_record_start(&in->sin);
++ break;
++ case VOICE_DISABLE:
++ if (!in->active) {
++ break;
++ }
++ in->active = 0;
++ spice_server_record_stop(&in->sin);
++ break;
++ }
++ return 0;
++}
++
++static struct audio_option audio_options[] = {
++ { /* end of list */ },
++};
++
++static struct audio_pcm_ops audio_callbacks = {
++ .init_out = line_out_init,
++ .fini_out = line_out_fini,
++ .run_out = line_out_run,
++ .write = line_out_write,
++ .ctl_out = line_out_ctl,
++
++ .init_in = line_in_init,
++ .fini_in = line_in_fini,
++ .run_in = line_in_run,
++ .read = line_in_read,
++ .ctl_in = line_in_ctl,
++};
++
++struct audio_driver spice_audio_driver = {
++ .name = "spice",
++ .descr = "spice audio driver",
++ .options = audio_options,
++ .init = spice_audio_init,
++ .fini = spice_audio_fini,
++ .pcm_ops = &audio_callbacks,
++ .max_voices_out = 1,
++ .max_voices_in = 1,
++ .voice_size_out = sizeof(SpiceVoiceOut),
++ .voice_size_in = sizeof(SpiceVoiceIn),
++};
++
++void qemu_spice_audio_init(void)
++{
++ spice_audio_driver.can_be_default = 1;
++}
+diff --git a/qemu-spice.h b/qemu-spice.h
+index f061004..6f19ba7 100644
+--- a/qemu-spice.h
++++ b/qemu-spice.h
+@@ -13,6 +13,7 @@ extern int using_spice;
+
+ void qemu_spice_init(void);
+ void qemu_spice_input_init(void);
++void qemu_spice_audio_init(void);
+ void qemu_spice_display_init(DisplayState *ds);
+
+ #else /* CONFIG_SPICE */
+diff --git a/spice.c b/spice.c
+index 3fe76cd..fc76ef7 100644
+--- a/spice.c
++++ b/spice.c
+@@ -204,6 +204,7 @@ void qemu_spice_init(void)
+ using_spice = 1;
+
+ qemu_spice_input_init();
++ qemu_spice_audio_init();
+
+ qemu_free(x509_key_file);
+ qemu_free(x509_cert_file);
+--
+1.7.2.3
+
diff --git a/0016-spice-add-virtio-serial-based-vdi-port-backend.patch b/0016-spice-add-virtio-serial-based-vdi-port-backend.patch
new file mode 100644
index 0000000..91528c9
--- /dev/null
+++ b/0016-spice-add-virtio-serial-based-vdi-port-backend.patch
@@ -0,0 +1,238 @@
+From ebf4cebd082442ed2bc11475fde301c18648298d Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel at redhat.com>
+Date: Tue, 20 Apr 2010 13:33:54 +0200
+Subject: [PATCH 16/39] spice: add virtio-serial based vdi port backend.
+
+Adds the spicevmc device. This is a communication channel between the
+spice client and the guest. It is used to send display information and
+mouse events from the spice clients to the guest.
+---
+ Makefile.target | 1 +
+ hw/spice-vmc.c | 203 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 204 insertions(+), 0 deletions(-)
+ create mode 100644 hw/spice-vmc.c
+
+diff --git a/Makefile.target b/Makefile.target
+index 4da33b5..90544c5 100644
+--- a/Makefile.target
++++ b/Makefile.target
+@@ -217,6 +217,7 @@ obj-i386-y += pc_piix.o
+ obj-i386-y += testdev.o
+ obj-i386-y += acpi.o acpi_piix4.o
+ obj-i386-$(CONFIG_SPICE) += qxl.o qxl-logger.o qxl-render.o
++obj-i386-$(CONFIG_SPICE) += spice-vmc.o
+
+ obj-i386-y += pcspk.o i8254.o
+ obj-i386-$(CONFIG_KVM_PIT) += i8254-kvm.o
+diff --git a/hw/spice-vmc.c b/hw/spice-vmc.c
+new file mode 100644
+index 0000000..3f6a2bb
+--- /dev/null
++++ b/hw/spice-vmc.c
+@@ -0,0 +1,203 @@
++/*
++
++ Spice Virtual Machine Channel (VMC).
++
++ A virtio-serial port used for spice to guest communication, over
++ which spice client and a daemon in the guest operating system
++ communicate.
++
++ Replaces the old vdi_port PCI device.
++
++*/
++
++#include <stdio.h>
++#include <stdbool.h>
++#include <spice.h>
++#include <spice-experimental.h>
++
++#include "virtio-serial.h"
++#include "qemu-spice.h"
++
++#define VMC_GUEST_DEVICE_NAME "com.redhat.spice.0"
++#define VMC_DEVICE_NAME "spicevmc"
++
++#define dprintf(_svc, _level, _fmt, ...) \
++ do { \
++ if (_svc->debug >= _level) { \
++ fprintf(stderr, "svc: " _fmt, ## __VA_ARGS__); \
++ } \
++ } while (0)
++
++typedef struct SpiceVirtualChannel {
++ VirtIOSerialPort port;
++ VMChangeStateEntry *vmstate;
++ SpiceVDIPortInstance sin;
++ bool active;
++ uint8_t *buffer;
++ uint8_t *datapos;
++ ssize_t bufsize, datalen;
++ uint32_t debug;
++} SpiceVirtualChannel;
++
++static int vmc_write(SpiceVDIPortInstance *sin, const uint8_t *buf, int len)
++{
++ SpiceVirtualChannel *svc = container_of(sin, SpiceVirtualChannel, sin);
++ ssize_t out;
++
++ out = virtio_serial_write(&svc->port, buf, len);
++ dprintf(svc, 2, "%s: %lu/%d\n", __func__, out, len);
++ return out;
++}
++
++static int vmc_read(SpiceVDIPortInstance *sin, uint8_t *buf, int len)
++{
++ SpiceVirtualChannel *svc = container_of(sin, SpiceVirtualChannel, sin);
++ int bytes = MIN(len, svc->datalen);
++
++ dprintf(svc, 2, "%s: %d/%zd\n", __func__, bytes, svc->datalen);
++ if (bytes) {
++ memcpy(buf, svc->datapos, bytes);
++ svc->datapos += bytes;
++ svc->datalen -= bytes;
++ if (0 == svc->datalen) {
++ virtio_serial_throttle_port(&svc->port, false);
++ }
++ }
++ return bytes;
++}
++
++static SpiceVDIPortInterface vmc_interface = {
++ .base.type = SPICE_INTERFACE_VDI_PORT,
++ .base.description = "spice virtual channel vdi port",
++ .base.major_version = SPICE_INTERFACE_VDI_PORT_MAJOR,
++ .base.minor_version = SPICE_INTERFACE_VDI_PORT_MINOR,
++ .write = vmc_write,
++ .read = vmc_read,
++};
++
++static void vmc_register_interface(SpiceVirtualChannel *svc)
++{
++ if (svc->active) {
++ return;
++ }
++ dprintf(svc, 1, "%s\n", __func__);
++ svc->sin.base.sif = &vmc_interface.base;
++ spice_server_add_interface(spice_server, &svc->sin.base);
++ svc->active = true;
++}
++
++static void vmc_unregister_interface(SpiceVirtualChannel *svc)
++{
++ if (!svc->active) {
++ return;
++ }
++ dprintf(svc, 1, "%s\n", __func__);
++ spice_server_remove_interface(&svc->sin.base);
++ svc->active = false;
++}
++
++
++static void vmc_change_state_handler(void *opaque, int running, int reason)
++{
++ SpiceVirtualChannel *svc = opaque;
++
++ if (running && svc->active) {
++ spice_server_vdi_port_wakeup(&svc->sin);
++ }
++}
++
++/*
++ * virtio-serial callbacks
++ */
++
++static void vmc_guest_open(VirtIOSerialPort *port)
++{
++ SpiceVirtualChannel *svc = DO_UPCAST(SpiceVirtualChannel, port, port);
++
++ dprintf(svc, 1, "%s\n", __func__);
++ vmc_register_interface(svc);
++}
++
++static void vmc_guest_close(VirtIOSerialPort *port)
++{
++ SpiceVirtualChannel *svc = DO_UPCAST(SpiceVirtualChannel, port, port);
++
++ dprintf(svc, 1, "%s\n", __func__);
++ vmc_unregister_interface(svc);
++}
++
++static void vmc_guest_ready(VirtIOSerialPort *port)
++{
++ SpiceVirtualChannel *svc = DO_UPCAST(SpiceVirtualChannel, port, port);
++
++ dprintf(svc, 1, "%s\n", __func__);
++ if (svc->active)
++ spice_server_vdi_port_wakeup(&svc->sin);
++}
++
++static void vmc_have_data(VirtIOSerialPort *port, const uint8_t *buf, size_t len)
++{
++ SpiceVirtualChannel *svc = DO_UPCAST(SpiceVirtualChannel, port, port);
++
++ dprintf(svc, 2, "%s: %zd\n", __func__, len);
++ assert(svc->datapos == 0);
++ if (svc->bufsize < len) {
++ svc->bufsize = len;
++ svc->buffer = qemu_realloc(svc->buffer, svc->bufsize);
++ }
++ memcpy(svc->buffer, buf, len);
++ svc->datapos = svc->buffer;
++ svc->datalen = len;
++ virtio_serial_throttle_port(&svc->port, true);
++ spice_server_vdi_port_wakeup(&svc->sin);
++}
++
++static int vmc_initfn(VirtIOSerialDevice *dev)
++{
++ VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, &dev->qdev);
++ SpiceVirtualChannel *svc = DO_UPCAST(SpiceVirtualChannel, port, port);
++
++ if (!using_spice)
++ return -1;
++
++ dprintf(svc, 1, "%s\n", __func__);
++ port->name = qemu_strdup(VMC_GUEST_DEVICE_NAME);
++ svc->vmstate = qemu_add_vm_change_state_handler(
++ vmc_change_state_handler, svc);
++ virtio_serial_open(port);
++ return 0;
++}
++
++static int vmc_exitfn(VirtIOSerialDevice *dev)
++{
++ VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, &dev->qdev);
++ SpiceVirtualChannel *svc = DO_UPCAST(SpiceVirtualChannel, port, port);
++
++ dprintf(svc, 1, "%s\n", __func__);
++ vmc_unregister_interface(svc);
++ qemu_del_vm_change_state_handler(svc->vmstate);
++ virtio_serial_close(port);
++ return 0;
++}
++
++static VirtIOSerialPortInfo vmc_info = {
++ .qdev.name = VMC_DEVICE_NAME,
++ .qdev.size = sizeof(SpiceVirtualChannel),
++ .init = vmc_initfn,
++ .exit = vmc_exitfn,
++ .guest_open = vmc_guest_open,
++ .guest_close = vmc_guest_close,
++ .guest_ready = vmc_guest_ready,
++ .have_data = vmc_have_data,
++ .qdev.props = (Property[]) {
++ DEFINE_PROP_UINT32("nr", SpiceVirtualChannel, port.id, VIRTIO_CONSOLE_BAD_ID),
++ DEFINE_PROP_UINT32("debug", SpiceVirtualChannel, debug, 1),
++ DEFINE_PROP_END_OF_LIST(),
++ }
++};
++
++static void vmc_register(void)
++{
++ virtio_serial_port_qdev_register(&vmc_info);
++}
++device_init(vmc_register)
+--
+1.7.2.3
+
diff --git a/0017-spice-add-pci-vdi-port-backend-obsolete.patch b/0017-spice-add-pci-vdi-port-backend-obsolete.patch
new file mode 100644
index 0000000..6505bb7
--- /dev/null
+++ b/0017-spice-add-pci-vdi-port-backend-obsolete.patch
@@ -0,0 +1,592 @@
+From ee782dec6adaced9c5bb99d02253505fb635fa12 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel at redhat.com>
+Date: Fri, 12 Mar 2010 16:26:18 +0100
+Subject: [PATCH 17/39] spice: add pci vdi port backend (obsolete).
+
+This is *not* intended to be merged upstream. It is just here
+because the virtio-serial windows guest drivers are not ready,
+so you can't go with the new spice-vmc yet.
+---
+ Makefile.target | 2 +-
+ hw/spice-vdi.c | 556 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 557 insertions(+), 1 deletions(-)
+ create mode 100644 hw/spice-vdi.c
+
+diff --git a/Makefile.target b/Makefile.target
+index 90544c5..025bdb8 100644
+--- a/Makefile.target
++++ b/Makefile.target
+@@ -217,7 +217,7 @@ obj-i386-y += pc_piix.o
+ obj-i386-y += testdev.o
+ obj-i386-y += acpi.o acpi_piix4.o
+ obj-i386-$(CONFIG_SPICE) += qxl.o qxl-logger.o qxl-render.o
+-obj-i386-$(CONFIG_SPICE) += spice-vmc.o
++obj-i386-$(CONFIG_SPICE) += spice-vmc.o spice-vdi.o
+
+ obj-i386-y += pcspk.o i8254.o
+ obj-i386-$(CONFIG_KVM_PIT) += i8254-kvm.o
+diff --git a/hw/spice-vdi.c b/hw/spice-vdi.c
+new file mode 100644
+index 0000000..23cbbe1
+--- /dev/null
++++ b/hw/spice-vdi.c
+@@ -0,0 +1,556 @@
++#include <pthread.h>
++#include <signal.h>
++
++#include "qemu-common.h"
++#include "qemu-spice.h"
++#include "hw/hw.h"
++#include "hw/pc.h"
++#include "hw/pci.h"
++#include "console.h"
++#include "hw/vga_int.h"
++#include "qemu-timer.h"
++#include "sysemu.h"
++#include "console.h"
++#include "pci.h"
++#include "hw.h"
++#include "cpu-common.h"
++
++#include <spice.h>
++#include <spice-experimental.h>
++#include <spice/ipc_ring.h>
++#include <spice/barrier.h>
++
++#undef SPICE_RING_PROD_ITEM
++#define SPICE_RING_PROD_ITEM(r, ret) { \
++ typeof(r) start = r; \
++ typeof(r) end = r + 1; \
++ uint32_t prod = (r)->prod & SPICE_RING_INDEX_MASK(r); \
++ typeof(&(r)->items[prod]) m_item = &(r)->items[prod]; \
++ if (!((uint8_t*)m_item >= (uint8_t*)(start) && (uint8_t*)(m_item + 1) <= (uint8_t*)(end))) { \
++ abort(); \
++ } \
++ ret = &m_item->el; \
++ }
++
++#undef SPICE_RING_CONS_ITEM
++#define SPICE_RING_CONS_ITEM(r, ret) { \
++ typeof(r) start = r; \
++ typeof(r) end = r + 1; \
++ uint32_t cons = (r)->cons & SPICE_RING_INDEX_MASK(r); \
++ typeof(&(r)->items[cons]) m_item = &(r)->items[cons]; \
++ if (!((uint8_t*)m_item >= (uint8_t*)(start) && (uint8_t*)(m_item + 1) <= (uint8_t*)(end))) { \
++ abort(); \
++ } \
++ ret = &m_item->el; \
++ }
++
++
++#undef ALIGN
++#define ALIGN(a, b) (((a) + ((b) - 1)) & ~((b) - 1))
++
++#define REDHAT_PCI_VENDOR_ID 0x1b36
++#define VDI_PORT_DEVICE_ID 0x0105
++#define VDI_PORT_REVISION 0x01
++
++#define VDI_PORT_INTERRUPT (1 << 0)
++
++#define VDI_PORT_MAGIC (*(uint32_t*)"VDIP")
++
++#define VDI_PORT_DEV_NAME "vdi_port"
++#define VDI_PORT_SAVE_VERSION 20
++
++#include <spice/start-packed.h>
++
++typedef struct SPICE_ATTR_PACKED VDIPortPacket {
++ uint32_t gen;
++ uint32_t size;
++ uint8_t data[512 - 2 * sizeof(uint32_t)];
++} VDIPortPacket;
++
++SPICE_RING_DECLARE(VDIPortRing, VDIPortPacket, 32);
++
++enum {
++ VDI_PORT_IO_RANGE_INDEX,
++ VDI_PORT_RAM_RANGE_INDEX,
++};
++
++enum {
++ VDI_PORT_IO_CONNECTION,
++ VDI_PORT_IO_NOTIFY = 4,
++ VDI_PORT_IO_UPDATE_IRQ = 8,
++
++ VDI_PORT_IO_RANGE_SIZE = 12
++};
++
++typedef struct SPICE_ATTR_PACKED VDIPortRam {
++ uint32_t magic;
++ uint32_t generation;
++ uint32_t int_pending;
++ uint32_t int_mask;
++ VDIPortRing input;
++ VDIPortRing output;
++ uint32_t reserv[32];
++} VDIPortRam;
++
++#include <spice/end-packed.h>
++
++typedef struct PCIVDIPortDevice {
++ PCIDevice pci_dev;
++ uint32_t io_base;
++ uint64_t ram_offset;
++ uint32_t ram_size;
++ VDIPortRam *ram;
++ uint32_t connected;
++ int running;
++ int new_gen_on_resume;
++ int active_interface;
++ SpiceVDIPortInstance sin;
++ int plug_read_pos;
++} PCIVDIPortDevice;
++
++static int debug = 1;
++
++static inline uint32_t msb_mask(uint32_t val)
++{
++ uint32_t mask;
++
++ do {
++ mask = ~(val - 1) & val;
++ val &= ~mask;
++ } while (mask < val);
++
++ return mask;
++}
++
++static inline void atomic_or(uint32_t *var, uint32_t add)
++{
++ __asm__ __volatile__ ("lock; orl %1, %0" : "+m" (*var) : "r" (add) : "memory");
++}
++
++static inline uint32_t atomic_exchange(uint32_t val, uint32_t *ptr)
++{
++ __asm__ __volatile__("xchgl %0, %1" : "+q"(val), "+m" (*ptr) : : "memory");
++ return val;
++}
++
++static void set_dirty(void *base, ram_addr_t offset, void *start, uint32_t length)
++{
++ assert(start >= base);
++
++ ram_addr_t addr = (ram_addr_t)((uint8_t*)start - (uint8_t*)base) + offset;
++ ram_addr_t end = ALIGN(addr + length, TARGET_PAGE_SIZE);
++
++ do {
++ cpu_physical_memory_set_dirty(addr);
++ addr += TARGET_PAGE_SIZE;
++ } while ( addr < end );
++}
++
++static inline void vdi_port_set_dirty(PCIVDIPortDevice *d, void *start, uint32_t length)
++{
++ set_dirty(d->ram, d->ram_offset, start, length);
++}
++
++static void vdi_port_new_gen(PCIVDIPortDevice *d)
++{
++ d->ram->generation = (d->ram->generation + 1 == 0) ? 1 : d->ram->generation + 1;
++ vdi_port_set_dirty(d, &d->ram->generation, sizeof(d->ram->generation));
++}
++
++static int vdi_port_irq_level(PCIVDIPortDevice *d)
++{
++ return !!(d->ram->int_pending & d->ram->int_mask);
++}
++
++static void vdi_port_notify_guest(PCIVDIPortDevice *d)
++{
++ uint32_t events = VDI_PORT_INTERRUPT;
++ uint32_t old_pending;
++
++ if (!d->connected) {
++ return;
++ }
++ old_pending = __sync_fetch_and_or(&d->ram->int_pending, events);
++ if ((old_pending & events) == events) {
++ return;
++ }
++ qemu_set_irq(d->pci_dev.irq[0], vdi_port_irq_level(d));
++ vdi_port_set_dirty(d, &d->ram->int_pending, sizeof(d->ram->int_pending));
++}
++
++static int vdi_port_interface_write(SpiceVDIPortInstance *sin,
++ const uint8_t *buf, int len)
++{
++ PCIVDIPortDevice *d = container_of(sin, PCIVDIPortDevice, sin);
++ VDIPortRing *ring = &d->ram->output;
++ int do_notify = false;
++ int actual_write = 0;
++ int l = len;
++
++ if (!d->running) {
++ return 0;
++ }
++
++ while (len) {
++ VDIPortPacket *packet;
++ int notify;
++ int wait;
++
++ SPICE_RING_PROD_WAIT(ring, wait);
++ if (wait) {
++ break;
++ }
++
++ SPICE_RING_PROD_ITEM(ring, packet);
++ packet->gen = d->ram->generation;
++ packet->size = MIN(len, sizeof(packet->data));
++ memcpy(packet->data, buf, packet->size);
++ vdi_port_set_dirty(d, packet, sizeof(*packet) - (sizeof(packet->data) - packet->size));
++
++ SPICE_RING_PUSH(ring, notify);
++ do_notify = do_notify || notify;
++ len -= packet->size;
++ buf += packet->size;
++ actual_write += packet->size;
++ }
++ vdi_port_set_dirty(d, ring, sizeof(*ring) - sizeof(ring->items));
++
++ if (do_notify) {
++ vdi_port_notify_guest(d);
++ }
++ if (debug > 1) {
++ fprintf(stderr, "%s: %d/%d\n", __FUNCTION__, actual_write, l);
++ }
++ return actual_write;
++}
++
++static int vdi_port_interface_read(SpiceVDIPortInstance *sin,
++ uint8_t *buf, int len)
++{
++ PCIVDIPortDevice *d = container_of(sin, PCIVDIPortDevice, sin);
++ VDIPortRing *ring = &d->ram->input;
++ uint32_t gen = d->ram->generation;
++ VDIPortPacket *packet;
++ int do_notify = false;
++ int actual_read = 0;
++ int l = len;
++
++ if (!d->running) {
++ return 0;
++ }
++
++ while (!SPICE_RING_IS_EMPTY(ring)) {
++ int notify;
++
++ SPICE_RING_CONS_ITEM(ring, packet);
++ if (packet->gen == gen) {
++ break;
++ }
++ SPICE_RING_POP(ring, notify);
++ do_notify = do_notify || notify;
++ }
++ while (len) {
++ VDIPortPacket *packet;
++ int wait;
++ int now;
++
++ SPICE_RING_CONS_WAIT(ring, wait);
++
++ if (wait) {
++ break;
++ }
++
++ SPICE_RING_CONS_ITEM(ring, packet);
++ if (packet->size > sizeof(packet->data)) {
++ vdi_port_set_dirty(d, ring, sizeof(*ring) - sizeof(ring->items));
++ printf("%s: bad packet size\n", __FUNCTION__);
++ return 0;
++ }
++ now = MIN(len, packet->size - d->plug_read_pos);
++ memcpy(buf, packet->data + d->plug_read_pos, now);
++ len -= now;
++ buf += now;
++ actual_read += now;
++ if ((d->plug_read_pos += now) == packet->size) {
++ int notify;
++
++ d->plug_read_pos = 0;
++ SPICE_RING_POP(ring, notify);
++ do_notify = do_notify || notify;
++ }
++ }
++ vdi_port_set_dirty(d, ring, sizeof(*ring) - sizeof(ring->items));
++
++ if (do_notify) {
++ vdi_port_notify_guest(d);
++ }
++ if (debug > 1) {
++ fprintf(stderr, "%s: %d/%d\n", __FUNCTION__, actual_read, l);
++ }
++ return actual_read;
++}
++
++static SpiceVDIPortInterface vdi_port_interface = {
++ .base.type = SPICE_INTERFACE_VDI_PORT,
++ .base.description = "vdi port",
++ .base.major_version = SPICE_INTERFACE_VDI_PORT_MAJOR,
++ .base.minor_version = SPICE_INTERFACE_VDI_PORT_MINOR,
++
++ .write = vdi_port_interface_write,
++ .read = vdi_port_interface_read,
++};
++
++static void vdi_port_register_interface(PCIVDIPortDevice *d)
++{
++ if (d->active_interface ) {
++ return;
++ }
++
++ if (debug) {
++ fprintf(stderr, "%s\n", __FUNCTION__);
++ }
++ d->sin.base.sif = &vdi_port_interface.base;
++ spice_server_add_interface(spice_server, &d->sin.base);
++ d->active_interface = true;
++}
++
++static void vdi_port_unregister_interface(PCIVDIPortDevice *d)
++{
++ if (!d->active_interface ) {
++ return;
++ }
++ if (debug) {
++ fprintf(stderr, "%s\n", __FUNCTION__);
++ }
++ spice_server_remove_interface(&d->sin.base);
++ d->active_interface = false;
++}
++
++static uint32_t vdi_port_dev_connect(PCIVDIPortDevice *d)
++{
++ if (d->connected) {
++ if (debug) {
++ fprintf(stderr, "%s: already connected\n", __FUNCTION__);
++ }
++ return 0;
++ }
++ vdi_port_new_gen(d);
++ d->connected = true;
++ vdi_port_register_interface(d);
++ return d->ram->generation;
++}
++
++static void vdi_port_dev_disconnect(PCIVDIPortDevice *d)
++{
++ if (!d->connected) {
++ if (debug) {
++ fprintf(stderr, "%s: not connected\n", __FUNCTION__);
++ }
++ return;
++ }
++ d->connected = false;
++ vdi_port_unregister_interface(d);
++}
++
++static void vdi_port_dev_notify(PCIVDIPortDevice *d)
++{
++ spice_server_vdi_port_wakeup(&d->sin);
++}
++
++static void vdi_port_write_dword(void *opaque, uint32_t addr, uint32_t val)
++{
++ PCIVDIPortDevice *d = opaque;
++ uint32_t io_port = addr - d->io_base;
++
++ if (debug > 1) {
++ fprintf(stderr, "%s: addr 0x%x val 0x%x\n", __FUNCTION__, addr, val);
++ }
++ switch (io_port) {
++ case VDI_PORT_IO_NOTIFY:
++ if (!d->connected) {
++ fprintf(stderr, "%s: not connected\n", __FUNCTION__);
++ return;
++ }
++ vdi_port_dev_notify(d);
++ break;
++ case VDI_PORT_IO_UPDATE_IRQ:
++ qemu_set_irq(d->pci_dev.irq[0], vdi_port_irq_level(d));
++ break;
++ case VDI_PORT_IO_CONNECTION:
++ vdi_port_dev_disconnect(d);
++ break;
++ default:
++ if (debug) {
++ fprintf(stderr, "%s: unexpected addr 0x%x val 0x%x\n",
++ __FUNCTION__, addr, val);
++ }
++ };
++}
++
++static uint32_t vdi_port_read_dword(void *opaque, uint32_t addr)
++{
++ PCIVDIPortDevice *d = opaque;
++ uint32_t io_port = addr - d->io_base;
++
++ if (debug > 1) {
++ fprintf(stderr, "%s: addr 0x%x\n", __FUNCTION__, addr);
++ }
++ if (io_port == VDI_PORT_IO_CONNECTION) {
++ return vdi_port_dev_connect(d);
++ } else {
++ fprintf(stderr, "%s: unexpected addr 0x%x\n", __FUNCTION__, addr);
++ }
++ return 0xffffffff;
++}
++
++static void vdi_port_io_map(PCIDevice *pci_dev, int region_num,
++ pcibus_t addr, pcibus_t size, int type)
++{
++ PCIVDIPortDevice *d = DO_UPCAST(PCIVDIPortDevice, pci_dev, pci_dev);
++
++ if (debug) {
++ fprintf(stderr, "%s: base 0x%lx size 0x%lx\n", __FUNCTION__, addr, size);
++ }
++ d->io_base = addr;
++ register_ioport_write(addr, size, 4, vdi_port_write_dword, pci_dev);
++ register_ioport_read(addr, size, 4, vdi_port_read_dword, pci_dev);
++}
++
++static void vdi_port_ram_map(PCIDevice *pci_dev, int region_num,
++ pcibus_t addr, pcibus_t size, int type)
++{
++ PCIVDIPortDevice *d = DO_UPCAST(PCIVDIPortDevice, pci_dev, pci_dev);
++
++ if (debug) {
++ fprintf(stderr, "%s: addr 0x%lx size 0x%lx\n", __FUNCTION__, addr, size);
++ }
++
++ assert((addr & (size - 1)) == 0);
++ assert(size == d->ram_size);
++
++ cpu_register_physical_memory(addr, size, d->ram_offset | IO_MEM_RAM);
++}
++
++static void vdi_port_reset(PCIVDIPortDevice *d)
++{
++ memset(d->ram, 0, sizeof(*d->ram));
++ SPICE_RING_INIT(&d->ram->input);
++ SPICE_RING_INIT(&d->ram->output);
++ d->ram->magic = VDI_PORT_MAGIC;
++ d->ram->generation = 0;
++ d->ram->int_pending = 0;
++ d->ram->int_mask = 0;
++ d->connected = false;
++ d->plug_read_pos = 0;
++ vdi_port_set_dirty(d, d->ram, sizeof(*d->ram));
++}
++
++static void vdi_port_reset_handler(DeviceState *dev)
++{
++ PCIVDIPortDevice *d = DO_UPCAST(PCIVDIPortDevice, pci_dev.qdev, dev);
++
++ if (d->connected) {
++ vdi_port_dev_disconnect(d);
++ }
++
++ vdi_port_reset(d);
++ qemu_set_irq(d->pci_dev.irq[0], vdi_port_irq_level(d));
++}
++
++static int vdi_port_pre_load(void* opaque)
++{
++ PCIVDIPortDevice* d = opaque;
++
++ vdi_port_unregister_interface(d);
++ return 0;
++}
++
++static int vdi_port_post_load(void* opaque,int version_id)
++{
++ PCIVDIPortDevice* d = opaque;
++
++ if (d->connected) {
++ vdi_port_register_interface(d);
++ }
++ return 0;
++}
++
++static void vdi_port_vm_change_state_handler(void *opaque, int running, int reason)
++{
++ PCIVDIPortDevice* d = opaque;
++
++ if (running) {
++ d->running = true;
++ if (d->new_gen_on_resume) {
++ d->new_gen_on_resume = false;
++ vdi_port_new_gen(d);
++ vdi_port_notify_guest(d);
++ }
++ qemu_set_irq(d->pci_dev.irq[0], vdi_port_irq_level(d));
++ vdi_port_dev_notify(d);
++ } else {
++ d->running = false;
++ }
++}
++
++static int vdi_port_init(PCIDevice *dev)
++{
++ PCIVDIPortDevice *vdi = (PCIVDIPortDevice *)dev;
++ uint8_t* config = vdi->pci_dev.config;
++ uint32_t ram_size = msb_mask(sizeof(VDIPortRam) * 2 - 1);
++
++ vdi->ram_offset = qemu_ram_alloc(&vdi->pci_dev.qdev, "bar1", ram_size);
++ vdi->ram = qemu_get_ram_ptr(vdi->ram_offset);
++ vdi_port_reset(vdi);
++ vdi->ram_size = ram_size;
++ vdi->new_gen_on_resume = false;
++ vdi->running = false;
++
++ pci_config_set_vendor_id(config, REDHAT_PCI_VENDOR_ID);
++ pci_config_set_device_id(config, VDI_PORT_DEVICE_ID);
++ pci_config_set_class(config, PCI_CLASS_COMMUNICATION_OTHER);
++ pci_set_byte(&config[PCI_REVISION_ID], VDI_PORT_REVISION);
++ pci_set_byte(&config[PCI_INTERRUPT_PIN], 1);
++
++ pci_register_bar(dev, VDI_PORT_IO_RANGE_INDEX,
++ msb_mask(VDI_PORT_IO_RANGE_SIZE * 2 - 1),
++ PCI_BASE_ADDRESS_SPACE_IO, vdi_port_io_map);
++
++ pci_register_bar(dev, VDI_PORT_RAM_RANGE_INDEX,
++ vdi->ram_size , PCI_BASE_ADDRESS_SPACE_MEMORY,
++ vdi_port_ram_map);
++
++ qemu_add_vm_change_state_handler(vdi_port_vm_change_state_handler, vdi);
++
++ return 0;
++}
++
++static VMStateDescription vdi_port_vmstate = {
++ .name = VDI_PORT_DEV_NAME,
++ .version_id = VDI_PORT_SAVE_VERSION,
++ .minimum_version_id = VDI_PORT_SAVE_VERSION,
++ .pre_load = vdi_port_pre_load,
++ .post_load = vdi_port_post_load,
++ .fields = (VMStateField []) {
++ VMSTATE_PCI_DEVICE(pci_dev, PCIVDIPortDevice),
++ VMSTATE_UINT32(connected, PCIVDIPortDevice),
++ VMSTATE_END_OF_LIST()
++ }
++};
++
++static PCIDeviceInfo vdi_port_info = {
++ .qdev.name = VDI_PORT_DEV_NAME,
++ .qdev.desc = "spice virtual desktop port (obsolete)",
++ .qdev.size = sizeof(PCIVDIPortDevice),
++ .qdev.vmsd = &vdi_port_vmstate,
++ .qdev.reset = vdi_port_reset_handler,
++
++ .init = vdi_port_init,
++};
++
++static void vdi_port_register(void)
++{
++ pci_qdev_register(&vdi_port_info);
++}
++
++device_init(vdi_port_register);
+--
+1.7.2.3
+
diff --git a/0018-use-memalign-instead-of-posix_memalign.patch b/0018-use-memalign-instead-of-posix_memalign.patch
new file mode 100644
index 0000000..d7264c4
--- /dev/null
+++ b/0018-use-memalign-instead-of-posix_memalign.patch
@@ -0,0 +1,29 @@
+From 898303cfd535d76ce971f3ac1310696937fbd286 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel at redhat.com>
+Date: Mon, 14 Jun 2010 09:53:48 +0200
+Subject: [PATCH 18/39] use memalign instead of posix_memalign
+
+---
+ osdep.c | 5 +++++
+ 1 files changed, 5 insertions(+), 0 deletions(-)
+
+diff --git a/osdep.c b/osdep.c
+index 2375a69..ed2fd40 100644
+--- a/osdep.c
++++ b/osdep.c
+@@ -100,7 +100,12 @@ void *qemu_memalign(size_t alignment, size_t size)
+ #if defined(_POSIX_C_SOURCE) && !defined(__sun__)
+ int ret;
+ void *ptr;
++#if 0
+ ret = posix_memalign(&ptr, alignment, size);
++#else
++ ptr = memalign(alignment, size);
++ ret = (ptr == NULL) ? -1 : 0;
++#endif
+ if (ret != 0) {
+ fprintf(stderr, "Failed to allocate %zu B: %s\n",
+ size, strerror(ret));
+--
+1.7.2.3
+
diff --git a/0019-spice-live-migration-wip.patch b/0019-spice-live-migration-wip.patch
new file mode 100644
index 0000000..4e6ef6b
--- /dev/null
+++ b/0019-spice-live-migration-wip.patch
@@ -0,0 +1,179 @@
+From 80b1dac2be1487d31e6766abe2359fcff1bf0481 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel at redhat.com>
+Date: Fri, 23 Apr 2010 13:28:21 +0200
+Subject: [PATCH 19/39] spice: live migration (wip).
+
+Handle spice client migration, i.e. inform a spice client connected
+about the new host and connection parameters, so it can move over the
+connection automatically.
+---
+ monitor.c | 1 +
+ qemu-monitor.hx | 11 +++++++
+ qemu-spice.h | 2 +
+ spice.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ 4 files changed, 101 insertions(+), 0 deletions(-)
+
+diff --git a/monitor.c b/monitor.c
+index e51df62..6674a8c 100644
+--- a/monitor.c
++++ b/monitor.c
+@@ -57,6 +57,7 @@
+ #include "osdep.h"
+ #include "exec-all.h"
+ #include "qemu-kvm.h"
++#include "qemu-spice.h"
+
+ //#define DEBUG
+ //#define DEBUG_COMPLETION
+diff --git a/qemu-monitor.hx b/qemu-monitor.hx
+index da7b796..c2570d9 100644
+--- a/qemu-monitor.hx
++++ b/qemu-monitor.hx
+@@ -2510,6 +2510,17 @@ ETEXI
+
+ HXCOMM DO NOT add new commands after 'info', move your addition before it!
+
++#if defined(CONFIG_SPICE)
++ {
++ .name = "spice_migrate_info",
++ .args_type = "hostname:s,port:i?,tls-port:i?,cert-subject:s?",
++ .params = "hostname port tls-port cert-subject",
++ .help = "send migration info to spice client",
++ .user_print = monitor_user_noop,
++ .mhandler.cmd_new = mon_spice_migrate,
++ },
++#endif
++
+ STEXI
+ @end table
+ ETEXI
+diff --git a/qemu-spice.h b/qemu-spice.h
+index 6f19ba7..3c8e959 100644
+--- a/qemu-spice.h
++++ b/qemu-spice.h
+@@ -16,6 +16,8 @@ void qemu_spice_input_init(void);
+ void qemu_spice_audio_init(void);
+ void qemu_spice_display_init(DisplayState *ds);
+
++int mon_spice_migrate(Monitor *mon, const QDict *qdict, QObject **ret_data);
++
+ #else /* CONFIG_SPICE */
+
+ #define using_spice 0
+diff --git a/spice.c b/spice.c
+index fc76ef7..1109b4f 100644
+--- a/spice.c
++++ b/spice.c
+@@ -11,6 +11,7 @@
+ #include "qemu-queue.h"
+ #include "qemu-x509.h"
+ #include "monitor.h"
++#include "hw/hw.h"
+
+ /* core bits */
+
+@@ -122,8 +123,90 @@ static SpiceCoreInterface core_interface = {
+ .watch_remove = watch_remove,
+ };
+
++/* handle client migration */
++
++static int spice_live(Monitor *mon, QEMUFile *f, int stage, void *opaque)
++{
++ static int last_stage;
++ static int migrate_client, client_connected;
++ int ret = 1;
++
++ if (last_stage != stage) {
++ last_stage = stage;
++ fprintf(stderr, "%s: stage %d\n", __FUNCTION__, stage);
++ } else {
++ fprintf(stderr, ".");
++ }
++
++ switch (stage) {
++ case 1:
++ migrate_client = 1;
++ client_connected = 0;
++ fprintf(stderr, "%s: start client migration\n", __FUNCTION__);
++ if (spice_server_migrate_start(spice_server) != 0) {
++ fprintf(stderr, "%s: fail -> no client migration\n", __FUNCTION__);
++ migrate_client = 0;
++ }
++ break;
++ case 2:
++ if (!migrate_client)
++ break;
++ switch (spice_server_migrate_client_state(spice_server)) {
++ case SPICE_MIGRATE_CLIENT_NONE:
++ fprintf(stderr, "%s: no client connected\n", __FUNCTION__);
++ migrate_client = 0;
++ break;
++ case SPICE_MIGRATE_CLIENT_WAITING:
++ ret = 0;
++ break;
++ case SPICE_MIGRATE_CLIENT_READY:
++ if (!client_connected) {
++ fprintf(stderr, "%s: client connected to target\n", __FUNCTION__);
++ client_connected = 1;
++ }
++ break;
++ }
++ break;
++ case 3:
++ if (migrate_client && client_connected) {
++ fprintf(stderr, "%s: finish client migration\n", __FUNCTION__);
++ spice_server_migrate_end(spice_server, 1);
++ }
++ break;
++ }
++ return ret;
++}
++
++static void spice_save(QEMUFile *f, void *opaque)
++{
++ fprintf(stderr, "%s:\n", __FUNCTION__);
++}
++
++static int spice_load(QEMUFile *f, void *opaque, int version_id)
++{
++ fprintf(stderr, "%s:\n", __FUNCTION__);
++ return 0;
++}
++
+ /* functions for the rest of qemu */
+
++int mon_spice_migrate(Monitor *mon, const QDict *qdict, QObject **ret_data)
++{
++ const char *hostname = qdict_get_str(qdict, "hostname");
++ const char *subject = qdict_get_try_str(qdict, "cert-subject");
++ int port = qdict_get_try_int(qdict, "port", -1);
++ int tls_port = qdict_get_try_int(qdict, "tls-port", -1);
++
++ if (!spice_server) {
++ qerror_report(QERR_DEVICE_NOT_ACTIVE, "spice");
++ return -1;
++ }
++
++ /* TODO: Convert to QError */
++ return spice_server_migrate_info(spice_server, hostname,
++ port, tls_port, subject);
++}
++
+ void qemu_spice_init(void)
+ {
+ QemuOpts *opts = QTAILQ_FIRST(&qemu_spice_opts.head);
+@@ -206,6 +289,10 @@ void qemu_spice_init(void)
+ qemu_spice_input_init();
+ qemu_spice_audio_init();
+
++ register_savevm_live(NULL, "spice", -1, 1, NULL,
++ spice_live, spice_save, spice_load,
++ spice_server);
++
+ qemu_free(x509_key_file);
+ qemu_free(x509_cert_file);
+ qemu_free(x509_cacert_file);
+--
+1.7.2.3
+
diff --git a/0020-spice-display-draw.h-is-internal-now.patch b/0020-spice-display-draw.h-is-internal-now.patch
new file mode 100644
index 0000000..e734447
--- /dev/null
+++ b/0020-spice-display-draw.h-is-internal-now.patch
@@ -0,0 +1,23 @@
+From 7241cc479a0f4a148ae60336add6d7be80da9ff0 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel at redhat.com>
+Date: Thu, 8 Jul 2010 14:11:18 +0200
+Subject: [PATCH 20/39] spice-display: draw.h is internal now
+
+---
+ spice-display.h | 2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/spice-display.h b/spice-display.h
+index 70a7be4..b55e7ea 100644
+--- a/spice-display.h
++++ b/spice-display.h
+@@ -1,5 +1,5 @@
+ #include <spice/ipc_ring.h>
+-#include <spice/draw.h>
++#include <spice/enums.h>
+ #include <spice/qxl_dev.h>
+
+ #include "pflib.h"
+--
+1.7.2.3
+
diff --git a/0021-spice-display-disable-debug.patch b/0021-spice-display-disable-debug.patch
new file mode 100644
index 0000000..bdaee85
--- /dev/null
+++ b/0021-spice-display-disable-debug.patch
@@ -0,0 +1,25 @@
+From c269c8b87769b25c9d69d40944de0e883458af86 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel at redhat.com>
+Date: Thu, 8 Jul 2010 14:31:10 +0200
+Subject: [PATCH 21/39] spice-display: disable debug
+
+---
+ spice-display.c | 2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/spice-display.c b/spice-display.c
+index a749e64..2291cc7 100644
+--- a/spice-display.c
++++ b/spice-display.c
+@@ -15,7 +15,7 @@
+
+ #include "spice-display.h"
+
+-static int debug = 1;
++static int debug = 0;
+
+ int qemu_spice_rect_is_empty(const QXLRect* r)
+ {
+--
+1.7.2.3
+
diff --git a/0022-spice-display-pci-rev-fixups.patch b/0022-spice-display-pci-rev-fixups.patch
new file mode 100644
index 0000000..87534af
--- /dev/null
+++ b/0022-spice-display-pci-rev-fixups.patch
@@ -0,0 +1,27 @@
+From 2912f038b4bfddd4c3dacb3b0102e45553859632 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel at redhat.com>
+Date: Thu, 8 Jul 2010 16:29:15 +0200
+Subject: [PATCH 22/39] spice-display: pci rev fixups
+
+---
+ spice-display.c | 4 ----
+ 1 files changed, 0 insertions(+), 4 deletions(-)
+
+diff --git a/spice-display.c b/spice-display.c
+index 2291cc7..87a71cd 100644
+--- a/spice-display.c
++++ b/spice-display.c
+@@ -334,10 +334,6 @@ static const QXLInterface dpy_interface = {
+ .base.major_version = SPICE_INTERFACE_QXL_MAJOR,
+ .base.minor_version = SPICE_INTERFACE_QXL_MINOR,
+
+- .pci_vendor = REDHAT_PCI_VENDOR_ID,
+- .pci_id = QXL_DEVICE_ID,
+- .pci_revision = QXL_REVISION,
+-
+ .attache_worker = interface_attach_worker,
+ .set_compression_level = interface_set_compression_level,
+ .set_mm_time = interface_set_mm_time,
+--
+1.7.2.3
+
diff --git a/0023-qxl-pci-rev-fixups.patch b/0023-qxl-pci-rev-fixups.patch
new file mode 100644
index 0000000..5a9fc24
--- /dev/null
+++ b/0023-qxl-pci-rev-fixups.patch
@@ -0,0 +1,52 @@
+From 727553e1a33dccab2b27ee5e184e003440765289 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel at redhat.com>
+Date: Thu, 8 Jul 2010 16:29:27 +0200
+Subject: [PATCH 23/39] qxl: pci rev fixups
+
+---
+ hw/qxl.c | 20 ++++++++------------
+ 1 files changed, 8 insertions(+), 12 deletions(-)
+
+diff --git a/hw/qxl.c b/hw/qxl.c
+index 475360c..2a0ea4e 100644
+--- a/hw/qxl.c
++++ b/hw/qxl.c
+@@ -511,10 +511,6 @@ static const QXLInterface qxl_interface = {
+ .base.major_version = SPICE_INTERFACE_QXL_MAJOR,
+ .base.minor_version = SPICE_INTERFACE_QXL_MINOR,
+
+- .pci_vendor = REDHAT_PCI_VENDOR_ID,
+- .pci_id = QXL_DEVICE_ID,
+- .pci_revision = QXL_REVISION,
+-
+ .attache_worker = interface_attach_worker,
+ .set_compression_level = interface_set_compression_level,
+ .set_mm_time = interface_set_mm_time,
+@@ -1136,16 +1132,16 @@ static int qxl_init(PCIDevice *dev)
+ qxl->generation = 1;
+
+ switch (qxl->revision) {
+- case 1: /* qxl-1 */
+- pci_device_id = 0x0100;
+- pci_device_rev = 1;
++ case 1: /* spice 0.4 -- qxl-1 */
++ pci_device_id = QXL_DEVICE_ID_STABLE;
++ pci_device_rev = QXL_REVISION_STABLE_V04;
+ break;
+- case 2: /* qxl-2 */
+- pci_device_id = 0x0100;
+- pci_device_rev = 2;
++ case 2: /* spice 0.6 -- qxl-2 */
++ pci_device_id = QXL_DEVICE_ID_STABLE;
++ pci_device_rev = QXL_REVISION_STABLE_V06;
+ break;
+- default: /* unstable */
+- pci_device_id = 0x01ff;
++ default: /* experimental */
++ pci_device_id = QXL_DEVICE_ID_DEVEL;
+ pci_device_rev = 1;
+ break;
+ }
+--
+1.7.2.3
+
diff --git a/0024-qxl-support-QXL_IO_DESTROY_ALL_SURFACES.patch b/0024-qxl-support-QXL_IO_DESTROY_ALL_SURFACES.patch
new file mode 100644
index 0000000..07882f3
--- /dev/null
+++ b/0024-qxl-support-QXL_IO_DESTROY_ALL_SURFACES.patch
@@ -0,0 +1,26 @@
+From c9f9044475d392e55de4dd6e343477ce1a57eabc Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel at redhat.com>
+Date: Thu, 8 Jul 2010 17:51:09 +0200
+Subject: [PATCH 24/39] qxl: support QXL_IO_DESTROY_ALL_SURFACES
+
+---
+ hw/qxl.c | 3 +++
+ 1 files changed, 3 insertions(+), 0 deletions(-)
+
+diff --git a/hw/qxl.c b/hw/qxl.c
+index 2a0ea4e..7bd4467 100644
+--- a/hw/qxl.c
++++ b/hw/qxl.c
+@@ -930,6 +930,9 @@ static void ioport_write(void *opaque, uint32_t addr, uint32_t val)
+ case QXL_IO_DESTROY_SURFACE_WAIT:
+ d->ssd.worker->destroy_surface_wait(d->ssd.worker, val);
+ break;
++ case QXL_IO_DESTROY_ALL_SURFACES:
++ d->ssd.worker->destroy_surfaces(d->ssd.worker);
++ break;
+ default:
+ fprintf(stderr, "%s: ioport=0x%x, abort()\n", __FUNCTION__, io_port);
+ abort();
+--
+1.7.2.3
+
diff --git a/0025-spice-vmc-two-bugfixes-in-vmc_read.patch b/0025-spice-vmc-two-bugfixes-in-vmc_read.patch
new file mode 100644
index 0000000..3b4178b
--- /dev/null
+++ b/0025-spice-vmc-two-bugfixes-in-vmc_read.patch
@@ -0,0 +1,57 @@
+From c8fa37e075cf59e8b21af9211f6a6348c92ed098 Mon Sep 17 00:00:00 2001
+From: Alon Levy <alevy at redhat.com>
+Date: Mon, 12 Jul 2010 22:48:59 +0300
+Subject: [PATCH 25/39] spice-vmc: two bugfixes in vmc_read
+
+ * throttling with no discard means possible recursion, make
+ vmc_read handle that.
+ * zero datapos when data is done (from rhel6 version)
+---
+ hw/spice-vmc.c | 13 ++++++++-----
+ 1 files changed, 8 insertions(+), 5 deletions(-)
+
+diff --git a/hw/spice-vmc.c b/hw/spice-vmc.c
+index 3f6a2bb..06e30e6 100644
+--- a/hw/spice-vmc.c
++++ b/hw/spice-vmc.c
+@@ -45,7 +45,7 @@ static int vmc_write(SpiceVDIPortInstance *sin, const uint8_t *buf, int len)
+ ssize_t out;
+
+ out = virtio_serial_write(&svc->port, buf, len);
+- dprintf(svc, 2, "%s: %lu/%d\n", __func__, out, len);
++ dprintf(svc, 3, "%s: %lu/%d\n", __func__, out, len);
+ return out;
+ }
+
+@@ -54,13 +54,16 @@ static int vmc_read(SpiceVDIPortInstance *sin, uint8_t *buf, int len)
+ SpiceVirtualChannel *svc = container_of(sin, SpiceVirtualChannel, sin);
+ int bytes = MIN(len, svc->datalen);
+
+- dprintf(svc, 2, "%s: %d/%zd\n", __func__, bytes, svc->datalen);
+- if (bytes) {
++ dprintf(svc, 2, "%s: %p %d/%d/%zd\n", __func__, svc->datapos, len, bytes, svc->datalen);
++ if (bytes > 0) {
+ memcpy(buf, svc->datapos, bytes);
+ svc->datapos += bytes;
+ svc->datalen -= bytes;
+- if (0 == svc->datalen) {
++ assert(svc->datalen >= 0);
++ if (svc->datalen == 0) {
++ svc->datapos = 0;
+ virtio_serial_throttle_port(&svc->port, false);
++ // ^^^ !!! may call vmc_have_data, so don't touch svc after it!
+ }
+ }
+ return bytes;
+@@ -140,7 +143,7 @@ static void vmc_have_data(VirtIOSerialPort *port, const uint8_t *buf, size_t len
+ SpiceVirtualChannel *svc = DO_UPCAST(SpiceVirtualChannel, port, port);
+
+ dprintf(svc, 2, "%s: %zd\n", __func__, len);
+- assert(svc->datapos == 0);
++ assert(svc->datalen == 0);
+ if (svc->bufsize < len) {
+ svc->bufsize = len;
+ svc->buffer = qemu_realloc(svc->buffer, svc->bufsize);
+--
+1.7.2.3
+
diff --git a/0026-spice-enabling-disabling-jpeg-and-zlib-over-glz-via-.patch b/0026-spice-enabling-disabling-jpeg-and-zlib-over-glz-via-.patch
new file mode 100644
index 0000000..0af9bae
--- /dev/null
+++ b/0026-spice-enabling-disabling-jpeg-and-zlib-over-glz-via-.patch
@@ -0,0 +1,84 @@
+From 0045574847883167f5c2b569811e049616ee611d Mon Sep 17 00:00:00 2001
+From: Yonit Halperin <yhalperi at redhat.com>
+Date: Wed, 14 Jul 2010 13:26:34 +0300
+Subject: [PATCH 26/39] spice: enabling/disabling jpeg and zlib-over-glz via spice command line args
+
+---
+ qemu-config.c | 6 ++++++
+ spice.c | 29 +++++++++++++++++++++++++++++
+ 2 files changed, 35 insertions(+), 0 deletions(-)
+
+diff --git a/qemu-config.c b/qemu-config.c
+index 74bfc62..3e4fcf9 100644
+--- a/qemu-config.c
++++ b/qemu-config.c
+@@ -384,6 +384,12 @@ QemuOptsList qemu_spice_opts = {
+ },{
+ .name = "tls-ciphers",
+ .type = QEMU_OPT_STRING,
++ },{
++ .name = "jpeg",
++ .type = QEMU_OPT_STRING,
++ },{
++ .name = "zlib-glz",
++ .type = QEMU_OPT_STRING,
+ },
+ { /* end if list */ }
+ },
+diff --git a/spice.c b/spice.c
+index 1109b4f..201e53c 100644
+--- a/spice.c
++++ b/spice.c
+@@ -207,6 +207,23 @@ int mon_spice_migrate(Monitor *mon, const QDict *qdict, QObject **ret_data)
+ port, tls_port, subject);
+ }
+
++static inline spice_wan_compression_t get_wan_compression_value(const char *str)
++{
++ if (!strcmp(str, "wan")) {
++ return SPICE_WAN_COMPRESSION_AUTO;
++ }
++
++ if (!strcmp(str, "never")) {
++ return SPICE_WAN_COMPRESSION_NEVER;
++ }
++
++ if (!strcmp(str, "always")) {
++ return SPICE_WAN_COMPRESSION_ALWAYS;
++ }
++
++ return SPICE_WAN_COMPRESSION_INVALID;
++}
++
+ void qemu_spice_init(void)
+ {
+ QemuOpts *opts = QTAILQ_FIRST(&qemu_spice_opts.head);
+@@ -218,6 +235,7 @@ void qemu_spice_init(void)
+ *x509_cert_file = NULL,
+ *x509_cacert_file = NULL;
+ int port, tls_port, len;
++ const char *jpeg, *zlib_glz;
+
+ if (!opts)
+ return;
+@@ -283,6 +301,17 @@ void qemu_spice_init(void)
+ /* TODO: make configurable via cmdline */
+ spice_server_set_image_compression(spice_server, SPICE_IMAGE_COMPRESS_AUTO_GLZ);
+
++ jpeg = qemu_opt_get(opts, "jpeg");
++ zlib_glz = qemu_opt_get(opts, "zlib-glz");
++
++ if (jpeg) {
++ spice_server_set_jpeg_compression(spice_server, get_wan_compression_value(jpeg));
++ }
++
++ if (zlib_glz) {
++ spice_server_set_zlib_glz_compression(spice_server, get_wan_compression_value(zlib_glz));
++ }
++
+ spice_server_init(spice_server, &core_interface);
+ using_spice = 1;
+
+--
+1.7.2.3
+
diff --git a/0027-ifdef-new-config-options.patch b/0027-ifdef-new-config-options.patch
new file mode 100644
index 0000000..9b185eb
--- /dev/null
+++ b/0027-ifdef-new-config-options.patch
@@ -0,0 +1,57 @@
+From 9200133d24ee5b5dab71ce922882c3534d9e8a5a Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel at redhat.com>
+Date: Thu, 15 Jul 2010 09:01:28 +0200
+Subject: [PATCH 27/39] ifdef new config options.
+
+---
+ spice.c | 6 +++++-
+ 1 files changed, 5 insertions(+), 1 deletions(-)
+
+diff --git a/spice.c b/spice.c
+index 201e53c..76e6ac1 100644
+--- a/spice.c
++++ b/spice.c
+@@ -207,6 +207,7 @@ int mon_spice_migrate(Monitor *mon, const QDict *qdict, QObject **ret_data)
+ port, tls_port, subject);
+ }
+
++#if defined(SPICE_SERVER_VERSION) && SPICE_SERVER_VERSION >= 0x000503
+ static inline spice_wan_compression_t get_wan_compression_value(const char *str)
+ {
+ if (!strcmp(str, "wan")) {
+@@ -223,6 +224,7 @@ static inline spice_wan_compression_t get_wan_compression_value(const char *str)
+
+ return SPICE_WAN_COMPRESSION_INVALID;
+ }
++#endif
+
+ void qemu_spice_init(void)
+ {
+@@ -235,7 +237,6 @@ void qemu_spice_init(void)
+ *x509_cert_file = NULL,
+ *x509_cacert_file = NULL;
+ int port, tls_port, len;
+- const char *jpeg, *zlib_glz;
+
+ if (!opts)
+ return;
+@@ -301,6 +302,8 @@ void qemu_spice_init(void)
+ /* TODO: make configurable via cmdline */
+ spice_server_set_image_compression(spice_server, SPICE_IMAGE_COMPRESS_AUTO_GLZ);
+
++#if defined(SPICE_SERVER_VERSION) && SPICE_SERVER_VERSION >= 0x000503
++ const char *jpeg, *zlib_glz;
+ jpeg = qemu_opt_get(opts, "jpeg");
+ zlib_glz = qemu_opt_get(opts, "zlib-glz");
+
+@@ -311,6 +314,7 @@ void qemu_spice_init(void)
+ if (zlib_glz) {
+ spice_server_set_zlib_glz_compression(spice_server, get_wan_compression_value(zlib_glz));
+ }
++#endif
+
+ spice_server_init(spice_server, &core_interface);
+ using_spice = 1;
+--
+1.7.2.3
+
diff --git a/0028-spice-vmc-add-counter-to-debug-statements.patch b/0028-spice-vmc-add-counter-to-debug-statements.patch
new file mode 100644
index 0000000..2a3b23c
--- /dev/null
+++ b/0028-spice-vmc-add-counter-to-debug-statements.patch
@@ -0,0 +1,27 @@
+From 2165916a311108d39c7aa45e5189af26712234b8 Mon Sep 17 00:00:00 2001
+From: Alon Levy <alevy at redhat.com>
+Date: Wed, 14 Jul 2010 16:30:35 +0300
+Subject: [PATCH 28/39] spice-vmc: add counter to debug statements
+
+---
+ hw/spice-vmc.c | 3 ++-
+ 1 files changed, 2 insertions(+), 1 deletions(-)
+
+diff --git a/hw/spice-vmc.c b/hw/spice-vmc.c
+index 06e30e6..041f243 100644
+--- a/hw/spice-vmc.c
++++ b/hw/spice-vmc.c
+@@ -23,8 +23,9 @@
+
+ #define dprintf(_svc, _level, _fmt, ...) \
+ do { \
++ static unsigned __dprintf_counter = 0; \
+ if (_svc->debug >= _level) { \
+- fprintf(stderr, "svc: " _fmt, ## __VA_ARGS__); \
++ fprintf(stderr, "svc: %3d: " _fmt, ++__dprintf_counter, ## __VA_ARGS__);\
+ } \
+ } while (0)
+
+--
+1.7.2.3
+
diff --git a/0029-spice-vmc-split-vmc_write-to-max-sized-virtio_serial.patch b/0029-spice-vmc-split-vmc_write-to-max-sized-virtio_serial.patch
new file mode 100644
index 0000000..0378f1c
--- /dev/null
+++ b/0029-spice-vmc-split-vmc_write-to-max-sized-virtio_serial.patch
@@ -0,0 +1,53 @@
+From f86c044ae075d142e658e866572eb0a37ecad2e1 Mon Sep 17 00:00:00 2001
+From: Alon Levy <alevy at redhat.com>
+Date: Thu, 22 Jul 2010 00:21:18 +0300
+Subject: [PATCH 29/39] spice-vmc: split vmc_write to max sized virtio_serial_write calls
+
+workaround for current windows driver limitation (RHBZ 617000)
+---
+ hw/spice-vmc.c | 21 ++++++++++++++++++---
+ 1 files changed, 18 insertions(+), 3 deletions(-)
+
+diff --git a/hw/spice-vmc.c b/hw/spice-vmc.c
+index 041f243..b9d64a2 100644
+--- a/hw/spice-vmc.c
++++ b/hw/spice-vmc.c
+@@ -21,6 +21,8 @@
+ #define VMC_GUEST_DEVICE_NAME "com.redhat.spice.0"
+ #define VMC_DEVICE_NAME "spicevmc"
+
++#define VMC_MAX_HOST_WRITE 2048
++
+ #define dprintf(_svc, _level, _fmt, ...) \
+ do { \
+ static unsigned __dprintf_counter = 0; \
+@@ -43,10 +45,23 @@ typedef struct SpiceVirtualChannel {
+ static int vmc_write(SpiceVDIPortInstance *sin, const uint8_t *buf, int len)
+ {
+ SpiceVirtualChannel *svc = container_of(sin, SpiceVirtualChannel, sin);
+- ssize_t out;
++ ssize_t out = 0;
++ ssize_t last_out;
++ uint8_t* p = (uint8_t*)buf;
++
++ while (len > 0) {
++ last_out = virtio_serial_write(&svc->port, p,
++ MIN(len, VMC_MAX_HOST_WRITE));
++ if (last_out > 0) {
++ out += last_out;
++ len -= last_out;
++ p += last_out;
++ } else {
++ break;
++ }
++ }
+
+- out = virtio_serial_write(&svc->port, buf, len);
+- dprintf(svc, 3, "%s: %lu/%d\n", __func__, out, len);
++ dprintf(svc, 3, "%s: %lu/%zd\n", __func__, out, len + out);
+ return out;
+ }
+
+--
+1.7.2.3
+
diff --git a/0030-qxl-add-800x480-resolution-to-qxl_modes-n900-native.patch b/0030-qxl-add-800x480-resolution-to-qxl_modes-n900-native.patch
new file mode 100644
index 0000000..4eb6f8c
--- /dev/null
+++ b/0030-qxl-add-800x480-resolution-to-qxl_modes-n900-native.patch
@@ -0,0 +1,24 @@
+From be78cc4a136f8ec63dc6d7efd8356625c639a877 Mon Sep 17 00:00:00 2001
+From: Alon Levy <alevy at redhat.com>
+Date: Tue, 3 Aug 2010 11:37:51 +0300
+Subject: [PATCH 30/39] qxl: add 800x480 resolution to qxl_modes (n900 native)
+
+---
+ hw/qxl.c | 1 +
+ 1 files changed, 1 insertions(+), 0 deletions(-)
+
+diff --git a/hw/qxl.c b/hw/qxl.c
+index 7bd4467..86c0e03 100644
+--- a/hw/qxl.c
++++ b/hw/qxl.c
+@@ -64,6 +64,7 @@
+
+ static QXLMode qxl_modes[] = {
+ QXL_MODE_EX(640, 480),
++ QXL_MODE_EX(800, 480),
+ QXL_MODE_EX(800, 600),
+ QXL_MODE_EX(832, 624),
+ QXL_MODE_EX(1024, 768),
+--
+1.7.2.3
+
diff --git a/0031-qxl-savevm-fixes.patch b/0031-qxl-savevm-fixes.patch
new file mode 100644
index 0000000..93054e8
--- /dev/null
+++ b/0031-qxl-savevm-fixes.patch
@@ -0,0 +1,259 @@
+From e55e5fd43113a5b266efa6d17e44f0e9231a98ed Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel at redhat.com>
+Date: Wed, 25 Aug 2010 16:08:37 +0000
+Subject: [PATCH 31/39] qxl: savevm fixes
+
+---
+ hw/qxl.c | 125 +++++++++++++++++++++++++++++++++++++++++++++----------------
+ hw/qxl.h | 6 +++
+ 2 files changed, 98 insertions(+), 33 deletions(-)
+
+diff --git a/hw/qxl.c b/hw/qxl.c
+index 86c0e03..4a15200 100644
+--- a/hw/qxl.c
++++ b/hw/qxl.c
+@@ -1087,6 +1087,14 @@ static void qxl_vm_change_state_handler(void *opaque, int running, int reason)
+ {
+ PCIQXLDevice *qxl = opaque;
+ qemu_spice_vm_change_state_handler(&qxl->ssd, running, reason);
++
++ if (!running && qxl->mode == QXL_MODE_NATIVE) {
++ /* dirty all vram (which holds surfaces) to make sure it is saved */
++ /* FIXME #1: should go out during "live" stage */
++ /* FIXME #2: we only need to save the areas which are actually used */
++ ram_addr_t addr = qxl->vram_offset;
++ qxl_set_dirty(addr, addr + qxl->vram_size);
++ }
+ }
+
+ /* display change listener */
+@@ -1134,6 +1142,8 @@ static int qxl_init(PCIDevice *dev)
+ qxl->id = device_id;
+ qxl->mode = QXL_MODE_UNDEFINED;
+ qxl->generation = 1;
++ qxl->num_memslots = NUM_MEMSLOTS;
++ qxl->num_surfaces = NUM_SURFACES;
+
+ switch (qxl->revision) {
+ case 1: /* spice 0.4 -- qxl-1 */
+@@ -1183,7 +1193,8 @@ static int qxl_init(PCIDevice *dev)
+ if (ram_size < 16 * 1024 * 1024)
+ ram_size = 16 * 1024 * 1024;
+ qxl->vga.vram_size = ram_size;
+- qxl->vga.vram_offset = qemu_ram_alloc(&qxl->pci.qdev, "bar0", qxl->vga.vram_size);
++ qxl->vga.vram_offset = qemu_ram_alloc(&qxl->pci.qdev, "qxl.vgavram",
++ qxl->vga.vram_size);
+ qxl->vga.vram_ptr = qemu_get_ram_ptr(qxl->vga.vram_offset);
+
+ pci_config_set_class(config, PCI_CLASS_DISPLAY_OTHER);
+@@ -1195,14 +1206,14 @@ static int qxl_init(PCIDevice *dev)
+ pci_set_byte(&config[PCI_INTERRUPT_PIN], 1);
+
+ qxl->rom_size = qxl_rom_size();
+- qxl->rom_offset = qemu_ram_alloc(&qxl->pci.qdev, "bar2", qxl->rom_size);
++ qxl->rom_offset = qemu_ram_alloc(&qxl->pci.qdev, "qxl.vrom", qxl->rom_size);
+ init_qxl_rom(qxl);
+ init_qxl_ram(qxl);
+
+ if (qxl->vram_size < 16 * 1024 * 1024)
+ qxl->vram_size = 16 * 1024 * 1024;
+ qxl->vram_size = msb_mask(qxl->vram_size * 2 - 1);
+- qxl->vram_offset = qemu_ram_alloc(&qxl->pci.qdev, "bar1", qxl->vram_size);
++ qxl->vram_offset = qemu_ram_alloc(&qxl->pci.qdev, "qxl.vram", qxl->vram_size);
+
+ pci_register_bar(&qxl->pci, QXL_IO_RANGE_INDEX,
+ msb_mask(QXL_IO_RANGE_SIZE * 2 - 1),
+@@ -1237,14 +1248,12 @@ static void qxl_pre_save(void *opaque)
+ uint8_t *ram_start = d->vga.vram_ptr;
+
+ dprintf(d, 1, "%s:\n", __FUNCTION__);
+-#if 1 /* wanna zap this */
+ if (d->last_release == NULL) {
+ d->last_release_offset = 0;
+ } else {
+ d->last_release_offset = (uint8_t *)d->last_release - ram_start;
+ }
+ assert(d->last_release_offset < d->vga.vram_size);
+-#endif
+ }
+
+ static int qxl_pre_load(void *opaque)
+@@ -1306,29 +1315,55 @@ static int qxl_post_load(void *opaque, int version)
+ }
+ dprintf(d, 1, "%s: done\n", __FUNCTION__);
+
+-#if 1 /* wanna zap this */
+- if (d->last_release_offset >= d->vga.vram_size) {
+- dprintf(d, 1, "%s: invalid last_release_offset %u, ram_size %u\n",
+- __FUNCTION__, d->last_release_offset, d->vga.vram_size);
+- exit(-1);
+- }
+-
++ assert(d->last_release_offset < d->vga.vram_size);
+ if (d->last_release_offset == 0) {
+ d->last_release = NULL;
+ } else {
+ d->last_release = (QXLReleaseInfo *)(ram_start + d->last_release_offset);
+ }
+-#endif
++
++ /* spice 0.4 compatibility -- accept but ignore */
++ free(d->worker_data);
++ d->worker_data = NULL;
++ d->worker_data_size = 0;
+
+ return 0;
+ }
+
+-#define QXL_VER 1
++#define QXL_SAVE_VERSION 20
++
++static bool qxl_test_worker_data(void *opaque, int version_id)
++{
++ PCIQXLDevice* d = opaque;
++
++ if (d->revision != 1) {
++ return false;
++ }
++ if (!d->worker_data_size) {
++ return false;
++ }
++ if (!d->worker_data) {
++ d->worker_data = qemu_malloc(d->worker_data_size);
++ }
++ return true;
++}
++
++static bool qxl_test_spice04(void *opaque, int version_id)
++{
++ PCIQXLDevice* d = opaque;
++ return d->revision == 1;
++}
++
++static bool qxl_test_spice06(void *opaque)
++{
++ PCIQXLDevice* d = opaque;
++ return d->revision > 1;
++}
+
+ static VMStateDescription qxl_memslot = {
+ .name = "qxl-memslot",
+- .version_id = QXL_VER,
+- .minimum_version_id = QXL_VER,
++ .version_id = QXL_SAVE_VERSION,
++ .minimum_version_id = QXL_SAVE_VERSION,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64(slot.mem_start, struct guest_slots),
+ VMSTATE_UINT64(slot.mem_end, struct guest_slots),
+@@ -1339,8 +1374,8 @@ static VMStateDescription qxl_memslot = {
+
+ static VMStateDescription qxl_surface = {
+ .name = "qxl-surface",
+- .version_id = QXL_VER,
+- .minimum_version_id = QXL_VER,
++ .version_id = QXL_SAVE_VERSION,
++ .minimum_version_id = QXL_SAVE_VERSION,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(width, QXLSurfaceCreate),
+ VMSTATE_UINT32(height, QXLSurfaceCreate),
+@@ -1355,34 +1390,58 @@ static VMStateDescription qxl_surface = {
+ }
+ };
+
++static VMStateDescription qxl_vmstate_spice06 = {
++ .name = "qxl/spice06",
++ .version_id = QXL_SAVE_VERSION,
++ .minimum_version_id = QXL_SAVE_VERSION,
++ .fields = (VMStateField []) {
++ VMSTATE_INT32_EQUAL(num_memslots, PCIQXLDevice),
++ VMSTATE_STRUCT_ARRAY(guest_slots, PCIQXLDevice, NUM_MEMSLOTS, 0,
++ qxl_memslot, struct guest_slots),
++ VMSTATE_STRUCT(guest_primary.surface, PCIQXLDevice, 0,
++ qxl_surface, QXLSurfaceCreate),
++ VMSTATE_INT32_EQUAL(num_surfaces, PCIQXLDevice),
++ VMSTATE_ARRAY(guest_surfaces.cmds, PCIQXLDevice, NUM_SURFACES, 0,
++ vmstate_info_uint64, uint64_t),
++ VMSTATE_UINT64(guest_cursor, PCIQXLDevice),
++ VMSTATE_END_OF_LIST()
++ },
++};
++
+ static VMStateDescription qxl_vmstate = {
+ .name = "qxl",
+- .version_id = QXL_VER,
+- .minimum_version_id = QXL_VER,
++ .version_id = QXL_SAVE_VERSION,
++ .minimum_version_id = QXL_SAVE_VERSION,
+ .pre_save = qxl_pre_save,
+ .pre_load = qxl_pre_load,
+ .post_load = qxl_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_PCI_DEVICE(pci, PCIQXLDevice),
+- VMSTATE_STRUCT(vga, PCIQXLDevice, QXL_VER, vmstate_vga_common, VGACommonState),
++ VMSTATE_STRUCT(vga, PCIQXLDevice, 0, vmstate_vga_common, VGACommonState),
+ VMSTATE_UINT32(shadow_rom.mode, PCIQXLDevice),
+-#if 1 /* wanna zap this */
+ VMSTATE_UINT32(num_free_res, PCIQXLDevice),
+ VMSTATE_UINT32(last_release_offset, PCIQXLDevice),
+-#endif
+ VMSTATE_UINT32(mode, PCIQXLDevice),
+ VMSTATE_UINT32(ssd.unique, PCIQXLDevice),
+-#if 1 /* new stuff */
+- VMSTATE_STRUCT_ARRAY(guest_slots, PCIQXLDevice, NUM_MEMSLOTS, QXL_VER,
+- qxl_memslot, struct guest_slots),
+- VMSTATE_STRUCT(guest_primary.surface, PCIQXLDevice, QXL_VER,
+- qxl_surface, QXLSurfaceCreate),
+- VMSTATE_ARRAY(guest_surfaces.cmds, PCIQXLDevice, NUM_SURFACES, QXL_VER,
+- vmstate_info_uint64, uint64_t),
+- VMSTATE_UINT64(guest_cursor, PCIQXLDevice),
+-#endif
++
++ /* spice 0.4 sends/expects them */
++ VMSTATE_VBUFFER_UINT32(vga.vram_ptr, PCIQXLDevice, 0, qxl_test_spice04, 0,
++ vga.vram_size),
++ VMSTATE_UINT32_TEST(worker_data_size, PCIQXLDevice, qxl_test_spice04),
++ VMSTATE_VBUFFER_UINT32(worker_data, PCIQXLDevice, 0, qxl_test_worker_data, 0,
++ worker_data_size),
++
+ VMSTATE_END_OF_LIST()
+- }
++ },
++ .subsections = (VMStateSubsection[]) {
++ {
++ /* additional spice 0.6 state */
++ .vmsd = &qxl_vmstate_spice06,
++ .needed = qxl_test_spice06,
++ },{
++ /* end of list */
++ },
++ },
+ };
+
+ static PCIDeviceInfo qxl_info = {
+diff --git a/hw/qxl.h b/hw/qxl.h
+index 1216405..caf3684 100644
+--- a/hw/qxl.h
++++ b/hw/qxl.h
+@@ -24,6 +24,9 @@ typedef struct PCIQXLDevice {
+ int generation;
+ uint32_t revision;
+
++ int32_t num_memslots;
++ int32_t num_surfaces;
++
+ struct guest_slots {
+ QXLMemSlot slot;
+ void *ptr;
+@@ -74,6 +77,9 @@ typedef struct PCIQXLDevice {
+ /* io bar */
+ uint32_t io_base;
+
++ /* spice 0.4 loadvm compatibility */
++ void *worker_data;
++ uint32_t worker_data_size;
+ } PCIQXLDevice;
+
+ #define PANIC_ON(x) if ((x)) { \
+--
+1.7.2.3
+
diff --git a/0032-Revert-spice-vmc-split-vmc_write-to-max-sized-virtio.patch b/0032-Revert-spice-vmc-split-vmc_write-to-max-sized-virtio.patch
new file mode 100644
index 0000000..54b7715
--- /dev/null
+++ b/0032-Revert-spice-vmc-split-vmc_write-to-max-sized-virtio.patch
@@ -0,0 +1,53 @@
+From f48f184b9d22bbd2e34fb4f3a7a760f0e98fae64 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel at redhat.com>
+Date: Wed, 8 Sep 2010 11:45:30 +0200
+Subject: [PATCH 32/39] Revert "spice-vmc: split vmc_write to max sized virtio_serial_write calls"
+
+This reverts commit 380b75548db5116e538dc646e84bceb1c4b0e61b.
+---
+ hw/spice-vmc.c | 21 +++------------------
+ 1 files changed, 3 insertions(+), 18 deletions(-)
+
+diff --git a/hw/spice-vmc.c b/hw/spice-vmc.c
+index b9d64a2..041f243 100644
+--- a/hw/spice-vmc.c
++++ b/hw/spice-vmc.c
+@@ -21,8 +21,6 @@
+ #define VMC_GUEST_DEVICE_NAME "com.redhat.spice.0"
+ #define VMC_DEVICE_NAME "spicevmc"
+
+-#define VMC_MAX_HOST_WRITE 2048
+-
+ #define dprintf(_svc, _level, _fmt, ...) \
+ do { \
+ static unsigned __dprintf_counter = 0; \
+@@ -45,23 +43,10 @@ typedef struct SpiceVirtualChannel {
+ static int vmc_write(SpiceVDIPortInstance *sin, const uint8_t *buf, int len)
+ {
+ SpiceVirtualChannel *svc = container_of(sin, SpiceVirtualChannel, sin);
+- ssize_t out = 0;
+- ssize_t last_out;
+- uint8_t* p = (uint8_t*)buf;
+-
+- while (len > 0) {
+- last_out = virtio_serial_write(&svc->port, p,
+- MIN(len, VMC_MAX_HOST_WRITE));
+- if (last_out > 0) {
+- out += last_out;
+- len -= last_out;
+- p += last_out;
+- } else {
+- break;
+- }
+- }
++ ssize_t out;
+
+- dprintf(svc, 3, "%s: %lu/%zd\n", __func__, out, len + out);
++ out = virtio_serial_write(&svc->port, buf, len);
++ dprintf(svc, 3, "%s: %lu/%d\n", __func__, out, len);
+ return out;
+ }
+
+--
+1.7.2.3
+
diff --git a/0033-Revert-spice-vmc-add-counter-to-debug-statements.patch b/0033-Revert-spice-vmc-add-counter-to-debug-statements.patch
new file mode 100644
index 0000000..6ff14e4
--- /dev/null
+++ b/0033-Revert-spice-vmc-add-counter-to-debug-statements.patch
@@ -0,0 +1,28 @@
+From a5d6e7e76bf5f5fb0e2c8232ddca2b850bfc1afa Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel at redhat.com>
+Date: Wed, 8 Sep 2010 11:45:49 +0200
+Subject: [PATCH 33/39] Revert "spice-vmc: add counter to debug statements"
+
+This reverts commit f3ab5192a20ee9dc7776b13ec0ba75030bb52a20.
+---
+ hw/spice-vmc.c | 3 +--
+ 1 files changed, 1 insertions(+), 2 deletions(-)
+
+diff --git a/hw/spice-vmc.c b/hw/spice-vmc.c
+index 041f243..06e30e6 100644
+--- a/hw/spice-vmc.c
++++ b/hw/spice-vmc.c
+@@ -23,9 +23,8 @@
+
+ #define dprintf(_svc, _level, _fmt, ...) \
+ do { \
+- static unsigned __dprintf_counter = 0; \
+ if (_svc->debug >= _level) { \
+- fprintf(stderr, "svc: %3d: " _fmt, ++__dprintf_counter, ## __VA_ARGS__);\
++ fprintf(stderr, "svc: " _fmt, ## __VA_ARGS__); \
+ } \
+ } while (0)
+
+--
+1.7.2.3
+
diff --git a/0034-Revert-spice-vmc-two-bugfixes-in-vmc_read.patch b/0034-Revert-spice-vmc-two-bugfixes-in-vmc_read.patch
new file mode 100644
index 0000000..ce11f48
--- /dev/null
+++ b/0034-Revert-spice-vmc-two-bugfixes-in-vmc_read.patch
@@ -0,0 +1,55 @@
+From 84115ef1adf343c34eebfb1045cbc5c72892e3b2 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel at redhat.com>
+Date: Wed, 8 Sep 2010 11:46:18 +0200
+Subject: [PATCH 34/39] Revert "spice-vmc: two bugfixes in vmc_read"
+
+This reverts commit 71983a37e30c68beab5e9056a4600d2958f77a04.
+---
+ hw/spice-vmc.c | 13 +++++--------
+ 1 files changed, 5 insertions(+), 8 deletions(-)
+
+diff --git a/hw/spice-vmc.c b/hw/spice-vmc.c
+index 06e30e6..3f6a2bb 100644
+--- a/hw/spice-vmc.c
++++ b/hw/spice-vmc.c
+@@ -45,7 +45,7 @@ static int vmc_write(SpiceVDIPortInstance *sin, const uint8_t *buf, int len)
+ ssize_t out;
+
+ out = virtio_serial_write(&svc->port, buf, len);
+- dprintf(svc, 3, "%s: %lu/%d\n", __func__, out, len);
++ dprintf(svc, 2, "%s: %lu/%d\n", __func__, out, len);
+ return out;
+ }
+
+@@ -54,16 +54,13 @@ static int vmc_read(SpiceVDIPortInstance *sin, uint8_t *buf, int len)
+ SpiceVirtualChannel *svc = container_of(sin, SpiceVirtualChannel, sin);
+ int bytes = MIN(len, svc->datalen);
+
+- dprintf(svc, 2, "%s: %p %d/%d/%zd\n", __func__, svc->datapos, len, bytes, svc->datalen);
+- if (bytes > 0) {
++ dprintf(svc, 2, "%s: %d/%zd\n", __func__, bytes, svc->datalen);
++ if (bytes) {
+ memcpy(buf, svc->datapos, bytes);
+ svc->datapos += bytes;
+ svc->datalen -= bytes;
+- assert(svc->datalen >= 0);
+- if (svc->datalen == 0) {
+- svc->datapos = 0;
++ if (0 == svc->datalen) {
+ virtio_serial_throttle_port(&svc->port, false);
+- // ^^^ !!! may call vmc_have_data, so don't touch svc after it!
+ }
+ }
+ return bytes;
+@@ -143,7 +140,7 @@ static void vmc_have_data(VirtIOSerialPort *port, const uint8_t *buf, size_t len
+ SpiceVirtualChannel *svc = DO_UPCAST(SpiceVirtualChannel, port, port);
+
+ dprintf(svc, 2, "%s: %zd\n", __func__, len);
+- assert(svc->datalen == 0);
++ assert(svc->datapos == 0);
+ if (svc->bufsize < len) {
+ svc->bufsize = len;
+ svc->buffer = qemu_realloc(svc->buffer, svc->bufsize);
+--
+1.7.2.3
+
diff --git a/0035-Revert-spice-live-migration-wip.patch b/0035-Revert-spice-live-migration-wip.patch
new file mode 100644
index 0000000..ace3f27
--- /dev/null
+++ b/0035-Revert-spice-live-migration-wip.patch
@@ -0,0 +1,181 @@
+From 3e0d1b6ed5f8e8b871803337008e104398e4db0a Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel at redhat.com>
+Date: Wed, 8 Sep 2010 11:48:57 +0200
+Subject: [PATCH 35/39] Revert "spice: live migration (wip)."
+
+This reverts commit 85b9db9ba993af737c9c402cf2f67db7b1b3cbce.
+
+Conflicts:
+
+ spice.c
+---
+ monitor.c | 1 -
+ qemu-monitor.hx | 11 -------
+ qemu-spice.h | 2 -
+ spice.c | 87 -------------------------------------------------------
+ 4 files changed, 0 insertions(+), 101 deletions(-)
+
+diff --git a/monitor.c b/monitor.c
+index 6674a8c..e51df62 100644
+--- a/monitor.c
++++ b/monitor.c
+@@ -57,7 +57,6 @@
+ #include "osdep.h"
+ #include "exec-all.h"
+ #include "qemu-kvm.h"
+-#include "qemu-spice.h"
+
+ //#define DEBUG
+ //#define DEBUG_COMPLETION
+diff --git a/qemu-monitor.hx b/qemu-monitor.hx
+index c2570d9..da7b796 100644
+--- a/qemu-monitor.hx
++++ b/qemu-monitor.hx
+@@ -2510,17 +2510,6 @@ ETEXI
+
+ HXCOMM DO NOT add new commands after 'info', move your addition before it!
+
+-#if defined(CONFIG_SPICE)
+- {
+- .name = "spice_migrate_info",
+- .args_type = "hostname:s,port:i?,tls-port:i?,cert-subject:s?",
+- .params = "hostname port tls-port cert-subject",
+- .help = "send migration info to spice client",
+- .user_print = monitor_user_noop,
+- .mhandler.cmd_new = mon_spice_migrate,
+- },
+-#endif
+-
+ STEXI
+ @end table
+ ETEXI
+diff --git a/qemu-spice.h b/qemu-spice.h
+index 3c8e959..6f19ba7 100644
+--- a/qemu-spice.h
++++ b/qemu-spice.h
+@@ -16,8 +16,6 @@ void qemu_spice_input_init(void);
+ void qemu_spice_audio_init(void);
+ void qemu_spice_display_init(DisplayState *ds);
+
+-int mon_spice_migrate(Monitor *mon, const QDict *qdict, QObject **ret_data);
+-
+ #else /* CONFIG_SPICE */
+
+ #define using_spice 0
+diff --git a/spice.c b/spice.c
+index 76e6ac1..e6f047d 100644
+--- a/spice.c
++++ b/spice.c
+@@ -11,7 +11,6 @@
+ #include "qemu-queue.h"
+ #include "qemu-x509.h"
+ #include "monitor.h"
+-#include "hw/hw.h"
+
+ /* core bits */
+
+@@ -123,90 +122,8 @@ static SpiceCoreInterface core_interface = {
+ .watch_remove = watch_remove,
+ };
+
+-/* handle client migration */
+-
+-static int spice_live(Monitor *mon, QEMUFile *f, int stage, void *opaque)
+-{
+- static int last_stage;
+- static int migrate_client, client_connected;
+- int ret = 1;
+-
+- if (last_stage != stage) {
+- last_stage = stage;
+- fprintf(stderr, "%s: stage %d\n", __FUNCTION__, stage);
+- } else {
+- fprintf(stderr, ".");
+- }
+-
+- switch (stage) {
+- case 1:
+- migrate_client = 1;
+- client_connected = 0;
+- fprintf(stderr, "%s: start client migration\n", __FUNCTION__);
+- if (spice_server_migrate_start(spice_server) != 0) {
+- fprintf(stderr, "%s: fail -> no client migration\n", __FUNCTION__);
+- migrate_client = 0;
+- }
+- break;
+- case 2:
+- if (!migrate_client)
+- break;
+- switch (spice_server_migrate_client_state(spice_server)) {
+- case SPICE_MIGRATE_CLIENT_NONE:
+- fprintf(stderr, "%s: no client connected\n", __FUNCTION__);
+- migrate_client = 0;
+- break;
+- case SPICE_MIGRATE_CLIENT_WAITING:
+- ret = 0;
+- break;
+- case SPICE_MIGRATE_CLIENT_READY:
+- if (!client_connected) {
+- fprintf(stderr, "%s: client connected to target\n", __FUNCTION__);
+- client_connected = 1;
+- }
+- break;
+- }
+- break;
+- case 3:
+- if (migrate_client && client_connected) {
+- fprintf(stderr, "%s: finish client migration\n", __FUNCTION__);
+- spice_server_migrate_end(spice_server, 1);
+- }
+- break;
+- }
+- return ret;
+-}
+-
+-static void spice_save(QEMUFile *f, void *opaque)
+-{
+- fprintf(stderr, "%s:\n", __FUNCTION__);
+-}
+-
+-static int spice_load(QEMUFile *f, void *opaque, int version_id)
+-{
+- fprintf(stderr, "%s:\n", __FUNCTION__);
+- return 0;
+-}
+-
+ /* functions for the rest of qemu */
+
+-int mon_spice_migrate(Monitor *mon, const QDict *qdict, QObject **ret_data)
+-{
+- const char *hostname = qdict_get_str(qdict, "hostname");
+- const char *subject = qdict_get_try_str(qdict, "cert-subject");
+- int port = qdict_get_try_int(qdict, "port", -1);
+- int tls_port = qdict_get_try_int(qdict, "tls-port", -1);
+-
+- if (!spice_server) {
+- qerror_report(QERR_DEVICE_NOT_ACTIVE, "spice");
+- return -1;
+- }
+-
+- /* TODO: Convert to QError */
+- return spice_server_migrate_info(spice_server, hostname,
+- port, tls_port, subject);
+-}
+-
+ #if defined(SPICE_SERVER_VERSION) && SPICE_SERVER_VERSION >= 0x000503
+ static inline spice_wan_compression_t get_wan_compression_value(const char *str)
+ {
+@@ -322,10 +239,6 @@ void qemu_spice_init(void)
+ qemu_spice_input_init();
+ qemu_spice_audio_init();
+
+- register_savevm_live(NULL, "spice", -1, 1, NULL,
+- spice_live, spice_save, spice_load,
+- spice_server);
+-
+ qemu_free(x509_key_file);
+ qemu_free(x509_cert_file);
+ qemu_free(x509_cacert_file);
+--
+1.7.2.3
+
diff --git a/0036-Revert-spice-add-pci-vdi-port-backend-obsolete.patch b/0036-Revert-spice-add-pci-vdi-port-backend-obsolete.patch
new file mode 100644
index 0000000..fcf7d08
--- /dev/null
+++ b/0036-Revert-spice-add-pci-vdi-port-backend-obsolete.patch
@@ -0,0 +1,590 @@
+From 7bad8970dd7db3e3e0e0b11626656c68f4238884 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel at redhat.com>
+Date: Wed, 8 Sep 2010 11:49:22 +0200
+Subject: [PATCH 36/39] Revert "spice: add pci vdi port backend (obsolete)."
+
+This reverts commit b56a2ed131bdb4ce42db8f33f87603c416e7a60a.
+---
+ Makefile.target | 2 +-
+ hw/spice-vdi.c | 556 -------------------------------------------------------
+ 2 files changed, 1 insertions(+), 557 deletions(-)
+ delete mode 100644 hw/spice-vdi.c
+
+diff --git a/Makefile.target b/Makefile.target
+index 025bdb8..90544c5 100644
+--- a/Makefile.target
++++ b/Makefile.target
+@@ -217,7 +217,7 @@ obj-i386-y += pc_piix.o
+ obj-i386-y += testdev.o
+ obj-i386-y += acpi.o acpi_piix4.o
+ obj-i386-$(CONFIG_SPICE) += qxl.o qxl-logger.o qxl-render.o
+-obj-i386-$(CONFIG_SPICE) += spice-vmc.o spice-vdi.o
++obj-i386-$(CONFIG_SPICE) += spice-vmc.o
+
+ obj-i386-y += pcspk.o i8254.o
+ obj-i386-$(CONFIG_KVM_PIT) += i8254-kvm.o
+diff --git a/hw/spice-vdi.c b/hw/spice-vdi.c
+deleted file mode 100644
+index 23cbbe1..0000000
+--- a/hw/spice-vdi.c
++++ /dev/null
+@@ -1,556 +0,0 @@
+-#include <pthread.h>
+-#include <signal.h>
+-
+-#include "qemu-common.h"
+-#include "qemu-spice.h"
+-#include "hw/hw.h"
+-#include "hw/pc.h"
+-#include "hw/pci.h"
+-#include "console.h"
+-#include "hw/vga_int.h"
+-#include "qemu-timer.h"
+-#include "sysemu.h"
+-#include "console.h"
+-#include "pci.h"
+-#include "hw.h"
+-#include "cpu-common.h"
+-
+-#include <spice.h>
+-#include <spice-experimental.h>
+-#include <spice/ipc_ring.h>
+-#include <spice/barrier.h>
+-
+-#undef SPICE_RING_PROD_ITEM
+-#define SPICE_RING_PROD_ITEM(r, ret) { \
+- typeof(r) start = r; \
+- typeof(r) end = r + 1; \
+- uint32_t prod = (r)->prod & SPICE_RING_INDEX_MASK(r); \
+- typeof(&(r)->items[prod]) m_item = &(r)->items[prod]; \
+- if (!((uint8_t*)m_item >= (uint8_t*)(start) && (uint8_t*)(m_item + 1) <= (uint8_t*)(end))) { \
+- abort(); \
+- } \
+- ret = &m_item->el; \
+- }
+-
+-#undef SPICE_RING_CONS_ITEM
+-#define SPICE_RING_CONS_ITEM(r, ret) { \
+- typeof(r) start = r; \
+- typeof(r) end = r + 1; \
+- uint32_t cons = (r)->cons & SPICE_RING_INDEX_MASK(r); \
+- typeof(&(r)->items[cons]) m_item = &(r)->items[cons]; \
+- if (!((uint8_t*)m_item >= (uint8_t*)(start) && (uint8_t*)(m_item + 1) <= (uint8_t*)(end))) { \
+- abort(); \
+- } \
+- ret = &m_item->el; \
+- }
+-
+-
+-#undef ALIGN
+-#define ALIGN(a, b) (((a) + ((b) - 1)) & ~((b) - 1))
+-
+-#define REDHAT_PCI_VENDOR_ID 0x1b36
+-#define VDI_PORT_DEVICE_ID 0x0105
+-#define VDI_PORT_REVISION 0x01
+-
+-#define VDI_PORT_INTERRUPT (1 << 0)
+-
+-#define VDI_PORT_MAGIC (*(uint32_t*)"VDIP")
+-
+-#define VDI_PORT_DEV_NAME "vdi_port"
+-#define VDI_PORT_SAVE_VERSION 20
+-
+-#include <spice/start-packed.h>
+-
+-typedef struct SPICE_ATTR_PACKED VDIPortPacket {
+- uint32_t gen;
+- uint32_t size;
+- uint8_t data[512 - 2 * sizeof(uint32_t)];
+-} VDIPortPacket;
+-
+-SPICE_RING_DECLARE(VDIPortRing, VDIPortPacket, 32);
+-
+-enum {
+- VDI_PORT_IO_RANGE_INDEX,
+- VDI_PORT_RAM_RANGE_INDEX,
+-};
+-
+-enum {
+- VDI_PORT_IO_CONNECTION,
+- VDI_PORT_IO_NOTIFY = 4,
+- VDI_PORT_IO_UPDATE_IRQ = 8,
+-
+- VDI_PORT_IO_RANGE_SIZE = 12
+-};
+-
+-typedef struct SPICE_ATTR_PACKED VDIPortRam {
+- uint32_t magic;
+- uint32_t generation;
+- uint32_t int_pending;
+- uint32_t int_mask;
+- VDIPortRing input;
+- VDIPortRing output;
+- uint32_t reserv[32];
+-} VDIPortRam;
+-
+-#include <spice/end-packed.h>
+-
+-typedef struct PCIVDIPortDevice {
+- PCIDevice pci_dev;
+- uint32_t io_base;
+- uint64_t ram_offset;
+- uint32_t ram_size;
+- VDIPortRam *ram;
+- uint32_t connected;
+- int running;
+- int new_gen_on_resume;
+- int active_interface;
+- SpiceVDIPortInstance sin;
+- int plug_read_pos;
+-} PCIVDIPortDevice;
+-
+-static int debug = 1;
+-
+-static inline uint32_t msb_mask(uint32_t val)
+-{
+- uint32_t mask;
+-
+- do {
+- mask = ~(val - 1) & val;
+- val &= ~mask;
+- } while (mask < val);
+-
+- return mask;
+-}
+-
+-static inline void atomic_or(uint32_t *var, uint32_t add)
+-{
+- __asm__ __volatile__ ("lock; orl %1, %0" : "+m" (*var) : "r" (add) : "memory");
+-}
+-
+-static inline uint32_t atomic_exchange(uint32_t val, uint32_t *ptr)
+-{
+- __asm__ __volatile__("xchgl %0, %1" : "+q"(val), "+m" (*ptr) : : "memory");
+- return val;
+-}
+-
+-static void set_dirty(void *base, ram_addr_t offset, void *start, uint32_t length)
+-{
+- assert(start >= base);
+-
+- ram_addr_t addr = (ram_addr_t)((uint8_t*)start - (uint8_t*)base) + offset;
+- ram_addr_t end = ALIGN(addr + length, TARGET_PAGE_SIZE);
+-
+- do {
+- cpu_physical_memory_set_dirty(addr);
+- addr += TARGET_PAGE_SIZE;
+- } while ( addr < end );
+-}
+-
+-static inline void vdi_port_set_dirty(PCIVDIPortDevice *d, void *start, uint32_t length)
+-{
+- set_dirty(d->ram, d->ram_offset, start, length);
+-}
+-
+-static void vdi_port_new_gen(PCIVDIPortDevice *d)
+-{
+- d->ram->generation = (d->ram->generation + 1 == 0) ? 1 : d->ram->generation + 1;
+- vdi_port_set_dirty(d, &d->ram->generation, sizeof(d->ram->generation));
+-}
+-
+-static int vdi_port_irq_level(PCIVDIPortDevice *d)
+-{
+- return !!(d->ram->int_pending & d->ram->int_mask);
+-}
+-
+-static void vdi_port_notify_guest(PCIVDIPortDevice *d)
+-{
+- uint32_t events = VDI_PORT_INTERRUPT;
+- uint32_t old_pending;
+-
+- if (!d->connected) {
+- return;
+- }
+- old_pending = __sync_fetch_and_or(&d->ram->int_pending, events);
+- if ((old_pending & events) == events) {
+- return;
+- }
+- qemu_set_irq(d->pci_dev.irq[0], vdi_port_irq_level(d));
+- vdi_port_set_dirty(d, &d->ram->int_pending, sizeof(d->ram->int_pending));
+-}
+-
+-static int vdi_port_interface_write(SpiceVDIPortInstance *sin,
+- const uint8_t *buf, int len)
+-{
+- PCIVDIPortDevice *d = container_of(sin, PCIVDIPortDevice, sin);
+- VDIPortRing *ring = &d->ram->output;
+- int do_notify = false;
+- int actual_write = 0;
+- int l = len;
+-
+- if (!d->running) {
+- return 0;
+- }
+-
+- while (len) {
+- VDIPortPacket *packet;
+- int notify;
+- int wait;
+-
+- SPICE_RING_PROD_WAIT(ring, wait);
+- if (wait) {
+- break;
+- }
+-
+- SPICE_RING_PROD_ITEM(ring, packet);
+- packet->gen = d->ram->generation;
+- packet->size = MIN(len, sizeof(packet->data));
+- memcpy(packet->data, buf, packet->size);
+- vdi_port_set_dirty(d, packet, sizeof(*packet) - (sizeof(packet->data) - packet->size));
+-
+- SPICE_RING_PUSH(ring, notify);
+- do_notify = do_notify || notify;
+- len -= packet->size;
+- buf += packet->size;
+- actual_write += packet->size;
+- }
+- vdi_port_set_dirty(d, ring, sizeof(*ring) - sizeof(ring->items));
+-
+- if (do_notify) {
+- vdi_port_notify_guest(d);
+- }
+- if (debug > 1) {
+- fprintf(stderr, "%s: %d/%d\n", __FUNCTION__, actual_write, l);
+- }
+- return actual_write;
+-}
+-
+-static int vdi_port_interface_read(SpiceVDIPortInstance *sin,
+- uint8_t *buf, int len)
+-{
+- PCIVDIPortDevice *d = container_of(sin, PCIVDIPortDevice, sin);
+- VDIPortRing *ring = &d->ram->input;
+- uint32_t gen = d->ram->generation;
+- VDIPortPacket *packet;
+- int do_notify = false;
+- int actual_read = 0;
+- int l = len;
+-
+- if (!d->running) {
+- return 0;
+- }
+-
+- while (!SPICE_RING_IS_EMPTY(ring)) {
+- int notify;
+-
+- SPICE_RING_CONS_ITEM(ring, packet);
+- if (packet->gen == gen) {
+- break;
+- }
+- SPICE_RING_POP(ring, notify);
+- do_notify = do_notify || notify;
+- }
+- while (len) {
+- VDIPortPacket *packet;
+- int wait;
+- int now;
+-
+- SPICE_RING_CONS_WAIT(ring, wait);
+-
+- if (wait) {
+- break;
+- }
+-
+- SPICE_RING_CONS_ITEM(ring, packet);
+- if (packet->size > sizeof(packet->data)) {
+- vdi_port_set_dirty(d, ring, sizeof(*ring) - sizeof(ring->items));
+- printf("%s: bad packet size\n", __FUNCTION__);
+- return 0;
+- }
+- now = MIN(len, packet->size - d->plug_read_pos);
+- memcpy(buf, packet->data + d->plug_read_pos, now);
+- len -= now;
+- buf += now;
+- actual_read += now;
+- if ((d->plug_read_pos += now) == packet->size) {
+- int notify;
+-
+- d->plug_read_pos = 0;
+- SPICE_RING_POP(ring, notify);
+- do_notify = do_notify || notify;
+- }
+- }
+- vdi_port_set_dirty(d, ring, sizeof(*ring) - sizeof(ring->items));
+-
+- if (do_notify) {
+- vdi_port_notify_guest(d);
+- }
+- if (debug > 1) {
+- fprintf(stderr, "%s: %d/%d\n", __FUNCTION__, actual_read, l);
+- }
+- return actual_read;
+-}
+-
+-static SpiceVDIPortInterface vdi_port_interface = {
+- .base.type = SPICE_INTERFACE_VDI_PORT,
+- .base.description = "vdi port",
+- .base.major_version = SPICE_INTERFACE_VDI_PORT_MAJOR,
+- .base.minor_version = SPICE_INTERFACE_VDI_PORT_MINOR,
+-
+- .write = vdi_port_interface_write,
+- .read = vdi_port_interface_read,
+-};
+-
+-static void vdi_port_register_interface(PCIVDIPortDevice *d)
+-{
+- if (d->active_interface ) {
+- return;
+- }
+-
+- if (debug) {
+- fprintf(stderr, "%s\n", __FUNCTION__);
+- }
+- d->sin.base.sif = &vdi_port_interface.base;
+- spice_server_add_interface(spice_server, &d->sin.base);
+- d->active_interface = true;
+-}
+-
+-static void vdi_port_unregister_interface(PCIVDIPortDevice *d)
+-{
+- if (!d->active_interface ) {
+- return;
+- }
+- if (debug) {
+- fprintf(stderr, "%s\n", __FUNCTION__);
+- }
+- spice_server_remove_interface(&d->sin.base);
+- d->active_interface = false;
+-}
+-
+-static uint32_t vdi_port_dev_connect(PCIVDIPortDevice *d)
+-{
+- if (d->connected) {
+- if (debug) {
+- fprintf(stderr, "%s: already connected\n", __FUNCTION__);
+- }
+- return 0;
+- }
+- vdi_port_new_gen(d);
+- d->connected = true;
+- vdi_port_register_interface(d);
+- return d->ram->generation;
+-}
+-
+-static void vdi_port_dev_disconnect(PCIVDIPortDevice *d)
+-{
+- if (!d->connected) {
+- if (debug) {
+- fprintf(stderr, "%s: not connected\n", __FUNCTION__);
+- }
+- return;
+- }
+- d->connected = false;
+- vdi_port_unregister_interface(d);
+-}
+-
+-static void vdi_port_dev_notify(PCIVDIPortDevice *d)
+-{
+- spice_server_vdi_port_wakeup(&d->sin);
+-}
+-
+-static void vdi_port_write_dword(void *opaque, uint32_t addr, uint32_t val)
+-{
+- PCIVDIPortDevice *d = opaque;
+- uint32_t io_port = addr - d->io_base;
+-
+- if (debug > 1) {
+- fprintf(stderr, "%s: addr 0x%x val 0x%x\n", __FUNCTION__, addr, val);
+- }
+- switch (io_port) {
+- case VDI_PORT_IO_NOTIFY:
+- if (!d->connected) {
+- fprintf(stderr, "%s: not connected\n", __FUNCTION__);
+- return;
+- }
+- vdi_port_dev_notify(d);
+- break;
+- case VDI_PORT_IO_UPDATE_IRQ:
+- qemu_set_irq(d->pci_dev.irq[0], vdi_port_irq_level(d));
+- break;
+- case VDI_PORT_IO_CONNECTION:
+- vdi_port_dev_disconnect(d);
+- break;
+- default:
+- if (debug) {
+- fprintf(stderr, "%s: unexpected addr 0x%x val 0x%x\n",
+- __FUNCTION__, addr, val);
+- }
+- };
+-}
+-
+-static uint32_t vdi_port_read_dword(void *opaque, uint32_t addr)
+-{
+- PCIVDIPortDevice *d = opaque;
+- uint32_t io_port = addr - d->io_base;
+-
+- if (debug > 1) {
+- fprintf(stderr, "%s: addr 0x%x\n", __FUNCTION__, addr);
+- }
+- if (io_port == VDI_PORT_IO_CONNECTION) {
+- return vdi_port_dev_connect(d);
+- } else {
+- fprintf(stderr, "%s: unexpected addr 0x%x\n", __FUNCTION__, addr);
+- }
+- return 0xffffffff;
+-}
+-
+-static void vdi_port_io_map(PCIDevice *pci_dev, int region_num,
+- pcibus_t addr, pcibus_t size, int type)
+-{
+- PCIVDIPortDevice *d = DO_UPCAST(PCIVDIPortDevice, pci_dev, pci_dev);
+-
+- if (debug) {
+- fprintf(stderr, "%s: base 0x%lx size 0x%lx\n", __FUNCTION__, addr, size);
+- }
+- d->io_base = addr;
+- register_ioport_write(addr, size, 4, vdi_port_write_dword, pci_dev);
+- register_ioport_read(addr, size, 4, vdi_port_read_dword, pci_dev);
+-}
+-
+-static void vdi_port_ram_map(PCIDevice *pci_dev, int region_num,
+- pcibus_t addr, pcibus_t size, int type)
+-{
+- PCIVDIPortDevice *d = DO_UPCAST(PCIVDIPortDevice, pci_dev, pci_dev);
+-
+- if (debug) {
+- fprintf(stderr, "%s: addr 0x%lx size 0x%lx\n", __FUNCTION__, addr, size);
+- }
+-
+- assert((addr & (size - 1)) == 0);
+- assert(size == d->ram_size);
+-
+- cpu_register_physical_memory(addr, size, d->ram_offset | IO_MEM_RAM);
+-}
+-
+-static void vdi_port_reset(PCIVDIPortDevice *d)
+-{
+- memset(d->ram, 0, sizeof(*d->ram));
+- SPICE_RING_INIT(&d->ram->input);
+- SPICE_RING_INIT(&d->ram->output);
+- d->ram->magic = VDI_PORT_MAGIC;
+- d->ram->generation = 0;
+- d->ram->int_pending = 0;
+- d->ram->int_mask = 0;
+- d->connected = false;
+- d->plug_read_pos = 0;
+- vdi_port_set_dirty(d, d->ram, sizeof(*d->ram));
+-}
+-
+-static void vdi_port_reset_handler(DeviceState *dev)
+-{
+- PCIVDIPortDevice *d = DO_UPCAST(PCIVDIPortDevice, pci_dev.qdev, dev);
+-
+- if (d->connected) {
+- vdi_port_dev_disconnect(d);
+- }
+-
+- vdi_port_reset(d);
+- qemu_set_irq(d->pci_dev.irq[0], vdi_port_irq_level(d));
+-}
+-
+-static int vdi_port_pre_load(void* opaque)
+-{
+- PCIVDIPortDevice* d = opaque;
+-
+- vdi_port_unregister_interface(d);
+- return 0;
+-}
+-
+-static int vdi_port_post_load(void* opaque,int version_id)
+-{
+- PCIVDIPortDevice* d = opaque;
+-
+- if (d->connected) {
+- vdi_port_register_interface(d);
+- }
+- return 0;
+-}
+-
+-static void vdi_port_vm_change_state_handler(void *opaque, int running, int reason)
+-{
+- PCIVDIPortDevice* d = opaque;
+-
+- if (running) {
+- d->running = true;
+- if (d->new_gen_on_resume) {
+- d->new_gen_on_resume = false;
+- vdi_port_new_gen(d);
+- vdi_port_notify_guest(d);
+- }
+- qemu_set_irq(d->pci_dev.irq[0], vdi_port_irq_level(d));
+- vdi_port_dev_notify(d);
+- } else {
+- d->running = false;
+- }
+-}
+-
+-static int vdi_port_init(PCIDevice *dev)
+-{
+- PCIVDIPortDevice *vdi = (PCIVDIPortDevice *)dev;
+- uint8_t* config = vdi->pci_dev.config;
+- uint32_t ram_size = msb_mask(sizeof(VDIPortRam) * 2 - 1);
+-
+- vdi->ram_offset = qemu_ram_alloc(&vdi->pci_dev.qdev, "bar1", ram_size);
+- vdi->ram = qemu_get_ram_ptr(vdi->ram_offset);
+- vdi_port_reset(vdi);
+- vdi->ram_size = ram_size;
+- vdi->new_gen_on_resume = false;
+- vdi->running = false;
+-
+- pci_config_set_vendor_id(config, REDHAT_PCI_VENDOR_ID);
+- pci_config_set_device_id(config, VDI_PORT_DEVICE_ID);
+- pci_config_set_class(config, PCI_CLASS_COMMUNICATION_OTHER);
+- pci_set_byte(&config[PCI_REVISION_ID], VDI_PORT_REVISION);
+- pci_set_byte(&config[PCI_INTERRUPT_PIN], 1);
+-
+- pci_register_bar(dev, VDI_PORT_IO_RANGE_INDEX,
+- msb_mask(VDI_PORT_IO_RANGE_SIZE * 2 - 1),
+- PCI_BASE_ADDRESS_SPACE_IO, vdi_port_io_map);
+-
+- pci_register_bar(dev, VDI_PORT_RAM_RANGE_INDEX,
+- vdi->ram_size , PCI_BASE_ADDRESS_SPACE_MEMORY,
+- vdi_port_ram_map);
+-
+- qemu_add_vm_change_state_handler(vdi_port_vm_change_state_handler, vdi);
+-
+- return 0;
+-}
+-
+-static VMStateDescription vdi_port_vmstate = {
+- .name = VDI_PORT_DEV_NAME,
+- .version_id = VDI_PORT_SAVE_VERSION,
+- .minimum_version_id = VDI_PORT_SAVE_VERSION,
+- .pre_load = vdi_port_pre_load,
+- .post_load = vdi_port_post_load,
+- .fields = (VMStateField []) {
+- VMSTATE_PCI_DEVICE(pci_dev, PCIVDIPortDevice),
+- VMSTATE_UINT32(connected, PCIVDIPortDevice),
+- VMSTATE_END_OF_LIST()
+- }
+-};
+-
+-static PCIDeviceInfo vdi_port_info = {
+- .qdev.name = VDI_PORT_DEV_NAME,
+- .qdev.desc = "spice virtual desktop port (obsolete)",
+- .qdev.size = sizeof(PCIVDIPortDevice),
+- .qdev.vmsd = &vdi_port_vmstate,
+- .qdev.reset = vdi_port_reset_handler,
+-
+- .init = vdi_port_init,
+-};
+-
+-static void vdi_port_register(void)
+-{
+- pci_qdev_register(&vdi_port_info);
+-}
+-
+-device_init(vdi_port_register);
+--
+1.7.2.3
+
diff --git a/0037-Revert-spice-add-virtio-serial-based-vdi-port-backen.patch b/0037-Revert-spice-add-virtio-serial-based-vdi-port-backen.patch
new file mode 100644
index 0000000..654ddc5
--- /dev/null
+++ b/0037-Revert-spice-add-virtio-serial-based-vdi-port-backen.patch
@@ -0,0 +1,236 @@
+From bebcc44cfe5da8a4881292fa564869a481eea4ae Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel at redhat.com>
+Date: Wed, 8 Sep 2010 11:49:40 +0200
+Subject: [PATCH 37/39] Revert "spice: add virtio-serial based vdi port backend."
+
+This reverts commit ef9e975b1d34c1426867cef832ba6238a401b740.
+---
+ Makefile.target | 1 -
+ hw/spice-vmc.c | 203 -------------------------------------------------------
+ 2 files changed, 0 insertions(+), 204 deletions(-)
+ delete mode 100644 hw/spice-vmc.c
+
+diff --git a/Makefile.target b/Makefile.target
+index 90544c5..4da33b5 100644
+--- a/Makefile.target
++++ b/Makefile.target
+@@ -217,7 +217,6 @@ obj-i386-y += pc_piix.o
+ obj-i386-y += testdev.o
+ obj-i386-y += acpi.o acpi_piix4.o
+ obj-i386-$(CONFIG_SPICE) += qxl.o qxl-logger.o qxl-render.o
+-obj-i386-$(CONFIG_SPICE) += spice-vmc.o
+
+ obj-i386-y += pcspk.o i8254.o
+ obj-i386-$(CONFIG_KVM_PIT) += i8254-kvm.o
+diff --git a/hw/spice-vmc.c b/hw/spice-vmc.c
+deleted file mode 100644
+index 3f6a2bb..0000000
+--- a/hw/spice-vmc.c
++++ /dev/null
+@@ -1,203 +0,0 @@
+-/*
+-
+- Spice Virtual Machine Channel (VMC).
+-
+- A virtio-serial port used for spice to guest communication, over
+- which spice client and a daemon in the guest operating system
+- communicate.
+-
+- Replaces the old vdi_port PCI device.
+-
+-*/
+-
+-#include <stdio.h>
+-#include <stdbool.h>
+-#include <spice.h>
+-#include <spice-experimental.h>
+-
+-#include "virtio-serial.h"
+-#include "qemu-spice.h"
+-
+-#define VMC_GUEST_DEVICE_NAME "com.redhat.spice.0"
+-#define VMC_DEVICE_NAME "spicevmc"
+-
+-#define dprintf(_svc, _level, _fmt, ...) \
+- do { \
+- if (_svc->debug >= _level) { \
+- fprintf(stderr, "svc: " _fmt, ## __VA_ARGS__); \
+- } \
+- } while (0)
+-
+-typedef struct SpiceVirtualChannel {
+- VirtIOSerialPort port;
+- VMChangeStateEntry *vmstate;
+- SpiceVDIPortInstance sin;
+- bool active;
+- uint8_t *buffer;
+- uint8_t *datapos;
+- ssize_t bufsize, datalen;
+- uint32_t debug;
+-} SpiceVirtualChannel;
+-
+-static int vmc_write(SpiceVDIPortInstance *sin, const uint8_t *buf, int len)
+-{
+- SpiceVirtualChannel *svc = container_of(sin, SpiceVirtualChannel, sin);
+- ssize_t out;
+-
+- out = virtio_serial_write(&svc->port, buf, len);
+- dprintf(svc, 2, "%s: %lu/%d\n", __func__, out, len);
+- return out;
+-}
+-
+-static int vmc_read(SpiceVDIPortInstance *sin, uint8_t *buf, int len)
+-{
+- SpiceVirtualChannel *svc = container_of(sin, SpiceVirtualChannel, sin);
+- int bytes = MIN(len, svc->datalen);
+-
+- dprintf(svc, 2, "%s: %d/%zd\n", __func__, bytes, svc->datalen);
+- if (bytes) {
+- memcpy(buf, svc->datapos, bytes);
+- svc->datapos += bytes;
+- svc->datalen -= bytes;
+- if (0 == svc->datalen) {
+- virtio_serial_throttle_port(&svc->port, false);
+- }
+- }
+- return bytes;
+-}
+-
+-static SpiceVDIPortInterface vmc_interface = {
+- .base.type = SPICE_INTERFACE_VDI_PORT,
+- .base.description = "spice virtual channel vdi port",
+- .base.major_version = SPICE_INTERFACE_VDI_PORT_MAJOR,
+- .base.minor_version = SPICE_INTERFACE_VDI_PORT_MINOR,
+- .write = vmc_write,
+- .read = vmc_read,
+-};
+-
+-static void vmc_register_interface(SpiceVirtualChannel *svc)
+-{
+- if (svc->active) {
+- return;
+- }
+- dprintf(svc, 1, "%s\n", __func__);
+- svc->sin.base.sif = &vmc_interface.base;
+- spice_server_add_interface(spice_server, &svc->sin.base);
+- svc->active = true;
+-}
+-
+-static void vmc_unregister_interface(SpiceVirtualChannel *svc)
+-{
+- if (!svc->active) {
+- return;
+- }
+- dprintf(svc, 1, "%s\n", __func__);
+- spice_server_remove_interface(&svc->sin.base);
+- svc->active = false;
+-}
+-
+-
+-static void vmc_change_state_handler(void *opaque, int running, int reason)
+-{
+- SpiceVirtualChannel *svc = opaque;
+-
+- if (running && svc->active) {
+- spice_server_vdi_port_wakeup(&svc->sin);
+- }
+-}
+-
+-/*
+- * virtio-serial callbacks
+- */
+-
+-static void vmc_guest_open(VirtIOSerialPort *port)
+-{
+- SpiceVirtualChannel *svc = DO_UPCAST(SpiceVirtualChannel, port, port);
+-
+- dprintf(svc, 1, "%s\n", __func__);
+- vmc_register_interface(svc);
+-}
+-
+-static void vmc_guest_close(VirtIOSerialPort *port)
+-{
+- SpiceVirtualChannel *svc = DO_UPCAST(SpiceVirtualChannel, port, port);
+-
+- dprintf(svc, 1, "%s\n", __func__);
+- vmc_unregister_interface(svc);
+-}
+-
+-static void vmc_guest_ready(VirtIOSerialPort *port)
+-{
+- SpiceVirtualChannel *svc = DO_UPCAST(SpiceVirtualChannel, port, port);
+-
+- dprintf(svc, 1, "%s\n", __func__);
+- if (svc->active)
+- spice_server_vdi_port_wakeup(&svc->sin);
+-}
+-
+-static void vmc_have_data(VirtIOSerialPort *port, const uint8_t *buf, size_t len)
+-{
+- SpiceVirtualChannel *svc = DO_UPCAST(SpiceVirtualChannel, port, port);
+-
+- dprintf(svc, 2, "%s: %zd\n", __func__, len);
+- assert(svc->datapos == 0);
+- if (svc->bufsize < len) {
+- svc->bufsize = len;
+- svc->buffer = qemu_realloc(svc->buffer, svc->bufsize);
+- }
+- memcpy(svc->buffer, buf, len);
+- svc->datapos = svc->buffer;
+- svc->datalen = len;
+- virtio_serial_throttle_port(&svc->port, true);
+- spice_server_vdi_port_wakeup(&svc->sin);
+-}
+-
+-static int vmc_initfn(VirtIOSerialDevice *dev)
+-{
+- VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, &dev->qdev);
+- SpiceVirtualChannel *svc = DO_UPCAST(SpiceVirtualChannel, port, port);
+-
+- if (!using_spice)
+- return -1;
+-
+- dprintf(svc, 1, "%s\n", __func__);
+- port->name = qemu_strdup(VMC_GUEST_DEVICE_NAME);
+- svc->vmstate = qemu_add_vm_change_state_handler(
+- vmc_change_state_handler, svc);
+- virtio_serial_open(port);
+- return 0;
+-}
+-
+-static int vmc_exitfn(VirtIOSerialDevice *dev)
+-{
+- VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, &dev->qdev);
+- SpiceVirtualChannel *svc = DO_UPCAST(SpiceVirtualChannel, port, port);
+-
+- dprintf(svc, 1, "%s\n", __func__);
+- vmc_unregister_interface(svc);
+- qemu_del_vm_change_state_handler(svc->vmstate);
+- virtio_serial_close(port);
+- return 0;
+-}
+-
+-static VirtIOSerialPortInfo vmc_info = {
+- .qdev.name = VMC_DEVICE_NAME,
+- .qdev.size = sizeof(SpiceVirtualChannel),
+- .init = vmc_initfn,
+- .exit = vmc_exitfn,
+- .guest_open = vmc_guest_open,
+- .guest_close = vmc_guest_close,
+- .guest_ready = vmc_guest_ready,
+- .have_data = vmc_have_data,
+- .qdev.props = (Property[]) {
+- DEFINE_PROP_UINT32("nr", SpiceVirtualChannel, port.id, VIRTIO_CONSOLE_BAD_ID),
+- DEFINE_PROP_UINT32("debug", SpiceVirtualChannel, debug, 1),
+- DEFINE_PROP_END_OF_LIST(),
+- }
+-};
+-
+-static void vmc_register(void)
+-{
+- virtio_serial_port_qdev_register(&vmc_info);
+-}
+-device_init(vmc_register)
+--
+1.7.2.3
+
diff --git a/0038-spice-add-virtio-serial-based-spice-vmchannel-backen.patch b/0038-spice-add-virtio-serial-based-spice-vmchannel-backen.patch
new file mode 100644
index 0000000..2423deb
--- /dev/null
+++ b/0038-spice-add-virtio-serial-based-spice-vmchannel-backen.patch
@@ -0,0 +1,297 @@
+From 5bdc01e675a51a123a813d62a8ae837db9360b7f Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel at redhat.com>
+Date: Tue, 20 Apr 2010 13:33:54 +0200
+Subject: [PATCH 38/39] spice: add virtio-serial based spice vmchannel backend.
+
+Adds the spicevmc device. This is a communication channel between the
+spice client and the guest. It is used to send display information and
+mouse events from the spice clients to the guest.
+---
+ Makefile.target | 1 +
+ hw/spice-vmc.c | 262 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 263 insertions(+), 0 deletions(-)
+ create mode 100644 hw/spice-vmc.c
+
+diff --git a/Makefile.target b/Makefile.target
+index 4da33b5..90544c5 100644
+--- a/Makefile.target
++++ b/Makefile.target
+@@ -217,6 +217,7 @@ obj-i386-y += pc_piix.o
+ obj-i386-y += testdev.o
+ obj-i386-y += acpi.o acpi_piix4.o
+ obj-i386-$(CONFIG_SPICE) += qxl.o qxl-logger.o qxl-render.o
++obj-i386-$(CONFIG_SPICE) += spice-vmc.o
+
+ obj-i386-y += pcspk.o i8254.o
+ obj-i386-$(CONFIG_KVM_PIT) += i8254-kvm.o
+diff --git a/hw/spice-vmc.c b/hw/spice-vmc.c
+new file mode 100644
+index 0000000..b77fc60
+--- /dev/null
++++ b/hw/spice-vmc.c
+@@ -0,0 +1,262 @@
++/*
++
++ Spice Virtual Machine Channel (VMC).
++
++ A virtio-serial port used for spice to guest communication, over
++ which spice client and a daemon in the guest operating system
++ communicate.
++
++ Replaces the old vdi_port PCI device.
++
++*/
++
++#include <stdio.h>
++#include <stdbool.h>
++#include <spice.h>
++#include <spice-experimental.h>
++
++#include "virtio-serial.h"
++#include "qemu-spice.h"
++
++#define VMC_GUEST_DEVICE_NAME "com.redhat.spice.0"
++#define VMC_DEVICE_NAME "spicevmc"
++
++/* windows guest driver bug workaround */
++#define VMC_MAX_HOST_WRITE 2048
++
++#define dprintf(_svc, _level, _fmt, ...) \
++ do { \
++ static unsigned __dprintf_counter = 0; \
++ if (_svc->debug >= _level) { \
++ fprintf(stderr, "svc: %3d: " _fmt, ++__dprintf_counter, ## __VA_ARGS__);\
++ } \
++ } while (0)
++
++typedef struct SpiceVirtualChannel {
++ VirtIOSerialPort port;
++ VMChangeStateEntry *vmstate;
++ SpiceCharDeviceInstance sin;
++ char *subtype;
++ bool active;
++ uint8_t *buffer;
++ uint8_t *datapos;
++ ssize_t bufsize, datalen;
++ uint32_t debug;
++} SpiceVirtualChannel;
++
++static int vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len)
++{
++ SpiceVirtualChannel *svc = container_of(sin, SpiceVirtualChannel, sin);
++ ssize_t out = 0;
++ ssize_t last_out;
++ uint8_t* p = (uint8_t*)buf;
++
++ while (len > 0) {
++ last_out = virtio_serial_write(&svc->port, p,
++ MIN(len, VMC_MAX_HOST_WRITE));
++ if (last_out > 0) {
++ out += last_out;
++ len -= last_out;
++ p += last_out;
++ } else {
++ break;
++ }
++ }
++
++ dprintf(svc, 3, "%s: %lu/%zd\n", __func__, out, len + out);
++ return out;
++}
++
++static int vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len)
++{
++ SpiceVirtualChannel *svc = container_of(sin, SpiceVirtualChannel, sin);
++ int bytes = MIN(len, svc->datalen);
++
++ dprintf(svc, 2, "%s: %p %d/%d/%zd\n", __func__, svc->datapos, len, bytes, svc->datalen);
++ if (bytes > 0) {
++ memcpy(buf, svc->datapos, bytes);
++ svc->datapos += bytes;
++ svc->datalen -= bytes;
++ assert(svc->datalen >= 0);
++ if (svc->datalen == 0) {
++ svc->datapos = 0;
++ virtio_serial_throttle_port(&svc->port, false);
++ // ^^^ !!! may call vmc_have_data, so don't touch svc after it!
++ }
++ }
++ return bytes;
++}
++
++static SpiceCharDeviceInterface vmc_interface = {
++ .base.type = SPICE_INTERFACE_CHAR_DEVICE,
++ .base.description = "spice virtual channel char device",
++ .base.major_version = SPICE_INTERFACE_CHAR_DEVICE_MAJOR,
++ .base.minor_version = SPICE_INTERFACE_CHAR_DEVICE_MINOR,
++ .write = vmc_write,
++ .read = vmc_read,
++};
++
++static void vmc_register_interface(SpiceVirtualChannel *svc)
++{
++ if (svc->active) {
++ return;
++ }
++ dprintf(svc, 1, "%s\n", __func__);
++ svc->sin.base.sif = &vmc_interface.base;
++ spice_server_add_interface(spice_server, &svc->sin.base);
++ svc->active = true;
++}
++
++static void vmc_unregister_interface(SpiceVirtualChannel *svc)
++{
++ if (!svc->active) {
++ return;
++ }
++ dprintf(svc, 1, "%s\n", __func__);
++ spice_server_remove_interface(&svc->sin.base);
++ svc->active = false;
++}
++
++
++static void vmc_change_state_handler(void *opaque, int running, int reason)
++{
++ SpiceVirtualChannel *svc = opaque;
++
++ if (running && svc->active) {
++ spice_server_char_device_wakeup(&svc->sin);
++ }
++}
++
++/*
++ * virtio-serial callbacks
++ */
++
++static void vmc_guest_open(VirtIOSerialPort *port)
++{
++ SpiceVirtualChannel *svc = DO_UPCAST(SpiceVirtualChannel, port, port);
++
++ dprintf(svc, 1, "%s\n", __func__);
++ vmc_register_interface(svc);
++}
++
++static void vmc_guest_close(VirtIOSerialPort *port)
++{
++ SpiceVirtualChannel *svc = DO_UPCAST(SpiceVirtualChannel, port, port);
++
++ dprintf(svc, 1, "%s\n", __func__);
++ vmc_unregister_interface(svc);
++}
++
++static void vmc_guest_ready(VirtIOSerialPort *port)
++{
++ SpiceVirtualChannel *svc = DO_UPCAST(SpiceVirtualChannel, port, port);
++
++ dprintf(svc, 1, "%s\n", __func__);
++ if (svc->active) {
++ spice_server_char_device_wakeup(&svc->sin);
++ }
++}
++
++static void vmc_have_data(VirtIOSerialPort *port, const uint8_t *buf, size_t len)
++{
++ SpiceVirtualChannel *svc = DO_UPCAST(SpiceVirtualChannel, port, port);
++
++ dprintf(svc, 2, "%s: %zd\n", __func__, len);
++ assert(svc->datalen == 0);
++ if (svc->bufsize < len) {
++ svc->bufsize = len;
++ svc->buffer = qemu_realloc(svc->buffer, svc->bufsize);
++ }
++ memcpy(svc->buffer, buf, len);
++ svc->datapos = svc->buffer;
++ svc->datalen = len;
++ virtio_serial_throttle_port(&svc->port, true);
++ spice_server_char_device_wakeup(&svc->sin);
++}
++
++static void vmc_print_optional_subtypes(void)
++{
++ const char** psubtype = spice_server_char_device_recognized_subtypes();
++ int i;
++
++ fprintf(stderr, "supported subtypes: ");
++ for(i=0; *psubtype != NULL; ++psubtype, ++i) {
++ if (i == 0) {
++ fprintf(stderr, *psubtype);
++ } else {
++ fprintf(stderr, ", %s", *psubtype);
++ }
++ }
++ fprintf(stderr, "\n");
++}
++
++static int vmc_initfn(VirtIOSerialDevice *dev)
++{
++ VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, &dev->qdev);
++ SpiceVirtualChannel *svc = DO_UPCAST(SpiceVirtualChannel, port, port);
++ const char** psubtype = spice_server_char_device_recognized_subtypes();
++ const char *subtype = NULL;
++
++ if (!using_spice) {
++ return -1;
++ }
++
++ dprintf(svc, 1, "%s\n", __func__);
++
++ if (svc->subtype == NULL) {
++ svc->subtype = strdup("vdagent");
++ }
++
++ for(;*psubtype != NULL; ++psubtype) {
++ if (strcmp(svc->subtype, *psubtype) == 0) {
++ subtype = *psubtype;
++ break;
++ }
++ }
++ if (subtype == NULL) {
++ fprintf(stderr, "spice-vmc: unsupported subtype\n");
++ vmc_print_optional_subtypes();
++ return -1;
++ }
++ port->name = qemu_strdup(VMC_GUEST_DEVICE_NAME);
++ svc->vmstate = qemu_add_vm_change_state_handler
++ (vmc_change_state_handler, svc);
++ svc->sin.subtype = svc->subtype;
++ virtio_serial_open(port);
++ return 0;
++}
++
++static int vmc_exitfn(VirtIOSerialDevice *dev)
++{
++ VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, &dev->qdev);
++ SpiceVirtualChannel *svc = DO_UPCAST(SpiceVirtualChannel, port, port);
++
++ dprintf(svc, 1, "%s\n", __func__);
++ vmc_unregister_interface(svc);
++ qemu_del_vm_change_state_handler(svc->vmstate);
++ virtio_serial_close(port);
++ return 0;
++}
++
++static VirtIOSerialPortInfo vmc_info = {
++ .qdev.name = VMC_DEVICE_NAME,
++ .qdev.size = sizeof(SpiceVirtualChannel),
++ .init = vmc_initfn,
++ .exit = vmc_exitfn,
++ .guest_open = vmc_guest_open,
++ .guest_close = vmc_guest_close,
++ .guest_ready = vmc_guest_ready,
++ .have_data = vmc_have_data,
++ .qdev.props = (Property[]) {
++ DEFINE_PROP_UINT32("nr", SpiceVirtualChannel, port.id, VIRTIO_CONSOLE_BAD_ID),
++ DEFINE_PROP_UINT32("debug", SpiceVirtualChannel, debug, 1),
++ DEFINE_PROP_STRING("subtype", SpiceVirtualChannel, subtype),
++ DEFINE_PROP_END_OF_LIST(),
++ }
++};
++
++static void vmc_register(void)
++{
++ virtio_serial_port_qdev_register(&vmc_info);
++}
++device_init(vmc_register)
+--
+1.7.2.3
+
diff --git a/0039-qxl-fix-release-ring-overrun.patch b/0039-qxl-fix-release-ring-overrun.patch
new file mode 100644
index 0000000..a8d041a
--- /dev/null
+++ b/0039-qxl-fix-release-ring-overrun.patch
@@ -0,0 +1,30 @@
+From 9394cbaab7701fe421d5c0168854d39d6a8ecfc2 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel at redhat.com>
+Date: Tue, 7 Sep 2010 16:45:27 +0200
+Subject: [PATCH 39/39] qxl: fix release ring overrun
+
+---
+ hw/qxl.c | 6 +++---
+ 1 files changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/hw/qxl.c b/hw/qxl.c
+index 4a15200..8448893 100644
+--- a/hw/qxl.c
++++ b/hw/qxl.c
+@@ -377,10 +377,10 @@ static inline void qxl_push_free_res(PCIQXLDevice *d)
+ QXLReleaseRing *ring = &d->ram->release_ring;
+ uint64_t *item;
+
+-#define QXL_FREE_BUNCH_SIZE 10
++#define QXL_FREE_BUNCH_SIZE 32
+
+- if (SPICE_RING_IS_EMPTY(ring) || (d->num_free_res == QXL_FREE_BUNCH_SIZE &&
+- ring->prod - ring->cons + 1 != ring->num_items)) {
++ if (SPICE_RING_IS_EMPTY(ring) || (d->num_free_res >= QXL_FREE_BUNCH_SIZE &&
++ ring->prod - ring->cons + 2 != ring->num_items)) {
+ int notify;
+
+ SPICE_RING_PUSH(ring, notify);
+--
+1.7.2.3
+
diff --git a/pc-add-a-Fedora-13-machine-type-for-backward-compat.patch b/pc-add-a-Fedora-13-machine-type-for-backward-compat.patch
new file mode 100644
index 0000000..25938aa
--- /dev/null
+++ b/pc-add-a-Fedora-13-machine-type-for-backward-compat.patch
@@ -0,0 +1,37 @@
+From: Justin M. Forbes <jforbes at redhat.com>
+Date: Thu, Aug 19 09:13:45 2010 -0500
+Subject: pc: Add a Fedora-13 machine type for backwards compatibility.
+
+In Fedora 13 a fedora-13 machine type was added as default to allow
+interaction with upstream stable qemu which did not support the same
+feature set. As a result we need to support this machine type through
+the Fedora 15 release.
+
+
+diff --git a/hw/pc_piix.c b/hw/pc_piix.c
+index 9e4bac8..eb1ed05 100644
+--- a/hw/pc_piix.c
++++ b/hw/pc_piix.c
+@@ -237,6 +237,14 @@ static QEMUMachine pc_machine = {
+ .is_default = 1,
+ };
+
++static QEMUMachine pc_machine_f13 = {
++ .name = "fedora-13",
++ .desc = "Standard PC",
++ .init = pc_init_pci,
++ .max_cpus = 255,
++ .is_default = 0,
++};
++
+ static QEMUMachine pc_machine_v0_12 = {
+ .name = "pc-0.12",
+ .desc = "Standard PC",
+@@ -348,6 +356,7 @@ static QEMUMachine isapc_machine = {
+ static void pc_machine_init(void)
+ {
+ qemu_register_machine(&pc_machine);
++ qemu_register_machine(&pc_machine_f13);
+ qemu_register_machine(&pc_machine_v0_12);
+ qemu_register_machine(&pc_machine_v0_11);
+ qemu_register_machine(&pc_machine_v0_10);
diff --git a/qemu.spec b/qemu.spec
index 5d3037d..b76a714 100644
--- a/qemu.spec
+++ b/qemu.spec
@@ -1,9 +1,7 @@
-%define githead 25fdf4a
-
Summary: QEMU is a FAST! processor emulator
Name: qemu
Version: 0.13.0
-Release: 0.5.20100809git%{githead}%{?dist}
+Release: 0.6.rc1%{?dist}
# Epoch because we pushed a qemu-1.0 package
Epoch: 2
License: GPLv2+ and LGPLv2+ and BSD
@@ -19,11 +17,7 @@ URL: http://www.qemu.org/
%define _smp_mflags %{nil}
%endif
-# Source0: http://downloads.sourceforge.net/sourceforge/kvm/qemu-kvm-%{version}.tar.gz
-# The source for this package was pulled from upstream's git. Use the
-# following commands to generate the tarball:
-# git archive --format=tar --prefix=qemu-kvm-0.13/ b81fe95 | gzip > qemu-kvm-0.13-b81fe95.tar.gz
-Source0: qemu-kvm-%{version}-%{githead}.tar.gz
+Source0: http://downloads.sourceforge.net/sourceforge/kvm/qemu-kvm-%{version}-rc1.tar.gz
Source1: qemu.init
# Loads kvm kernel modules at boot
@@ -39,6 +33,51 @@ Source6: ksmtuned.init
Source7: ksmtuned
Source8: ksmtuned.conf
+# This patch must be carried through F-15 to support guests created
+# with F-13/
+Patch00: pc-add-a-Fedora-13-machine-type-for-backward-compat.patch
+
+# Patches from Fedora qemu git (http://git.fedorahosted.org/git/qemu-kvm-fedora.git)
+Patch01: 0001-add-pflib-PixelFormat-conversion-library.patch
+Patch02: 0002-configure-add-logging.patch
+Patch03: 0003-add-spice-into-the-configure-file.patch
+Patch04: 0004-spice-core-bits.patch
+Patch05: 0005-spice-add-keyboard.patch
+Patch06: 0006-spice-add-mouse.patch
+Patch07: 0007-spice-simple-display.patch
+Patch08: 0008-spice-add-tablet-support.patch
+Patch09: 0009-vgabios-update-to-0.6c-pcibios-patches.patch
+Patch10: 0010-switch-stdvga-to-pci-vgabios.patch
+Patch11: 0011-switch-vmware_vga-to-pci-vgabios.patch
+Patch12: 0012-all-vga-refuse-hotplugging.patch
+Patch13: 0013-spice-tls-support.patch
+Patch14: 0014-spice-add-qxl-device.patch
+Patch15: 0015-spice-add-audio.patch
+Patch16: 0016-spice-add-virtio-serial-based-vdi-port-backend.patch
+Patch17: 0017-spice-add-pci-vdi-port-backend-obsolete.patch
+Patch18: 0018-use-memalign-instead-of-posix_memalign.patch
+Patch19: 0019-spice-live-migration-wip.patch
+Patch20: 0020-spice-display-draw.h-is-internal-now.patch
+Patch21: 0021-spice-display-disable-debug.patch
+Patch22: 0022-spice-display-pci-rev-fixups.patch
+Patch23: 0023-qxl-pci-rev-fixups.patch
+Patch24: 0024-qxl-support-QXL_IO_DESTROY_ALL_SURFACES.patch
+Patch25: 0025-spice-vmc-two-bugfixes-in-vmc_read.patch
+Patch26: 0026-spice-enabling-disabling-jpeg-and-zlib-over-glz-via-.patch
+Patch27: 0027-ifdef-new-config-options.patch
+Patch28: 0028-spice-vmc-add-counter-to-debug-statements.patch
+Patch29: 0029-spice-vmc-split-vmc_write-to-max-sized-virtio_serial.patch
+Patch30: 0030-qxl-add-800x480-resolution-to-qxl_modes-n900-native.patch
+Patch31: 0031-qxl-savevm-fixes.patch
+Patch32: 0032-Revert-spice-vmc-split-vmc_write-to-max-sized-virtio.patch
+Patch33: 0033-Revert-spice-vmc-add-counter-to-debug-statements.patch
+Patch34: 0034-Revert-spice-vmc-two-bugfixes-in-vmc_read.patch
+Patch35: 0035-Revert-spice-live-migration-wip.patch
+Patch36: 0036-Revert-spice-add-pci-vdi-port-backend-obsolete.patch
+Patch37: 0037-Revert-spice-add-virtio-serial-based-vdi-port-backen.patch
+Patch38: 0038-spice-add-virtio-serial-based-spice-vmchannel-backen.patch
+Patch39: 0039-qxl-fix-release-ring-overrun.patch
+
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
BuildRequires: SDL-devel zlib-devel which texi2html gnutls-devel cyrus-sasl-devel
BuildRequires: libaio-devel
@@ -48,7 +87,8 @@ BuildRequires: pulseaudio-libs-devel
BuildRequires: ncurses-devel
BuildRequires: texinfo
%ifarch x86_64
-BuildRequires: spice-protocol spice-server-devel
+BuildRequires: spice-protocol >= 0.6.0
+BuildRequires: spice-server-devel >= 0.6.0
%endif
Requires: %{name}-user = %{epoch}:%{version}-%{release}
Requires: %{name}-system-x86 = %{epoch}:%{version}-%{release}
@@ -231,7 +271,48 @@ such as kvm_stat.
%endif
%prep
-%setup -q -n qemu-kvm-%{version}
+%setup -q -n qemu-kvm-%{version}-rc1
+
+%patch00 -p1
+%patch01 -p1
+%patch02 -p1
+%patch03 -p1
+%patch04 -p1
+%patch05 -p1
+%patch06 -p1
+%patch07 -p1
+%patch08 -p1
+%patch09 -p1
+%patch10 -p1
+%patch11 -p1
+%patch12 -p1
+%patch13 -p1
+%patch14 -p1
+%patch15 -p1
+%patch16 -p1
+%patch17 -p1
+%patch18 -p1
+%patch19 -p1
+%patch20 -p1
+%patch21 -p1
+%patch22 -p1
+%patch23 -p1
+%patch24 -p1
+%patch25 -p1
+%patch26 -p1
+%patch27 -p1
+%patch28 -p1
+%patch29 -p1
+%patch30 -p1
+%patch31 -p1
+%patch32 -p1
+%patch33 -p1
+%patch34 -p1
+%patch35 -p1
+%patch36 -p1
+%patch37 -p1
+%patch38 -p1
+%patch39 -p1
%build
# By default we build everything, but allow x86 to build a minimal version
@@ -546,6 +627,10 @@ fi
%{_mandir}/man1/qemu-img.1*
%changelog
+* Tue Aug 10 2010 Justin M. Forbes <jforbes at redhat.com> - 2:0.13.0-0.6.rc1
+- Move away from git snapshots as 0.13 is close to release
+- Updates for spice 0.6
+
* Tue Aug 10 2010 Justin M. Forbes <jforbes at redhat.com> - 2:0.13.0-0.5.20100809git25fdf4a
- Fix typo in e1000 gpxe rom requires.
- Add links to newer vgabios
diff --git a/sources b/sources
index 7b285f1..d6e74dd 100644
--- a/sources
+++ b/sources
@@ -1 +1 @@
-7f083a69c98b6b57bb2a4d52a7b50a72 qemu-kvm-0.13.0-25fdf4a.tar.gz
+b00b0206a49bd962024931b57781dea2 qemu-kvm-0.13.0-rc1.tar.gz
More information about the scm-commits
mailing list