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 +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 ++ * ++ * 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 +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 +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 ++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 +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 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 ++ ++#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 ++#include ++#include ++ ++#include ++#include ++ ++#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 +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 ++#include ++#include ++ ++#include ++ ++#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 +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 +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 ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++#include ++#include ++ ++#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 +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 + #include ++#include + #include + + #include +@@ -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 +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 +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 +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 +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 +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 +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 ++#include ++#include ++#include ++ ++#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 ++#include ++#include ++#include ++ ++#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 ++#include ++#include ++#include ++#include ++#include ++ ++#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 +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 +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 ++#include ++#include ++#include ++ ++#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 +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 ++#include ++ ++#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 ++#include ++#include ++#include ++ ++#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 ++ ++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 ++ ++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 +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 +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 +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 +-#include ++#include + #include + + #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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +-#include +- +-#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 +-#include +-#include +-#include +- +-#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 +- +-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 +- +-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 +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 +-#include +-#include +-#include +- +-#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 +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 ++#include ++#include ++#include ++ ++#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 +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 +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 - 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 - 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