qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] Implementing qxl-virtio on QEMU
@ 2012-08-24 19:14 Erlon Cruz
  2012-08-24 19:14 ` [Qemu-devel] [PATCH 1/4] qxl: create a qxl common struct! Erlon Cruz
                   ` (4 more replies)
  0 siblings, 5 replies; 10+ messages in thread
From: Erlon Cruz @ 2012-08-24 19:14 UTC (permalink / raw)
  To: qemu-devel; +Cc: alevy

The following patches makes provides video support to non PCI architectures, please review!

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [Qemu-devel] [PATCH 1/4] qxl: create a qxl common struct!
  2012-08-24 19:14 [Qemu-devel] Implementing qxl-virtio on QEMU Erlon Cruz
@ 2012-08-24 19:14 ` Erlon Cruz
  2012-08-27  5:51   ` Gerd Hoffmann
  2012-08-24 19:14 ` [Qemu-devel] [PATCH 2/4] qxl: split qxl functions in common and pci files Erlon Cruz
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 10+ messages in thread
From: Erlon Cruz @ 2012-08-24 19:14 UTC (permalink / raw)
  To: qemu-devel
  Cc: Erlon R. Cruz, Fabiano Fidêncio, Fabiano Fidêncio,
	Rafael F. Santos, alevy

From: Fabiano Fidêncio <fabiano@fidencio.org>

This commit is creating a QXLDevice struct, entirely based on
PCIQXLDevice struct, but separating parts that will be shared between
PCIQXLDevice and VirtIOQXLDevice. All functions were changed to support
the new QXLDevice struct.

Signed-off-by: Erlon R. Cruz <erlon.cruz@fit-tecnologia.org.br>
Signed-off-by: Fabiano Fidêncio <fabiano.fidencio@fit-tecnologia.org.br>
Signed-off-by: Rafael F. Santos <fonsecasantos.rafael@gmail.com>
---
 hw/qxl-logger.c |   16 +-
 hw/qxl-render.c |   29 ++--
 hw/qxl.c        |  436 ++++++++++++++++++++++++++++---------------------------
 hw/qxl.h        |  110 ++++++++------
 4 files changed, 303 insertions(+), 288 deletions(-)

diff --git a/hw/qxl-logger.c b/hw/qxl-logger.c
index fe2878c..de66c19 100644
--- a/hw/qxl-logger.c
+++ b/hw/qxl-logger.c
@@ -100,7 +100,7 @@ static const char *qxl_v2n(const char *n[], size_t l, int v)
 }
 #define qxl_name(_list, _value) qxl_v2n(_list, ARRAY_SIZE(_list), _value)
 
-static int qxl_log_image(PCIQXLDevice *qxl, QXLPHYSICAL addr, int group_id)
+static int qxl_log_image(QXLDevice *qxl, QXLPHYSICAL addr, int group_id)
 {
     QXLImage *image;
     QXLImageDescriptor *desc;
@@ -134,7 +134,7 @@ static void qxl_log_rect(QXLRect *rect)
             rect->left, rect->top);
 }
 
-static int qxl_log_cmd_draw_copy(PCIQXLDevice *qxl, QXLCopy *copy,
+static int qxl_log_cmd_draw_copy(QXLDevice *qxl, QXLCopy *copy,
                                  int group_id)
 {
     int ret;
@@ -151,7 +151,7 @@ static int qxl_log_cmd_draw_copy(PCIQXLDevice *qxl, QXLCopy *copy,
     return 0;
 }
 
-static int qxl_log_cmd_draw(PCIQXLDevice *qxl, QXLDrawable *draw, int group_id)
+static int qxl_log_cmd_draw(QXLDevice *qxl, QXLDrawable *draw, int group_id)
 {
     fprintf(stderr, ": surface_id %d type %s effect %s",
             draw->surface_id,
@@ -165,7 +165,7 @@ static int qxl_log_cmd_draw(PCIQXLDevice *qxl, QXLDrawable *draw, int group_id)
     return 0;
 }
 
-static int qxl_log_cmd_draw_compat(PCIQXLDevice *qxl, QXLCompatDrawable *draw,
+static int qxl_log_cmd_draw_compat(QXLDevice *qxl, QXLCompatDrawable *draw,
                                    int group_id)
 {
     fprintf(stderr, ": type %s effect %s",
@@ -184,7 +184,7 @@ static int qxl_log_cmd_draw_compat(PCIQXLDevice *qxl, QXLCompatDrawable *draw,
     return 0;
 }
 
-static void qxl_log_cmd_surface(PCIQXLDevice *qxl, QXLSurfaceCmd *cmd)
+static void qxl_log_cmd_surface(QXLDevice *qxl, QXLSurfaceCmd *cmd)
 {
     fprintf(stderr, ": %s id %d",
             qxl_name(qxl_surface_cmd, cmd->type),
@@ -202,7 +202,7 @@ static void qxl_log_cmd_surface(PCIQXLDevice *qxl, QXLSurfaceCmd *cmd)
     }
 }
 
-int qxl_log_cmd_cursor(PCIQXLDevice *qxl, QXLCursorCmd *cmd, int group_id)
+int qxl_log_cmd_cursor(QXLDevice *qxl, QXLCursorCmd *cmd, int group_id)
 {
     QXLCursor *cursor;
 
@@ -233,13 +233,13 @@ int qxl_log_cmd_cursor(PCIQXLDevice *qxl, QXLCursorCmd *cmd, int group_id)
     return 0;
 }
 
-int qxl_log_command(PCIQXLDevice *qxl, const char *ring, QXLCommandExt *ext)
+int qxl_log_command(QXLDevice *qxl, const char *ring, QXLCommandExt *ext)
 {
     bool compat = ext->flags & QXL_COMMAND_FLAG_COMPAT;
     void *data;
     int ret;
 
-    if (!qxl->cmdlog) {
+    if (!qxl->pci.cmdlog) {
         return 0;
     }
     fprintf(stderr, "%" PRId64 " qxl-%d/%s:", qemu_get_clock_ns(vm_clock),
diff --git a/hw/qxl-render.c b/hw/qxl-render.c
index e2e3fe2..243ddb9 100644
--- a/hw/qxl-render.c
+++ b/hw/qxl-render.c
@@ -21,18 +21,18 @@
 
 #include "qxl.h"
 
-static void qxl_blit(PCIQXLDevice *qxl, QXLRect *rect)
+static void qxl_blit(QXLDevice *qxl, QXLRect *rect)
 {
     uint8_t *src;
-    uint8_t *dst = qxl->vga.ds->surface->data;
+    uint8_t *dst = qxl->pci.vga.ds->surface->data;
     int len, i;
 
-    if (is_buffer_shared(qxl->vga.ds->surface)) {
+    if (is_buffer_shared(qxl->pci.vga.ds->surface)) {
         return;
     }
     if (!qxl->guest_primary.data) {
         trace_qxl_render_blit_guest_primary_initialized();
-        qxl->guest_primary.data = memory_region_get_ram_ptr(&qxl->vga.vram);
+        qxl->guest_primary.data = memory_region_get_ram_ptr(&qxl->pci.vga.vram);
     }
     trace_qxl_render_blit(qxl->guest_primary.qxl_stride,
             rect->left, rect->right, rect->top, rect->bottom);
@@ -57,7 +57,7 @@ static void qxl_blit(PCIQXLDevice *qxl, QXLRect *rect)
     }
 }
 
-void qxl_render_resize(PCIQXLDevice *qxl)
+void qxl_render_resize(QXLDevice *qxl)
 {
     QXLSurfaceCreate *sc = &qxl->guest_primary.surface;
 
@@ -87,7 +87,7 @@ void qxl_render_resize(PCIQXLDevice *qxl)
     }
 }
 
-static void qxl_set_rect_to_surface(PCIQXLDevice *qxl, QXLRect *area)
+static void qxl_set_rect_to_surface(QXLDevice *qxl, QXLRect *area)
 {
     area->left   = 0;
     area->right  = qxl->guest_primary.surface.width;
@@ -95,15 +95,16 @@ static void qxl_set_rect_to_surface(PCIQXLDevice *qxl, QXLRect *area)
     area->bottom = qxl->guest_primary.surface.height;
 }
 
-static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl)
+static void qxl_render_update_area_unlocked(QXLDevice *qxl)
 {
-    VGACommonState *vga = &qxl->vga;
+    VGACommonState *vga = &qxl->pci.vga;
     int i;
     DisplaySurface *surface = vga->ds->surface;
 
     if (qxl->guest_primary.resized) {
         qxl->guest_primary.resized = 0;
-        qxl->guest_primary.data = memory_region_get_ram_ptr(&qxl->vga.vram);
+        qxl->guest_primary.data =
+            memory_region_get_ram_ptr(&qxl->pci.vga.vram);
         qxl_set_rect_to_surface(qxl, &qxl->dirty[0]);
         qxl->num_dirty_rects = 1;
         trace_qxl_render_guest_primary_resized(
@@ -148,7 +149,7 @@ static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl)
  * callbacks are called by spice_server thread, defering to bh called from the
  * io thread.
  */
-void qxl_render_update(PCIQXLDevice *qxl)
+void qxl_render_update(QXLDevice *qxl)
 {
     QXLCookie *cookie;
 
@@ -172,14 +173,14 @@ void qxl_render_update(PCIQXLDevice *qxl)
 
 void qxl_render_update_area_bh(void *opaque)
 {
-    PCIQXLDevice *qxl = opaque;
+    QXLDevice *qxl = opaque;
 
     qemu_mutex_lock(&qxl->ssd.lock);
     qxl_render_update_area_unlocked(qxl);
     qemu_mutex_unlock(&qxl->ssd.lock);
 }
 
-void qxl_render_update_area_done(PCIQXLDevice *qxl, QXLCookie *cookie)
+void qxl_render_update_area_done(QXLDevice *qxl, QXLCookie *cookie)
 {
     qemu_mutex_lock(&qxl->ssd.lock);
     trace_qxl_render_update_area_done(cookie);
@@ -189,7 +190,7 @@ void qxl_render_update_area_done(PCIQXLDevice *qxl, QXLCookie *cookie)
     g_free(cookie);
 }
 
-static QEMUCursor *qxl_cursor(PCIQXLDevice *qxl, QXLCursor *cursor)
+static QEMUCursor *qxl_cursor(QXLDevice *qxl, QXLCursor *cursor)
 {
     QEMUCursor *c;
     uint8_t *image, *mask;
@@ -228,7 +229,7 @@ fail:
 
 
 /* called from spice server thread context only */
-int qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext)
+int qxl_render_cursor(QXLDevice *qxl, QXLCommandExt *ext)
 {
     QXLCursorCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id);
     QXLCursor *cursor;
diff --git a/hw/qxl.c b/hw/qxl.c
index c2dd3b4..c8a36aa 100644
--- a/hw/qxl.c
+++ b/hw/qxl.c
@@ -126,19 +126,19 @@ static QXLMode qxl_modes[] = {
     QXL_MODE_EX(3200, 2400),
 };
 
-static PCIQXLDevice *qxl0;
+static QXLDevice *qxl0;
 
-static void qxl_send_events(PCIQXLDevice *d, uint32_t events);
-static int qxl_destroy_primary(PCIQXLDevice *d, qxl_async_io async);
-static void qxl_reset_memslots(PCIQXLDevice *d);
-static void qxl_reset_surfaces(PCIQXLDevice *d);
-static void qxl_ring_set_dirty(PCIQXLDevice *qxl);
+static void qxl_send_events(QXLDevice *d, uint32_t events);
+static int qxl_destroy_primary(QXLDevice *d, qxl_async_io async);
+static void qxl_reset_memslots(QXLDevice *d);
+static void qxl_reset_surfaces(QXLDevice *d);
+static void qxl_ring_set_dirty(QXLDevice *qxl);
 
-void qxl_set_guest_bug(PCIQXLDevice *qxl, const char *msg, ...)
+void qxl_set_guest_bug(QXLDevice *qxl, const char *msg, ...)
 {
     qxl_send_events(qxl, QXL_INTERRUPT_ERROR);
-    qxl->guest_bug = 1;
-    if (qxl->guestdebug) {
+    qxl->pci.guest_bug = 1;
+    if (qxl->pci.guestdebug) {
         va_list ap;
         va_start(ap, msg);
         fprintf(stderr, "qxl-%d: guest bug: ", qxl->id);
@@ -148,12 +148,12 @@ void qxl_set_guest_bug(PCIQXLDevice *qxl, const char *msg, ...)
     }
 }
 
-static void qxl_clear_guest_bug(PCIQXLDevice *qxl)
+static void qxl_clear_guest_bug(QXLDevice *qxl)
 {
-    qxl->guest_bug = 0;
+    qxl->pci.guest_bug = 0;
 }
 
-void qxl_spice_update_area(PCIQXLDevice *qxl, uint32_t surface_id,
+void qxl_spice_update_area(QXLDevice *qxl, uint32_t surface_id,
                            struct QXLRect *area, struct QXLRect *dirty_rects,
                            uint32_t num_dirty_rects,
                            uint32_t clear_dirty_region,
@@ -173,7 +173,7 @@ void qxl_spice_update_area(PCIQXLDevice *qxl, uint32_t surface_id,
     }
 }
 
-static void qxl_spice_destroy_surface_wait_complete(PCIQXLDevice *qxl,
+static void qxl_spice_destroy_surface_wait_complete(QXLDevice *qxl,
                                                     uint32_t id)
 {
     trace_qxl_spice_destroy_surface_wait_complete(qxl->id, id);
@@ -183,7 +183,7 @@ static void qxl_spice_destroy_surface_wait_complete(PCIQXLDevice *qxl,
     qemu_mutex_unlock(&qxl->track_lock);
 }
 
-static void qxl_spice_destroy_surface_wait(PCIQXLDevice *qxl, uint32_t id,
+static void qxl_spice_destroy_surface_wait(QXLDevice *qxl, uint32_t id,
                                            qxl_async_io async)
 {
     QXLCookie *cookie;
@@ -199,7 +199,7 @@ static void qxl_spice_destroy_surface_wait(PCIQXLDevice *qxl, uint32_t id,
     }
 }
 
-static void qxl_spice_flush_surfaces_async(PCIQXLDevice *qxl)
+static void qxl_spice_flush_surfaces_async(QXLDevice *qxl)
 {
     trace_qxl_spice_flush_surfaces_async(qxl->id, qxl->guest_surfaces.count,
                                          qxl->num_free_res);
@@ -208,26 +208,26 @@ static void qxl_spice_flush_surfaces_async(PCIQXLDevice *qxl)
                                   QXL_IO_FLUSH_SURFACES_ASYNC));
 }
 
-void qxl_spice_loadvm_commands(PCIQXLDevice *qxl, struct QXLCommandExt *ext,
+void qxl_spice_loadvm_commands(QXLDevice *qxl, struct QXLCommandExt *ext,
                                uint32_t count)
 {
     trace_qxl_spice_loadvm_commands(qxl->id, ext, count);
     qxl->ssd.worker->loadvm_commands(qxl->ssd.worker, ext, count);
 }
 
-void qxl_spice_oom(PCIQXLDevice *qxl)
+void qxl_spice_oom(QXLDevice *qxl)
 {
     trace_qxl_spice_oom(qxl->id);
     qxl->ssd.worker->oom(qxl->ssd.worker);
 }
 
-void qxl_spice_reset_memslots(PCIQXLDevice *qxl)
+void qxl_spice_reset_memslots(QXLDevice *qxl)
 {
     trace_qxl_spice_reset_memslots(qxl->id);
     qxl->ssd.worker->reset_memslots(qxl->ssd.worker);
 }
 
-static void qxl_spice_destroy_surfaces_complete(PCIQXLDevice *qxl)
+static void qxl_spice_destroy_surfaces_complete(QXLDevice *qxl)
 {
     trace_qxl_spice_destroy_surfaces_complete(qxl->id);
     qemu_mutex_lock(&qxl->track_lock);
@@ -236,7 +236,7 @@ static void qxl_spice_destroy_surfaces_complete(PCIQXLDevice *qxl)
     qemu_mutex_unlock(&qxl->track_lock);
 }
 
-static void qxl_spice_destroy_surfaces(PCIQXLDevice *qxl, qxl_async_io async)
+static void qxl_spice_destroy_surfaces(QXLDevice *qxl, qxl_async_io async)
 {
     trace_qxl_spice_destroy_surfaces(qxl->id, async);
     if (async) {
@@ -249,13 +249,13 @@ static void qxl_spice_destroy_surfaces(PCIQXLDevice *qxl, qxl_async_io async)
     }
 }
 
-void qxl_spice_reset_image_cache(PCIQXLDevice *qxl)
+void qxl_spice_reset_image_cache(QXLDevice *qxl)
 {
     trace_qxl_spice_reset_image_cache(qxl->id);
     qxl->ssd.worker->reset_image_cache(qxl->ssd.worker);
 }
 
-void qxl_spice_reset_cursor(PCIQXLDevice *qxl)
+void qxl_spice_reset_cursor(QXLDevice *qxl)
 {
     trace_qxl_spice_reset_cursor(qxl->id);
     qxl->ssd.worker->reset_cursor(qxl->ssd.worker);
@@ -286,9 +286,10 @@ static ram_addr_t qxl_rom_size(void)
     return rom_size;
 }
 
-static void init_qxl_rom(PCIQXLDevice *d)
+static void init_qxl_rom(QXLDevice *d)
 {
-    QXLRom *rom = memory_region_get_ram_ptr(&d->rom_bar);
+    QXLRom *rom;
+    rom = memory_region_get_ram_ptr(&d->pci.rom_bar);
     QXLModes *modes = (QXLModes *)(rom + 1);
     uint32_t ram_header_size;
     uint32_t surface0_area_size;
@@ -296,11 +297,11 @@ static void init_qxl_rom(PCIQXLDevice *d)
     uint32_t fb;
     int i, n;
 
-    memset(rom, 0, d->rom_size);
+    memset(rom, 0, d->pci.rom_size);
 
     rom->magic         = cpu_to_le32(QXL_ROM_MAGIC);
     rom->id            = cpu_to_le32(d->id);
-    rom->log_level     = cpu_to_le32(d->guestdebug);
+    rom->log_level     = cpu_to_le32(d->pci.guestdebug);
     rom->modes_offset  = cpu_to_le32(sizeof(QXLRom));
 
     rom->slot_gen_bits = MEMSLOT_GENERATION_BITS;
@@ -311,7 +312,7 @@ static void init_qxl_rom(PCIQXLDevice *d)
 
     for (i = 0, n = 0; i < ARRAY_SIZE(qxl_modes); i++) {
         fb = qxl_modes[i].y_res * qxl_modes[i].stride;
-        if (fb > d->vgamem_size) {
+        if (fb > d->pci.vgamem_size) {
             continue;
         }
         modes->modes[n].id          = cpu_to_le32(i);
@@ -327,8 +328,8 @@ static void init_qxl_rom(PCIQXLDevice *d)
     modes->n_modes     = cpu_to_le32(n);
 
     ram_header_size    = ALIGN(sizeof(QXLRam), 4096);
-    surface0_area_size = ALIGN(d->vgamem_size, 4096);
-    num_pages          = d->vga.vram_size;
+    surface0_area_size = ALIGN(d->pci.vgamem_size, 4096);
+    num_pages          = d->pci.vga.vram_size;
     num_pages         -= ram_header_size;
     num_pages         -= surface0_area_size;
     num_pages          = num_pages / TARGET_PAGE_SIZE;
@@ -337,19 +338,19 @@ static void init_qxl_rom(PCIQXLDevice *d)
     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);
+    rom->ram_header_offset  = cpu_to_le32(d->pci.vga.vram_size - ram_header_size);
 
     d->shadow_rom = *rom;
     d->rom        = rom;
     d->modes      = modes;
 }
 
-static void init_qxl_ram(PCIQXLDevice *d)
+static void init_qxl_ram(QXLDevice *d)
 {
     uint8_t *buf;
     uint64_t *item;
 
-    buf = d->vga.vram_ptr;
+    buf = d->pci.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);
@@ -370,36 +371,36 @@ static void qxl_set_dirty(MemoryRegion *mr, ram_addr_t addr, ram_addr_t end)
     memory_region_set_dirty(mr, addr, end - addr);
 }
 
-static void qxl_rom_set_dirty(PCIQXLDevice *qxl)
+static void qxl_rom_set_dirty(QXLDevice *qxl)
 {
-    qxl_set_dirty(&qxl->rom_bar, 0, qxl->rom_size);
+    qxl_set_dirty(&qxl->pci.rom_bar, 0, qxl->pci.rom_size);
 }
 
 /* called from spice server thread context only */
-static void qxl_ram_set_dirty(PCIQXLDevice *qxl, void *ptr)
+static void qxl_ram_set_dirty(QXLDevice *qxl, void *ptr)
 {
-    void *base = qxl->vga.vram_ptr;
+    void *base = qxl->pci.vga.vram_ptr;
     intptr_t offset;
 
     offset = ptr - base;
     offset &= ~(TARGET_PAGE_SIZE-1);
-    assert(offset < qxl->vga.vram_size);
-    qxl_set_dirty(&qxl->vga.vram, offset, offset + TARGET_PAGE_SIZE);
+    assert(offset < qxl->pci.vga.vram_size);
+    qxl_set_dirty(&qxl->pci.vga.vram, offset, offset + TARGET_PAGE_SIZE);
 }
 
 /* can be called from spice server thread context */
-static void qxl_ring_set_dirty(PCIQXLDevice *qxl)
+static void qxl_ring_set_dirty(QXLDevice *qxl)
 {
     ram_addr_t addr = qxl->shadow_rom.ram_header_offset;
-    ram_addr_t end  = qxl->vga.vram_size;
-    qxl_set_dirty(&qxl->vga.vram, addr, end);
+    ram_addr_t end  = qxl->pci.vga.vram_size;
+    qxl_set_dirty(&qxl->pci.vga.vram, addr, end);
 }
 
 /*
  * keep track of some command state, for savevm/loadvm.
  * called from spice server thread context only
  */
-static int qxl_track_command(PCIQXLDevice *qxl, struct QXLCommandExt *ext)
+static int qxl_track_command(QXLDevice *qxl, struct QXLCommandExt *ext)
 {
     switch (le32_to_cpu(ext->cmd.type)) {
     case QXL_CMD_SURFACE:
@@ -452,35 +453,35 @@ static int qxl_track_command(PCIQXLDevice *qxl, struct QXLCommandExt *ext)
 
 static void interface_attach_worker(QXLInstance *sin, QXLWorker *qxl_worker)
 {
-    PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
+    QXLDevice *qxl = container_of(sin, QXLDevice, ssd.qxl);
 
-    trace_qxl_interface_attach_worker(qxl->id);
     qxl->ssd.worker = qxl_worker;
+    trace_qxl_interface_attach_worker(qxl->id);
 }
 
 static void interface_set_compression_level(QXLInstance *sin, int level)
 {
-    PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
+    QXLDevice *qxl = container_of(sin, QXLDevice, ssd.qxl);
 
     trace_qxl_interface_set_compression_level(qxl->id, level);
     qxl->shadow_rom.compression_level = cpu_to_le32(level);
-    qxl->rom->compression_level = cpu_to_le32(level);
     qxl_rom_set_dirty(qxl);
+    qxl->rom->compression_level = cpu_to_le32(level);
 }
 
 static void interface_set_mm_time(QXLInstance *sin, uint32_t mm_time)
 {
-    PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
+    QXLDevice *qxl = container_of(sin, QXLDevice, ssd.qxl);
 
     trace_qxl_interface_set_mm_time(qxl->id, mm_time);
     qxl->shadow_rom.mm_clock = cpu_to_le32(mm_time);
-    qxl->rom->mm_clock = cpu_to_le32(mm_time);
     qxl_rom_set_dirty(qxl);
+    qxl->rom->mm_clock = cpu_to_le32(mm_time);
 }
 
 static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info)
 {
-    PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
+    QXLDevice *qxl = container_of(sin, QXLDevice, ssd.qxl);
 
     trace_qxl_interface_get_init_info(qxl->id);
     info->memslot_gen_bits = MEMSLOT_GENERATION_BITS;
@@ -542,18 +543,17 @@ static const char *io_port_to_string(uint32_t io_port)
     return io_port_to_string[io_port];
 }
 
-/* called from spice server thread context only */
 static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext)
 {
-    PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
+    QXLDevice *qxl = container_of(sin, QXLDevice, ssd.qxl);
     SimpleSpiceUpdate *update;
     QXLCommandRing *ring;
     QXLCommand *cmd;
     int notify, ret;
 
-    trace_qxl_ring_command_check(qxl->id, qxl_mode_to_string(qxl->mode));
+    trace_qxl_ring_command_check(qxl->id, qxl_mode_to_string(qxl->pci.mode));
 
-    switch (qxl->mode) {
+    switch (qxl->pci.mode) {
     case QXL_MODE_VGA:
         ret = false;
         qemu_mutex_lock(&qxl->ssd.lock);
@@ -565,7 +565,7 @@ static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext)
         }
         qemu_mutex_unlock(&qxl->ssd.lock);
         if (ret) {
-            trace_qxl_ring_command_get(qxl->id, qxl_mode_to_string(qxl->mode));
+            trace_qxl_ring_command_get(qxl->id, qxl_mode_to_string(qxl->pci.mode));
             qxl_log_command(qxl, "vga", ext);
         }
         return ret;
@@ -573,7 +573,7 @@ static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext)
     case QXL_MODE_NATIVE:
     case QXL_MODE_UNDEFINED:
         ring = &qxl->ram->cmd_ring;
-        if (qxl->guest_bug || SPICE_RING_IS_EMPTY(ring)) {
+        if (qxl->pci.guest_bug || SPICE_RING_IS_EMPTY(ring)) {
             return false;
         }
         SPICE_RING_CONS_ITEM(qxl, ring, cmd);
@@ -591,7 +591,7 @@ static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext)
         qxl->guest_primary.commands++;
         qxl_track_command(qxl, ext);
         qxl_log_command(qxl, "cmd", ext);
-        trace_qxl_ring_command_get(qxl->id, qxl_mode_to_string(qxl->mode));
+        trace_qxl_ring_command_get(qxl->id, qxl_mode_to_string(qxl->pci.mode));
         return true;
     default:
         return false;
@@ -601,11 +601,11 @@ static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext)
 /* called from spice server thread context only */
 static int interface_req_cmd_notification(QXLInstance *sin)
 {
-    PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
+    QXLDevice *qxl = container_of(sin, QXLDevice, ssd.qxl);
     int wait = 1;
 
     trace_qxl_ring_command_req_notification(qxl->id);
-    switch (qxl->mode) {
+    switch (qxl->pci.mode) {
     case QXL_MODE_COMPAT:
     case QXL_MODE_NATIVE:
     case QXL_MODE_UNDEFINED:
@@ -620,7 +620,7 @@ static int interface_req_cmd_notification(QXLInstance *sin)
 }
 
 /* called from spice server thread context only */
-static inline void qxl_push_free_res(PCIQXLDevice *d, int flush)
+static inline void qxl_push_free_res(QXLDevice *d, int flush)
 {
     QXLReleaseRing *ring = &d->ram->release_ring;
     uint64_t *item;
@@ -642,7 +642,7 @@ static inline void qxl_push_free_res(PCIQXLDevice *d, int flush)
     }
 
     SPICE_RING_PUSH(ring, notify);
-    trace_qxl_ring_res_push(d->id, qxl_mode_to_string(d->mode),
+    trace_qxl_ring_res_push(d->id, qxl_mode_to_string(d->pci.mode),
            d->guest_surfaces.count, d->num_free_res,
            d->last_release, notify ? "yes" : "no");
     trace_qxl_ring_res_push_rest(d->id, ring->prod - ring->cons,
@@ -664,7 +664,7 @@ static inline void qxl_push_free_res(PCIQXLDevice *d, int flush)
 static void interface_release_resource(QXLInstance *sin,
                                        struct QXLReleaseInfoExt ext)
 {
-    PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
+    QXLDevice *qxl = container_of(sin, QXLDevice, ssd.qxl);
     QXLReleaseRing *ring;
     uint64_t *item, id;
 
@@ -693,8 +693,8 @@ static void interface_release_resource(QXLInstance *sin,
     } 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, &qxl->last_release->next);
         qxl_ram_set_dirty(qxl, &ext.info->next);
     }
     qxl->last_release = ext.info;
@@ -706,14 +706,14 @@ static void interface_release_resource(QXLInstance *sin,
 /* called from spice server thread context only */
 static int interface_get_cursor_command(QXLInstance *sin, struct QXLCommandExt *ext)
 {
-    PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
+    QXLDevice *qxl = container_of(sin, QXLDevice, ssd.qxl);
     QXLCursorRing *ring;
     QXLCommand *cmd;
     int notify;
 
-    trace_qxl_ring_cursor_check(qxl->id, qxl_mode_to_string(qxl->mode));
+    trace_qxl_ring_cursor_check(qxl->id, qxl_mode_to_string(qxl->pci.mode));
 
-    switch (qxl->mode) {
+    switch (qxl->pci.mode) {
     case QXL_MODE_COMPAT:
     case QXL_MODE_NATIVE:
     case QXL_MODE_UNDEFINED:
@@ -739,7 +739,7 @@ static int interface_get_cursor_command(QXLInstance *sin, struct QXLCommandExt *
         if (qxl->id == 0) {
             qxl_render_cursor(qxl, ext);
         }
-        trace_qxl_ring_cursor_get(qxl->id, qxl_mode_to_string(qxl->mode));
+        trace_qxl_ring_cursor_get(qxl->id, qxl_mode_to_string(qxl->pci.mode));
         return true;
     default:
         return false;
@@ -749,11 +749,11 @@ static int interface_get_cursor_command(QXLInstance *sin, struct QXLCommandExt *
 /* called from spice server thread context only */
 static int interface_req_cursor_notification(QXLInstance *sin)
 {
-    PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
+    QXLDevice *qxl = container_of(sin, QXLDevice, ssd.qxl);
     int wait = 1;
 
     trace_qxl_ring_cursor_req_notification(qxl->id);
-    switch (qxl->mode) {
+    switch (qxl->pci.mode) {
     case QXL_MODE_COMPAT:
     case QXL_MODE_NATIVE:
     case QXL_MODE_UNDEFINED:
@@ -782,7 +782,7 @@ static void interface_notify_update(QXLInstance *sin, uint32_t update_id)
 /* called from spice server thread context only */
 static int interface_flush_resources(QXLInstance *sin)
 {
-    PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
+    QXLDevice *qxl = container_of(sin, QXLDevice, ssd.qxl);
     int ret;
 
     ret = qxl->num_free_res;
@@ -792,10 +792,10 @@ static int interface_flush_resources(QXLInstance *sin)
     return ret;
 }
 
-static void qxl_create_guest_primary_complete(PCIQXLDevice *d);
+static void qxl_create_guest_primary_complete(QXLDevice *d);
 
 /* called from spice server thread context only */
-static void interface_async_complete_io(PCIQXLDevice *qxl, QXLCookie *cookie)
+static void interface_async_complete_io(QXLDevice *qxl, QXLCookie *cookie)
 {
     uint32_t current_async;
 
@@ -841,7 +841,7 @@ static void interface_update_area_complete(QXLInstance *sin,
         uint32_t surface_id,
         QXLRect *dirty, uint32_t num_updated_rects)
 {
-    PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
+    QXLDevice *qxl = container_of(sin, QXLDevice, ssd.qxl);
     int i;
     int qxl_i;
 
@@ -883,7 +883,7 @@ static void interface_update_area_complete(QXLInstance *sin,
 /* called from spice server thread context only */
 static void interface_async_complete(QXLInstance *sin, uint64_t cookie_token)
 {
-    PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
+    QXLDevice *qxl = container_of(sin, QXLDevice, ssd.qxl);
     QXLCookie *cookie = (QXLCookie *)(uintptr_t)cookie_token;
 
     switch (cookie->type) {
@@ -924,38 +924,38 @@ static const QXLInterface qxl_interface = {
     .update_area_complete    = interface_update_area_complete,
 };
 
-static void qxl_enter_vga_mode(PCIQXLDevice *d)
+static void qxl_enter_vga_mode(QXLDevice *d)
 {
-    if (d->mode == QXL_MODE_VGA) {
+    if (d->pci.mode == QXL_MODE_VGA) {
         return;
     }
     trace_qxl_enter_vga_mode(d->id);
     qemu_spice_create_host_primary(&d->ssd);
-    d->mode = QXL_MODE_VGA;
+    d->pci.mode = QXL_MODE_VGA;
     memset(&d->ssd.dirty, 0, sizeof(d->ssd.dirty));
-    vga_dirty_log_start(&d->vga);
+    vga_dirty_log_start(&d->pci.vga);
 }
 
-static void qxl_exit_vga_mode(PCIQXLDevice *d)
+static void qxl_exit_vga_mode(QXLDevice *d)
 {
-    if (d->mode != QXL_MODE_VGA) {
+    if (d->pci.mode != QXL_MODE_VGA) {
         return;
     }
     trace_qxl_exit_vga_mode(d->id);
-    vga_dirty_log_stop(&d->vga);
+    vga_dirty_log_stop(&d->pci.vga);
     qxl_destroy_primary(d, QXL_SYNC);
 }
 
-static void qxl_update_irq(PCIQXLDevice *d)
+static void qxl_update_irq(QXLDevice *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);
+    qemu_set_irq(d->pci.pci.irq[0], level);
     qxl_ring_set_dirty(d);
 }
 
-static void qxl_check_state(PCIQXLDevice *d)
+static void qxl_check_state(QXLDevice *d)
 {
     QXLRam *ram = d->ram;
 
@@ -963,7 +963,7 @@ static void qxl_check_state(PCIQXLDevice *d)
     assert(!d->ssd.running || SPICE_RING_IS_EMPTY(&ram->cursor_ring));
 }
 
-static void qxl_reset_state(PCIQXLDevice *d)
+static void qxl_reset_state(QXLDevice *d)
 {
     QXLRom *rom = d->rom;
 
@@ -977,7 +977,7 @@ static void qxl_reset_state(PCIQXLDevice *d)
     memset(&d->ssd.dirty, 0, sizeof(d->ssd.dirty));
 }
 
-static void qxl_soft_reset(PCIQXLDevice *d)
+static void qxl_soft_reset(QXLDevice *d)
 {
     trace_qxl_soft_reset(d->id);
     qxl_check_state(d);
@@ -987,11 +987,11 @@ static void qxl_soft_reset(PCIQXLDevice *d)
     if (d->id == 0) {
         qxl_enter_vga_mode(d);
     } else {
-        d->mode = QXL_MODE_UNDEFINED;
+        d->pci.mode = QXL_MODE_UNDEFINED;
     }
 }
 
-static void qxl_hard_reset(PCIQXLDevice *d, int loadvm)
+static void qxl_hard_reset(QXLDevice *d, int loadvm)
 {
     trace_qxl_hard_reset(d->id, loadvm);
 
@@ -1012,7 +1012,7 @@ static void qxl_hard_reset(PCIQXLDevice *d, int loadvm)
 
 static void qxl_reset_handler(DeviceState *dev)
 {
-    PCIQXLDevice *d = DO_UPCAST(PCIQXLDevice, pci.qdev, dev);
+    QXLDevice *d = (QXLDevice *) DO_UPCAST(PCIQXLDevice, pci.qdev, dev);
 
     qxl_hard_reset(d, 0);
 }
@@ -1020,10 +1020,10 @@ static void qxl_reset_handler(DeviceState *dev)
 static void qxl_vga_ioport_write(void *opaque, uint32_t addr, uint32_t val)
 {
     VGACommonState *vga = opaque;
-    PCIQXLDevice *qxl = container_of(vga, PCIQXLDevice, vga);
+    QXLDevice *qxl = (QXLDevice *) container_of(vga, PCIQXLDevice, vga);
 
-    trace_qxl_io_write_vga(qxl->id, qxl_mode_to_string(qxl->mode), addr, val);
-    if (qxl->mode != QXL_MODE_VGA) {
+    trace_qxl_io_write_vga(qxl->id, qxl_mode_to_string(qxl->pci.mode), addr, val);
+    if (qxl->pci.mode != QXL_MODE_VGA) {
         qxl_destroy_primary(qxl, QXL_SYNC);
         qxl_soft_reset(qxl);
     }
@@ -1044,7 +1044,7 @@ static const MemoryRegionPortio qxl_vga_portio_list[] = {
     PORTIO_END_OF_LIST(),
 };
 
-static int qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta,
+static int qxl_add_memslot(QXLDevice *d, uint32_t slot_id, uint64_t delta,
                            qxl_async_io async)
 {
     static const int regions[] = {
@@ -1079,8 +1079,8 @@ static int qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta,
 
     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;
+        pci_start = d->pci.pci.io_regions[pci_region].addr;
+        pci_end = pci_start + d->pci.pci.io_regions[pci_region].size;
         /* mapped? */
         if (pci_start == -1) {
             continue;
@@ -1103,11 +1103,11 @@ static int qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta,
 
     switch (pci_region) {
     case QXL_RAM_RANGE_INDEX:
-        virt_start = (intptr_t)memory_region_get_ram_ptr(&d->vga.vram);
+        virt_start = (intptr_t)memory_region_get_ram_ptr(&d->pci.vga.vram);
         break;
     case QXL_VRAM_RANGE_INDEX:
     case 4 /* vram 64bit */:
-        virt_start = (intptr_t)memory_region_get_ram_ptr(&d->vram_bar);
+        virt_start = (intptr_t)memory_region_get_ram_ptr(&d->pci.vram_bar);
         break;
     default:
         /* should not happen */
@@ -1131,27 +1131,27 @@ static int qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta,
     return 0;
 }
 
-static void qxl_del_memslot(PCIQXLDevice *d, uint32_t slot_id)
+static void qxl_del_memslot(QXLDevice *d, uint32_t slot_id)
 {
     qemu_spice_del_memslot(&d->ssd, MEMSLOT_GROUP_HOST, slot_id);
     d->guest_slots[slot_id].active = 0;
 }
 
-static void qxl_reset_memslots(PCIQXLDevice *d)
+static void qxl_reset_memslots(QXLDevice *d)
 {
     qxl_spice_reset_memslots(d);
     memset(&d->guest_slots, 0, sizeof(d->guest_slots));
 }
 
-static void qxl_reset_surfaces(PCIQXLDevice *d)
+static void qxl_reset_surfaces(QXLDevice *d)
 {
     trace_qxl_reset_surfaces(d->id);
-    d->mode = QXL_MODE_UNDEFINED;
+    d->pci.mode = QXL_MODE_UNDEFINED;
     qxl_spice_destroy_surfaces(d, QXL_SYNC);
 }
 
 /* can be also called from spice server thread context */
-void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL pqxl, int group_id)
+void *qxl_phys2virt(QXLDevice *qxl, QXLPHYSICAL pqxl, int group_id)
 {
     uint64_t phys   = le64_to_cpu(pqxl);
     uint32_t slot   = (phys >> (64 -  8)) & 0xff;
@@ -1188,13 +1188,13 @@ void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL pqxl, int group_id)
     return NULL;
 }
 
-static void qxl_create_guest_primary_complete(PCIQXLDevice *qxl)
+static void qxl_create_guest_primary_complete(QXLDevice *qxl)
 {
     /* for local rendering */
     qxl_render_resize(qxl);
 }
 
-static void qxl_create_guest_primary(PCIQXLDevice *qxl, int loadvm,
+static void qxl_create_guest_primary(QXLDevice *qxl, int loadvm,
                                      qxl_async_io async)
 {
     QXLDevSurfaceCreate surface;
@@ -1204,13 +1204,13 @@ static void qxl_create_guest_primary(PCIQXLDevice *qxl, int loadvm,
     int requested_stride = le32_to_cpu(sc->stride);
 
     size = abs(requested_stride) * requested_height;
-    if (size > qxl->vgamem_size) {
+    if (size > qxl->pci.vgamem_size) {
         qxl_set_guest_bug(qxl, "%s: requested primary larger then framebuffer"
                                " size", __func__);
         return;
     }
 
-    if (qxl->mode == QXL_MODE_NATIVE) {
+    if (qxl->pci.mode == QXL_MODE_NATIVE) {
         qxl_set_guest_bug(qxl, "%s: nop since already in QXL_MODE_NATIVE",
                       __func__);
     }
@@ -1235,7 +1235,7 @@ static void qxl_create_guest_primary(PCIQXLDevice *qxl, int loadvm,
         surface.flags |= QXL_SURF_FLAG_KEEP_DATA;
     }
 
-    qxl->mode = QXL_MODE_NATIVE;
+    qxl->pci.mode = QXL_MODE_NATIVE;
     qxl->cmdflags = 0;
     qemu_spice_create_primary_surface(&qxl->ssd, 0, &surface, async);
 
@@ -1246,24 +1246,24 @@ static void qxl_create_guest_primary(PCIQXLDevice *qxl, int loadvm,
 
 /* return 1 if surface destoy was initiated (in QXL_ASYNC case) or
  * done (in QXL_SYNC case), 0 otherwise. */
-static int qxl_destroy_primary(PCIQXLDevice *d, qxl_async_io async)
+static int qxl_destroy_primary(QXLDevice *d, qxl_async_io async)
 {
-    if (d->mode == QXL_MODE_UNDEFINED) {
+    if (d->pci.mode == QXL_MODE_UNDEFINED) {
         return 0;
     }
     trace_qxl_destroy_primary(d->id);
-    d->mode = QXL_MODE_UNDEFINED;
+    d->pci.mode = QXL_MODE_UNDEFINED;
     qemu_spice_destroy_primary_surface(&d->ssd, 0, async);
     qxl_spice_reset_cursor(d);
     return 1;
 }
 
-static void qxl_set_mode(PCIQXLDevice *d, int modenr, int loadvm)
+static void qxl_set_mode(QXLDevice *d, int modenr, int loadvm)
 {
-    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;
+    pcibus_t start = d->pci.pci.io_regions[QXL_RAM_RANGE_INDEX].addr;
+    pcibus_t end   = d->pci.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;
+    uint64_t devmem = d->pci.pci.io_regions[QXL_RAM_RANGE_INDEX].addr;
     QXLMemSlot slot = {
         .mem_start = start,
         .mem_end = end
@@ -1290,7 +1290,7 @@ static void qxl_set_mode(PCIQXLDevice *d, int modenr, int loadvm)
     d->guest_primary.surface = surface;
     qxl_create_guest_primary(d, 0, QXL_SYNC);
 
-    d->mode = QXL_MODE_COMPAT;
+    d->pci.mode = QXL_MODE_COMPAT;
     d->cmdflags = QXL_COMMAND_FLAG_COMPAT;
 #ifdef QXL_COMMAND_FLAG_COMPAT_16BPP /* new in spice 0.6.1 */
     if (mode->bits == 16) {
@@ -1305,12 +1305,12 @@ static void qxl_set_mode(PCIQXLDevice *d, int modenr, int loadvm)
 static void ioport_write(void *opaque, target_phys_addr_t addr,
                          uint64_t val, unsigned size)
 {
-    PCIQXLDevice *d = opaque;
+    QXLDevice *d = opaque;
     uint32_t io_port = addr;
     qxl_async_io async = QXL_SYNC;
     uint32_t orig_io_port = io_port;
 
-    if (d->guest_bug && !io_port == QXL_IO_RESET) {
+    if (d->pci.guest_bug && !io_port == QXL_IO_RESET) {
         return;
     }
 
@@ -1326,7 +1326,7 @@ static void ioport_write(void *opaque, target_phys_addr_t addr,
     case QXL_IO_CREATE_PRIMARY_ASYNC:
         break;
     default:
-        if (d->mode != QXL_MODE_VGA) {
+        if (d->pci.mode != QXL_MODE_VGA) {
             break;
         }
         trace_qxl_io_unexpected_vga_mode(d->id,
@@ -1376,7 +1376,7 @@ async_common:
     default:
         break;
     }
-    trace_qxl_io_write(d->id, qxl_mode_to_string(d->mode), addr, val, size,
+    trace_qxl_io_write(d->id, qxl_mode_to_string(d->pci.mode), addr, val, size,
                        async);
 
     switch (io_port) {
@@ -1416,7 +1416,7 @@ async_common:
         qxl_set_mode(d, val, 0);
         break;
     case QXL_IO_LOG:
-        if (d->guestdebug) {
+        if (d->pci.guestdebug) {
             fprintf(stderr, "qxl/guest-%d: %" PRId64 ": %s", d->id,
                     qemu_get_clock_ns(vm_clock), d->ram->log_buf);
         }
@@ -1461,7 +1461,7 @@ async_common:
         }
         if (!qxl_destroy_primary(d, async)) {
             trace_qxl_io_destroy_primary_ignored(d->id,
-                                                 qxl_mode_to_string(d->mode));
+                                                 qxl_mode_to_string(d->pci.mode));
             goto cancel_async;
         }
         break;
@@ -1487,7 +1487,7 @@ async_common:
         qxl_spice_flush_surfaces_async(d);
         break;
     case QXL_IO_DESTROY_ALL_SURFACES:
-        d->mode = QXL_MODE_UNDEFINED;
+        d->pci.mode = QXL_MODE_UNDEFINED;
         qxl_spice_destroy_surfaces(d, async);
         break;
     default:
@@ -1506,7 +1506,7 @@ cancel_async:
 static uint64_t ioport_read(void *opaque, target_phys_addr_t addr,
                             unsigned size)
 {
-    PCIQXLDevice *d = opaque;
+    QXLDevice *d = opaque;
 
     trace_qxl_io_read_unexpected(d->id);
     return 0xff;
@@ -1523,7 +1523,7 @@ static const MemoryRegionOps qxl_io_ops = {
 
 static void pipe_read(void *opaque)
 {
-    PCIQXLDevice *d = opaque;
+    QXLDevice *d = opaque;
     char dummy;
     int len;
 
@@ -1533,7 +1533,7 @@ static void pipe_read(void *opaque)
     qxl_update_irq(d);
 }
 
-static void qxl_send_events(PCIQXLDevice *d, uint32_t events)
+static void qxl_send_events(QXLDevice *d, uint32_t events)
 {
     uint32_t old_pending;
     uint32_t le_events = cpu_to_le32(events);
@@ -1552,7 +1552,7 @@ static void qxl_send_events(PCIQXLDevice *d, uint32_t events)
     }
 }
 
-static void init_pipe_signaling(PCIQXLDevice *d)
+static void init_pipe_signaling(QXLDevice *d)
 {
     if (pipe(d->pipe) < 0) {
         fprintf(stderr, "%s:%s: qxl pipe creation failed\n",
@@ -1571,10 +1571,10 @@ static void init_pipe_signaling(PCIQXLDevice *d)
 
 static void qxl_hw_update(void *opaque)
 {
-    PCIQXLDevice *qxl = opaque;
-    VGACommonState *vga = &qxl->vga;
+    QXLDevice *qxl = opaque;
+    VGACommonState *vga = &qxl->pci.vga;
 
-    switch (qxl->mode) {
+    switch (qxl->pci.mode) {
     case QXL_MODE_VGA:
         vga->update(vga);
         break;
@@ -1589,18 +1589,18 @@ static void qxl_hw_update(void *opaque)
 
 static void qxl_hw_invalidate(void *opaque)
 {
-    PCIQXLDevice *qxl = opaque;
-    VGACommonState *vga = &qxl->vga;
+    QXLDevice *qxl = opaque;
+    VGACommonState *vga = &qxl->pci.vga;
 
     vga->invalidate(vga);
 }
 
 static void qxl_hw_screen_dump(void *opaque, const char *filename, bool cswitch)
 {
-    PCIQXLDevice *qxl = opaque;
-    VGACommonState *vga = &qxl->vga;
+    QXLDevice *qxl = opaque;
+    VGACommonState *vga = &qxl->pci.vga;
 
-    switch (qxl->mode) {
+    switch (qxl->pci.mode) {
     case QXL_MODE_COMPAT:
     case QXL_MODE_NATIVE:
         qxl_render_update(qxl);
@@ -1616,29 +1616,29 @@ static void qxl_hw_screen_dump(void *opaque, const char *filename, bool cswitch)
 
 static void qxl_hw_text_update(void *opaque, console_ch_t *chardata)
 {
-    PCIQXLDevice *qxl = opaque;
-    VGACommonState *vga = &qxl->vga;
+    QXLDevice *qxl = opaque;
+    VGACommonState *vga = &qxl->pci.vga;
 
-    if (qxl->mode == QXL_MODE_VGA) {
+    if (qxl->pci.mode == QXL_MODE_VGA) {
         vga->text_update(vga, chardata);
         return;
     }
 }
 
-static void qxl_dirty_surfaces(PCIQXLDevice *qxl)
+static void qxl_dirty_surfaces(QXLDevice *qxl)
 {
     intptr_t vram_start;
     int i;
 
-    if (qxl->mode != QXL_MODE_NATIVE && qxl->mode != QXL_MODE_COMPAT) {
+    if (qxl->pci.mode != QXL_MODE_NATIVE && qxl->pci.mode != QXL_MODE_COMPAT) {
         return;
     }
 
     /* dirty the primary surface */
-    qxl_set_dirty(&qxl->vga.vram, qxl->shadow_rom.draw_area_offset,
+    qxl_set_dirty(&qxl->pci.vga.vram, qxl->shadow_rom.draw_area_offset,
                   qxl->shadow_rom.surface0_area_size);
 
-    vram_start =  (intptr_t)memory_region_get_ram_ptr(&qxl->vram_bar);
+    vram_start =  (intptr_t)memory_region_get_ram_ptr(&qxl->pci.vram_bar);
 
     /* dirty the off-screen surfaces */
     for (i = 0; i < NUM_SURFACES; i++) {
@@ -1662,14 +1662,14 @@ static void qxl_dirty_surfaces(PCIQXLDevice *qxl)
         surface_size = cmd->u.surface_create.height *
                        abs(cmd->u.surface_create.stride);
         trace_qxl_surfaces_dirty(qxl->id, i, (int)surface_offset, surface_size);
-        qxl_set_dirty(&qxl->vram_bar, surface_offset, surface_size);
+        qxl_set_dirty(&qxl->pci.vram_bar, surface_offset, surface_size);
     }
 }
 
 static void qxl_vm_change_state_handler(void *opaque, int running,
                                         RunState state)
 {
-    PCIQXLDevice *qxl = opaque;
+    QXLDevice *qxl = opaque;
     qemu_spice_vm_change_state_handler(&qxl->ssd, running, state);
 
     if (running) {
@@ -1689,21 +1689,21 @@ static void qxl_vm_change_state_handler(void *opaque, int running,
 
 static void display_update(struct DisplayState *ds, int x, int y, int w, int h)
 {
-    if (qxl0->mode == QXL_MODE_VGA) {
+    if (qxl0->pci.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) {
+    if (qxl0->pci.mode == QXL_MODE_VGA) {
         qemu_spice_display_resize(&qxl0->ssd);
     }
 }
 
 static void display_refresh(struct DisplayState *ds)
 {
-    if (qxl0->mode == QXL_MODE_VGA) {
+    if (qxl0->pci.mode == QXL_MODE_VGA) {
         qemu_spice_display_refresh(&qxl0->ssd);
     } else {
         qemu_mutex_lock(&qxl0->ssd.lock);
@@ -1718,20 +1718,20 @@ static DisplayChangeListener display_listener = {
     .dpy_refresh = display_refresh,
 };
 
-static void qxl_init_ramsize(PCIQXLDevice *qxl)
+static void qxl_init_ramsize(QXLDevice *qxl)
 {
     /* vga mode framebuffer / primary surface (bar 0, first part) */
-    if (qxl->vgamem_size_mb < 8) {
-        qxl->vgamem_size_mb = 8;
+    if (qxl->pci.vgamem_size_mb < 8) {
+        qxl->pci.vgamem_size_mb = 8;
     }
-    qxl->vgamem_size = qxl->vgamem_size_mb * 1024 * 1024;
+    qxl->pci.vgamem_size = qxl->pci.vgamem_size_mb * 1024 * 1024;
 
     /* vga ram (bar 0, total) */
     if (qxl->ram_size_mb != -1) {
-        qxl->vga.vram_size = qxl->ram_size_mb * 1024 * 1024;
+        qxl->pci.vga.vram_size = qxl->ram_size_mb * 1024 * 1024;
     }
-    if (qxl->vga.vram_size < qxl->vgamem_size * 2) {
-        qxl->vga.vram_size = qxl->vgamem_size * 2;
+    if (qxl->pci.vga.vram_size < qxl->pci.vgamem_size * 2) {
+        qxl->pci.vga.vram_size = qxl->pci.vgamem_size * 2;
     }
 
     /* vram32 (surfaces, 32bit, bar 1) */
@@ -1750,32 +1750,32 @@ static void qxl_init_ramsize(PCIQXLDevice *qxl)
         qxl->vram_size = qxl->vram32_size;
     }
 
-    if (qxl->revision == 1) {
+    if (qxl->pci.revision == 1) {
         qxl->vram32_size = 4096;
         qxl->vram_size = 4096;
     }
-    qxl->vgamem_size = msb_mask(qxl->vgamem_size * 2 - 1);
-    qxl->vga.vram_size = msb_mask(qxl->vga.vram_size * 2 - 1);
+    qxl->pci.vgamem_size = msb_mask(qxl->pci.vgamem_size * 2 - 1);
+    qxl->pci.vga.vram_size = msb_mask(qxl->pci.vga.vram_size * 2 - 1);
     qxl->vram32_size = msb_mask(qxl->vram32_size * 2 - 1);
     qxl->vram_size = msb_mask(qxl->vram_size * 2 - 1);
 }
 
-static int qxl_init_common(PCIQXLDevice *qxl)
+static int qxl_init_common(QXLDevice *qxl)
 {
-    uint8_t* config = qxl->pci.config;
+    uint8_t* config = qxl->pci.pci.config;
     uint32_t pci_device_rev;
     uint32_t io_size;
 
-    qxl->mode = QXL_MODE_UNDEFINED;
-    qxl->generation = 1;
+    qxl->pci.mode = QXL_MODE_UNDEFINED;
+    qxl->pci.generation = 1;
     qxl->num_memslots = NUM_MEMSLOTS;
     qxl->num_surfaces = NUM_SURFACES;
     qemu_mutex_init(&qxl->track_lock);
     qemu_mutex_init(&qxl->async_lock);
     qxl->current_async = QXL_UNDEFINED_IO;
-    qxl->guest_bug = 0;
+    qxl->pci.guest_bug = 0;
 
-    switch (qxl->revision) {
+    switch (qxl->pci.revision) {
     case 1: /* spice 0.4 -- qxl-1 */
         pci_device_rev = QXL_REVISION_STABLE_V04;
         io_size = 8;
@@ -1794,52 +1794,54 @@ static int qxl_init_common(PCIQXLDevice *qxl)
     pci_set_byte(&config[PCI_REVISION_ID], pci_device_rev);
     pci_set_byte(&config[PCI_INTERRUPT_PIN], 1);
 
-    qxl->rom_size = qxl_rom_size();
-    memory_region_init_ram(&qxl->rom_bar, "qxl.vrom", qxl->rom_size);
-    vmstate_register_ram(&qxl->rom_bar, &qxl->pci.qdev);
+    qxl->pci.rom_size = qxl_rom_size();
+    memory_region_init_ram(&qxl->pci.rom_bar,"qxl-pci.vrom",
+            qxl->pci.rom_size);
+    vmstate_register_ram(&qxl->pci.rom_bar, &qxl->pci.pci.qdev);
     init_qxl_rom(qxl);
     init_qxl_ram(qxl);
 
-    memory_region_init_ram(&qxl->vram_bar, "qxl.vram", qxl->vram_size);
-    vmstate_register_ram(&qxl->vram_bar, &qxl->pci.qdev);
-    memory_region_init_alias(&qxl->vram32_bar, "qxl.vram32", &qxl->vram_bar,
-                             0, qxl->vram32_size);
+    memory_region_init_ram(&qxl->pci.vram_bar, "qxl-pci.vram",
+            qxl->vram_size);
+    vmstate_register_ram(&qxl->pci.vram_bar, &qxl->pci.pci.qdev);
+    memory_region_init_alias(&qxl->pci.vram32_bar, "qxl-pci.vram32",
+            &qxl->pci.vram_bar, 0, qxl->vram32_size);
 
-    memory_region_init_io(&qxl->io_bar, &qxl_io_ops, qxl,
-                          "qxl-ioports", io_size);
+    memory_region_init_io(&qxl->pci.io_bar, &qxl_io_ops, qxl,
+                          "qxl-pci.ioports", io_size);
     if (qxl->id == 0) {
-        vga_dirty_log_start(&qxl->vga);
+        vga_dirty_log_start(&qxl->pci.vga);
     }
 
 
-    pci_register_bar(&qxl->pci, QXL_IO_RANGE_INDEX,
-                     PCI_BASE_ADDRESS_SPACE_IO, &qxl->io_bar);
+    pci_register_bar(&qxl->pci.pci, QXL_IO_RANGE_INDEX,
+                     PCI_BASE_ADDRESS_SPACE_IO, &qxl->pci.io_bar);
 
-    pci_register_bar(&qxl->pci, QXL_ROM_RANGE_INDEX,
-                     PCI_BASE_ADDRESS_SPACE_MEMORY, &qxl->rom_bar);
+    pci_register_bar(&qxl->pci.pci, QXL_ROM_RANGE_INDEX,
+                     PCI_BASE_ADDRESS_SPACE_MEMORY, &qxl->pci.rom_bar);
 
-    pci_register_bar(&qxl->pci, QXL_RAM_RANGE_INDEX,
-                     PCI_BASE_ADDRESS_SPACE_MEMORY, &qxl->vga.vram);
+    pci_register_bar(&qxl->pci.pci, QXL_RAM_RANGE_INDEX,
+                     PCI_BASE_ADDRESS_SPACE_MEMORY, &qxl->pci.vga.vram);
 
-    pci_register_bar(&qxl->pci, QXL_VRAM_RANGE_INDEX,
-                     PCI_BASE_ADDRESS_SPACE_MEMORY, &qxl->vram32_bar);
+    pci_register_bar(&qxl->pci.pci, QXL_VRAM_RANGE_INDEX,
+                     PCI_BASE_ADDRESS_SPACE_MEMORY, &qxl->pci.vram32_bar);
 
     if (qxl->vram32_size < qxl->vram_size) {
         /*
          * Make the 64bit vram bar show up only in case it is
          * configured to be larger than the 32bit vram bar.
          */
-        pci_register_bar(&qxl->pci, QXL_VRAM64_RANGE_INDEX,
+        pci_register_bar(&qxl->pci.pci, QXL_VRAM64_RANGE_INDEX,
                          PCI_BASE_ADDRESS_SPACE_MEMORY |
                          PCI_BASE_ADDRESS_MEM_TYPE_64 |
                          PCI_BASE_ADDRESS_MEM_PREFETCH,
-                         &qxl->vram_bar);
+                         &qxl->pci.vram_bar);
     }
 
     /* print pci bar details */
     dprint(qxl, 1, "ram/%s: %d MB [region 0]\n",
            qxl->id == 0 ? "pri" : "sec",
-           qxl->vga.vram_size / (1024*1024));
+           qxl->pci.vga.vram_size / (1024*1024));
     dprint(qxl, 1, "vram/32: %d MB [region 1]\n",
            qxl->vram32_size / (1024*1024));
     dprint(qxl, 1, "vram/64: %d MB %s\n",
@@ -1861,13 +1863,13 @@ static int qxl_init_common(PCIQXLDevice *qxl)
 
 static int qxl_init_primary(PCIDevice *dev)
 {
-    PCIQXLDevice *qxl = DO_UPCAST(PCIQXLDevice, pci, dev);
-    VGACommonState *vga = &qxl->vga;
+    QXLDevice *qxl = (QXLDevice *) DO_UPCAST(PCIQXLDevice, pci, dev);
+    VGACommonState *vga = &qxl->pci.vga;
     PortioList *qxl_vga_port_list = g_new(PortioList, 1);
 
     qxl->id = 0;
     qxl_init_ramsize(qxl);
-    vga->vram_size_mb = qxl->vga.vram_size >> 20;
+    vga->vram_size_mb = qxl->pci.vga.vram_size >> 20;
     vga_common_init(vga);
     vga_init(vga, pci_address_space(dev), pci_address_space_io(dev), false);
     portio_list_init(qxl_vga_port_list, qxl_vga_portio_list, vga, "vga");
@@ -1886,21 +1888,21 @@ static int qxl_init_primary(PCIDevice *dev)
 static int qxl_init_secondary(PCIDevice *dev)
 {
     static int device_id = 1;
-    PCIQXLDevice *qxl = DO_UPCAST(PCIQXLDevice, pci, dev);
+    QXLDevice *qxl = (QXLDevice *) DO_UPCAST(PCIQXLDevice, pci, dev);
 
     qxl->id = device_id++;
     qxl_init_ramsize(qxl);
-    memory_region_init_ram(&qxl->vga.vram, "qxl.vgavram", qxl->vga.vram_size);
-    vmstate_register_ram(&qxl->vga.vram, &qxl->pci.qdev);
-    qxl->vga.vram_ptr = memory_region_get_ram_ptr(&qxl->vga.vram);
+    memory_region_init_ram(&qxl->pci.vga.vram, "qxl-pci.vgavram", qxl->pci.vga.vram_size);
+    vmstate_register_ram(&qxl->pci.vga.vram, &qxl->pci.pci.qdev);
+    qxl->pci.vga.vram_ptr = memory_region_get_ram_ptr(&qxl->pci.vga.vram);
 
     return qxl_init_common(qxl);
 }
 
 static void qxl_pre_save(void *opaque)
 {
-    PCIQXLDevice* d = opaque;
-    uint8_t *ram_start = d->vga.vram_ptr;
+    QXLDevice* d = opaque;
+    uint8_t *ram_start = d->pci.vga.vram_ptr;
 
     trace_qxl_pre_save(d->id);
     if (d->last_release == NULL) {
@@ -1908,12 +1910,12 @@ static void qxl_pre_save(void *opaque)
     } else {
         d->last_release_offset = (uint8_t *)d->last_release - ram_start;
     }
-    assert(d->last_release_offset < d->vga.vram_size);
+    assert(d->last_release_offset < d->pci.vga.vram_size);
 }
 
 static int qxl_pre_load(void *opaque)
 {
-    PCIQXLDevice* d = opaque;
+    QXLDevice* d = opaque;
 
     trace_qxl_pre_load(d->id);
     qxl_hard_reset(d, 1);
@@ -1921,7 +1923,7 @@ static int qxl_pre_load(void *opaque)
     return 0;
 }
 
-static void qxl_create_memslots(PCIQXLDevice *d)
+static void qxl_create_memslots(QXLDevice *d)
 {
     int i;
 
@@ -1935,12 +1937,12 @@ static void qxl_create_memslots(PCIQXLDevice *d)
 
 static int qxl_post_load(void *opaque, int version)
 {
-    PCIQXLDevice* d = opaque;
-    uint8_t *ram_start = d->vga.vram_ptr;
+    QXLDevice *d = opaque;
+    uint8_t *ram_start = d->pci.vga.vram_ptr;
     QXLCommandExt *cmds;
     int in, out, newmode;
 
-    assert(d->last_release_offset < d->vga.vram_size);
+    assert(d->last_release_offset < d->pci.vga.vram_size);
     if (d->last_release_offset == 0) {
         d->last_release = NULL;
     } else {
@@ -1949,9 +1951,9 @@ static int qxl_post_load(void *opaque, int version)
 
     d->modes = (QXLModes*)((uint8_t*)d->rom + d->rom->modes_offset);
 
-    trace_qxl_post_load(d->id, qxl_mode_to_string(d->mode));
-    newmode = d->mode;
-    d->mode = QXL_MODE_UNDEFINED;
+    trace_qxl_post_load(d->id, qxl_mode_to_string(d->pci.mode));
+    newmode = d->pci.mode;
+    d->pci.mode = QXL_MODE_UNDEFINED;
 
     switch (newmode) {
     case QXL_MODE_UNDEFINED:
@@ -2036,20 +2038,20 @@ static VMStateDescription qxl_vmstate = {
     .fields = (VMStateField []) {
         VMSTATE_PCI_DEVICE(pci, PCIQXLDevice),
         VMSTATE_STRUCT(vga, PCIQXLDevice, 0, vmstate_vga_common, VGACommonState),
-        VMSTATE_UINT32(shadow_rom.mode, PCIQXLDevice),
-        VMSTATE_UINT32(num_free_res, PCIQXLDevice),
-        VMSTATE_UINT32(last_release_offset, PCIQXLDevice),
+        VMSTATE_UINT32(shadow_rom.mode, QXLDevice),
+        VMSTATE_UINT32(num_free_res, QXLDevice),
+        VMSTATE_UINT32(last_release_offset, QXLDevice),
         VMSTATE_UINT32(mode, PCIQXLDevice),
-        VMSTATE_UINT32(ssd.unique, PCIQXLDevice),
-        VMSTATE_INT32_EQUAL(num_memslots, PCIQXLDevice),
-        VMSTATE_STRUCT_ARRAY(guest_slots, PCIQXLDevice, NUM_MEMSLOTS, 0,
+        VMSTATE_UINT32(ssd.unique, QXLDevice),
+        VMSTATE_INT32_EQUAL(num_memslots, QXLDevice),
+        VMSTATE_STRUCT_ARRAY(guest_slots, QXLDevice, NUM_MEMSLOTS, 0,
                              qxl_memslot, struct guest_slots),
-        VMSTATE_STRUCT(guest_primary.surface, PCIQXLDevice, 0,
+        VMSTATE_STRUCT(guest_primary.surface, QXLDevice, 0,
                        qxl_surface, QXLSurfaceCreate),
-        VMSTATE_INT32_EQUAL(num_surfaces, PCIQXLDevice),
-        VMSTATE_ARRAY(guest_surfaces.cmds, PCIQXLDevice, NUM_SURFACES, 0,
+        VMSTATE_INT32_EQUAL(num_surfaces, QXLDevice),
+        VMSTATE_ARRAY(guest_surfaces.cmds, QXLDevice, NUM_SURFACES, 0,
                       vmstate_info_uint64, uint64_t),
-        VMSTATE_UINT64(guest_cursor, PCIQXLDevice),
+        VMSTATE_UINT64(guest_cursor, QXLDevice),
         VMSTATE_END_OF_LIST()
     },
 };
@@ -2057,16 +2059,16 @@ static VMStateDescription qxl_vmstate = {
 static Property qxl_properties[] = {
         DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size,
                            64 * 1024 * 1024),
-        DEFINE_PROP_UINT32("vram_size", PCIQXLDevice, vram32_size,
+        DEFINE_PROP_UINT32("vram_size", QXLDevice, vram32_size,
                            64 * 1024 * 1024),
         DEFINE_PROP_UINT32("revision", PCIQXLDevice, revision,
                            QXL_DEFAULT_REVISION),
-        DEFINE_PROP_UINT32("debug", PCIQXLDevice, debug, 0),
+        DEFINE_PROP_UINT32("debug", QXLDevice, debug, 0),
         DEFINE_PROP_UINT32("guestdebug", PCIQXLDevice, guestdebug, 0),
         DEFINE_PROP_UINT32("cmdlog", PCIQXLDevice, cmdlog, 0),
-        DEFINE_PROP_UINT32("ram_size_mb",  PCIQXLDevice, ram_size_mb, -1),
-        DEFINE_PROP_UINT32("vram_size_mb", PCIQXLDevice, vram32_size_mb, -1),
-        DEFINE_PROP_UINT32("vram64_size_mb", PCIQXLDevice, vram_size_mb, -1),
+        DEFINE_PROP_UINT32("ram_size_mb",  QXLDevice, ram_size_mb, -1),
+        DEFINE_PROP_UINT32("vram_size_mb", QXLDevice, vram32_size_mb, -1),
+        DEFINE_PROP_UINT32("vram64_size_mb", QXLDevice, vram_size_mb, -1),
         DEFINE_PROP_UINT32("vgamem_mb", PCIQXLDevice, vgamem_size_mb, 16),
         DEFINE_PROP_END_OF_LIST(),
 };
@@ -2091,7 +2093,7 @@ static void qxl_primary_class_init(ObjectClass *klass, void *data)
 static TypeInfo qxl_primary_info = {
     .name          = "qxl-vga",
     .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(PCIQXLDevice),
+    .instance_size = sizeof(QXLDevice),
     .class_init    = qxl_primary_class_init,
 };
 
@@ -2113,7 +2115,7 @@ static void qxl_secondary_class_init(ObjectClass *klass, void *data)
 static TypeInfo qxl_secondary_info = {
     .name          = "qxl",
     .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(PCIQXLDevice),
+    .instance_size = sizeof(QXLDevice),
     .class_init    = qxl_secondary_class_init,
 };
 
diff --git a/hw/qxl.h b/hw/qxl.h
index 172baf6..f25e341 100644
--- a/hw/qxl.h
+++ b/hw/qxl.h
@@ -25,19 +25,44 @@ enum qxl_mode {
 #define QXL_NUM_DIRTY_RECTS 64
 
 typedef struct PCIQXLDevice {
-    PCIDevice          pci;
-    SimpleSpiceDisplay ssd;
-    int                id;
-    uint32_t           debug;
-    uint32_t           guestdebug;
-    uint32_t           cmdlog;
+    PCIDevice         pci;
+    int               generation;
+    uint32_t          revision;
+    uint32_t          guestdebug;
+    uint32_t          cmdlog;
+
+    uint32_t          guest_bug;
+
+    enum qxl_mode     mode;
+
+    /* ram pci bar */
+    VGACommonState    vga;
+    uint32_t          vgamem_size;
+
+    /* rom pci bar */
+    uint32_t          rom_size;
+    MemoryRegion      rom_bar;
+
+    /* vram pci bar */
+    MemoryRegion      vram_bar;
+    MemoryRegion      vram32_bar;
+
+    /* io bar */
+    MemoryRegion      io_bar;
+
+    /* user-friendly properties (in megabytes) */
+    uint32_t          vgamem_size_mb;
+} PCIQXLDevice;
+
+#define QXL_DEFAULT_REVISION QXL_REVISION_STABLE_V10
 
-    uint32_t           guest_bug;
+typedef struct QXLDevice {
+    PCIQXLDevice       pci;
 
-    enum qxl_mode      mode;
+    SimpleSpiceDisplay ssd;
+    int                id;
     uint32_t           cmdflags;
-    int                generation;
-    uint32_t           revision;
+    uint32_t           debug;
 
     int32_t            num_memslots;
     int32_t            num_surfaces;
@@ -71,49 +96,39 @@ typedef struct PCIQXLDevice {
     } guest_surfaces;
     QXLPHYSICAL        guest_cursor;
 
-    QemuMutex          track_lock;
-
     /* thread signaling */
     QemuThread         main;
     int                pipe[2];
 
+    QemuMutex          track_lock;
+
     /* ram pci bar */
-    QXLRam             *ram;
-    VGACommonState     vga;
+    QXLRam            *ram;
     uint32_t           num_free_res;
-    QXLReleaseInfo     *last_release;
+    QXLReleaseInfo    *last_release;
     uint32_t           last_release_offset;
     uint32_t           oom_running;
-    uint32_t           vgamem_size;
 
     /* rom pci bar */
     QXLRom             shadow_rom;
-    QXLRom             *rom;
-    QXLModes           *modes;
-    uint32_t           rom_size;
-    MemoryRegion       rom_bar;
+    QXLRom            *rom;
+    QXLModes          *modes;
 
     /* vram pci bar */
     uint32_t           vram_size;
-    MemoryRegion       vram_bar;
     uint32_t           vram32_size;
-    MemoryRegion       vram32_bar;
-
-    /* io bar */
-    MemoryRegion       io_bar;
-
-    /* user-friendly properties (in megabytes) */
-    uint32_t          ram_size_mb;
-    uint32_t          vram_size_mb;
-    uint32_t          vram32_size_mb;
-    uint32_t          vgamem_size_mb;
 
     /* qxl_render_update state */
     int                render_update_cookie_num;
     int                num_dirty_rects;
     QXLRect            dirty[QXL_NUM_DIRTY_RECTS];
     QEMUBH            *update_area_bh;
-} PCIQXLDevice;
+
+    /* user-friendly properties (in megabytes) */
+    uint32_t           ram_size_mb;
+    uint32_t           vram_size_mb;
+    uint32_t           vram32_size_mb;
+} QXLDevice;
 
 #define PANIC_ON(x) if ((x)) {                         \
     printf("%s: PANIC %s failed\n", __FUNCTION__, #x); \
@@ -128,32 +143,29 @@ typedef struct PCIQXLDevice {
         }                                                               \
     } while (0)
 
-#define QXL_DEFAULT_REVISION QXL_REVISION_STABLE_V10
-
 /* qxl.c */
-void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL phys, int group_id);
-void qxl_set_guest_bug(PCIQXLDevice *qxl, const char *msg, ...)
+void *qxl_phys2virt(QXLDevice *qxl, QXLPHYSICAL phys, int group_id);
+void qxl_set_guest_bug(QXLDevice *qxl, const char *msg, ...)
     GCC_FMT_ATTR(2, 3);
-
-void qxl_spice_update_area(PCIQXLDevice *qxl, uint32_t surface_id,
+void qxl_spice_update_area(QXLDevice *qxl, uint32_t surface_id,
                            struct QXLRect *area, struct QXLRect *dirty_rects,
                            uint32_t num_dirty_rects,
                            uint32_t clear_dirty_region,
                            qxl_async_io async, QXLCookie *cookie);
-void qxl_spice_loadvm_commands(PCIQXLDevice *qxl, struct QXLCommandExt *ext,
+void qxl_spice_loadvm_commands(QXLDevice *qxl, struct QXLCommandExt *ext,
                                uint32_t count);
-void qxl_spice_oom(PCIQXLDevice *qxl);
-void qxl_spice_reset_memslots(PCIQXLDevice *qxl);
-void qxl_spice_reset_image_cache(PCIQXLDevice *qxl);
-void qxl_spice_reset_cursor(PCIQXLDevice *qxl);
+void qxl_spice_oom(QXLDevice *qxl);
+void qxl_spice_reset_memslots(QXLDevice *qxl);
+void qxl_spice_reset_image_cache(QXLDevice *qxl);
+void qxl_spice_reset_cursor(QXLDevice *qxl);
 
 /* qxl-logger.c */
-int qxl_log_cmd_cursor(PCIQXLDevice *qxl, QXLCursorCmd *cmd, int group_id);
-int qxl_log_command(PCIQXLDevice *qxl, const char *ring, QXLCommandExt *ext);
+int qxl_log_cmd_cursor(QXLDevice *qxl, QXLCursorCmd *cmd, int group_id);
+int qxl_log_command(QXLDevice *qxl, const char *ring, QXLCommandExt *ext);
 
 /* qxl-render.c */
-void qxl_render_resize(PCIQXLDevice *qxl);
-void qxl_render_update(PCIQXLDevice *qxl);
-int qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext);
-void qxl_render_update_area_done(PCIQXLDevice *qxl, QXLCookie *cookie);
+void qxl_render_resize(QXLDevice *qxl);
+void qxl_render_update(QXLDevice *qxl);
+int qxl_render_cursor(QXLDevice *qxl, QXLCommandExt *ext);
+void qxl_render_update_area_done(QXLDevice *qxl, QXLCookie *cookie);
 void qxl_render_update_area_bh(void *opaque);
-- 
1.7.4.1

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [Qemu-devel] [PATCH 2/4] qxl: split qxl functions in common and pci files
  2012-08-24 19:14 [Qemu-devel] Implementing qxl-virtio on QEMU Erlon Cruz
  2012-08-24 19:14 ` [Qemu-devel] [PATCH 1/4] qxl: create a qxl common struct! Erlon Cruz
@ 2012-08-24 19:14 ` Erlon Cruz
  2012-08-27  6:05   ` Gerd Hoffmann
  2012-08-24 19:14 ` [Qemu-devel] [PATCH 3/4] qxl: implement VirtIO QXL (dummy) device support Erlon Cruz
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 10+ messages in thread
From: Erlon Cruz @ 2012-08-24 19:14 UTC (permalink / raw)
  To: qemu-devel
  Cc: Erlon R. Cruz, Fabiano Fidêncio, Fabiano Fidêncio,
	Rafael F. Santos, alevy

From: Fabiano Fidêncio <fabiano@fidencio.org>

This commit splits qxl functions into common functions (located in
qxl.c) and pci-specific functions (located in qxl-pci.c).
All prototypes are being kept in qxl.h, as common MACROS and inline
functions. Moreover, this commit is exposing a lot of APIs, don't know
if it's the correct way to do it, but it was the only way that we saw to
do it.

Signed-off-by: Erlon R. Cruz <erlon.cruz@fit-tecnologia.org.br>
Signed-off-by: Fabiano Fidêncio <fabiano.fidencio@fit-tecnologia.org.br>
Signed-off-by: Rafael F. Santos <fonsecasantos.rafael@gmail.com>
---
 hw/i386/Makefile.objs |    2 +-
 hw/qxl-pci.c          | 1607 +++++++++++++++++++++++++++++++++++++++++++++
 hw/qxl.c              | 1733 +------------------------------------------------
 hw/qxl.h              |  120 ++++
 4 files changed, 1763 insertions(+), 1699 deletions(-)
 create mode 100644 hw/qxl-pci.c

diff --git a/hw/i386/Makefile.objs b/hw/i386/Makefile.objs
index 8c764bb..1953e7f 100644
--- a/hw/i386/Makefile.objs
+++ b/hw/i386/Makefile.objs
@@ -10,6 +10,6 @@ obj-$(CONFIG_XEN) += xen_platform.o xen_apic.o
 obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen-host-pci-device.o
 obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pt.o xen_pt_config_init.o xen_pt_msi.o
 obj-y += kvm/
-obj-$(CONFIG_SPICE) += qxl.o qxl-logger.o qxl-render.o
+obj-$(CONFIG_SPICE) += qxl.o qxl-logger.o qxl-render.o qxl-pci.o
 
 obj-y := $(addprefix ../,$(obj-y))
diff --git a/hw/qxl-pci.c b/hw/qxl-pci.c
new file mode 100644
index 0000000..6b4ec45
--- /dev/null
+++ b/hw/qxl-pci.c
@@ -0,0 +1,1607 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * written by Yaniv Kamay, Izik Eidus, Gerd Hoffmann
+ * maintained by Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu-common.h"
+#include "qemu-timer.h"
+#include "qemu-queue.h"
+#include "monitor.h"
+#include "sysemu.h"
+#include "trace.h"
+
+#include "qxl.h"
+
+static QXLDevice *qxl0;
+
+void qxl_set_guest_bug(QXLDevice *qxl, const char *msg, ...)
+{
+    qxl_send_events(qxl, QXL_INTERRUPT_ERROR);
+    qxl->pci.guest_bug = 1;
+    if (qxl->pci.guestdebug) {
+        va_list ap;
+        va_start(ap, msg);
+        fprintf(stderr, "qxl-%d: guest bug: ", qxl->id);
+        vfprintf(stderr, msg, ap);
+        fprintf(stderr, "\n");
+        va_end(ap);
+    }
+}
+
+static void qxl_clear_guest_bug(QXLDevice *qxl)
+{
+    qxl->pci.guest_bug = 0;
+}
+
+static void qxl_spice_destroy_surface_wait_complete(QXLDevice *qxl,
+                                                    uint32_t id)
+{
+    trace_qxl_spice_destroy_surface_wait_complete(qxl->id, id);
+    qemu_mutex_lock(&qxl->track_lock);
+    qxl->guest_surfaces.cmds[id] = 0;
+    qxl->guest_surfaces.count--;
+    qemu_mutex_unlock(&qxl->track_lock);
+}
+
+static void qxl_spice_destroy_surface_wait(QXLDevice *qxl, uint32_t id,
+                                           qxl_async_io async)
+{
+    QXLCookie *cookie;
+
+    trace_qxl_spice_destroy_surface_wait(qxl->id, id, async);
+    if (async) {
+        cookie = qxl_cookie_new(QXL_COOKIE_TYPE_IO,
+                                QXL_IO_DESTROY_SURFACE_ASYNC);
+        cookie->u.surface_id = id;
+        spice_qxl_destroy_surface_async(&qxl->ssd.qxl, id, (uintptr_t)cookie);
+    } else {
+        qxl->ssd.worker->destroy_surface_wait(qxl->ssd.worker, id);
+    }
+}
+
+static void qxl_spice_flush_surfaces_async(QXLDevice *qxl)
+{
+    trace_qxl_spice_flush_surfaces_async(qxl->id, qxl->guest_surfaces.count,
+                                         qxl->num_free_res);
+    spice_qxl_flush_surfaces_async(&qxl->ssd.qxl,
+        (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO,
+                                  QXL_IO_FLUSH_SURFACES_ASYNC));
+}
+
+static void qxl_spice_destroy_surfaces_complete(QXLDevice *qxl)
+{
+    trace_qxl_spice_destroy_surfaces_complete(qxl->id);
+    qemu_mutex_lock(&qxl->track_lock);
+    memset(&qxl->guest_surfaces.cmds, 0, sizeof(qxl->guest_surfaces.cmds));
+    qxl->guest_surfaces.count = 0;
+    qemu_mutex_unlock(&qxl->track_lock);
+}
+
+static void qxl_spice_destroy_surfaces(QXLDevice *qxl, qxl_async_io async)
+{
+    trace_qxl_spice_destroy_surfaces(qxl->id, async);
+    if (async) {
+        spice_qxl_destroy_surfaces_async(&qxl->ssd.qxl,
+                (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO,
+                                          QXL_IO_DESTROY_ALL_SURFACES_ASYNC));
+    } else {
+        qxl->ssd.worker->destroy_surfaces(qxl->ssd.worker);
+        qxl_spice_destroy_surfaces_complete(qxl);
+    }
+}
+
+/* can be called from spice server thread context */
+static void qxl_set_dirty(MemoryRegion *mr, ram_addr_t addr, ram_addr_t end)
+{
+    memory_region_set_dirty(mr, addr, end - addr);
+}
+
+void qxl_rom_set_dirty(QXLDevice *qxl)
+{
+    qxl_set_dirty(&qxl->pci.rom_bar, 0, qxl->pci.rom_size);
+}
+
+/* called from spice server thread context only */
+void qxl_ram_set_dirty(QXLDevice *qxl, void *ptr)
+{
+    void *base = qxl->pci.vga.vram_ptr;
+    intptr_t offset;
+
+    offset = ptr - base;
+    offset &= ~(TARGET_PAGE_SIZE-1);
+    assert(offset < qxl->pci.vga.vram_size);
+    qxl_set_dirty(&qxl->pci.vga.vram, offset, offset + TARGET_PAGE_SIZE);
+}
+
+/* can be called from spice server thread context */
+void qxl_ring_set_dirty(QXLDevice *qxl)
+{
+    ram_addr_t addr = qxl->shadow_rom.ram_header_offset;
+    ram_addr_t end  = qxl->pci.vga.vram_size;
+    qxl_set_dirty(&qxl->pci.vga.vram, addr, end);
+}
+
+/*
+ * keep track of some command state, for savevm/loadvm.
+ * called from spice server thread context only
+ */
+static int qxl_track_command(QXLDevice *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);
+
+        if (!cmd) {
+            return 1;
+        }
+        uint32_t id = le32_to_cpu(cmd->surface_id);
+
+        if (id >= NUM_SURFACES) {
+            qxl_set_guest_bug(qxl, "QXL_CMD_SURFACE id %d >= %d", id,
+                              NUM_SURFACES);
+            return 1;
+        }
+        qemu_mutex_lock(&qxl->track_lock);
+        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--;
+        }
+        qemu_mutex_unlock(&qxl->track_lock);
+        break;
+    }
+    case QXL_CMD_CURSOR:
+    {
+        QXLCursorCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id);
+
+        if (!cmd) {
+            return 1;
+        }
+        if (cmd->type == QXL_CURSOR_SET) {
+            qemu_mutex_lock(&qxl->track_lock);
+            qxl->guest_cursor = ext->cmd.data;
+            qemu_mutex_unlock(&qxl->track_lock);
+        }
+        break;
+    }
+    }
+    return 0;
+}
+
+/* spice display interface callbacks */
+
+const char *qxl_mode_to_string(int mode)
+{
+    switch (mode) {
+    case QXL_MODE_COMPAT:
+        return "compat";
+    case QXL_MODE_NATIVE:
+        return "native";
+    case QXL_MODE_UNDEFINED:
+        return "undefined";
+    case QXL_MODE_VGA:
+        return "vga";
+    }
+    return "INVALID";
+}
+
+static const char *io_port_to_string(uint32_t io_port)
+{
+    if (io_port >= QXL_IO_RANGE_SIZE) {
+        return "out of range";
+    }
+    static const char *io_port_to_string[QXL_IO_RANGE_SIZE + 1] = {
+        [QXL_IO_NOTIFY_CMD]             = "QXL_IO_NOTIFY_CMD",
+        [QXL_IO_NOTIFY_CURSOR]          = "QXL_IO_NOTIFY_CURSOR",
+        [QXL_IO_UPDATE_AREA]            = "QXL_IO_UPDATE_AREA",
+        [QXL_IO_UPDATE_IRQ]             = "QXL_IO_UPDATE_IRQ",
+        [QXL_IO_NOTIFY_OOM]             = "QXL_IO_NOTIFY_OOM",
+        [QXL_IO_RESET]                  = "QXL_IO_RESET",
+        [QXL_IO_SET_MODE]               = "QXL_IO_SET_MODE",
+        [QXL_IO_LOG]                    = "QXL_IO_LOG",
+        [QXL_IO_MEMSLOT_ADD]            = "QXL_IO_MEMSLOT_ADD",
+        [QXL_IO_MEMSLOT_DEL]            = "QXL_IO_MEMSLOT_DEL",
+        [QXL_IO_DETACH_PRIMARY]         = "QXL_IO_DETACH_PRIMARY",
+        [QXL_IO_ATTACH_PRIMARY]         = "QXL_IO_ATTACH_PRIMARY",
+        [QXL_IO_CREATE_PRIMARY]         = "QXL_IO_CREATE_PRIMARY",
+        [QXL_IO_DESTROY_PRIMARY]        = "QXL_IO_DESTROY_PRIMARY",
+        [QXL_IO_DESTROY_SURFACE_WAIT]   = "QXL_IO_DESTROY_SURFACE_WAIT",
+        [QXL_IO_DESTROY_ALL_SURFACES]   = "QXL_IO_DESTROY_ALL_SURFACES",
+        [QXL_IO_UPDATE_AREA_ASYNC]      = "QXL_IO_UPDATE_AREA_ASYNC",
+        [QXL_IO_MEMSLOT_ADD_ASYNC]      = "QXL_IO_MEMSLOT_ADD_ASYNC",
+        [QXL_IO_CREATE_PRIMARY_ASYNC]   = "QXL_IO_CREATE_PRIMARY_ASYNC",
+        [QXL_IO_DESTROY_PRIMARY_ASYNC]  = "QXL_IO_DESTROY_PRIMARY_ASYNC",
+        [QXL_IO_DESTROY_SURFACE_ASYNC]  = "QXL_IO_DESTROY_SURFACE_ASYNC",
+        [QXL_IO_DESTROY_ALL_SURFACES_ASYNC]
+                                        = "QXL_IO_DESTROY_ALL_SURFACES_ASYNC",
+        [QXL_IO_FLUSH_SURFACES_ASYNC]   = "QXL_IO_FLUSH_SURFACES_ASYNC",
+        [QXL_IO_FLUSH_RELEASE]          = "QXL_IO_FLUSH_RELEASE",
+    };
+    return io_port_to_string[io_port];
+}
+
+static void qxl_create_guest_primary_complete(QXLDevice *d);
+
+/* called from spice server thread context only */
+int interface_get_command_pci(QXLDevice *qxl, QXLCommandExt *ext)
+{
+    SimpleSpiceUpdate *update;
+    QXLCommandRing *ring;
+    QXLCommand *cmd;
+    int notify, ret;
+
+    trace_qxl_ring_command_check(qxl->id, qxl_mode_to_string(qxl->pci.mode));
+
+    switch (qxl->pci.mode) {
+    case QXL_MODE_VGA:
+        ret = false;
+        qemu_mutex_lock(&qxl->ssd.lock);
+        if (qxl->ssd.update != NULL) {
+            update = qxl->ssd.update;
+            qxl->ssd.update = NULL;
+            *ext = update->ext;
+            ret = true;
+        }
+        qemu_mutex_unlock(&qxl->ssd.lock);
+        if (ret) {
+            trace_qxl_ring_command_get(qxl->id, qxl_mode_to_string(qxl->pci.mode));
+            qxl_log_command(qxl, "vga", ext);
+        }
+        return ret;
+    case QXL_MODE_COMPAT:
+    case QXL_MODE_NATIVE:
+    case QXL_MODE_UNDEFINED:
+        ring = &qxl->ram->cmd_ring;
+        if (qxl->pci.guest_bug || SPICE_RING_IS_EMPTY(ring)) {
+            return false;
+        }
+        SPICE_RING_CONS_ITEM(qxl, ring, cmd);
+        if (!cmd) {
+            return false;
+        }
+        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);
+        trace_qxl_ring_command_get(qxl->id, qxl_mode_to_string(qxl->pci.mode));
+        return true;
+    default:
+        return false;
+    }
+}
+
+int interface_get_cursor_command_pci(QXLDevice *qxl, struct QXLCommandExt *ext)
+{
+    QXLCursorRing *ring;
+    QXLCommand *cmd;
+    int notify;
+
+    trace_qxl_ring_cursor_check(qxl->id, qxl_mode_to_string(qxl->pci.mode));
+
+    switch (qxl->pci.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(qxl, ring, cmd);
+        if (!cmd) {
+            return false;
+        }
+        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);
+        }
+        trace_qxl_ring_cursor_get(qxl->id, qxl_mode_to_string(qxl->pci.mode));
+        return true;
+    default:
+        return false;
+    }
+}
+
+void interface_async_complete_io(QXLDevice *qxl, QXLCookie *cookie)
+{
+    uint32_t current_async;
+
+    qemu_mutex_lock(&qxl->async_lock);
+    current_async = qxl->current_async;
+    qxl->current_async = QXL_UNDEFINED_IO;
+    qemu_mutex_unlock(&qxl->async_lock);
+
+    trace_qxl_interface_async_complete_io(qxl->id, current_async, cookie);
+    if (!cookie) {
+        fprintf(stderr, "qxl: %s: error, cookie is NULL\n", __func__);
+        return;
+    }
+    if (cookie && current_async != cookie->io) {
+        fprintf(stderr,
+                "qxl: %s: error: current_async = %d != %"
+                PRId64 " = cookie->io\n", __func__, current_async, cookie->io);
+    }
+    switch (current_async) {
+    case QXL_IO_MEMSLOT_ADD_ASYNC:
+    case QXL_IO_DESTROY_PRIMARY_ASYNC:
+    case QXL_IO_UPDATE_AREA_ASYNC:
+    case QXL_IO_FLUSH_SURFACES_ASYNC:
+        break;
+    case QXL_IO_CREATE_PRIMARY_ASYNC:
+        qxl_create_guest_primary_complete(qxl);
+        break;
+    case QXL_IO_DESTROY_ALL_SURFACES_ASYNC:
+        qxl_spice_destroy_surfaces_complete(qxl);
+        break;
+    case QXL_IO_DESTROY_SURFACE_ASYNC:
+        qxl_spice_destroy_surface_wait_complete(qxl, cookie->u.surface_id);
+        break;
+    default:
+        fprintf(stderr, "qxl: %s: unexpected current_async %d\n", __func__,
+                current_async);
+    }
+    qxl_send_events(qxl, QXL_INTERRUPT_IO_CMD);
+}
+
+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,
+
+    .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,
+
+    /* the callbacks below are called from spice server thread context */
+    .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,
+    .async_complete          = interface_async_complete,
+    .update_area_complete    = interface_update_area_complete,
+};
+
+static void qxl_enter_vga_mode(QXLDevice *d)
+{
+    if (d->pci.mode == QXL_MODE_VGA) {
+        return;
+    }
+    trace_qxl_enter_vga_mode(d->id);
+    qemu_spice_create_host_primary(&d->ssd);
+    d->pci.mode = QXL_MODE_VGA;
+    memset(&d->ssd.dirty, 0, sizeof(d->ssd.dirty));
+    vga_dirty_log_start(&d->pci.vga);
+}
+
+static void qxl_exit_vga_mode(QXLDevice *d)
+{
+    if (d->pci.mode != QXL_MODE_VGA) {
+        return;
+    }
+    trace_qxl_exit_vga_mode(d->id);
+    vga_dirty_log_stop(&d->pci.vga);
+    qxl_destroy_primary(d, QXL_SYNC);
+}
+
+static void qxl_update_irq(QXLDevice *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.pci.irq[0], level);
+    qxl_ring_set_dirty(d);
+}
+
+static void qxl_check_state(QXLDevice *d)
+{
+    QXLRam *ram = d->ram;
+
+    assert(!d->ssd.running || SPICE_RING_IS_EMPTY(&ram->cmd_ring));
+    assert(!d->ssd.running || SPICE_RING_IS_EMPTY(&ram->cursor_ring));
+}
+
+static void qxl_reset_state(QXLDevice *d)
+{
+    QXLRom *rom = d->rom;
+
+    qxl_check_state(d);
+    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(QXLDevice *d)
+{
+    trace_qxl_soft_reset(d->id);
+    qxl_check_state(d);
+    qxl_clear_guest_bug(d);
+    d->current_async = QXL_UNDEFINED_IO;
+
+    if (d->id == 0) {
+        qxl_enter_vga_mode(d);
+    } else {
+        d->pci.mode = QXL_MODE_UNDEFINED;
+    }
+}
+
+static void qxl_hard_reset(QXLDevice *d, int loadvm)
+{
+    trace_qxl_hard_reset(d->id, loadvm);
+
+    qxl_spice_reset_cursor(d);
+    qxl_spice_reset_image_cache(d);
+    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);
+}
+
+static void qxl_reset_handler(DeviceState *dev)
+{
+    QXLDevice *d = (QXLDevice *) 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;
+    QXLDevice *qxl = (QXLDevice *) container_of(vga, PCIQXLDevice, vga);
+
+    trace_qxl_io_write_vga(qxl->id, qxl_mode_to_string(qxl->pci.mode), addr, val);
+    if (qxl->pci.mode != QXL_MODE_VGA) {
+        qxl_destroy_primary(qxl, QXL_SYNC);
+        qxl_soft_reset(qxl);
+    }
+    vga_ioport_write(opaque, addr, val);
+}
+
+static const MemoryRegionPortio qxl_vga_portio_list[] = {
+    { 0x04,  2, 1, .read  = vga_ioport_read,
+                   .write = qxl_vga_ioport_write }, /* 3b4 */
+    { 0x0a,  1, 1, .read  = vga_ioport_read,
+                   .write = qxl_vga_ioport_write }, /* 3ba */
+    { 0x10, 16, 1, .read  = vga_ioport_read,
+                   .write = qxl_vga_ioport_write }, /* 3c0 */
+    { 0x24,  2, 1, .read  = vga_ioport_read,
+                   .write = qxl_vga_ioport_write }, /* 3d4 */
+    { 0x2a,  1, 1, .read  = vga_ioport_read,
+                   .write = qxl_vga_ioport_write }, /* 3da */
+    PORTIO_END_OF_LIST(),
+};
+
+static int qxl_add_memslot(QXLDevice *d, uint32_t slot_id, uint64_t delta,
+                           qxl_async_io async)
+{
+    static const int regions[] = {
+        QXL_RAM_RANGE_INDEX,
+        QXL_VRAM_RANGE_INDEX,
+        QXL_VRAM64_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);
+
+    trace_qxl_memslot_add_guest(d->id, slot_id, guest_start, guest_end);
+
+    if (slot_id >= NUM_MEMSLOTS) {
+        qxl_set_guest_bug(d, "%s: slot_id >= NUM_MEMSLOTS %d >= %d", __func__,
+                      slot_id, NUM_MEMSLOTS);
+        return 1;
+    }
+    if (guest_start > guest_end) {
+        qxl_set_guest_bug(d, "%s: guest_start > guest_end 0x%" PRIx64
+                         " > 0x%" PRIx64, __func__, guest_start, guest_end);
+        return 1;
+    }
+
+    for (i = 0; i < ARRAY_SIZE(regions); i++) {
+        pci_region = regions[i];
+        pci_start = d->pci.pci.io_regions[pci_region].addr;
+        pci_end = pci_start + d->pci.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;
+    }
+    if (i == ARRAY_SIZE(regions)) {
+        qxl_set_guest_bug(d, "%s: finished loop without match", __func__);
+        return 1;
+    }
+
+    switch (pci_region) {
+    case QXL_RAM_RANGE_INDEX:
+        virt_start = (intptr_t)memory_region_get_ram_ptr(&d->pci.vga.vram);
+        break;
+    case QXL_VRAM_RANGE_INDEX:
+    case 4 /* vram 64bit */:
+        virt_start = (intptr_t)memory_region_get_ram_ptr(&d->pci.vram_bar);
+        break;
+    default:
+        /* should not happen */
+        qxl_set_guest_bug(d, "%s: pci_region = %d", __func__, pci_region);
+        return 1;
+    }
+
+    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;
+    qxl_rom_set_dirty(d);
+
+    qemu_spice_add_memslot(&d->ssd, &memslot, async);
+    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;
+    return 0;
+}
+
+static void qxl_del_memslot(QXLDevice *d, uint32_t slot_id)
+{
+    qemu_spice_del_memslot(&d->ssd, MEMSLOT_GROUP_HOST, slot_id);
+    d->guest_slots[slot_id].active = 0;
+}
+
+void qxl_reset_memslots(QXLDevice *d)
+{
+    qxl_spice_reset_memslots(d);
+    memset(&d->guest_slots, 0, sizeof(d->guest_slots));
+}
+
+void qxl_reset_surfaces(QXLDevice *d)
+{
+    trace_qxl_reset_surfaces(d->id);
+    d->pci.mode = QXL_MODE_UNDEFINED;
+    qxl_spice_destroy_surfaces(d, QXL_SYNC);
+}
+
+/* can be also called from spice server thread context */
+void *qxl_phys2virt(QXLDevice *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 *)(intptr_t)offset;
+    case MEMSLOT_GROUP_GUEST:
+        if (slot >= NUM_MEMSLOTS) {
+            qxl_set_guest_bug(qxl, "slot too large %d >= %d", slot,
+                              NUM_MEMSLOTS);
+            return NULL;
+        }
+        if (!qxl->guest_slots[slot].active) {
+            qxl_set_guest_bug(qxl, "inactive slot %d\n", slot);
+            return NULL;
+        }
+        if (offset < qxl->guest_slots[slot].delta) {
+            qxl_set_guest_bug(qxl,
+                          "slot %d offset %"PRIu64" < delta %"PRIu64"\n",
+                          slot, offset, qxl->guest_slots[slot].delta);
+            return NULL;
+        }
+        offset -= qxl->guest_slots[slot].delta;
+        if (offset > qxl->guest_slots[slot].size) {
+            qxl_set_guest_bug(qxl,
+                          "slot %d offset %"PRIu64" > size %"PRIu64"\n",
+                          slot, offset, qxl->guest_slots[slot].size);
+            return NULL;
+        }
+        return qxl->guest_slots[slot].ptr + offset;
+    }
+    return NULL;
+}
+
+static void qxl_create_guest_primary_complete(QXLDevice *qxl)
+{
+    /* for local rendering */
+    qxl_render_resize(qxl);
+}
+
+static void qxl_create_guest_primary(QXLDevice *qxl, int loadvm,
+                                     qxl_async_io async)
+{
+    QXLDevSurfaceCreate surface;
+    QXLSurfaceCreate *sc = &qxl->guest_primary.surface;
+    int size;
+    int requested_height = le32_to_cpu(sc->height);
+    int requested_stride = le32_to_cpu(sc->stride);
+
+    size = abs(requested_stride) * requested_height;
+    if (size > qxl->pci.vgamem_size) {
+        qxl_set_guest_bug(qxl, "%s: requested primary larger then framebuffer"
+                               " size", __func__);
+        return;
+    }
+
+    if (qxl->pci.mode == QXL_MODE_NATIVE) {
+        qxl_set_guest_bug(qxl, "%s: nop since already in QXL_MODE_NATIVE",
+                      __func__);
+    }
+    qxl_exit_vga_mode(qxl);
+
+    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);
+    trace_qxl_create_guest_primary(qxl->id, sc->width, sc->height, sc->mem,
+                                   sc->format, sc->position);
+    trace_qxl_create_guest_primary_rest(qxl->id, sc->stride, sc->type,
+                                        sc->flags);
+
+    surface.mouse_mode = true;
+    surface.group_id   = MEMSLOT_GROUP_GUEST;
+    if (loadvm) {
+        surface.flags |= QXL_SURF_FLAG_KEEP_DATA;
+    }
+
+    qxl->pci.mode = QXL_MODE_NATIVE;
+    qxl->cmdflags = 0;
+    qemu_spice_create_primary_surface(&qxl->ssd, 0, &surface, async);
+
+    if (async == QXL_SYNC) {
+        qxl_create_guest_primary_complete(qxl);
+    }
+}
+
+/* return 1 if surface destoy was initiated (in QXL_ASYNC case) or
+ * done (in QXL_SYNC case), 0 otherwise. */
+int qxl_destroy_primary(QXLDevice *d, qxl_async_io async)
+{
+    if (d->pci.mode == QXL_MODE_UNDEFINED) {
+        return 0;
+    }
+    trace_qxl_destroy_primary(d->id);
+    d->pci.mode = QXL_MODE_UNDEFINED;
+    qemu_spice_destroy_primary_surface(&d->ssd, 0, async);
+    qxl_spice_reset_cursor(d);
+    return 1;
+}
+
+static void qxl_set_mode(QXLDevice *d, int modenr, int loadvm)
+{
+    pcibus_t start = d->pci.pci.io_regions[QXL_RAM_RANGE_INDEX].addr;
+    pcibus_t end   = d->pci.pci.io_regions[QXL_RAM_RANGE_INDEX].size + start;
+    QXLMode *mode = d->modes->modes + modenr;
+    uint64_t devmem = d->pci.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,
+        .flags      = loadvm ? QXL_SURF_FLAG_KEEP_DATA : 0,
+        .mouse_mode = true,
+        .mem        = devmem + d->shadow_rom.draw_area_offset,
+    };
+
+    trace_qxl_set_mode(d->id, modenr, mode->x_res, mode->y_res, mode->bits,
+                       devmem);
+    if (!loadvm) {
+        qxl_hard_reset(d, 0);
+    }
+
+    d->guest_slots[0].slot = slot;
+    assert(qxl_add_memslot(d, 0, devmem, QXL_SYNC) == 0);
+
+    d->guest_primary.surface = surface;
+    qxl_create_guest_primary(d, 0, QXL_SYNC);
+
+    d->pci.mode = QXL_MODE_COMPAT;
+    d->cmdflags = QXL_COMMAND_FLAG_COMPAT;
+#ifdef QXL_COMMAND_FLAG_COMPAT_16BPP /* new in spice 0.6.1 */
+    if (mode->bits == 16) {
+        d->cmdflags |= QXL_COMMAND_FLAG_COMPAT_16BPP;
+    }
+#endif
+    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, target_phys_addr_t addr,
+                         uint64_t val, unsigned size)
+{
+    QXLDevice *d = opaque;
+    uint32_t io_port = addr;
+    qxl_async_io async = QXL_SYNC;
+    uint32_t orig_io_port = io_port;
+
+    if (d->pci.guest_bug && !io_port == QXL_IO_RESET) {
+        return;
+    }
+
+    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:
+    case QXL_IO_UPDATE_IRQ:
+    case QXL_IO_LOG:
+    case QXL_IO_MEMSLOT_ADD_ASYNC:
+    case QXL_IO_CREATE_PRIMARY_ASYNC:
+        break;
+    default:
+        if (d->pci.mode != QXL_MODE_VGA) {
+            break;
+        }
+        trace_qxl_io_unexpected_vga_mode(d->id,
+            io_port, io_port_to_string(io_port));
+        /* be nice to buggy guest drivers */
+        if (io_port >= QXL_IO_UPDATE_AREA_ASYNC &&
+            io_port <= QXL_IO_DESTROY_ALL_SURFACES_ASYNC) {
+            qxl_send_events(d, QXL_INTERRUPT_IO_CMD);
+        }
+        return;
+    }
+
+    /* we change the io_port to avoid ifdeffery in the main switch */
+    orig_io_port = io_port;
+    switch (io_port) {
+    case QXL_IO_UPDATE_AREA_ASYNC:
+        io_port = QXL_IO_UPDATE_AREA;
+        goto async_common;
+    case QXL_IO_MEMSLOT_ADD_ASYNC:
+        io_port = QXL_IO_MEMSLOT_ADD;
+        goto async_common;
+    case QXL_IO_CREATE_PRIMARY_ASYNC:
+        io_port = QXL_IO_CREATE_PRIMARY;
+        goto async_common;
+    case QXL_IO_DESTROY_PRIMARY_ASYNC:
+        io_port = QXL_IO_DESTROY_PRIMARY;
+        goto async_common;
+    case QXL_IO_DESTROY_SURFACE_ASYNC:
+        io_port = QXL_IO_DESTROY_SURFACE_WAIT;
+        goto async_common;
+    case QXL_IO_DESTROY_ALL_SURFACES_ASYNC:
+        io_port = QXL_IO_DESTROY_ALL_SURFACES;
+        goto async_common;
+    case QXL_IO_FLUSH_SURFACES_ASYNC:
+async_common:
+        async = QXL_ASYNC;
+        qemu_mutex_lock(&d->async_lock);
+        if (d->current_async != QXL_UNDEFINED_IO) {
+            qxl_set_guest_bug(d, "%d async started before last (%d) complete",
+                io_port, d->current_async);
+            qemu_mutex_unlock(&d->async_lock);
+            return;
+        }
+        d->current_async = orig_io_port;
+        qemu_mutex_unlock(&d->async_lock);
+        break;
+    default:
+        break;
+    }
+    trace_qxl_io_write(d->id, qxl_mode_to_string(d->pci.mode), addr, val, size,
+                       async);
+
+    switch (io_port) {
+    case QXL_IO_UPDATE_AREA:
+    {
+        QXLCookie *cookie = NULL;
+        QXLRect update = d->ram->update_area;
+
+        if (async == QXL_ASYNC) {
+            cookie = qxl_cookie_new(QXL_COOKIE_TYPE_IO,
+                                    QXL_IO_UPDATE_AREA_ASYNC);
+            cookie->u.area = update;
+        }
+        qxl_spice_update_area(d, d->ram->update_surface,
+                              cookie ? &cookie->u.area : &update,
+                              NULL, 0, 0, async, cookie);
+        break;
+    }
+    case QXL_IO_NOTIFY_CMD:
+        qemu_spice_wakeup(&d->ssd);
+        break;
+    case QXL_IO_NOTIFY_CURSOR:
+        qemu_spice_wakeup(&d->ssd);
+        break;
+    case QXL_IO_UPDATE_IRQ:
+        qxl_update_irq(d);
+        break;
+    case QXL_IO_NOTIFY_OOM:
+        if (!SPICE_RING_IS_EMPTY(&d->ram->release_ring)) {
+            break;
+        }
+        d->oom_running = 1;
+        qxl_spice_oom(d);
+        d->oom_running = 0;
+        break;
+    case QXL_IO_SET_MODE:
+        qxl_set_mode(d, val, 0);
+        break;
+    case QXL_IO_LOG:
+        if (d->pci.guestdebug) {
+            fprintf(stderr, "qxl/guest-%d: %" PRId64 ": %s", d->id,
+                    qemu_get_clock_ns(vm_clock), d->ram->log_buf);
+        }
+        break;
+    case QXL_IO_RESET:
+        qxl_hard_reset(d, 0);
+        break;
+    case QXL_IO_MEMSLOT_ADD:
+        if (val >= NUM_MEMSLOTS) {
+            qxl_set_guest_bug(d, "QXL_IO_MEMSLOT_ADD: val out of range");
+            break;
+        }
+        if (d->guest_slots[val].active) {
+            qxl_set_guest_bug(d,
+                        "QXL_IO_MEMSLOT_ADD: memory slot already active");
+            break;
+        }
+        d->guest_slots[val].slot = d->ram->mem_slot;
+        qxl_add_memslot(d, val, 0, async);
+        break;
+    case QXL_IO_MEMSLOT_DEL:
+        if (val >= NUM_MEMSLOTS) {
+            qxl_set_guest_bug(d, "QXL_IO_MEMSLOT_DEL: val out of range");
+            break;
+        }
+        qxl_del_memslot(d, val);
+        break;
+    case QXL_IO_CREATE_PRIMARY:
+        if (val != 0) {
+            qxl_set_guest_bug(d, "QXL_IO_CREATE_PRIMARY (async=%d): val != 0",
+                          async);
+            goto cancel_async;
+        }
+        d->guest_primary.surface = d->ram->create_surface;
+        qxl_create_guest_primary(d, 0, async);
+        break;
+    case QXL_IO_DESTROY_PRIMARY:
+        if (val != 0) {
+            qxl_set_guest_bug(d, "QXL_IO_DESTROY_PRIMARY (async=%d): val != 0",
+                          async);
+            goto cancel_async;
+        }
+        if (!qxl_destroy_primary(d, async)) {
+            trace_qxl_io_destroy_primary_ignored(d->id,
+                                                 qxl_mode_to_string(d->pci.mode));
+            goto cancel_async;
+        }
+        break;
+    case QXL_IO_DESTROY_SURFACE_WAIT:
+        if (val >= NUM_SURFACES) {
+            qxl_set_guest_bug(d, "QXL_IO_DESTROY_SURFACE (async=%d):"
+                             "%" PRIu64 " >= NUM_SURFACES", async, val);
+            goto cancel_async;
+        }
+        qxl_spice_destroy_surface_wait(d, val, async);
+        break;
+    case QXL_IO_FLUSH_RELEASE: {
+        QXLReleaseRing *ring = &d->ram->release_ring;
+        if (ring->prod - ring->cons + 1 == ring->num_items) {
+            fprintf(stderr,
+                "ERROR: no flush, full release ring [p%d,%dc]\n",
+                ring->prod, ring->cons);
+        }
+        qxl_push_free_res(d, 1 /* flush */);
+        break;
+    }
+    case QXL_IO_FLUSH_SURFACES_ASYNC:
+        qxl_spice_flush_surfaces_async(d);
+        break;
+    case QXL_IO_DESTROY_ALL_SURFACES:
+        d->pci.mode = QXL_MODE_UNDEFINED;
+        qxl_spice_destroy_surfaces(d, async);
+        break;
+    default:
+        qxl_set_guest_bug(d, "%s: unexpected ioport=0x%x\n", __func__, io_port);
+    }
+    return;
+cancel_async:
+    if (async) {
+        qxl_send_events(d, QXL_INTERRUPT_IO_CMD);
+        qemu_mutex_lock(&d->async_lock);
+        d->current_async = QXL_UNDEFINED_IO;
+        qemu_mutex_unlock(&d->async_lock);
+    }
+}
+
+static uint64_t ioport_read(void *opaque, target_phys_addr_t addr,
+                            unsigned size)
+{
+    QXLDevice *d = opaque;
+
+    trace_qxl_io_read_unexpected(d->id);
+    return 0xff;
+}
+
+static const MemoryRegionOps qxl_io_ops = {
+    .read = ioport_read,
+    .write = ioport_write,
+    .valid = {
+        .min_access_size = 1,
+        .max_access_size = 1,
+    },
+};
+
+static void pipe_read(void *opaque)
+{
+    QXLDevice *d = opaque;
+    char dummy;
+    int len;
+
+    do {
+        len = read(d->pipe[0], &dummy, sizeof(dummy));
+    } while (len == sizeof(dummy));
+    qxl_update_irq(d);
+}
+
+void qxl_send_events(QXLDevice *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 (qemu_thread_is_self(&d->main)) {
+        qxl_update_irq(d);
+    } else {
+        if (write(d->pipe[1], d, 1) != 1) {
+            dprint(d, 1, "%s: write to pipe failed\n", __func__);
+        }
+    }
+}
+
+static void init_pipe_signaling(QXLDevice *d)
+{
+    if (pipe(d->pipe) < 0) {
+        fprintf(stderr, "%s:%s: qxl pipe creation failed\n",
+                __FILE__, __func__);
+        exit(1);
+    }
+    fcntl(d->pipe[0], F_SETFL, O_NONBLOCK);
+    fcntl(d->pipe[1], F_SETFL, O_NONBLOCK);
+    fcntl(d->pipe[0], F_SETOWN, getpid());
+
+    qemu_thread_get_self(&d->main);
+    qemu_set_fd_handler(d->pipe[0], pipe_read, NULL, d);
+}
+
+/* graphics console */
+
+static void qxl_hw_update(void *opaque)
+{
+    QXLDevice *qxl = opaque;
+    VGACommonState *vga = &qxl->pci.vga;
+
+    switch (qxl->pci.mode) {
+    case QXL_MODE_VGA:
+        vga->update(vga);
+        break;
+    case QXL_MODE_COMPAT:
+    case QXL_MODE_NATIVE:
+        qxl_render_update(qxl);
+        break;
+    default:
+        break;
+    }
+}
+
+static void qxl_hw_invalidate(void *opaque)
+{
+    QXLDevice *qxl = opaque;
+    VGACommonState *vga = &qxl->pci.vga;
+
+    vga->invalidate(vga);
+}
+
+static void qxl_hw_screen_dump(void *opaque, const char *filename, bool cswitch)
+{
+    QXLDevice *qxl = opaque;
+    VGACommonState *vga = &qxl->pci.vga;
+
+    switch (qxl->pci.mode) {
+    case QXL_MODE_COMPAT:
+    case QXL_MODE_NATIVE:
+        qxl_render_update(qxl);
+        ppm_save(filename, qxl->ssd.ds->surface);
+        break;
+    case QXL_MODE_VGA:
+        vga->screen_dump(vga, filename, cswitch);
+        break;
+    default:
+        break;
+    }
+}
+
+static void qxl_hw_text_update(void *opaque, console_ch_t *chardata)
+{
+    QXLDevice *qxl = opaque;
+    VGACommonState *vga = &qxl->pci.vga;
+
+    if (qxl->pci.mode == QXL_MODE_VGA) {
+        vga->text_update(vga, chardata);
+        return;
+    }
+}
+
+static void qxl_dirty_surfaces(QXLDevice *qxl)
+{
+    intptr_t vram_start;
+    int i;
+
+    if (qxl->pci.mode != QXL_MODE_NATIVE && qxl->pci.mode != QXL_MODE_COMPAT) {
+        return;
+    }
+
+    /* dirty the primary surface */
+    qxl_set_dirty(&qxl->pci.vga.vram, qxl->shadow_rom.draw_area_offset,
+                  qxl->shadow_rom.surface0_area_size);
+
+    vram_start =  (intptr_t)memory_region_get_ram_ptr(&qxl->pci.vram_bar);
+
+    /* dirty the off-screen surfaces */
+    for (i = 0; i < NUM_SURFACES; i++) {
+        QXLSurfaceCmd *cmd;
+        intptr_t surface_offset;
+        int surface_size;
+
+        if (qxl->guest_surfaces.cmds[i] == 0) {
+            continue;
+        }
+
+        cmd = qxl_phys2virt(qxl, qxl->guest_surfaces.cmds[i],
+                            MEMSLOT_GROUP_GUEST);
+        assert(cmd);
+        assert(cmd->type == QXL_SURFACE_CMD_CREATE);
+        surface_offset = (intptr_t)qxl_phys2virt(qxl,
+                                                 cmd->u.surface_create.data,
+                                                 MEMSLOT_GROUP_GUEST);
+        assert(surface_offset);
+        surface_offset -= vram_start;
+        surface_size = cmd->u.surface_create.height *
+                       abs(cmd->u.surface_create.stride);
+        trace_qxl_surfaces_dirty(qxl->id, i, (int)surface_offset, surface_size);
+        qxl_set_dirty(&qxl->pci.vram_bar, surface_offset, surface_size);
+    }
+}
+
+static void qxl_vm_change_state_handler(void *opaque, int running,
+                                        RunState state)
+{
+    QXLDevice *qxl = opaque;
+    qemu_spice_vm_change_state_handler(&qxl->ssd, running, state);
+
+    if (running) {
+        /*
+         * if qxl_send_events was called from spice server context before
+         * migration ended, qxl_update_irq for these events might not have been
+         * called
+         */
+         qxl_update_irq(qxl);
+    } else {
+        /* make sure surfaces are saved before migration */
+        qxl_dirty_surfaces(qxl);
+    }
+}
+
+/* display change listener */
+
+static void display_update(struct DisplayState *ds, int x, int y, int w, int h)
+{
+    if (qxl0->pci.mode == QXL_MODE_VGA) {
+        qemu_spice_display_update(&qxl0->ssd, x, y, w, h);
+    }
+}
+
+static void display_resize(struct DisplayState *ds)
+{
+    if (qxl0->pci.mode == QXL_MODE_VGA) {
+        qemu_spice_display_resize(&qxl0->ssd);
+    }
+}
+
+static void display_refresh(struct DisplayState *ds)
+{
+    if (qxl0->pci.mode == QXL_MODE_VGA) {
+        qemu_spice_display_refresh(&qxl0->ssd);
+    } else {
+        qemu_mutex_lock(&qxl0->ssd.lock);
+        qemu_spice_cursor_refresh_unlocked(&qxl0->ssd);
+        qemu_mutex_unlock(&qxl0->ssd.lock);
+    }
+}
+
+static DisplayChangeListener display_listener = {
+    .dpy_update  = display_update,
+    .dpy_resize  = display_resize,
+    .dpy_refresh = display_refresh,
+};
+
+static void qxl_init_ramsize(QXLDevice *qxl)
+{
+    /* vga mode framebuffer / primary surface (bar 0, first part) */
+    if (qxl->pci.vgamem_size_mb < 8) {
+        qxl->pci.vgamem_size_mb = 8;
+    }
+    qxl->pci.vgamem_size = qxl->pci.vgamem_size_mb * 1024 * 1024;
+
+    /* vga ram (bar 0, total) */
+    if (qxl->ram_size_mb != -1) {
+        qxl->pci.vga.vram_size = qxl->ram_size_mb * 1024 * 1024;
+    }
+    if (qxl->pci.vga.vram_size < qxl->pci.vgamem_size * 2) {
+        qxl->pci.vga.vram_size = qxl->pci.vgamem_size * 2;
+    }
+
+    /* vram32 (surfaces, 32bit, bar 1) */
+    if (qxl->vram32_size_mb != -1) {
+        qxl->vram32_size = qxl->vram32_size_mb * 1024 * 1024;
+    }
+    if (qxl->vram32_size < 4096) {
+        qxl->vram32_size = 4096;
+    }
+
+    /* vram (surfaces, 64bit, bar 4+5) */
+    if (qxl->vram_size_mb != -1) {
+        qxl->vram_size = qxl->vram_size_mb * 1024 * 1024;
+    }
+    if (qxl->vram_size < qxl->vram32_size) {
+        qxl->vram_size = qxl->vram32_size;
+    }
+
+    if (qxl->pci.revision == 1) {
+        qxl->vram32_size = 4096;
+        qxl->vram_size = 4096;
+    }
+    qxl->pci.vgamem_size = msb_mask(qxl->pci.vgamem_size * 2 - 1);
+    qxl->pci.vga.vram_size = msb_mask(qxl->pci.vga.vram_size * 2 - 1);
+    qxl->vram32_size = msb_mask(qxl->vram32_size * 2 - 1);
+    qxl->vram_size = msb_mask(qxl->vram_size * 2 - 1);
+}
+
+static int qxl_init_common(QXLDevice *qxl)
+{
+    uint8_t* config = qxl->pci.pci.config;
+    uint32_t pci_device_rev;
+    uint32_t io_size;
+
+    qxl->pci.mode = QXL_MODE_UNDEFINED;
+    qxl->pci.generation = 1;
+    qxl->num_memslots = NUM_MEMSLOTS;
+    qxl->num_surfaces = NUM_SURFACES;
+    qemu_mutex_init(&qxl->track_lock);
+    qemu_mutex_init(&qxl->async_lock);
+    qxl->current_async = QXL_UNDEFINED_IO;
+    qxl->pci.guest_bug = 0;
+
+    switch (qxl->pci.revision) {
+    case 1: /* spice 0.4 -- qxl-1 */
+        pci_device_rev = QXL_REVISION_STABLE_V04;
+        io_size = 8;
+        break;
+    case 2: /* spice 0.6 -- qxl-2 */
+        pci_device_rev = QXL_REVISION_STABLE_V06;
+        io_size = 16;
+        break;
+    case 3: /* qxl-3 */
+    default:
+        pci_device_rev = QXL_DEFAULT_REVISION;
+        io_size = msb_mask(QXL_IO_RANGE_SIZE * 2 - 1);
+        break;
+    }
+
+    pci_set_byte(&config[PCI_REVISION_ID], pci_device_rev);
+    pci_set_byte(&config[PCI_INTERRUPT_PIN], 1);
+
+    qxl->pci.rom_size = qxl_rom_size();
+    memory_region_init_ram(&qxl->pci.rom_bar,"qxl-pci.vrom",
+            qxl->pci.rom_size);
+    vmstate_register_ram(&qxl->pci.rom_bar, &qxl->pci.pci.qdev);
+    init_qxl_rom(qxl);
+    init_qxl_ram(qxl);
+
+    memory_region_init_ram(&qxl->pci.vram_bar, "qxl-pci.vram",
+            qxl->vram_size);
+    vmstate_register_ram(&qxl->pci.vram_bar, &qxl->pci.pci.qdev);
+    memory_region_init_alias(&qxl->pci.vram32_bar, "qxl-pci.vram32",
+            &qxl->pci.vram_bar, 0, qxl->vram32_size);
+
+    memory_region_init_io(&qxl->pci.io_bar, &qxl_io_ops, qxl,
+                          "qxl-pci.ioports", io_size);
+    if (qxl->id == 0) {
+        vga_dirty_log_start(&qxl->pci.vga);
+    }
+
+
+    pci_register_bar(&qxl->pci.pci, QXL_IO_RANGE_INDEX,
+                     PCI_BASE_ADDRESS_SPACE_IO, &qxl->pci.io_bar);
+
+    pci_register_bar(&qxl->pci.pci, QXL_ROM_RANGE_INDEX,
+                     PCI_BASE_ADDRESS_SPACE_MEMORY, &qxl->pci.rom_bar);
+
+    pci_register_bar(&qxl->pci.pci, QXL_RAM_RANGE_INDEX,
+                     PCI_BASE_ADDRESS_SPACE_MEMORY, &qxl->pci.vga.vram);
+
+    pci_register_bar(&qxl->pci.pci, QXL_VRAM_RANGE_INDEX,
+                     PCI_BASE_ADDRESS_SPACE_MEMORY, &qxl->pci.vram32_bar);
+
+    if (qxl->vram32_size < qxl->vram_size) {
+        /*
+         * Make the 64bit vram bar show up only in case it is
+         * configured to be larger than the 32bit vram bar.
+         */
+        pci_register_bar(&qxl->pci.pci, QXL_VRAM64_RANGE_INDEX,
+                         PCI_BASE_ADDRESS_SPACE_MEMORY |
+                         PCI_BASE_ADDRESS_MEM_TYPE_64 |
+                         PCI_BASE_ADDRESS_MEM_PREFETCH,
+                         &qxl->pci.vram_bar);
+    }
+
+    /* print pci bar details */
+    dprint(qxl, 1, "ram/%s: %d MB [region 0]\n",
+           qxl->id == 0 ? "pri" : "sec",
+           qxl->pci.vga.vram_size / (1024*1024));
+    dprint(qxl, 1, "vram/32: %d MB [region 1]\n",
+           qxl->vram32_size / (1024*1024));
+    dprint(qxl, 1, "vram/64: %d MB %s\n",
+           qxl->vram_size / (1024*1024),
+           qxl->vram32_size < qxl->vram_size ? "[region 4]" : "[unmapped]");
+
+    qxl->ssd.qxl.base.sif = &qxl_interface.base;
+    qxl->ssd.qxl.id = qxl->id;
+    qemu_spice_add_interface(&qxl->ssd.qxl.base);
+    qemu_add_vm_change_state_handler(qxl_vm_change_state_handler, qxl);
+
+    init_pipe_signaling(qxl);
+    qxl_reset_state(qxl);
+
+    qxl->update_area_bh = qemu_bh_new(qxl_render_update_area_bh, qxl);
+
+    return 0;
+}
+
+static int qxl_init_primary(PCIDevice *dev)
+{
+    QXLDevice *qxl = (QXLDevice *) DO_UPCAST(PCIQXLDevice, pci, dev);
+    VGACommonState *vga = &qxl->pci.vga;
+    PortioList *qxl_vga_port_list = g_new(PortioList, 1);
+
+    qxl->id = 0;
+    qxl_init_ramsize(qxl);
+    vga->vram_size_mb = qxl->pci.vga.vram_size >> 20;
+    vga_common_init(vga);
+    vga_init(vga, pci_address_space(dev), pci_address_space_io(dev), false);
+    portio_list_init(qxl_vga_port_list, qxl_vga_portio_list, vga, "vga");
+    portio_list_add(qxl_vga_port_list, pci_address_space_io(dev), 0x3b0);
+
+    vga->ds = graphic_console_init(qxl_hw_update, qxl_hw_invalidate,
+                                   qxl_hw_screen_dump, qxl_hw_text_update, qxl);
+    qemu_spice_display_init_common(&qxl->ssd, vga->ds);
+
+    qxl0 = qxl;
+    register_displaychangelistener(vga->ds, &display_listener);
+
+    return qxl_init_common(qxl);
+}
+
+static int qxl_init_secondary(PCIDevice *dev)
+{
+    static int device_id = 1;
+    QXLDevice *qxl = (QXLDevice *) DO_UPCAST(PCIQXLDevice, pci, dev);
+
+    qxl->id = device_id++;
+    qxl_init_ramsize(qxl);
+    memory_region_init_ram(&qxl->pci.vga.vram, "qxl-pci.vgavram", qxl->pci.vga.vram_size);
+    vmstate_register_ram(&qxl->pci.vga.vram, &qxl->pci.pci.qdev);
+    qxl->pci.vga.vram_ptr = memory_region_get_ram_ptr(&qxl->pci.vga.vram);
+
+    return qxl_init_common(qxl);
+}
+
+static void qxl_pre_save(void *opaque)
+{
+    QXLDevice* d = opaque;
+    uint8_t *ram_start = d->pci.vga.vram_ptr;
+
+    trace_qxl_pre_save(d->id);
+    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->pci.vga.vram_size);
+}
+
+static int qxl_pre_load(void *opaque)
+{
+    QXLDevice* d = opaque;
+
+    trace_qxl_pre_load(d->id);
+    qxl_hard_reset(d, 1);
+    qxl_exit_vga_mode(d);
+    return 0;
+}
+
+static void qxl_create_memslots(QXLDevice *d)
+{
+    int i;
+
+    for (i = 0; i < NUM_MEMSLOTS; i++) {
+        if (!d->guest_slots[i].active) {
+            continue;
+        }
+        qxl_add_memslot(d, i, 0, QXL_SYNC);
+    }
+}
+
+static int qxl_post_load(void *opaque, int version)
+{
+    QXLDevice *d = opaque;
+    uint8_t *ram_start = d->pci.vga.vram_ptr;
+    QXLCommandExt *cmds;
+    int in, out, newmode;
+
+    assert(d->last_release_offset < d->pci.vga.vram_size);
+    if (d->last_release_offset == 0) {
+        d->last_release = NULL;
+    } else {
+        d->last_release = (QXLReleaseInfo *)(ram_start + d->last_release_offset);
+    }
+
+    d->modes = (QXLModes*)((uint8_t*)d->rom + d->rom->modes_offset);
+
+    trace_qxl_post_load(d->id, qxl_mode_to_string(d->pci.mode));
+    newmode = d->pci.mode;
+    d->pci.mode = QXL_MODE_UNDEFINED;
+
+    switch (newmode) {
+    case QXL_MODE_UNDEFINED:
+        break;
+    case QXL_MODE_VGA:
+        qxl_create_memslots(d);
+        qxl_enter_vga_mode(d);
+        break;
+    case QXL_MODE_NATIVE:
+        qxl_create_memslots(d);
+        qxl_create_guest_primary(d, 1, QXL_SYNC);
+
+        /* replay surface-create and cursor-set commands */
+        cmds = g_malloc0(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++;
+        }
+        if (d->guest_cursor) {
+            cmds[out].cmd.data = d->guest_cursor;
+            cmds[out].cmd.type = QXL_CMD_CURSOR;
+            cmds[out].group_id = MEMSLOT_GROUP_GUEST;
+            out++;
+        }
+        qxl_spice_loadvm_commands(d, cmds, out);
+        g_free(cmds);
+
+        break;
+    case QXL_MODE_COMPAT:
+        /* note: no need to call qxl_create_memslots, qxl_set_mode
+         * creates the mem slot. */
+        qxl_set_mode(d, d->shadow_rom.mode, 1);
+        break;
+    }
+    return 0;
+}
+
+#define QXL_SAVE_VERSION 21
+
+static VMStateDescription qxl_memslot = {
+    .name               = "qxl-memslot",
+    .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),
+        VMSTATE_UINT32(active,         struct guest_slots),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static VMStateDescription qxl_surface = {
+    .name               = "qxl-surface",
+    .version_id         = QXL_SAVE_VERSION,
+    .minimum_version_id = QXL_SAVE_VERSION,
+    .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_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, 0, vmstate_vga_common, VGACommonState),
+        VMSTATE_UINT32(shadow_rom.mode, QXLDevice),
+        VMSTATE_UINT32(num_free_res, QXLDevice),
+        VMSTATE_UINT32(last_release_offset, QXLDevice),
+        VMSTATE_UINT32(mode, PCIQXLDevice),
+        VMSTATE_UINT32(ssd.unique, QXLDevice),
+        VMSTATE_INT32_EQUAL(num_memslots, QXLDevice),
+        VMSTATE_STRUCT_ARRAY(guest_slots, QXLDevice, NUM_MEMSLOTS, 0,
+                             qxl_memslot, struct guest_slots),
+        VMSTATE_STRUCT(guest_primary.surface, QXLDevice, 0,
+                       qxl_surface, QXLSurfaceCreate),
+        VMSTATE_INT32_EQUAL(num_surfaces, QXLDevice),
+        VMSTATE_ARRAY(guest_surfaces.cmds, QXLDevice, NUM_SURFACES, 0,
+                      vmstate_info_uint64, uint64_t),
+        VMSTATE_UINT64(guest_cursor, QXLDevice),
+        VMSTATE_END_OF_LIST()
+    },
+};
+
+static Property qxl_properties[] = {
+        DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size,
+                           64 * 1024 * 1024),
+        DEFINE_PROP_UINT32("vram_size", QXLDevice, vram32_size,
+                           64 * 1024 * 1024),
+        DEFINE_PROP_UINT32("revision", PCIQXLDevice, revision,
+                           QXL_DEFAULT_REVISION),
+        DEFINE_PROP_UINT32("debug", QXLDevice, debug, 0),
+        DEFINE_PROP_UINT32("guestdebug", PCIQXLDevice, guestdebug, 0),
+        DEFINE_PROP_UINT32("cmdlog", PCIQXLDevice, cmdlog, 0),
+        DEFINE_PROP_UINT32("ram_size_mb",  QXLDevice, ram_size_mb, -1),
+        DEFINE_PROP_UINT32("vram_size_mb", QXLDevice, vram32_size_mb, -1),
+        DEFINE_PROP_UINT32("vram64_size_mb", QXLDevice, vram_size_mb, -1),
+        DEFINE_PROP_UINT32("vgamem_mb", PCIQXLDevice, vgamem_size_mb, 16),
+        DEFINE_PROP_END_OF_LIST(),
+};
+
+static void qxl_primary_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->no_hotplug = 1;
+    k->init = qxl_init_primary;
+    k->romfile = "vgabios-qxl.bin";
+    k->vendor_id = REDHAT_PCI_VENDOR_ID;
+    k->device_id = QXL_DEVICE_ID_STABLE;
+    k->class_id = PCI_CLASS_DISPLAY_VGA;
+    dc->desc = "Spice QXL GPU (primary, vga compatible)";
+    dc->reset = qxl_reset_handler;
+    dc->vmsd = &qxl_vmstate;
+    dc->props = qxl_properties;
+}
+
+static TypeInfo qxl_primary_info = {
+    .name          = "qxl-vga",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(QXLDevice),
+    .class_init    = qxl_primary_class_init,
+};
+
+static void qxl_secondary_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->init = qxl_init_secondary;
+    k->vendor_id = REDHAT_PCI_VENDOR_ID;
+    k->device_id = QXL_DEVICE_ID_STABLE;
+    k->class_id = PCI_CLASS_DISPLAY_OTHER;
+    dc->desc = "Spice QXL GPU (secondary)";
+    dc->reset = qxl_reset_handler;
+    dc->vmsd = &qxl_vmstate;
+    dc->props = qxl_properties;
+}
+
+static TypeInfo qxl_secondary_info = {
+    .name          = "qxl",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(QXLDevice),
+    .class_init    = qxl_secondary_class_init,
+};
+
+static void qxl_register_types(void)
+{
+    type_register_static(&qxl_primary_info);
+    type_register_static(&qxl_secondary_info);
+}
+
+type_init(qxl_register_types)
diff --git a/hw/qxl.c b/hw/qxl.c
index c8a36aa..c9a63e2 100644
--- a/hw/qxl.c
+++ b/hw/qxl.c
@@ -27,44 +27,6 @@
 
 #include "qxl.h"
 
-/*
- * NOTE: SPICE_RING_PROD_ITEM accesses memory on the pci bar and as
- * such can be changed by the guest, so to avoid a guest trigerrable
- * abort we just qxl_set_guest_bug and set the return to NULL. Still
- * it may happen as a result of emulator bug as well.
- */
-#undef SPICE_RING_PROD_ITEM
-#define SPICE_RING_PROD_ITEM(qxl, 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))) { \
-            qxl_set_guest_bug(qxl, "SPICE_RING_PROD_ITEM indices mismatch " \
-                          "! %p <= %p < %p", (uint8_t *)start,          \
-                          (uint8_t *)m_item, (uint8_t *)end);           \
-            ret = NULL;                                                 \
-        } else {                                                        \
-            ret = &m_item->el;                                          \
-        }                                                               \
-    }
-
-#undef SPICE_RING_CONS_ITEM
-#define SPICE_RING_CONS_ITEM(qxl, 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))) { \
-            qxl_set_guest_bug(qxl, "SPICE_RING_CONS_ITEM indices mismatch " \
-                          "! %p <= %p < %p", (uint8_t *)start,          \
-                          (uint8_t *)m_item, (uint8_t *)end);           \
-            ret = NULL;                                                 \
-        } else {                                                        \
-            ret = &m_item->el;                                          \
-        }                                                               \
-    }
-
 #undef ALIGN
 #define ALIGN(a, b) (((a) + ((b) - 1)) & ~((b) - 1))
 
@@ -126,88 +88,6 @@ static QXLMode qxl_modes[] = {
     QXL_MODE_EX(3200, 2400),
 };
 
-static QXLDevice *qxl0;
-
-static void qxl_send_events(QXLDevice *d, uint32_t events);
-static int qxl_destroy_primary(QXLDevice *d, qxl_async_io async);
-static void qxl_reset_memslots(QXLDevice *d);
-static void qxl_reset_surfaces(QXLDevice *d);
-static void qxl_ring_set_dirty(QXLDevice *qxl);
-
-void qxl_set_guest_bug(QXLDevice *qxl, const char *msg, ...)
-{
-    qxl_send_events(qxl, QXL_INTERRUPT_ERROR);
-    qxl->pci.guest_bug = 1;
-    if (qxl->pci.guestdebug) {
-        va_list ap;
-        va_start(ap, msg);
-        fprintf(stderr, "qxl-%d: guest bug: ", qxl->id);
-        vfprintf(stderr, msg, ap);
-        fprintf(stderr, "\n");
-        va_end(ap);
-    }
-}
-
-static void qxl_clear_guest_bug(QXLDevice *qxl)
-{
-    qxl->pci.guest_bug = 0;
-}
-
-void qxl_spice_update_area(QXLDevice *qxl, uint32_t surface_id,
-                           struct QXLRect *area, struct QXLRect *dirty_rects,
-                           uint32_t num_dirty_rects,
-                           uint32_t clear_dirty_region,
-                           qxl_async_io async, struct QXLCookie *cookie)
-{
-    trace_qxl_spice_update_area(qxl->id, surface_id, area->left, area->right,
-                                area->top, area->bottom);
-    trace_qxl_spice_update_area_rest(qxl->id, num_dirty_rects,
-                                     clear_dirty_region);
-    if (async == QXL_SYNC) {
-        qxl->ssd.worker->update_area(qxl->ssd.worker, surface_id, area,
-                        dirty_rects, num_dirty_rects, clear_dirty_region);
-    } else {
-        assert(cookie != NULL);
-        spice_qxl_update_area_async(&qxl->ssd.qxl, surface_id, area,
-                                    clear_dirty_region, (uintptr_t)cookie);
-    }
-}
-
-static void qxl_spice_destroy_surface_wait_complete(QXLDevice *qxl,
-                                                    uint32_t id)
-{
-    trace_qxl_spice_destroy_surface_wait_complete(qxl->id, id);
-    qemu_mutex_lock(&qxl->track_lock);
-    qxl->guest_surfaces.cmds[id] = 0;
-    qxl->guest_surfaces.count--;
-    qemu_mutex_unlock(&qxl->track_lock);
-}
-
-static void qxl_spice_destroy_surface_wait(QXLDevice *qxl, uint32_t id,
-                                           qxl_async_io async)
-{
-    QXLCookie *cookie;
-
-    trace_qxl_spice_destroy_surface_wait(qxl->id, id, async);
-    if (async) {
-        cookie = qxl_cookie_new(QXL_COOKIE_TYPE_IO,
-                                QXL_IO_DESTROY_SURFACE_ASYNC);
-        cookie->u.surface_id = id;
-        spice_qxl_destroy_surface_async(&qxl->ssd.qxl, id, (uintptr_t)cookie);
-    } else {
-        qxl->ssd.worker->destroy_surface_wait(qxl->ssd.worker, id);
-    }
-}
-
-static void qxl_spice_flush_surfaces_async(QXLDevice *qxl)
-{
-    trace_qxl_spice_flush_surfaces_async(qxl->id, qxl->guest_surfaces.count,
-                                         qxl->num_free_res);
-    spice_qxl_flush_surfaces_async(&qxl->ssd.qxl,
-        (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO,
-                                  QXL_IO_FLUSH_SURFACES_ASYNC));
-}
-
 void qxl_spice_loadvm_commands(QXLDevice *qxl, struct QXLCommandExt *ext,
                                uint32_t count)
 {
@@ -227,28 +107,6 @@ void qxl_spice_reset_memslots(QXLDevice *qxl)
     qxl->ssd.worker->reset_memslots(qxl->ssd.worker);
 }
 
-static void qxl_spice_destroy_surfaces_complete(QXLDevice *qxl)
-{
-    trace_qxl_spice_destroy_surfaces_complete(qxl->id);
-    qemu_mutex_lock(&qxl->track_lock);
-    memset(&qxl->guest_surfaces.cmds, 0, sizeof(qxl->guest_surfaces.cmds));
-    qxl->guest_surfaces.count = 0;
-    qemu_mutex_unlock(&qxl->track_lock);
-}
-
-static void qxl_spice_destroy_surfaces(QXLDevice *qxl, qxl_async_io async)
-{
-    trace_qxl_spice_destroy_surfaces(qxl->id, async);
-    if (async) {
-        spice_qxl_destroy_surfaces_async(&qxl->ssd.qxl,
-                (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO,
-                                          QXL_IO_DESTROY_ALL_SURFACES_ASYNC));
-    } else {
-        qxl->ssd.worker->destroy_surfaces(qxl->ssd.worker);
-        qxl_spice_destroy_surfaces_complete(qxl);
-    }
-}
-
 void qxl_spice_reset_image_cache(QXLDevice *qxl)
 {
     trace_qxl_spice_reset_image_cache(qxl->id);
@@ -264,20 +122,27 @@ void qxl_spice_reset_cursor(QXLDevice *qxl)
     qemu_mutex_unlock(&qxl->track_lock);
 }
 
-
-static inline uint32_t msb_mask(uint32_t val)
+void qxl_spice_update_area(QXLDevice *qxl, uint32_t surface_id,
+                           struct QXLRect *area, struct QXLRect *dirty_rects,
+                           uint32_t num_dirty_rects,
+                           uint32_t clear_dirty_region,
+                           qxl_async_io async, struct QXLCookie *cookie)
 {
-    uint32_t mask;
-
-    do {
-        mask = ~(val - 1) & val;
-        val &= ~mask;
-    } while (mask < val);
-
-    return mask;
+    trace_qxl_spice_update_area(qxl->id, surface_id, area->left, area->right,
+                                area->top, area->bottom);
+    trace_qxl_spice_update_area_rest(qxl->id, num_dirty_rects,
+                                     clear_dirty_region);
+    if (async == QXL_SYNC) {
+        qxl->ssd.worker->update_area(qxl->ssd.worker, surface_id, area,
+                        dirty_rects, num_dirty_rects, clear_dirty_region);
+    } else {
+        assert(cookie != NULL);
+        spice_qxl_update_area_async(&qxl->ssd.qxl, surface_id, area,
+                                    clear_dirty_region, (uintptr_t)cookie);
+    }
 }
 
-static ram_addr_t qxl_rom_size(void)
+ram_addr_t qxl_rom_size(void)
 {
     uint32_t rom_size = sizeof(QXLRom) + sizeof(QXLModes) + sizeof(qxl_modes);
 
@@ -286,7 +151,7 @@ static ram_addr_t qxl_rom_size(void)
     return rom_size;
 }
 
-static void init_qxl_rom(QXLDevice *d)
+void init_qxl_rom(QXLDevice *d)
 {
     QXLRom *rom;
     rom = memory_region_get_ram_ptr(&d->pci.rom_bar);
@@ -345,7 +210,7 @@ static void init_qxl_rom(QXLDevice *d)
     d->modes      = modes;
 }
 
-static void init_qxl_ram(QXLDevice *d)
+void init_qxl_ram(QXLDevice *d)
 {
     uint8_t *buf;
     uint64_t *item;
@@ -365,93 +230,9 @@ static void init_qxl_ram(QXLDevice *d)
     qxl_ring_set_dirty(d);
 }
 
-/* can be called from spice server thread context */
-static void qxl_set_dirty(MemoryRegion *mr, ram_addr_t addr, ram_addr_t end)
-{
-    memory_region_set_dirty(mr, addr, end - addr);
-}
-
-static void qxl_rom_set_dirty(QXLDevice *qxl)
-{
-    qxl_set_dirty(&qxl->pci.rom_bar, 0, qxl->pci.rom_size);
-}
-
-/* called from spice server thread context only */
-static void qxl_ram_set_dirty(QXLDevice *qxl, void *ptr)
-{
-    void *base = qxl->pci.vga.vram_ptr;
-    intptr_t offset;
-
-    offset = ptr - base;
-    offset &= ~(TARGET_PAGE_SIZE-1);
-    assert(offset < qxl->pci.vga.vram_size);
-    qxl_set_dirty(&qxl->pci.vga.vram, offset, offset + TARGET_PAGE_SIZE);
-}
-
-/* can be called from spice server thread context */
-static void qxl_ring_set_dirty(QXLDevice *qxl)
-{
-    ram_addr_t addr = qxl->shadow_rom.ram_header_offset;
-    ram_addr_t end  = qxl->pci.vga.vram_size;
-    qxl_set_dirty(&qxl->pci.vga.vram, addr, end);
-}
-
-/*
- * keep track of some command state, for savevm/loadvm.
- * called from spice server thread context only
- */
-static int qxl_track_command(QXLDevice *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);
-
-        if (!cmd) {
-            return 1;
-        }
-        uint32_t id = le32_to_cpu(cmd->surface_id);
-
-        if (id >= NUM_SURFACES) {
-            qxl_set_guest_bug(qxl, "QXL_CMD_SURFACE id %d >= %d", id,
-                              NUM_SURFACES);
-            return 1;
-        }
-        qemu_mutex_lock(&qxl->track_lock);
-        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--;
-        }
-        qemu_mutex_unlock(&qxl->track_lock);
-        break;
-    }
-    case QXL_CMD_CURSOR:
-    {
-        QXLCursorCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id);
-
-        if (!cmd) {
-            return 1;
-        }
-        if (cmd->type == QXL_CURSOR_SET) {
-            qemu_mutex_lock(&qxl->track_lock);
-            qxl->guest_cursor = ext->cmd.data;
-            qemu_mutex_unlock(&qxl->track_lock);
-        }
-        break;
-    }
-    }
-    return 0;
-}
-
 /* spice display interface callbacks */
 
-static void interface_attach_worker(QXLInstance *sin, QXLWorker *qxl_worker)
+void interface_attach_worker(QXLInstance *sin, QXLWorker *qxl_worker)
 {
     QXLDevice *qxl = container_of(sin, QXLDevice, ssd.qxl);
 
@@ -459,7 +240,7 @@ static void interface_attach_worker(QXLInstance *sin, QXLWorker *qxl_worker)
     trace_qxl_interface_attach_worker(qxl->id);
 }
 
-static void interface_set_compression_level(QXLInstance *sin, int level)
+void interface_set_compression_level(QXLInstance *sin, int level)
 {
     QXLDevice *qxl = container_of(sin, QXLDevice, ssd.qxl);
 
@@ -469,7 +250,7 @@ static void interface_set_compression_level(QXLInstance *sin, int level)
     qxl->rom->compression_level = cpu_to_le32(level);
 }
 
-static void interface_set_mm_time(QXLInstance *sin, uint32_t mm_time)
+void interface_set_mm_time(QXLInstance *sin, uint32_t mm_time)
 {
     QXLDevice *qxl = container_of(sin, QXLDevice, ssd.qxl);
 
@@ -479,7 +260,7 @@ static void interface_set_mm_time(QXLInstance *sin, uint32_t mm_time)
     qxl->rom->mm_clock = cpu_to_le32(mm_time);
 }
 
-static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info)
+void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info)
 {
     QXLDevice *qxl = container_of(sin, QXLDevice, ssd.qxl);
 
@@ -493,113 +274,15 @@ static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info)
     info->n_surfaces = NUM_SURFACES;
 }
 
-static const char *qxl_mode_to_string(int mode)
-{
-    switch (mode) {
-    case QXL_MODE_COMPAT:
-        return "compat";
-    case QXL_MODE_NATIVE:
-        return "native";
-    case QXL_MODE_UNDEFINED:
-        return "undefined";
-    case QXL_MODE_VGA:
-        return "vga";
-    }
-    return "INVALID";
-}
-
-static const char *io_port_to_string(uint32_t io_port)
-{
-    if (io_port >= QXL_IO_RANGE_SIZE) {
-        return "out of range";
-    }
-    static const char *io_port_to_string[QXL_IO_RANGE_SIZE + 1] = {
-        [QXL_IO_NOTIFY_CMD]             = "QXL_IO_NOTIFY_CMD",
-        [QXL_IO_NOTIFY_CURSOR]          = "QXL_IO_NOTIFY_CURSOR",
-        [QXL_IO_UPDATE_AREA]            = "QXL_IO_UPDATE_AREA",
-        [QXL_IO_UPDATE_IRQ]             = "QXL_IO_UPDATE_IRQ",
-        [QXL_IO_NOTIFY_OOM]             = "QXL_IO_NOTIFY_OOM",
-        [QXL_IO_RESET]                  = "QXL_IO_RESET",
-        [QXL_IO_SET_MODE]               = "QXL_IO_SET_MODE",
-        [QXL_IO_LOG]                    = "QXL_IO_LOG",
-        [QXL_IO_MEMSLOT_ADD]            = "QXL_IO_MEMSLOT_ADD",
-        [QXL_IO_MEMSLOT_DEL]            = "QXL_IO_MEMSLOT_DEL",
-        [QXL_IO_DETACH_PRIMARY]         = "QXL_IO_DETACH_PRIMARY",
-        [QXL_IO_ATTACH_PRIMARY]         = "QXL_IO_ATTACH_PRIMARY",
-        [QXL_IO_CREATE_PRIMARY]         = "QXL_IO_CREATE_PRIMARY",
-        [QXL_IO_DESTROY_PRIMARY]        = "QXL_IO_DESTROY_PRIMARY",
-        [QXL_IO_DESTROY_SURFACE_WAIT]   = "QXL_IO_DESTROY_SURFACE_WAIT",
-        [QXL_IO_DESTROY_ALL_SURFACES]   = "QXL_IO_DESTROY_ALL_SURFACES",
-        [QXL_IO_UPDATE_AREA_ASYNC]      = "QXL_IO_UPDATE_AREA_ASYNC",
-        [QXL_IO_MEMSLOT_ADD_ASYNC]      = "QXL_IO_MEMSLOT_ADD_ASYNC",
-        [QXL_IO_CREATE_PRIMARY_ASYNC]   = "QXL_IO_CREATE_PRIMARY_ASYNC",
-        [QXL_IO_DESTROY_PRIMARY_ASYNC]  = "QXL_IO_DESTROY_PRIMARY_ASYNC",
-        [QXL_IO_DESTROY_SURFACE_ASYNC]  = "QXL_IO_DESTROY_SURFACE_ASYNC",
-        [QXL_IO_DESTROY_ALL_SURFACES_ASYNC]
-                                        = "QXL_IO_DESTROY_ALL_SURFACES_ASYNC",
-        [QXL_IO_FLUSH_SURFACES_ASYNC]   = "QXL_IO_FLUSH_SURFACES_ASYNC",
-        [QXL_IO_FLUSH_RELEASE]          = "QXL_IO_FLUSH_RELEASE",
-    };
-    return io_port_to_string[io_port];
-}
-
-static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext)
+int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext)
 {
     QXLDevice *qxl = container_of(sin, QXLDevice, ssd.qxl);
-    SimpleSpiceUpdate *update;
-    QXLCommandRing *ring;
-    QXLCommand *cmd;
-    int notify, ret;
-
-    trace_qxl_ring_command_check(qxl->id, qxl_mode_to_string(qxl->pci.mode));
 
-    switch (qxl->pci.mode) {
-    case QXL_MODE_VGA:
-        ret = false;
-        qemu_mutex_lock(&qxl->ssd.lock);
-        if (qxl->ssd.update != NULL) {
-            update = qxl->ssd.update;
-            qxl->ssd.update = NULL;
-            *ext = update->ext;
-            ret = true;
-        }
-        qemu_mutex_unlock(&qxl->ssd.lock);
-        if (ret) {
-            trace_qxl_ring_command_get(qxl->id, qxl_mode_to_string(qxl->pci.mode));
-            qxl_log_command(qxl, "vga", ext);
-        }
-        return ret;
-    case QXL_MODE_COMPAT:
-    case QXL_MODE_NATIVE:
-    case QXL_MODE_UNDEFINED:
-        ring = &qxl->ram->cmd_ring;
-        if (qxl->pci.guest_bug || SPICE_RING_IS_EMPTY(ring)) {
-            return false;
-        }
-        SPICE_RING_CONS_ITEM(qxl, ring, cmd);
-        if (!cmd) {
-            return false;
-        }
-        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);
-        trace_qxl_ring_command_get(qxl->id, qxl_mode_to_string(qxl->pci.mode));
-        return true;
-    default:
-        return false;
-    }
+    return interface_get_command_pci(qxl, ext);
 }
 
 /* called from spice server thread context only */
-static int interface_req_cmd_notification(QXLInstance *sin)
+int interface_req_cmd_notification(QXLInstance *sin)
 {
     QXLDevice *qxl = container_of(sin, QXLDevice, ssd.qxl);
     int wait = 1;
@@ -620,48 +303,7 @@ static int interface_req_cmd_notification(QXLInstance *sin)
 }
 
 /* called from spice server thread context only */
-static inline void qxl_push_free_res(QXLDevice *d, int flush)
-{
-    QXLReleaseRing *ring = &d->ram->release_ring;
-    uint64_t *item;
-    int notify;
-
-#define QXL_FREE_BUNCH_SIZE 32
-
-    if (ring->prod - ring->cons + 1 == ring->num_items) {
-        /* ring full -- can't push */
-        return;
-    }
-    if (!flush && d->oom_running) {
-        /* collect everything from oom handler before pushing */
-        return;
-    }
-    if (!flush && d->num_free_res < QXL_FREE_BUNCH_SIZE) {
-        /* collect a bit more before pushing */
-        return;
-    }
-
-    SPICE_RING_PUSH(ring, notify);
-    trace_qxl_ring_res_push(d->id, qxl_mode_to_string(d->pci.mode),
-           d->guest_surfaces.count, d->num_free_res,
-           d->last_release, notify ? "yes" : "no");
-    trace_qxl_ring_res_push_rest(d->id, ring->prod - ring->cons,
-           ring->num_items, ring->prod, ring->cons);
-    if (notify) {
-        qxl_send_events(d, QXL_INTERRUPT_DISPLAY);
-    }
-    SPICE_RING_PROD_ITEM(d, ring, item);
-    if (!item) {
-        return;
-    }
-    *item = 0;
-    d->num_free_res = 0;
-    d->last_release = NULL;
-    qxl_ring_set_dirty(d);
-}
-
-/* called from spice server thread context only */
-static void interface_release_resource(QXLInstance *sin,
+void interface_release_resource(QXLInstance *sin,
                                        struct QXLReleaseInfoExt ext)
 {
     QXLDevice *qxl = container_of(sin, QXLDevice, ssd.qxl);
@@ -704,50 +346,15 @@ static void interface_release_resource(QXLInstance *sin,
 }
 
 /* called from spice server thread context only */
-static int interface_get_cursor_command(QXLInstance *sin, struct QXLCommandExt *ext)
+int interface_get_cursor_command(QXLInstance *sin, struct QXLCommandExt *ext)
 {
     QXLDevice *qxl = container_of(sin, QXLDevice, ssd.qxl);
-    QXLCursorRing *ring;
-    QXLCommand *cmd;
-    int notify;
-
-    trace_qxl_ring_cursor_check(qxl->id, qxl_mode_to_string(qxl->pci.mode));
 
-    switch (qxl->pci.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(qxl, ring, cmd);
-        if (!cmd) {
-            return false;
-        }
-        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);
-        }
-        trace_qxl_ring_cursor_get(qxl->id, qxl_mode_to_string(qxl->pci.mode));
-        return true;
-    default:
-        return false;
-    }
+    return interface_get_cursor_command_pci(qxl, ext);
 }
 
 /* called from spice server thread context only */
-static int interface_req_cursor_notification(QXLInstance *sin)
+int interface_req_cursor_notification(QXLInstance *sin)
 {
     QXLDevice *qxl = container_of(sin, QXLDevice, ssd.qxl);
     int wait = 1;
@@ -768,7 +375,7 @@ static int interface_req_cursor_notification(QXLInstance *sin)
 }
 
 /* called from spice server thread context */
-static void interface_notify_update(QXLInstance *sin, uint32_t update_id)
+void interface_notify_update(QXLInstance *sin, uint32_t update_id)
 {
     /*
      * Called by spice-server as a result of a QXL_CMD_UPDATE which is not in
@@ -780,7 +387,7 @@ static void interface_notify_update(QXLInstance *sin, uint32_t update_id)
 }
 
 /* called from spice server thread context only */
-static int interface_flush_resources(QXLInstance *sin)
+int interface_flush_resources(QXLInstance *sin)
 {
     QXLDevice *qxl = container_of(sin, QXLDevice, ssd.qxl);
     int ret;
@@ -792,52 +399,8 @@ static int interface_flush_resources(QXLInstance *sin)
     return ret;
 }
 
-static void qxl_create_guest_primary_complete(QXLDevice *d);
-
-/* called from spice server thread context only */
-static void interface_async_complete_io(QXLDevice *qxl, QXLCookie *cookie)
-{
-    uint32_t current_async;
-
-    qemu_mutex_lock(&qxl->async_lock);
-    current_async = qxl->current_async;
-    qxl->current_async = QXL_UNDEFINED_IO;
-    qemu_mutex_unlock(&qxl->async_lock);
-
-    trace_qxl_interface_async_complete_io(qxl->id, current_async, cookie);
-    if (!cookie) {
-        fprintf(stderr, "qxl: %s: error, cookie is NULL\n", __func__);
-        return;
-    }
-    if (cookie && current_async != cookie->io) {
-        fprintf(stderr,
-                "qxl: %s: error: current_async = %d != %"
-                PRId64 " = cookie->io\n", __func__, current_async, cookie->io);
-    }
-    switch (current_async) {
-    case QXL_IO_MEMSLOT_ADD_ASYNC:
-    case QXL_IO_DESTROY_PRIMARY_ASYNC:
-    case QXL_IO_UPDATE_AREA_ASYNC:
-    case QXL_IO_FLUSH_SURFACES_ASYNC:
-        break;
-    case QXL_IO_CREATE_PRIMARY_ASYNC:
-        qxl_create_guest_primary_complete(qxl);
-        break;
-    case QXL_IO_DESTROY_ALL_SURFACES_ASYNC:
-        qxl_spice_destroy_surfaces_complete(qxl);
-        break;
-    case QXL_IO_DESTROY_SURFACE_ASYNC:
-        qxl_spice_destroy_surface_wait_complete(qxl, cookie->u.surface_id);
-        break;
-    default:
-        fprintf(stderr, "qxl: %s: unexpected current_async %d\n", __func__,
-                current_async);
-    }
-    qxl_send_events(qxl, QXL_INTERRUPT_IO_CMD);
-}
-
 /* called from spice server thread context only */
-static void interface_update_area_complete(QXLInstance *sin,
+void interface_update_area_complete(QXLInstance *sin,
         uint32_t surface_id,
         QXLRect *dirty, uint32_t num_updated_rects)
 {
@@ -881,7 +444,7 @@ static void interface_update_area_complete(QXLInstance *sin,
 }
 
 /* called from spice server thread context only */
-static void interface_async_complete(QXLInstance *sin, uint64_t cookie_token)
+void interface_async_complete(QXLInstance *sin, uint64_t cookie_token)
 {
     QXLDevice *qxl = container_of(sin, QXLDevice, ssd.qxl);
     QXLCookie *cookie = (QXLCookie *)(uintptr_t)cookie_token;
@@ -900,1229 +463,3 @@ static void interface_async_complete(QXLInstance *sin, uint64_t cookie_token)
         g_free(cookie);
     }
 }
-
-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,
-
-    .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,
-
-    /* the callbacks below are called from spice server thread context */
-    .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,
-    .async_complete          = interface_async_complete,
-    .update_area_complete    = interface_update_area_complete,
-};
-
-static void qxl_enter_vga_mode(QXLDevice *d)
-{
-    if (d->pci.mode == QXL_MODE_VGA) {
-        return;
-    }
-    trace_qxl_enter_vga_mode(d->id);
-    qemu_spice_create_host_primary(&d->ssd);
-    d->pci.mode = QXL_MODE_VGA;
-    memset(&d->ssd.dirty, 0, sizeof(d->ssd.dirty));
-    vga_dirty_log_start(&d->pci.vga);
-}
-
-static void qxl_exit_vga_mode(QXLDevice *d)
-{
-    if (d->pci.mode != QXL_MODE_VGA) {
-        return;
-    }
-    trace_qxl_exit_vga_mode(d->id);
-    vga_dirty_log_stop(&d->pci.vga);
-    qxl_destroy_primary(d, QXL_SYNC);
-}
-
-static void qxl_update_irq(QXLDevice *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.pci.irq[0], level);
-    qxl_ring_set_dirty(d);
-}
-
-static void qxl_check_state(QXLDevice *d)
-{
-    QXLRam *ram = d->ram;
-
-    assert(!d->ssd.running || SPICE_RING_IS_EMPTY(&ram->cmd_ring));
-    assert(!d->ssd.running || SPICE_RING_IS_EMPTY(&ram->cursor_ring));
-}
-
-static void qxl_reset_state(QXLDevice *d)
-{
-    QXLRom *rom = d->rom;
-
-    qxl_check_state(d);
-    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(QXLDevice *d)
-{
-    trace_qxl_soft_reset(d->id);
-    qxl_check_state(d);
-    qxl_clear_guest_bug(d);
-    d->current_async = QXL_UNDEFINED_IO;
-
-    if (d->id == 0) {
-        qxl_enter_vga_mode(d);
-    } else {
-        d->pci.mode = QXL_MODE_UNDEFINED;
-    }
-}
-
-static void qxl_hard_reset(QXLDevice *d, int loadvm)
-{
-    trace_qxl_hard_reset(d->id, loadvm);
-
-    qxl_spice_reset_cursor(d);
-    qxl_spice_reset_image_cache(d);
-    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);
-}
-
-static void qxl_reset_handler(DeviceState *dev)
-{
-    QXLDevice *d = (QXLDevice *) 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;
-    QXLDevice *qxl = (QXLDevice *) container_of(vga, PCIQXLDevice, vga);
-
-    trace_qxl_io_write_vga(qxl->id, qxl_mode_to_string(qxl->pci.mode), addr, val);
-    if (qxl->pci.mode != QXL_MODE_VGA) {
-        qxl_destroy_primary(qxl, QXL_SYNC);
-        qxl_soft_reset(qxl);
-    }
-    vga_ioport_write(opaque, addr, val);
-}
-
-static const MemoryRegionPortio qxl_vga_portio_list[] = {
-    { 0x04,  2, 1, .read  = vga_ioport_read,
-                   .write = qxl_vga_ioport_write }, /* 3b4 */
-    { 0x0a,  1, 1, .read  = vga_ioport_read,
-                   .write = qxl_vga_ioport_write }, /* 3ba */
-    { 0x10, 16, 1, .read  = vga_ioport_read,
-                   .write = qxl_vga_ioport_write }, /* 3c0 */
-    { 0x24,  2, 1, .read  = vga_ioport_read,
-                   .write = qxl_vga_ioport_write }, /* 3d4 */
-    { 0x2a,  1, 1, .read  = vga_ioport_read,
-                   .write = qxl_vga_ioport_write }, /* 3da */
-    PORTIO_END_OF_LIST(),
-};
-
-static int qxl_add_memslot(QXLDevice *d, uint32_t slot_id, uint64_t delta,
-                           qxl_async_io async)
-{
-    static const int regions[] = {
-        QXL_RAM_RANGE_INDEX,
-        QXL_VRAM_RANGE_INDEX,
-        QXL_VRAM64_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);
-
-    trace_qxl_memslot_add_guest(d->id, slot_id, guest_start, guest_end);
-
-    if (slot_id >= NUM_MEMSLOTS) {
-        qxl_set_guest_bug(d, "%s: slot_id >= NUM_MEMSLOTS %d >= %d", __func__,
-                      slot_id, NUM_MEMSLOTS);
-        return 1;
-    }
-    if (guest_start > guest_end) {
-        qxl_set_guest_bug(d, "%s: guest_start > guest_end 0x%" PRIx64
-                         " > 0x%" PRIx64, __func__, guest_start, guest_end);
-        return 1;
-    }
-
-    for (i = 0; i < ARRAY_SIZE(regions); i++) {
-        pci_region = regions[i];
-        pci_start = d->pci.pci.io_regions[pci_region].addr;
-        pci_end = pci_start + d->pci.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;
-    }
-    if (i == ARRAY_SIZE(regions)) {
-        qxl_set_guest_bug(d, "%s: finished loop without match", __func__);
-        return 1;
-    }
-
-    switch (pci_region) {
-    case QXL_RAM_RANGE_INDEX:
-        virt_start = (intptr_t)memory_region_get_ram_ptr(&d->pci.vga.vram);
-        break;
-    case QXL_VRAM_RANGE_INDEX:
-    case 4 /* vram 64bit */:
-        virt_start = (intptr_t)memory_region_get_ram_ptr(&d->pci.vram_bar);
-        break;
-    default:
-        /* should not happen */
-        qxl_set_guest_bug(d, "%s: pci_region = %d", __func__, pci_region);
-        return 1;
-    }
-
-    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;
-    qxl_rom_set_dirty(d);
-
-    qemu_spice_add_memslot(&d->ssd, &memslot, async);
-    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;
-    return 0;
-}
-
-static void qxl_del_memslot(QXLDevice *d, uint32_t slot_id)
-{
-    qemu_spice_del_memslot(&d->ssd, MEMSLOT_GROUP_HOST, slot_id);
-    d->guest_slots[slot_id].active = 0;
-}
-
-static void qxl_reset_memslots(QXLDevice *d)
-{
-    qxl_spice_reset_memslots(d);
-    memset(&d->guest_slots, 0, sizeof(d->guest_slots));
-}
-
-static void qxl_reset_surfaces(QXLDevice *d)
-{
-    trace_qxl_reset_surfaces(d->id);
-    d->pci.mode = QXL_MODE_UNDEFINED;
-    qxl_spice_destroy_surfaces(d, QXL_SYNC);
-}
-
-/* can be also called from spice server thread context */
-void *qxl_phys2virt(QXLDevice *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 *)(intptr_t)offset;
-    case MEMSLOT_GROUP_GUEST:
-        if (slot >= NUM_MEMSLOTS) {
-            qxl_set_guest_bug(qxl, "slot too large %d >= %d", slot,
-                              NUM_MEMSLOTS);
-            return NULL;
-        }
-        if (!qxl->guest_slots[slot].active) {
-            qxl_set_guest_bug(qxl, "inactive slot %d\n", slot);
-            return NULL;
-        }
-        if (offset < qxl->guest_slots[slot].delta) {
-            qxl_set_guest_bug(qxl,
-                          "slot %d offset %"PRIu64" < delta %"PRIu64"\n",
-                          slot, offset, qxl->guest_slots[slot].delta);
-            return NULL;
-        }
-        offset -= qxl->guest_slots[slot].delta;
-        if (offset > qxl->guest_slots[slot].size) {
-            qxl_set_guest_bug(qxl,
-                          "slot %d offset %"PRIu64" > size %"PRIu64"\n",
-                          slot, offset, qxl->guest_slots[slot].size);
-            return NULL;
-        }
-        return qxl->guest_slots[slot].ptr + offset;
-    }
-    return NULL;
-}
-
-static void qxl_create_guest_primary_complete(QXLDevice *qxl)
-{
-    /* for local rendering */
-    qxl_render_resize(qxl);
-}
-
-static void qxl_create_guest_primary(QXLDevice *qxl, int loadvm,
-                                     qxl_async_io async)
-{
-    QXLDevSurfaceCreate surface;
-    QXLSurfaceCreate *sc = &qxl->guest_primary.surface;
-    int size;
-    int requested_height = le32_to_cpu(sc->height);
-    int requested_stride = le32_to_cpu(sc->stride);
-
-    size = abs(requested_stride) * requested_height;
-    if (size > qxl->pci.vgamem_size) {
-        qxl_set_guest_bug(qxl, "%s: requested primary larger then framebuffer"
-                               " size", __func__);
-        return;
-    }
-
-    if (qxl->pci.mode == QXL_MODE_NATIVE) {
-        qxl_set_guest_bug(qxl, "%s: nop since already in QXL_MODE_NATIVE",
-                      __func__);
-    }
-    qxl_exit_vga_mode(qxl);
-
-    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);
-    trace_qxl_create_guest_primary(qxl->id, sc->width, sc->height, sc->mem,
-                                   sc->format, sc->position);
-    trace_qxl_create_guest_primary_rest(qxl->id, sc->stride, sc->type,
-                                        sc->flags);
-
-    surface.mouse_mode = true;
-    surface.group_id   = MEMSLOT_GROUP_GUEST;
-    if (loadvm) {
-        surface.flags |= QXL_SURF_FLAG_KEEP_DATA;
-    }
-
-    qxl->pci.mode = QXL_MODE_NATIVE;
-    qxl->cmdflags = 0;
-    qemu_spice_create_primary_surface(&qxl->ssd, 0, &surface, async);
-
-    if (async == QXL_SYNC) {
-        qxl_create_guest_primary_complete(qxl);
-    }
-}
-
-/* return 1 if surface destoy was initiated (in QXL_ASYNC case) or
- * done (in QXL_SYNC case), 0 otherwise. */
-static int qxl_destroy_primary(QXLDevice *d, qxl_async_io async)
-{
-    if (d->pci.mode == QXL_MODE_UNDEFINED) {
-        return 0;
-    }
-    trace_qxl_destroy_primary(d->id);
-    d->pci.mode = QXL_MODE_UNDEFINED;
-    qemu_spice_destroy_primary_surface(&d->ssd, 0, async);
-    qxl_spice_reset_cursor(d);
-    return 1;
-}
-
-static void qxl_set_mode(QXLDevice *d, int modenr, int loadvm)
-{
-    pcibus_t start = d->pci.pci.io_regions[QXL_RAM_RANGE_INDEX].addr;
-    pcibus_t end   = d->pci.pci.io_regions[QXL_RAM_RANGE_INDEX].size + start;
-    QXLMode *mode = d->modes->modes + modenr;
-    uint64_t devmem = d->pci.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,
-        .flags      = loadvm ? QXL_SURF_FLAG_KEEP_DATA : 0,
-        .mouse_mode = true,
-        .mem        = devmem + d->shadow_rom.draw_area_offset,
-    };
-
-    trace_qxl_set_mode(d->id, modenr, mode->x_res, mode->y_res, mode->bits,
-                       devmem);
-    if (!loadvm) {
-        qxl_hard_reset(d, 0);
-    }
-
-    d->guest_slots[0].slot = slot;
-    assert(qxl_add_memslot(d, 0, devmem, QXL_SYNC) == 0);
-
-    d->guest_primary.surface = surface;
-    qxl_create_guest_primary(d, 0, QXL_SYNC);
-
-    d->pci.mode = QXL_MODE_COMPAT;
-    d->cmdflags = QXL_COMMAND_FLAG_COMPAT;
-#ifdef QXL_COMMAND_FLAG_COMPAT_16BPP /* new in spice 0.6.1 */
-    if (mode->bits == 16) {
-        d->cmdflags |= QXL_COMMAND_FLAG_COMPAT_16BPP;
-    }
-#endif
-    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, target_phys_addr_t addr,
-                         uint64_t val, unsigned size)
-{
-    QXLDevice *d = opaque;
-    uint32_t io_port = addr;
-    qxl_async_io async = QXL_SYNC;
-    uint32_t orig_io_port = io_port;
-
-    if (d->pci.guest_bug && !io_port == QXL_IO_RESET) {
-        return;
-    }
-
-    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:
-    case QXL_IO_UPDATE_IRQ:
-    case QXL_IO_LOG:
-    case QXL_IO_MEMSLOT_ADD_ASYNC:
-    case QXL_IO_CREATE_PRIMARY_ASYNC:
-        break;
-    default:
-        if (d->pci.mode != QXL_MODE_VGA) {
-            break;
-        }
-        trace_qxl_io_unexpected_vga_mode(d->id,
-            io_port, io_port_to_string(io_port));
-        /* be nice to buggy guest drivers */
-        if (io_port >= QXL_IO_UPDATE_AREA_ASYNC &&
-            io_port <= QXL_IO_DESTROY_ALL_SURFACES_ASYNC) {
-            qxl_send_events(d, QXL_INTERRUPT_IO_CMD);
-        }
-        return;
-    }
-
-    /* we change the io_port to avoid ifdeffery in the main switch */
-    orig_io_port = io_port;
-    switch (io_port) {
-    case QXL_IO_UPDATE_AREA_ASYNC:
-        io_port = QXL_IO_UPDATE_AREA;
-        goto async_common;
-    case QXL_IO_MEMSLOT_ADD_ASYNC:
-        io_port = QXL_IO_MEMSLOT_ADD;
-        goto async_common;
-    case QXL_IO_CREATE_PRIMARY_ASYNC:
-        io_port = QXL_IO_CREATE_PRIMARY;
-        goto async_common;
-    case QXL_IO_DESTROY_PRIMARY_ASYNC:
-        io_port = QXL_IO_DESTROY_PRIMARY;
-        goto async_common;
-    case QXL_IO_DESTROY_SURFACE_ASYNC:
-        io_port = QXL_IO_DESTROY_SURFACE_WAIT;
-        goto async_common;
-    case QXL_IO_DESTROY_ALL_SURFACES_ASYNC:
-        io_port = QXL_IO_DESTROY_ALL_SURFACES;
-        goto async_common;
-    case QXL_IO_FLUSH_SURFACES_ASYNC:
-async_common:
-        async = QXL_ASYNC;
-        qemu_mutex_lock(&d->async_lock);
-        if (d->current_async != QXL_UNDEFINED_IO) {
-            qxl_set_guest_bug(d, "%d async started before last (%d) complete",
-                io_port, d->current_async);
-            qemu_mutex_unlock(&d->async_lock);
-            return;
-        }
-        d->current_async = orig_io_port;
-        qemu_mutex_unlock(&d->async_lock);
-        break;
-    default:
-        break;
-    }
-    trace_qxl_io_write(d->id, qxl_mode_to_string(d->pci.mode), addr, val, size,
-                       async);
-
-    switch (io_port) {
-    case QXL_IO_UPDATE_AREA:
-    {
-        QXLCookie *cookie = NULL;
-        QXLRect update = d->ram->update_area;
-
-        if (async == QXL_ASYNC) {
-            cookie = qxl_cookie_new(QXL_COOKIE_TYPE_IO,
-                                    QXL_IO_UPDATE_AREA_ASYNC);
-            cookie->u.area = update;
-        }
-        qxl_spice_update_area(d, d->ram->update_surface,
-                              cookie ? &cookie->u.area : &update,
-                              NULL, 0, 0, async, cookie);
-        break;
-    }
-    case QXL_IO_NOTIFY_CMD:
-        qemu_spice_wakeup(&d->ssd);
-        break;
-    case QXL_IO_NOTIFY_CURSOR:
-        qemu_spice_wakeup(&d->ssd);
-        break;
-    case QXL_IO_UPDATE_IRQ:
-        qxl_update_irq(d);
-        break;
-    case QXL_IO_NOTIFY_OOM:
-        if (!SPICE_RING_IS_EMPTY(&d->ram->release_ring)) {
-            break;
-        }
-        d->oom_running = 1;
-        qxl_spice_oom(d);
-        d->oom_running = 0;
-        break;
-    case QXL_IO_SET_MODE:
-        qxl_set_mode(d, val, 0);
-        break;
-    case QXL_IO_LOG:
-        if (d->pci.guestdebug) {
-            fprintf(stderr, "qxl/guest-%d: %" PRId64 ": %s", d->id,
-                    qemu_get_clock_ns(vm_clock), d->ram->log_buf);
-        }
-        break;
-    case QXL_IO_RESET:
-        qxl_hard_reset(d, 0);
-        break;
-    case QXL_IO_MEMSLOT_ADD:
-        if (val >= NUM_MEMSLOTS) {
-            qxl_set_guest_bug(d, "QXL_IO_MEMSLOT_ADD: val out of range");
-            break;
-        }
-        if (d->guest_slots[val].active) {
-            qxl_set_guest_bug(d,
-                        "QXL_IO_MEMSLOT_ADD: memory slot already active");
-            break;
-        }
-        d->guest_slots[val].slot = d->ram->mem_slot;
-        qxl_add_memslot(d, val, 0, async);
-        break;
-    case QXL_IO_MEMSLOT_DEL:
-        if (val >= NUM_MEMSLOTS) {
-            qxl_set_guest_bug(d, "QXL_IO_MEMSLOT_DEL: val out of range");
-            break;
-        }
-        qxl_del_memslot(d, val);
-        break;
-    case QXL_IO_CREATE_PRIMARY:
-        if (val != 0) {
-            qxl_set_guest_bug(d, "QXL_IO_CREATE_PRIMARY (async=%d): val != 0",
-                          async);
-            goto cancel_async;
-        }
-        d->guest_primary.surface = d->ram->create_surface;
-        qxl_create_guest_primary(d, 0, async);
-        break;
-    case QXL_IO_DESTROY_PRIMARY:
-        if (val != 0) {
-            qxl_set_guest_bug(d, "QXL_IO_DESTROY_PRIMARY (async=%d): val != 0",
-                          async);
-            goto cancel_async;
-        }
-        if (!qxl_destroy_primary(d, async)) {
-            trace_qxl_io_destroy_primary_ignored(d->id,
-                                                 qxl_mode_to_string(d->pci.mode));
-            goto cancel_async;
-        }
-        break;
-    case QXL_IO_DESTROY_SURFACE_WAIT:
-        if (val >= NUM_SURFACES) {
-            qxl_set_guest_bug(d, "QXL_IO_DESTROY_SURFACE (async=%d):"
-                             "%" PRIu64 " >= NUM_SURFACES", async, val);
-            goto cancel_async;
-        }
-        qxl_spice_destroy_surface_wait(d, val, async);
-        break;
-    case QXL_IO_FLUSH_RELEASE: {
-        QXLReleaseRing *ring = &d->ram->release_ring;
-        if (ring->prod - ring->cons + 1 == ring->num_items) {
-            fprintf(stderr,
-                "ERROR: no flush, full release ring [p%d,%dc]\n",
-                ring->prod, ring->cons);
-        }
-        qxl_push_free_res(d, 1 /* flush */);
-        break;
-    }
-    case QXL_IO_FLUSH_SURFACES_ASYNC:
-        qxl_spice_flush_surfaces_async(d);
-        break;
-    case QXL_IO_DESTROY_ALL_SURFACES:
-        d->pci.mode = QXL_MODE_UNDEFINED;
-        qxl_spice_destroy_surfaces(d, async);
-        break;
-    default:
-        qxl_set_guest_bug(d, "%s: unexpected ioport=0x%x\n", __func__, io_port);
-    }
-    return;
-cancel_async:
-    if (async) {
-        qxl_send_events(d, QXL_INTERRUPT_IO_CMD);
-        qemu_mutex_lock(&d->async_lock);
-        d->current_async = QXL_UNDEFINED_IO;
-        qemu_mutex_unlock(&d->async_lock);
-    }
-}
-
-static uint64_t ioport_read(void *opaque, target_phys_addr_t addr,
-                            unsigned size)
-{
-    QXLDevice *d = opaque;
-
-    trace_qxl_io_read_unexpected(d->id);
-    return 0xff;
-}
-
-static const MemoryRegionOps qxl_io_ops = {
-    .read = ioport_read,
-    .write = ioport_write,
-    .valid = {
-        .min_access_size = 1,
-        .max_access_size = 1,
-    },
-};
-
-static void pipe_read(void *opaque)
-{
-    QXLDevice *d = opaque;
-    char dummy;
-    int len;
-
-    do {
-        len = read(d->pipe[0], &dummy, sizeof(dummy));
-    } while (len == sizeof(dummy));
-    qxl_update_irq(d);
-}
-
-static void qxl_send_events(QXLDevice *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 (qemu_thread_is_self(&d->main)) {
-        qxl_update_irq(d);
-    } else {
-        if (write(d->pipe[1], d, 1) != 1) {
-            dprint(d, 1, "%s: write to pipe failed\n", __func__);
-        }
-    }
-}
-
-static void init_pipe_signaling(QXLDevice *d)
-{
-    if (pipe(d->pipe) < 0) {
-        fprintf(stderr, "%s:%s: qxl pipe creation failed\n",
-                __FILE__, __func__);
-        exit(1);
-    }
-    fcntl(d->pipe[0], F_SETFL, O_NONBLOCK);
-    fcntl(d->pipe[1], F_SETFL, O_NONBLOCK);
-    fcntl(d->pipe[0], F_SETOWN, getpid());
-
-    qemu_thread_get_self(&d->main);
-    qemu_set_fd_handler(d->pipe[0], pipe_read, NULL, d);
-}
-
-/* graphics console */
-
-static void qxl_hw_update(void *opaque)
-{
-    QXLDevice *qxl = opaque;
-    VGACommonState *vga = &qxl->pci.vga;
-
-    switch (qxl->pci.mode) {
-    case QXL_MODE_VGA:
-        vga->update(vga);
-        break;
-    case QXL_MODE_COMPAT:
-    case QXL_MODE_NATIVE:
-        qxl_render_update(qxl);
-        break;
-    default:
-        break;
-    }
-}
-
-static void qxl_hw_invalidate(void *opaque)
-{
-    QXLDevice *qxl = opaque;
-    VGACommonState *vga = &qxl->pci.vga;
-
-    vga->invalidate(vga);
-}
-
-static void qxl_hw_screen_dump(void *opaque, const char *filename, bool cswitch)
-{
-    QXLDevice *qxl = opaque;
-    VGACommonState *vga = &qxl->pci.vga;
-
-    switch (qxl->pci.mode) {
-    case QXL_MODE_COMPAT:
-    case QXL_MODE_NATIVE:
-        qxl_render_update(qxl);
-        ppm_save(filename, qxl->ssd.ds->surface);
-        break;
-    case QXL_MODE_VGA:
-        vga->screen_dump(vga, filename, cswitch);
-        break;
-    default:
-        break;
-    }
-}
-
-static void qxl_hw_text_update(void *opaque, console_ch_t *chardata)
-{
-    QXLDevice *qxl = opaque;
-    VGACommonState *vga = &qxl->pci.vga;
-
-    if (qxl->pci.mode == QXL_MODE_VGA) {
-        vga->text_update(vga, chardata);
-        return;
-    }
-}
-
-static void qxl_dirty_surfaces(QXLDevice *qxl)
-{
-    intptr_t vram_start;
-    int i;
-
-    if (qxl->pci.mode != QXL_MODE_NATIVE && qxl->pci.mode != QXL_MODE_COMPAT) {
-        return;
-    }
-
-    /* dirty the primary surface */
-    qxl_set_dirty(&qxl->pci.vga.vram, qxl->shadow_rom.draw_area_offset,
-                  qxl->shadow_rom.surface0_area_size);
-
-    vram_start =  (intptr_t)memory_region_get_ram_ptr(&qxl->pci.vram_bar);
-
-    /* dirty the off-screen surfaces */
-    for (i = 0; i < NUM_SURFACES; i++) {
-        QXLSurfaceCmd *cmd;
-        intptr_t surface_offset;
-        int surface_size;
-
-        if (qxl->guest_surfaces.cmds[i] == 0) {
-            continue;
-        }
-
-        cmd = qxl_phys2virt(qxl, qxl->guest_surfaces.cmds[i],
-                            MEMSLOT_GROUP_GUEST);
-        assert(cmd);
-        assert(cmd->type == QXL_SURFACE_CMD_CREATE);
-        surface_offset = (intptr_t)qxl_phys2virt(qxl,
-                                                 cmd->u.surface_create.data,
-                                                 MEMSLOT_GROUP_GUEST);
-        assert(surface_offset);
-        surface_offset -= vram_start;
-        surface_size = cmd->u.surface_create.height *
-                       abs(cmd->u.surface_create.stride);
-        trace_qxl_surfaces_dirty(qxl->id, i, (int)surface_offset, surface_size);
-        qxl_set_dirty(&qxl->pci.vram_bar, surface_offset, surface_size);
-    }
-}
-
-static void qxl_vm_change_state_handler(void *opaque, int running,
-                                        RunState state)
-{
-    QXLDevice *qxl = opaque;
-    qemu_spice_vm_change_state_handler(&qxl->ssd, running, state);
-
-    if (running) {
-        /*
-         * if qxl_send_events was called from spice server context before
-         * migration ended, qxl_update_irq for these events might not have been
-         * called
-         */
-         qxl_update_irq(qxl);
-    } else {
-        /* make sure surfaces are saved before migration */
-        qxl_dirty_surfaces(qxl);
-    }
-}
-
-/* display change listener */
-
-static void display_update(struct DisplayState *ds, int x, int y, int w, int h)
-{
-    if (qxl0->pci.mode == QXL_MODE_VGA) {
-        qemu_spice_display_update(&qxl0->ssd, x, y, w, h);
-    }
-}
-
-static void display_resize(struct DisplayState *ds)
-{
-    if (qxl0->pci.mode == QXL_MODE_VGA) {
-        qemu_spice_display_resize(&qxl0->ssd);
-    }
-}
-
-static void display_refresh(struct DisplayState *ds)
-{
-    if (qxl0->pci.mode == QXL_MODE_VGA) {
-        qemu_spice_display_refresh(&qxl0->ssd);
-    } else {
-        qemu_mutex_lock(&qxl0->ssd.lock);
-        qemu_spice_cursor_refresh_unlocked(&qxl0->ssd);
-        qemu_mutex_unlock(&qxl0->ssd.lock);
-    }
-}
-
-static DisplayChangeListener display_listener = {
-    .dpy_update  = display_update,
-    .dpy_resize  = display_resize,
-    .dpy_refresh = display_refresh,
-};
-
-static void qxl_init_ramsize(QXLDevice *qxl)
-{
-    /* vga mode framebuffer / primary surface (bar 0, first part) */
-    if (qxl->pci.vgamem_size_mb < 8) {
-        qxl->pci.vgamem_size_mb = 8;
-    }
-    qxl->pci.vgamem_size = qxl->pci.vgamem_size_mb * 1024 * 1024;
-
-    /* vga ram (bar 0, total) */
-    if (qxl->ram_size_mb != -1) {
-        qxl->pci.vga.vram_size = qxl->ram_size_mb * 1024 * 1024;
-    }
-    if (qxl->pci.vga.vram_size < qxl->pci.vgamem_size * 2) {
-        qxl->pci.vga.vram_size = qxl->pci.vgamem_size * 2;
-    }
-
-    /* vram32 (surfaces, 32bit, bar 1) */
-    if (qxl->vram32_size_mb != -1) {
-        qxl->vram32_size = qxl->vram32_size_mb * 1024 * 1024;
-    }
-    if (qxl->vram32_size < 4096) {
-        qxl->vram32_size = 4096;
-    }
-
-    /* vram (surfaces, 64bit, bar 4+5) */
-    if (qxl->vram_size_mb != -1) {
-        qxl->vram_size = qxl->vram_size_mb * 1024 * 1024;
-    }
-    if (qxl->vram_size < qxl->vram32_size) {
-        qxl->vram_size = qxl->vram32_size;
-    }
-
-    if (qxl->pci.revision == 1) {
-        qxl->vram32_size = 4096;
-        qxl->vram_size = 4096;
-    }
-    qxl->pci.vgamem_size = msb_mask(qxl->pci.vgamem_size * 2 - 1);
-    qxl->pci.vga.vram_size = msb_mask(qxl->pci.vga.vram_size * 2 - 1);
-    qxl->vram32_size = msb_mask(qxl->vram32_size * 2 - 1);
-    qxl->vram_size = msb_mask(qxl->vram_size * 2 - 1);
-}
-
-static int qxl_init_common(QXLDevice *qxl)
-{
-    uint8_t* config = qxl->pci.pci.config;
-    uint32_t pci_device_rev;
-    uint32_t io_size;
-
-    qxl->pci.mode = QXL_MODE_UNDEFINED;
-    qxl->pci.generation = 1;
-    qxl->num_memslots = NUM_MEMSLOTS;
-    qxl->num_surfaces = NUM_SURFACES;
-    qemu_mutex_init(&qxl->track_lock);
-    qemu_mutex_init(&qxl->async_lock);
-    qxl->current_async = QXL_UNDEFINED_IO;
-    qxl->pci.guest_bug = 0;
-
-    switch (qxl->pci.revision) {
-    case 1: /* spice 0.4 -- qxl-1 */
-        pci_device_rev = QXL_REVISION_STABLE_V04;
-        io_size = 8;
-        break;
-    case 2: /* spice 0.6 -- qxl-2 */
-        pci_device_rev = QXL_REVISION_STABLE_V06;
-        io_size = 16;
-        break;
-    case 3: /* qxl-3 */
-    default:
-        pci_device_rev = QXL_DEFAULT_REVISION;
-        io_size = msb_mask(QXL_IO_RANGE_SIZE * 2 - 1);
-        break;
-    }
-
-    pci_set_byte(&config[PCI_REVISION_ID], pci_device_rev);
-    pci_set_byte(&config[PCI_INTERRUPT_PIN], 1);
-
-    qxl->pci.rom_size = qxl_rom_size();
-    memory_region_init_ram(&qxl->pci.rom_bar,"qxl-pci.vrom",
-            qxl->pci.rom_size);
-    vmstate_register_ram(&qxl->pci.rom_bar, &qxl->pci.pci.qdev);
-    init_qxl_rom(qxl);
-    init_qxl_ram(qxl);
-
-    memory_region_init_ram(&qxl->pci.vram_bar, "qxl-pci.vram",
-            qxl->vram_size);
-    vmstate_register_ram(&qxl->pci.vram_bar, &qxl->pci.pci.qdev);
-    memory_region_init_alias(&qxl->pci.vram32_bar, "qxl-pci.vram32",
-            &qxl->pci.vram_bar, 0, qxl->vram32_size);
-
-    memory_region_init_io(&qxl->pci.io_bar, &qxl_io_ops, qxl,
-                          "qxl-pci.ioports", io_size);
-    if (qxl->id == 0) {
-        vga_dirty_log_start(&qxl->pci.vga);
-    }
-
-
-    pci_register_bar(&qxl->pci.pci, QXL_IO_RANGE_INDEX,
-                     PCI_BASE_ADDRESS_SPACE_IO, &qxl->pci.io_bar);
-
-    pci_register_bar(&qxl->pci.pci, QXL_ROM_RANGE_INDEX,
-                     PCI_BASE_ADDRESS_SPACE_MEMORY, &qxl->pci.rom_bar);
-
-    pci_register_bar(&qxl->pci.pci, QXL_RAM_RANGE_INDEX,
-                     PCI_BASE_ADDRESS_SPACE_MEMORY, &qxl->pci.vga.vram);
-
-    pci_register_bar(&qxl->pci.pci, QXL_VRAM_RANGE_INDEX,
-                     PCI_BASE_ADDRESS_SPACE_MEMORY, &qxl->pci.vram32_bar);
-
-    if (qxl->vram32_size < qxl->vram_size) {
-        /*
-         * Make the 64bit vram bar show up only in case it is
-         * configured to be larger than the 32bit vram bar.
-         */
-        pci_register_bar(&qxl->pci.pci, QXL_VRAM64_RANGE_INDEX,
-                         PCI_BASE_ADDRESS_SPACE_MEMORY |
-                         PCI_BASE_ADDRESS_MEM_TYPE_64 |
-                         PCI_BASE_ADDRESS_MEM_PREFETCH,
-                         &qxl->pci.vram_bar);
-    }
-
-    /* print pci bar details */
-    dprint(qxl, 1, "ram/%s: %d MB [region 0]\n",
-           qxl->id == 0 ? "pri" : "sec",
-           qxl->pci.vga.vram_size / (1024*1024));
-    dprint(qxl, 1, "vram/32: %d MB [region 1]\n",
-           qxl->vram32_size / (1024*1024));
-    dprint(qxl, 1, "vram/64: %d MB %s\n",
-           qxl->vram_size / (1024*1024),
-           qxl->vram32_size < qxl->vram_size ? "[region 4]" : "[unmapped]");
-
-    qxl->ssd.qxl.base.sif = &qxl_interface.base;
-    qxl->ssd.qxl.id = qxl->id;
-    qemu_spice_add_interface(&qxl->ssd.qxl.base);
-    qemu_add_vm_change_state_handler(qxl_vm_change_state_handler, qxl);
-
-    init_pipe_signaling(qxl);
-    qxl_reset_state(qxl);
-
-    qxl->update_area_bh = qemu_bh_new(qxl_render_update_area_bh, qxl);
-
-    return 0;
-}
-
-static int qxl_init_primary(PCIDevice *dev)
-{
-    QXLDevice *qxl = (QXLDevice *) DO_UPCAST(PCIQXLDevice, pci, dev);
-    VGACommonState *vga = &qxl->pci.vga;
-    PortioList *qxl_vga_port_list = g_new(PortioList, 1);
-
-    qxl->id = 0;
-    qxl_init_ramsize(qxl);
-    vga->vram_size_mb = qxl->pci.vga.vram_size >> 20;
-    vga_common_init(vga);
-    vga_init(vga, pci_address_space(dev), pci_address_space_io(dev), false);
-    portio_list_init(qxl_vga_port_list, qxl_vga_portio_list, vga, "vga");
-    portio_list_add(qxl_vga_port_list, pci_address_space_io(dev), 0x3b0);
-
-    vga->ds = graphic_console_init(qxl_hw_update, qxl_hw_invalidate,
-                                   qxl_hw_screen_dump, qxl_hw_text_update, qxl);
-    qemu_spice_display_init_common(&qxl->ssd, vga->ds);
-
-    qxl0 = qxl;
-    register_displaychangelistener(vga->ds, &display_listener);
-
-    return qxl_init_common(qxl);
-}
-
-static int qxl_init_secondary(PCIDevice *dev)
-{
-    static int device_id = 1;
-    QXLDevice *qxl = (QXLDevice *) DO_UPCAST(PCIQXLDevice, pci, dev);
-
-    qxl->id = device_id++;
-    qxl_init_ramsize(qxl);
-    memory_region_init_ram(&qxl->pci.vga.vram, "qxl-pci.vgavram", qxl->pci.vga.vram_size);
-    vmstate_register_ram(&qxl->pci.vga.vram, &qxl->pci.pci.qdev);
-    qxl->pci.vga.vram_ptr = memory_region_get_ram_ptr(&qxl->pci.vga.vram);
-
-    return qxl_init_common(qxl);
-}
-
-static void qxl_pre_save(void *opaque)
-{
-    QXLDevice* d = opaque;
-    uint8_t *ram_start = d->pci.vga.vram_ptr;
-
-    trace_qxl_pre_save(d->id);
-    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->pci.vga.vram_size);
-}
-
-static int qxl_pre_load(void *opaque)
-{
-    QXLDevice* d = opaque;
-
-    trace_qxl_pre_load(d->id);
-    qxl_hard_reset(d, 1);
-    qxl_exit_vga_mode(d);
-    return 0;
-}
-
-static void qxl_create_memslots(QXLDevice *d)
-{
-    int i;
-
-    for (i = 0; i < NUM_MEMSLOTS; i++) {
-        if (!d->guest_slots[i].active) {
-            continue;
-        }
-        qxl_add_memslot(d, i, 0, QXL_SYNC);
-    }
-}
-
-static int qxl_post_load(void *opaque, int version)
-{
-    QXLDevice *d = opaque;
-    uint8_t *ram_start = d->pci.vga.vram_ptr;
-    QXLCommandExt *cmds;
-    int in, out, newmode;
-
-    assert(d->last_release_offset < d->pci.vga.vram_size);
-    if (d->last_release_offset == 0) {
-        d->last_release = NULL;
-    } else {
-        d->last_release = (QXLReleaseInfo *)(ram_start + d->last_release_offset);
-    }
-
-    d->modes = (QXLModes*)((uint8_t*)d->rom + d->rom->modes_offset);
-
-    trace_qxl_post_load(d->id, qxl_mode_to_string(d->pci.mode));
-    newmode = d->pci.mode;
-    d->pci.mode = QXL_MODE_UNDEFINED;
-
-    switch (newmode) {
-    case QXL_MODE_UNDEFINED:
-        break;
-    case QXL_MODE_VGA:
-        qxl_create_memslots(d);
-        qxl_enter_vga_mode(d);
-        break;
-    case QXL_MODE_NATIVE:
-        qxl_create_memslots(d);
-        qxl_create_guest_primary(d, 1, QXL_SYNC);
-
-        /* replay surface-create and cursor-set commands */
-        cmds = g_malloc0(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++;
-        }
-        if (d->guest_cursor) {
-            cmds[out].cmd.data = d->guest_cursor;
-            cmds[out].cmd.type = QXL_CMD_CURSOR;
-            cmds[out].group_id = MEMSLOT_GROUP_GUEST;
-            out++;
-        }
-        qxl_spice_loadvm_commands(d, cmds, out);
-        g_free(cmds);
-
-        break;
-    case QXL_MODE_COMPAT:
-        /* note: no need to call qxl_create_memslots, qxl_set_mode
-         * creates the mem slot. */
-        qxl_set_mode(d, d->shadow_rom.mode, 1);
-        break;
-    }
-    return 0;
-}
-
-#define QXL_SAVE_VERSION 21
-
-static VMStateDescription qxl_memslot = {
-    .name               = "qxl-memslot",
-    .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),
-        VMSTATE_UINT32(active,         struct guest_slots),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static VMStateDescription qxl_surface = {
-    .name               = "qxl-surface",
-    .version_id         = QXL_SAVE_VERSION,
-    .minimum_version_id = QXL_SAVE_VERSION,
-    .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_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, 0, vmstate_vga_common, VGACommonState),
-        VMSTATE_UINT32(shadow_rom.mode, QXLDevice),
-        VMSTATE_UINT32(num_free_res, QXLDevice),
-        VMSTATE_UINT32(last_release_offset, QXLDevice),
-        VMSTATE_UINT32(mode, PCIQXLDevice),
-        VMSTATE_UINT32(ssd.unique, QXLDevice),
-        VMSTATE_INT32_EQUAL(num_memslots, QXLDevice),
-        VMSTATE_STRUCT_ARRAY(guest_slots, QXLDevice, NUM_MEMSLOTS, 0,
-                             qxl_memslot, struct guest_slots),
-        VMSTATE_STRUCT(guest_primary.surface, QXLDevice, 0,
-                       qxl_surface, QXLSurfaceCreate),
-        VMSTATE_INT32_EQUAL(num_surfaces, QXLDevice),
-        VMSTATE_ARRAY(guest_surfaces.cmds, QXLDevice, NUM_SURFACES, 0,
-                      vmstate_info_uint64, uint64_t),
-        VMSTATE_UINT64(guest_cursor, QXLDevice),
-        VMSTATE_END_OF_LIST()
-    },
-};
-
-static Property qxl_properties[] = {
-        DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size,
-                           64 * 1024 * 1024),
-        DEFINE_PROP_UINT32("vram_size", QXLDevice, vram32_size,
-                           64 * 1024 * 1024),
-        DEFINE_PROP_UINT32("revision", PCIQXLDevice, revision,
-                           QXL_DEFAULT_REVISION),
-        DEFINE_PROP_UINT32("debug", QXLDevice, debug, 0),
-        DEFINE_PROP_UINT32("guestdebug", PCIQXLDevice, guestdebug, 0),
-        DEFINE_PROP_UINT32("cmdlog", PCIQXLDevice, cmdlog, 0),
-        DEFINE_PROP_UINT32("ram_size_mb",  QXLDevice, ram_size_mb, -1),
-        DEFINE_PROP_UINT32("vram_size_mb", QXLDevice, vram32_size_mb, -1),
-        DEFINE_PROP_UINT32("vram64_size_mb", QXLDevice, vram_size_mb, -1),
-        DEFINE_PROP_UINT32("vgamem_mb", PCIQXLDevice, vgamem_size_mb, 16),
-        DEFINE_PROP_END_OF_LIST(),
-};
-
-static void qxl_primary_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    k->no_hotplug = 1;
-    k->init = qxl_init_primary;
-    k->romfile = "vgabios-qxl.bin";
-    k->vendor_id = REDHAT_PCI_VENDOR_ID;
-    k->device_id = QXL_DEVICE_ID_STABLE;
-    k->class_id = PCI_CLASS_DISPLAY_VGA;
-    dc->desc = "Spice QXL GPU (primary, vga compatible)";
-    dc->reset = qxl_reset_handler;
-    dc->vmsd = &qxl_vmstate;
-    dc->props = qxl_properties;
-}
-
-static TypeInfo qxl_primary_info = {
-    .name          = "qxl-vga",
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(QXLDevice),
-    .class_init    = qxl_primary_class_init,
-};
-
-static void qxl_secondary_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    k->init = qxl_init_secondary;
-    k->vendor_id = REDHAT_PCI_VENDOR_ID;
-    k->device_id = QXL_DEVICE_ID_STABLE;
-    k->class_id = PCI_CLASS_DISPLAY_OTHER;
-    dc->desc = "Spice QXL GPU (secondary)";
-    dc->reset = qxl_reset_handler;
-    dc->vmsd = &qxl_vmstate;
-    dc->props = qxl_properties;
-}
-
-static TypeInfo qxl_secondary_info = {
-    .name          = "qxl",
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(QXLDevice),
-    .class_init    = qxl_secondary_class_init,
-};
-
-static void qxl_register_types(void)
-{
-    type_register_static(&qxl_primary_info);
-    type_register_static(&qxl_secondary_info);
-}
-
-type_init(qxl_register_types)
diff --git a/hw/qxl.h b/hw/qxl.h
index f25e341..516e7da 100644
--- a/hw/qxl.h
+++ b/hw/qxl.h
@@ -143,6 +143,44 @@ typedef struct QXLDevice {
         }                                                               \
     } while (0)
 
+/*
+ * NOTE: SPICE_RING_PROD_ITEM accesses memory on the pci bar and as
+ * such can be changed by the guest, so to avoid a guest trigerrable
+ * abort we just qxl_set_guest_bug and set the return to NULL. Still
+ * it may happen as a result of emulator bug as well.
+ */
+#undef SPICE_RING_PROD_ITEM
+#define SPICE_RING_PROD_ITEM(qxl, 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))) { \
+            qxl_set_guest_bug(qxl, "SPICE_RING_PROD_ITEM indices mismatch " \
+                          "! %p <= %p < %p", (uint8_t *)start,          \
+                          (uint8_t *)m_item, (uint8_t *)end);           \
+            ret = NULL;                                                 \
+        } else {                                                        \
+            ret = &m_item->el;                                          \
+        }                                                               \
+    }
+
+#undef SPICE_RING_CONS_ITEM
+#define SPICE_RING_CONS_ITEM(qxl, 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))) { \
+            qxl_set_guest_bug(qxl, "SPICE_RING_CONS_ITEM indices mismatch " \
+                          "! %p <= %p < %p", (uint8_t *)start,          \
+                          (uint8_t *)m_item, (uint8_t *)end);           \
+            ret = NULL;                                                 \
+        } else {                                                        \
+            ret = &m_item->el;                                          \
+        }                                                               \
+    }
+
 /* qxl.c */
 void *qxl_phys2virt(QXLDevice *qxl, QXLPHYSICAL phys, int group_id);
 void qxl_set_guest_bug(QXLDevice *qxl, const char *msg, ...)
@@ -158,6 +196,22 @@ void qxl_spice_oom(QXLDevice *qxl);
 void qxl_spice_reset_memslots(QXLDevice *qxl);
 void qxl_spice_reset_image_cache(QXLDevice *qxl);
 void qxl_spice_reset_cursor(QXLDevice *qxl);
+void init_qxl_rom(QXLDevice *d);
+void init_qxl_ram(QXLDevice *d);
+void interface_attach_worker(QXLInstance *sin, QXLWorker *qxl_worker);
+void interface_set_compression_level(QXLInstance *sin, int level);
+void interface_set_mm_time(QXLInstance *sin, uint32_t mm_time);
+void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info);
+int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext);
+int interface_req_cmd_notification(QXLInstance *sin);
+void interface_release_resource(QXLInstance *sin, struct QXLReleaseInfoExt ext);
+int interface_get_cursor_command(QXLInstance *sin, struct QXLCommandExt *ext);
+int interface_req_cursor_notification(QXLInstance *sin);
+void interface_notify_update(QXLInstance *sin, uint32_t update_id);
+int interface_flush_resources(QXLInstance *sin);
+void interface_update_area_complete(QXLInstance *sin, uint32_t surface_id, QXLRect *dirty, uint32_t num_updated_rects);
+void interface_async_complete(QXLInstance *sin, uint64_t cookie_token);
+ram_addr_t qxl_rom_size(void);
 
 /* qxl-logger.c */
 int qxl_log_cmd_cursor(QXLDevice *qxl, QXLCursorCmd *cmd, int group_id);
@@ -169,3 +223,69 @@ void qxl_render_update(QXLDevice *qxl);
 int qxl_render_cursor(QXLDevice *qxl, QXLCommandExt *ext);
 void qxl_render_update_area_done(QXLDevice *qxl, QXLCookie *cookie);
 void qxl_render_update_area_bh(void *opaque);
+
+/* qxl-pci.c */
+void qxl_send_events(QXLDevice *d, uint32_t events);
+int qxl_destroy_primary(QXLDevice *d, qxl_async_io async);
+void qxl_reset_memslots(QXLDevice *d);
+void qxl_reset_surfaces(QXLDevice *d);
+void qxl_ring_set_dirty(QXLDevice *qxl);
+void qxl_rom_set_dirty(QXLDevice *qxl);
+void qxl_ram_set_dirty(QXLDevice *qxl, void *ptr);
+void interface_async_complete_io(QXLDevice *qxl, QXLCookie *cookie);
+int interface_get_command_pci(QXLDevice *qxl, struct QXLCommandExt *ext);
+int interface_get_cursor_command_pci(QXLDevice *qxl, struct QXLCommandExt *ext);
+const char *qxl_mode_to_string(int mode);
+
+/* called from spice server thread context only */
+static inline void qxl_push_free_res(QXLDevice *d, int flush)
+{
+    QXLReleaseRing *ring = &d->ram->release_ring;
+    uint64_t *item;
+    int notify;
+
+#define QXL_FREE_BUNCH_SIZE 32
+
+    if (ring->prod - ring->cons + 1 == ring->num_items) {
+        /* ring full -- can't push */
+        return;
+    }
+    if (!flush && d->oom_running) {
+        /* collect everything from oom handler before pushing */
+        return;
+    }
+    if (!flush && d->num_free_res < QXL_FREE_BUNCH_SIZE) {
+        /* collect a bit more before pushing */
+        return;
+    }
+
+    SPICE_RING_PUSH(ring, notify);
+    trace_qxl_ring_res_push(d->id, qxl_mode_to_string(d->pci.mode),
+           d->guest_surfaces.count, d->num_free_res,
+           d->last_release, notify ? "yes" : "no");
+    trace_qxl_ring_res_push_rest(d->id, ring->prod - ring->cons,
+           ring->num_items, ring->prod, ring->cons);
+    if (notify) {
+        qxl_send_events(d, QXL_INTERRUPT_DISPLAY);
+    }
+    SPICE_RING_PROD_ITEM(d, ring, item);
+    if (!item) {
+        return;
+    }
+    *item = 0;
+    d->num_free_res = 0;
+    d->last_release = NULL;
+    qxl_ring_set_dirty(d);
+}
+
+static inline uint32_t msb_mask(uint32_t val)
+{
+    uint32_t mask;
+
+    do {
+        mask = ~(val - 1) & val;
+        val &= ~mask;
+    } while (mask < val);
+
+    return mask;
+}
-- 
1.7.4.1

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [Qemu-devel] [PATCH 3/4] qxl: implement VirtIO QXL (dummy) device support
  2012-08-24 19:14 [Qemu-devel] Implementing qxl-virtio on QEMU Erlon Cruz
  2012-08-24 19:14 ` [Qemu-devel] [PATCH 1/4] qxl: create a qxl common struct! Erlon Cruz
  2012-08-24 19:14 ` [Qemu-devel] [PATCH 2/4] qxl: split qxl functions in common and pci files Erlon Cruz
@ 2012-08-24 19:14 ` Erlon Cruz
  2012-08-24 19:14 ` [Qemu-devel] [PATCH 4/4] qxl: introducing virtio-qxl Erlon Cruz
  2012-08-27  6:08 ` [Qemu-devel] Implementing qxl-virtio on QEMU Gerd Hoffmann
  4 siblings, 0 replies; 10+ messages in thread
From: Erlon Cruz @ 2012-08-24 19:14 UTC (permalink / raw)
  To: qemu-devel
  Cc: Erlon R. Cruz, Fabiano Fidêncio, Fabiano Fidêncio,
	Rafael F. Santos, alevy

From: Fabiano Fidêncio <fabiano@fidencio.org>

This commit introduces the devices stubs that use Virtio infrastructure
to transport QXL commands.

Signed-off-by: Erlon R. Cruz <erlon.cruz@fit-tecnologia.org.br>
Signed-off-by: Fabiano Fidêncio <fabiano.fidencio@fit-tecnologia.org.br>
Signed-off-by: Rafael F. Santos <fonsecasantos.rafael@gmail.com>
---
 hw/i386/Makefile.objs |    2 +-
 hw/pci.h              |    1 +
 hw/qxl-virtio.c       |   93 +++++++++++++++++++++++++++++++++++++++++++++++++
 hw/virtio-pci.c       |    4 +-
 hw/virtio-pci.h       |    2 +
 hw/virtio.h           |    2 +
 qemu-config.c         |   13 +++++++
 qemu-options.hx       |    7 ++++
 vl.c                  |   15 +++++++-
 9 files changed, 135 insertions(+), 4 deletions(-)
 create mode 100644 hw/qxl-virtio.c

diff --git a/hw/i386/Makefile.objs b/hw/i386/Makefile.objs
index 1953e7f..3cf5a24 100644
--- a/hw/i386/Makefile.objs
+++ b/hw/i386/Makefile.objs
@@ -10,6 +10,6 @@ obj-$(CONFIG_XEN) += xen_platform.o xen_apic.o
 obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen-host-pci-device.o
 obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pt.o xen_pt_config_init.o xen_pt_msi.o
 obj-y += kvm/
-obj-$(CONFIG_SPICE) += qxl.o qxl-logger.o qxl-render.o qxl-pci.o
+obj-$(CONFIG_SPICE) += qxl.o qxl-logger.o qxl-render.o qxl-pci.o qxl-virtio.o
 
 obj-y := $(addprefix ../,$(obj-y))
diff --git a/hw/pci.h b/hw/pci.h
index 4b6ab3d..9271f7a 100644
--- a/hw/pci.h
+++ b/hw/pci.h
@@ -76,6 +76,7 @@
 #define PCI_DEVICE_ID_VIRTIO_BALLOON     0x1002
 #define PCI_DEVICE_ID_VIRTIO_CONSOLE     0x1003
 #define PCI_DEVICE_ID_VIRTIO_SCSI        0x1004
+#define PCI_DEVICE_ID_VIRTIO_QXL         0x1005
 
 #define FMT_PCIBUS                      PRIx64
 
diff --git a/hw/qxl-virtio.c b/hw/qxl-virtio.c
new file mode 100644
index 0000000..d290de8
--- /dev/null
+++ b/hw/qxl-virtio.c
@@ -0,0 +1,93 @@
+/*
+ * VirtioQXL Device
+ *
+ * (C) Copyright IBM Corp. 2012
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "pci.h"
+#include "virtio.h"
+#include "virtio-pci.h"
+
+VirtIODevice *virtio_qxl_init(DeviceState *dev)
+{
+    return NULL;
+}
+
+void virtio_qxl_exit(VirtIODevice *vdev)
+{
+    return;
+}
+
+
+//setting up virtio device
+static int virtio_qxl_init_pci(PCIDevice *pci_dev)
+{
+    VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
+    VirtIODevice *vdev = NULL;
+
+    vdev = virtio_qxl_init(&pci_dev->qdev);
+    if (!vdev) {
+        return -1;
+    }
+    vdev->nvectors = proxy->nvectors;
+    virtio_init_pci(proxy, vdev);
+    /* make the actual value visible */
+    proxy->nvectors = vdev->nvectors;
+    return 0;
+}
+
+static void virtio_qxl_exit_pci(PCIDevice *pci_dev)
+{
+    VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
+
+    virtio_pci_stop_ioeventfd(proxy);
+    virtio_qxl_exit(proxy->vdev);
+    virtio_exit_pci(pci_dev);
+}
+
+static void virtio_qxl_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->init = virtio_qxl_init_pci;
+    k->exit = virtio_qxl_exit_pci;
+    k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
+    k->device_id = PCI_DEVICE_ID_VIRTIO_QXL;
+    k->revision = VIRTIO_PCI_ABI_VERSION;
+    k->class_id = PCI_CLASS_COMMUNICATION_OTHER;
+    dc->reset = virtio_pci_reset;
+//    dc->props = qxl_properties; //we will use the same used by qxl-pci
+}
+
+static TypeInfo virtio_qxl_info = {
+    .name           = "virtio-qxl-pci",
+    .parent         = TYPE_PCI_DEVICE,
+    .instance_size  = sizeof(VirtIOPCIProxy),
+    .class_init     = virtio_qxl_class_init,
+};
+
+static void virtio_qxl_register_types(void)
+{
+    type_register_static(&virtio_qxl_info);
+}
+
+type_init(virtio_qxl_register_types);
diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c
index 5e6e09e..2626a8a 100644
--- a/hw/virtio-pci.c
+++ b/hw/virtio-pci.c
@@ -221,7 +221,7 @@ assign_error:
     error_report("%s: failed. Fallback to a userspace (slower).", __func__);
 }
 
-static void virtio_pci_stop_ioeventfd(VirtIOPCIProxy *proxy)
+void virtio_pci_stop_ioeventfd(VirtIOPCIProxy *proxy)
 {
     int r;
     int n;
@@ -779,7 +779,7 @@ static int virtio_blk_init_pci(PCIDevice *pci_dev)
     return 0;
 }
 
-static void virtio_exit_pci(PCIDevice *pci_dev)
+void virtio_exit_pci(PCIDevice *pci_dev)
 {
     VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
 
diff --git a/hw/virtio-pci.h b/hw/virtio-pci.h
index ac9d522..4032b20 100644
--- a/hw/virtio-pci.h
+++ b/hw/virtio-pci.h
@@ -53,6 +53,8 @@ typedef struct {
 
 void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev);
 void virtio_pci_reset(DeviceState *d);
+void virtio_pci_stop_ioeventfd(VirtIOPCIProxy *proxy);
+void virtio_exit_pci(PCIDevice *pci_dev);
 
 /* Virtio ABI version, if we increment this, we break the guest driver. */
 #define VIRTIO_PCI_ABI_VERSION          0
diff --git a/hw/virtio.h b/hw/virtio.h
index 7a4f564..7a7566e 100644
--- a/hw/virtio.h
+++ b/hw/virtio.h
@@ -200,6 +200,7 @@ VirtIODevice *virtio_serial_init(DeviceState *dev, virtio_serial_conf *serial);
 VirtIODevice *virtio_balloon_init(DeviceState *dev);
 typedef struct VirtIOSCSIConf VirtIOSCSIConf;
 VirtIODevice *virtio_scsi_init(DeviceState *dev, VirtIOSCSIConf *conf);
+VirtIODevice *virtio_qxl_init(DeviceState *dev);
 #ifdef CONFIG_LINUX
 VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf);
 #endif
@@ -210,6 +211,7 @@ void virtio_blk_exit(VirtIODevice *vdev);
 void virtio_serial_exit(VirtIODevice *vdev);
 void virtio_balloon_exit(VirtIODevice *vdev);
 void virtio_scsi_exit(VirtIODevice *vdev);
+void virtio_qxl_exit(VirtIODevice *vdev);
 
 #define DEFINE_VIRTIO_COMMON_FEATURES(_state, _field) \
 	DEFINE_PROP_BIT("indirect_desc", _state, _field, \
diff --git a/qemu-config.c b/qemu-config.c
index c05ffbc..32a99db 100644
--- a/qemu-config.c
+++ b/qemu-config.c
@@ -643,6 +643,18 @@ QemuOptsList qemu_boot_opts = {
     },
 };
 
+QemuOptsList qemu_virtio_qxl_opts = {
+    .name = "virtio-qxl",
+    .head = QTAILQ_HEAD_INITIALIZER(qemu_virtio_qxl_opts.head),
+    .desc = {
+        /*
+         * no elements => accepty any
+         * sanity checking will happen later when setting device properties
+         */
+        { /* end of list */}
+    },
+};
+
 static QemuOptsList *vm_config_groups[32] = {
     &qemu_drive_opts,
     &qemu_chardev_opts,
@@ -659,6 +671,7 @@ static QemuOptsList *vm_config_groups[32] = {
     &qemu_boot_opts,
     &qemu_iscsi_opts,
     &qemu_sandbox_opts,
+    &qemu_virtio_qxl_opts,
     NULL,
 };
 
diff --git a/qemu-options.hx b/qemu-options.hx
index 3c411c4..41a6728 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -569,6 +569,13 @@ ETEXI
 
 DEFHEADING()
 
+DEFHEADING(QXL VirtIO option:)
+
+DEF("virtio-qxl", 0, QEMU_OPTION_virtio_qxl, "-virtio-qxl\n",
+QEMU_ARCH_ALL)
+
+DEFHEADING()
+
 DEFHEADING(File system options:)
 
 DEF("fsdev", HAS_ARG, QEMU_OPTION_fsdev,
diff --git a/vl.c b/vl.c
index 7c577fa..14c647f 100644
--- a/vl.c
+++ b/vl.c
@@ -233,6 +233,7 @@ int boot_menu;
 uint8_t *boot_splash_filedata;
 int boot_splash_filedata_size;
 uint8_t qemu_extra_params_fw[2];
+int virtio_qxl_enabled = 0;
 
 typedef struct FWBootEntry FWBootEntry;
 
@@ -2951,6 +2952,18 @@ int main(int argc, char **argv, char **envp)
                 qemu_opt_set(device, "mount_tag", "v_synth");
                 break;
             }
+            case QEMU_OPTION_virtio_qxl: {
+                QemuOpts *device;
+
+                virtio_qxl_enabled = 1;
+                vga_model = "none";
+                default_vga = 0;
+                device = qemu_opts_create(qemu_find_opts("device"), NULL, 0,
+                                          NULL);
+                qemu_opt_set(device, "driver", "virtio-qxl-pci");
+                break;
+
+            }
             case QEMU_OPTION_serial:
                 add_device_config(DEV_SERIAL, optarg);
                 default_serial = 0;
@@ -3692,7 +3705,7 @@ int main(int argc, char **argv, char **envp)
     }
 #endif
 #ifdef CONFIG_SPICE
-    if (using_spice && !qxl_enabled) {
+    if (using_spice && !qxl_enabled && !virtio_qxl_enabled) {
         qemu_spice_display_init(ds);
     }
 #endif
-- 
1.7.4.1

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [Qemu-devel] [PATCH 4/4] qxl: introducing virtio-qxl
  2012-08-24 19:14 [Qemu-devel] Implementing qxl-virtio on QEMU Erlon Cruz
                   ` (2 preceding siblings ...)
  2012-08-24 19:14 ` [Qemu-devel] [PATCH 3/4] qxl: implement VirtIO QXL (dummy) device support Erlon Cruz
@ 2012-08-24 19:14 ` Erlon Cruz
  2012-08-27  6:08   ` Gerd Hoffmann
  2012-08-27  6:08 ` [Qemu-devel] Implementing qxl-virtio on QEMU Gerd Hoffmann
  4 siblings, 1 reply; 10+ messages in thread
From: Erlon Cruz @ 2012-08-24 19:14 UTC (permalink / raw)
  To: qemu-devel
  Cc: Erlon R. Cruz, Fabiano Fidêncio, Fabiano Fidêncio,
	Rafael F. Santos, alevy

From: Fabiano Fidêncio <fabiano@fidencio.org>

The VirtioQXL device is a graphical video device that makes possible to
use of SPICE protocol over virtio transport.
QXL commands are generated in the guest xf86 driver and pushed to host
through in a single VQ. The commands are copied to device memory and then
consumed by spice server. The SPICE command rings (Cursor,release and
Command) are used as they are, i.e., like with the commands, the ring
elements and its control fields are push/poped from guest to host. After
consuming an element, the host updates the guest's ring. As with the PCI
QXL driver, the spice server send rendering (QXL) commands to the spice
client which actually render them. So far the driver has no VGA
functionality.

To enable the VirtIOQXL device, use '-virtio-qxl'. Video output will be
shown only after start some graphical application. As there will be no
early boot messages output, is helpful to redirect the serial console to
stdout (-serial stdio, plus passing a kernel parameter 'console=ttyS0'

Issues:
    We need to increase virtio ring from 1024 to 8192 elements.
    Please, any tip to fix it is welcome :)

Signed-off-by: Erlon R. Cruz <erlon.cruz@fit-tecnologia.org.br>
Signed-off-by: Fabiano Fidêncio <fabiano.fidencio@fit-tecnologia.org.br>
Signed-off-by: Rafael F. Santos <fonsecasantos.rafael@gmail.com>
---
 hw/ppc/Makefile.objs |    2 +-
 hw/qxl-pci.c         |    1 +
 hw/qxl-render.c      |   37 ++--
 hw/qxl-virtio.c      |  588 +++++++++++++++++++++++++++++++++++++++++++++++++-
 hw/qxl-virtio.h      |   61 ++++++
 hw/qxl.c             |  111 +++++++---
 hw/qxl.h             |   76 ++++++--
 hw/spapr.c           |    3 +-
 hw/virtio.h          |    2 +-
 qemu-config.c        |    9 +-
 qemu-options.hx      |    2 +-
 sysemu.h             |    1 +
 vl.c                 |   12 +-
 13 files changed, 831 insertions(+), 74 deletions(-)
 create mode 100644 hw/qxl-virtio.h

diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs
index 951e407..5f8a011 100644
--- a/hw/ppc/Makefile.objs
+++ b/hw/ppc/Makefile.objs
@@ -21,7 +21,7 @@ obj-y += virtex_ml507.o
 # PowerPC OpenPIC
 obj-y += openpic.o
 obj-$(CONFIG_FDT) += ../device_tree.o
-
+obj-$(CONFIG_SPICE) += qxl.o qxl-logger.o qxl-render.o qxl-pci.o qxl-virtio.o
 # Xilinx PPC peripherals
 obj-y += xilinx_ethlite.o
 
diff --git a/hw/qxl-pci.c b/hw/qxl-pci.c
index 6b4ec45..bb7de17 100644
--- a/hw/qxl-pci.c
+++ b/hw/qxl-pci.c
@@ -1253,6 +1253,7 @@ static int qxl_init_common(QXLDevice *qxl)
     qemu_mutex_init(&qxl->async_lock);
     qxl->current_async = QXL_UNDEFINED_IO;
     qxl->pci.guest_bug = 0;
+    qxl->transport = QXL_TRANSPORT_PCI;
 
     switch (qxl->pci.revision) {
     case 1: /* spice 0.4 -- qxl-1 */
diff --git a/hw/qxl-render.c b/hw/qxl-render.c
index 243ddb9..d96b968 100644
--- a/hw/qxl-render.c
+++ b/hw/qxl-render.c
@@ -24,15 +24,19 @@
 static void qxl_blit(QXLDevice *qxl, QXLRect *rect)
 {
     uint8_t *src;
-    uint8_t *dst = qxl->pci.vga.ds->surface->data;
+    uint8_t *dst = (qxl->transport == QXL_TRANSPORT_PCI) ?
+        qxl->pci.vga.ds->surface->data : qxl->ssd.ds->surface->data;
     int len, i;
 
-    if (is_buffer_shared(qxl->pci.vga.ds->surface)) {
+    if (qxl->transport == QXL_TRANSPORT_PCI &&
+        is_buffer_shared(qxl->pci.vga.ds->surface)) {
         return;
     }
     if (!qxl->guest_primary.data) {
         trace_qxl_render_blit_guest_primary_initialized();
-        qxl->guest_primary.data = memory_region_get_ram_ptr(&qxl->pci.vga.vram);
+        qxl->guest_primary.data = (qxl->transport == QXL_TRANSPORT_PCI) ?
+            memory_region_get_ram_ptr(&qxl->pci.vga.vram) :
+            qxl->virtio.ram_ptr;
     }
     trace_qxl_render_blit(qxl->guest_primary.qxl_stride,
             rect->left, rect->right, rect->top, rect->bottom);
@@ -97,14 +101,15 @@ static void qxl_set_rect_to_surface(QXLDevice *qxl, QXLRect *area)
 
 static void qxl_render_update_area_unlocked(QXLDevice *qxl)
 {
-    VGACommonState *vga = &qxl->pci.vga;
     int i;
-    DisplaySurface *surface = vga->ds->surface;
+    DisplayState *ds = (qxl->transport == QXL_TRANSPORT_PCI) ?
+                        qxl->pci.vga.ds : qxl->ssd.ds;
+    DisplaySurface *surface = ds->surface;
 
     if (qxl->guest_primary.resized) {
         qxl->guest_primary.resized = 0;
-        qxl->guest_primary.data =
-            memory_region_get_ram_ptr(&qxl->pci.vga.vram);
+        qxl->guest_primary.data = (qxl->transport == QXL_TRANSPORT_PCI) ?
+            memory_region_get_ram_ptr(&qxl->pci.vga.vram) : qxl->virtio.ram_ptr;
         qxl_set_rect_to_surface(qxl, &qxl->dirty[0]);
         qxl->num_dirty_rects = 1;
         trace_qxl_render_guest_primary_resized(
@@ -117,25 +122,25 @@ static void qxl_render_update_area_unlocked(QXLDevice *qxl)
     if (surface->width != qxl->guest_primary.surface.width ||
         surface->height != qxl->guest_primary.surface.height) {
         if (qxl->guest_primary.qxl_stride > 0) {
-            qemu_free_displaysurface(vga->ds);
+            qemu_free_displaysurface(ds);
             qemu_create_displaysurface_from(qxl->guest_primary.surface.width,
                                             qxl->guest_primary.surface.height,
                                             qxl->guest_primary.bits_pp,
                                             qxl->guest_primary.abs_stride,
                                             qxl->guest_primary.data);
         } else {
-            qemu_resize_displaysurface(vga->ds,
+            qemu_resize_displaysurface(ds,
                     qxl->guest_primary.surface.width,
                     qxl->guest_primary.surface.height);
         }
-        dpy_resize(vga->ds);
+        dpy_resize(ds);
     }
     for (i = 0; i < qxl->num_dirty_rects; i++) {
         if (qemu_spice_rect_is_empty(qxl->dirty+i)) {
             break;
         }
         qxl_blit(qxl, qxl->dirty+i);
-        dpy_update(vga->ds,
+        dpy_update(ds,
                    qxl->dirty[i].left, qxl->dirty[i].top,
                    qxl->dirty[i].right - qxl->dirty[i].left,
                    qxl->dirty[i].bottom - qxl->dirty[i].top);
@@ -155,7 +160,7 @@ void qxl_render_update(QXLDevice *qxl)
 
     qemu_mutex_lock(&qxl->ssd.lock);
 
-    if (!runstate_is_running() || !qxl->guest_primary.commands) {
+    if (qxl->transport == QXL_TRANSPORT_PCI && ((!runstate_is_running() || !qxl->guest_primary.commands))) {
         qxl_render_update_area_unlocked(qxl);
         qemu_mutex_unlock(&qxl->ssd.lock);
         return;
@@ -231,7 +236,9 @@ fail:
 /* called from spice server thread context only */
 int qxl_render_cursor(QXLDevice *qxl, QXLCommandExt *ext)
 {
-    QXLCursorCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id);
+    QXLCursorCmd *cmd = (qxl->transport == QXL_TRANSPORT_PCI) ?
+                        qxl_phys2virt(qxl, ext->cmd.data, ext->group_id) :
+                        (QXLCursorCmd *)ext->cmd.data;
     QXLCursor *cursor;
     QEMUCursor *c;
 
@@ -250,7 +257,9 @@ int qxl_render_cursor(QXLDevice *qxl, QXLCommandExt *ext)
     }
     switch (cmd->type) {
     case QXL_CURSOR_SET:
-        cursor = qxl_phys2virt(qxl, cmd->u.set.shape, ext->group_id);
+        cursor = (qxl->transport == QXL_TRANSPORT_PCI) ?
+                 qxl_phys2virt(qxl, cmd->u.set.shape, ext->group_id) :
+                 (QXLCursor *)cmd->u.set.shape;
         if (!cursor) {
             return 1;
         }
diff --git a/hw/qxl-virtio.c b/hw/qxl-virtio.c
index d290de8..6acd7f0 100644
--- a/hw/qxl-virtio.c
+++ b/hw/qxl-virtio.c
@@ -23,21 +23,599 @@
  */
 
 #include "pci.h"
-#include "virtio.h"
+#include "qxl.h"
+#include "qxl-virtio.h"
 #include "virtio-pci.h"
 
+extern uint64_t virtio_qxl_ram;
+static QXLDevice *qxl0;
+
+typedef struct QXLBridgeReq
+{
+    QXLDevice *dev;
+    VirtQueueElement elem;
+    struct vbr_proto_hdr *hdr;
+    void *payload;
+    uint8_t *status;
+    QEMUIOVector qiov;
+} QXLBridgeReq;
+
+QXLDevMemSlot slot = {
+    .slot_group_id = 0,
+    .slot_id = 0,
+    .generation = 0,
+    .virt_start = 0,
+    .virt_end = ~0,
+    .addr_delta = 0,
+    .qxl_ram_size = ~0,
+};
+
+void *offset_to_virt(QXLDevice *qxl, QXLPHYSICAL pqxl, int group_id)
+{
+    void *addr;
+    int mappable_len = qxl->virtio.config.ramsize +
+                       qxl->virtio.config.vramsize +
+                       qxl->virtio.config.romsize;
+
+    if (pqxl < mappable_len) {
+        addr = (void *)(qxl->virtio.ram_ptr + pqxl);
+        return addr;
+    } else {
+        dprint(qxl, 1, "Error mapping offset %llu\n",
+                (long long unsigned) pqxl);
+        /* For some reason some offsets are tried to be remapped twice.
+         * When this happens we fall here and should just return the offset
+         * as it were (i.e, already mapped)
+         */
+        return (void *)pqxl;
+    }
+}
+
+/*******************************************************************************
+ *  Spice Display Interface callbacks
+ ******************************************************************************/
+/* called from spice server thread context only */
+static void qxl_map_command(QXLDevice *qxl, struct QXLCommandExt *ext)
+{
+    switch (ext->cmd.type) {
+        case QXL_CMD_SURFACE:
+        {
+            QXLSurfaceCmd *cmd = offset_to_virt(qxl,
+                                                ext->cmd.data,
+                                                ext->group_id);
+            char *hostaddr;
+
+            ext->cmd.data = (QXLPHYSICAL)cmd;
+
+            if (cmd->type == QXL_SURFACE_CMD_DESTROY) {
+                break;
+            }
+
+            hostaddr = offset_to_virt(qxl,
+                                      cmd->u.surface_create.data,
+                                      ext->group_id);
+            cmd->u.surface_create.data = (QXLPHYSICAL)hostaddr;
+            break;
+        }
+        case QXL_CMD_CURSOR:
+        {
+            QXLCursorCmd *cmd = offset_to_virt(qxl,
+                                               ext->cmd.data,
+                                               ext->group_id);
+            char *hostaddr;
+
+            ext->cmd.data = (QXLPHYSICAL)cmd;
+
+            if (cmd->type != QXL_CURSOR_SET) {
+                break;
+            }
+
+            hostaddr = offset_to_virt(qxl, cmd->u.set.shape, ext->group_id);
+            cmd->u.set.shape = (QXLPHYSICAL)hostaddr;
+            break;
+        }
+        case QXL_CMD_DRAW:
+        {
+            struct QXLDrawable *draw = offset_to_virt(qxl,
+                                                      ext->cmd.data,
+                                                      ext->group_id);
+            char *hostaddr;
+            QXLPHYSICAL addr;
+            QXLImage *image;
+            QXLDataChunk *chunk, *prev;
+
+            ext->cmd.data = (QXLPHYSICAL)draw;
+
+            if (draw->type != QXL_DRAW_COPY) {
+                break;
+            }
+
+            hostaddr = offset_to_virt(qxl,
+                                      draw->u.copy.src_bitmap,
+                                      ext->group_id);
+            draw->u.copy.src_bitmap = (QXLPHYSICAL)hostaddr;
+            image = (void *)draw->u.copy.src_bitmap;
+
+            if (image->descriptor.type == SPICE_IMAGE_TYPE_SURFACE) {
+                break;
+            }
+
+            hostaddr = offset_to_virt(qxl,
+                                      image->bitmap.data,
+                                      ext->group_id);
+            image->bitmap.data = (QXLPHYSICAL)hostaddr;
+
+            if (image->bitmap.flags & QXL_BITMAP_DIRECT) {
+                break;
+            }
+
+            addr = image->bitmap.data;
+            prev = 0;
+            while (addr) {
+                chunk = offset_to_virt(qxl, addr, ext->group_id);
+                if (prev) {
+                    prev->next_chunk = (QXLPHYSICAL)chunk;
+                }
+                chunk->prev_chunk = (QXLPHYSICAL)prev;
+                prev = chunk;
+                addr = chunk->next_chunk;
+            }
+            prev->next_chunk = 0;
+            break;
+        }
+    }
+}
+
+QXLInterface qxl_interface = {
+    .base.type                  = SPICE_INTERFACE_QXL,
+    .base.description           = "virtio qxl gpu",
+    .base.major_version         = SPICE_INTERFACE_QXL_MAJOR,
+    .base.minor_version         = SPICE_INTERFACE_QXL_MINOR,
+
+    .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,
+
+    /* the callbacks below are called from spice server thead context */
+    .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,
+    .async_complete             = interface_async_complete,
+    .update_area_complete       = interface_update_area_complete,
+};
+
+static void setup_virtioqxl_mem(QXLDevice *qxl)
+{
+
+     struct virtioqxl_config *cfg;
+     int total_size;
+
+     cfg = &qxl->virtio.config;
+     cfg->configsize = sizeof(*cfg);
+     cfg->ramsize = qxl->vram_size;
+     cfg->vramsize = qxl->vram_size;
+     cfg->romsize = qxl_rom_size();
+     total_size = qxl->virtio.config.ramsize +
+                  qxl->virtio.config.vramsize +
+                  qxl->virtio.config.romsize;
+
+     qxl->virtio.ram_ptr = g_malloc(total_size);
+     memset(qxl->virtio.ram_ptr,0x0,total_size);
+     qxl->virtio.vram_ptr = qxl->virtio.ram_ptr + qxl->virtio.config.ramsize;
+     qxl->virtio.rom_ptr = qxl->virtio.ram_ptr +
+                      qxl->virtio.config.ramsize +
+                      qxl->virtio.config.vramsize;
+
+     init_qxl_rom(qxl);
+     init_qxl_ram(qxl);
+}
+
+static void create_primary_surface(QXLDevice *qxl)
+{
+    QXLDevSurfaceCreate surface = { 0, };
+    QXLSurfaceCreate *sc;
+
+    if (qxl->virtio.primary_created) {
+        return;
+    }
+
+    qxl->virtio.primary_created = 1;
+    qxl->guest_primary.surface = qxl->ram->create_surface;
+    sc = &qxl->guest_primary.surface;
+
+    surface.width      = sc->width      = le32_to_cpu(sc->width);
+    surface.height     = sc->height     = le32_to_cpu(sc->height);
+    surface.stride     = sc->stride     = le32_to_cpu(sc->stride);
+    surface.format     = sc->format     = le32_to_cpu(sc->format);
+    surface.position   = sc->position   = le32_to_cpu(sc->position);
+    surface.mouse_mode = true;
+    surface.flags      = sc->flags      = le32_to_cpu(sc->flags);
+    surface.type       = sc->type       = le32_to_cpu(sc->type);
+    sc->mem            = le64_to_cpu(sc->mem);
+    surface.mem        = (uint64_t)offset_to_virt(qxl,sc->mem,0);
+    surface.group_id   = MEMSLOT_GROUP_HOST;
+
+    qxl->ssd.worker->create_primary_surface(qxl->ssd.worker, 0, &surface);
+    qxl_render_resize(qxl);
+}
+
+static void ioport_write(QXLDevice *qxl,
+                         target_phys_addr_t addr,
+                         uint64_t val,
+                         unsigned size)
+{
+
+    uint32_t io_port = addr;
+
+    switch (io_port) {
+        case QXL_IO_RESET:
+            qxl->ssd.worker->reset_cursor(qxl->ssd.worker);
+            qxl->ssd.worker->reset_image_cache(qxl->ssd.worker);
+            qxl->ssd.worker->destroy_surfaces(qxl->ssd.worker);
+            qxl->ssd.worker->reset_memslots(qxl->ssd.worker);
+            qxl->ssd.worker->add_memslot(qxl->ssd.worker, &slot);
+            init_qxl_ram(qxl);
+            qxl->virtio.primary_created = 0;
+            break;
+        case QXL_IO_MEMSLOT_ADD:
+        case QXL_IO_MEMSLOT_DEL:
+            break;
+        case QXL_IO_CREATE_PRIMARY:
+            create_primary_surface(qxl);
+            break;
+        case QXL_IO_UPDATE_AREA:
+        {
+            if (!qxl->virtio.primary_created) {
+                return;
+            }
+            QXLRect rect = qxl->ram->update_area;
+            rect.top= le32_to_cpu(qxl->ram->update_area.top);
+            rect.bottom = le32_to_cpu(qxl->ram->update_area.bottom);
+            rect.left = le32_to_cpu(qxl->ram->update_area.left);
+            rect.right = le32_to_cpu(qxl->ram->update_area.right);
+            uint32_t sid = le32_to_cpu(qxl->ram->update_surface);
+
+            qxl->ssd.worker->update_area(qxl->ssd.worker, sid, &rect, NULL,
+                                         0, 1);
+            break;
+
+        }
+        case QXL_IO_NOTIFY_CURSOR:
+        case QXL_IO_NOTIFY_CMD:
+            qxl->ssd.worker->wakeup(qxl->ssd.worker);
+            break;
+        case QXL_IO_NOTIFY_OOM:
+            qxl->ssd.worker->oom(qxl->ssd.worker);
+            break;
+        default:
+            fprintf(stderr,"Not handled IO port %d\n",io_port);
+            break;
+    }
+}
+
+/*******************************************************************************
+ *  Virtio Transport Stuff
+ ******************************************************************************/
+static inline QXLDevice *to_virtio_qxl(VirtIODevice *vdev)
+{
+    return (QXLDevice *)vdev;
+}
+
+static void virtio_qxl_update_config(VirtIODevice *vdev, uint8_t *config)
+{
+}
+
+static uint32_t virtio_qxl_get_features(VirtIODevice *vdev,
+                                          uint32_t requested_features)
+{
+    return requested_features;
+}
+
+static void virtio_qxl_reset(VirtIODevice *vdev)
+{
+}
+
+static QXLBridgeReq *qxl_alloc_request(QXLDevice *s)
+{
+    QXLBridgeReq *req = g_malloc(sizeof(*req));
+    req->dev = s;
+    req->qiov.size = 0;
+    return req;
+}
+
+static QXLBridgeReq *qxl_get_request(QXLDevice *s)
+{
+    QXLBridgeReq *req = qxl_alloc_request(s);
+
+    if (req != NULL) {
+        if (!virtqueue_pop(s->virtio.vq, &req->elem)) {
+            g_free(req);
+            return NULL;
+        }
+    }
+
+    return req;
+}
+
+static void do_qxl_request(QXLBridgeReq *req)
+{
+    int i;
+    QXLDevice *qxl = req->dev;
+
+    req->hdr = req->elem.out_sg[0].iov_base;
+    req->status = (void *)req->elem.in_sg[req->elem.in_num - 1].iov_base;
+
+    switch (req->hdr->function) {
+        case VIRTIOQXL_GETCFG:
+        {
+            struct virtioqxl_config *cfg;
+            cfg = req->payload = req->elem.in_sg[0].iov_base;
+
+            qemu_iovec_init_external(&req->qiov, &req->elem.out_sg[1],
+                    req->elem.out_num - 1);
+
+            cfg->configsize = sizeof(*cfg);
+            cfg->romsize = qxl->virtio.config.romsize;
+            cfg->vramsize = qxl->virtio.config.vramsize;
+            cfg->ramsize = qxl->virtio.config.ramsize;
+            break;
+        }
+        case VIRTIOQXL_GET_RAM:
+        {
+            int data_len = 0;
+            uint8_t *start, *p;
+            VirtQueueElement *e = &req->elem;
+
+            p = start = qxl->virtio.ram_ptr + req->hdr->param;
+
+            for (i = 0; i < (e->in_num - 1); i++) {
+                memcpy(e->in_sg[i].iov_base, p, e->in_sg[i].iov_len);
+                p += e->in_sg[i].iov_len;
+                data_len += e->in_sg[i].iov_len;
+            }
+            break;
+        }
+        case VIRTIOQXL_SET_RAM:
+        {
+            int data_len = 0;
+            uint8_t *start, *p;
+            VirtQueueElement *e = &req->elem;
+
+            p = start = qxl->virtio.ram_ptr + req->hdr->param;
+
+            for (i = 1; i < e->out_num; i++) {
+                memcpy(p, e->out_sg[i].iov_base, e->out_sg[i].iov_len);
+                p += e->out_sg[i].iov_len;
+                data_len += e->out_sg[i].iov_len;
+            }
+            break;
+        }
+        case VIRTIOQXL_GET_ROM:
+        {
+            int data_len = 0;
+            uint8_t *p = qxl->virtio.rom_ptr;
+            VirtQueueElement *e = &req->elem;
+
+            for (i = 0; i < (e->in_num - 1); i++) {
+                memcpy(e->in_sg[i].iov_base, p, e->in_sg[i].iov_len);
+                p += e->in_sg[i].iov_len;
+                data_len += e->in_sg[i].iov_len;
+            }
+            break;
+        }
+        case VIRTIOQXL_IOPORT_WRITE:
+        {
+            struct iowrite_cmd *cmd;
+            cmd = req->payload = req->elem.out_sg[1].iov_base;
+
+            ioport_write(qxl, cmd->port, cmd->arg, 0);
+            break;
+        }
+        default:
+            break;
+    }
+    req->status = VIRTIOQXL_STATUS_DONE;
+}
+
+static void complete_qxl_request(QXLBridgeReq *req)
+{
+    QXLDevice *s = req->dev;
+
+    virtqueue_push(s->virtio.vq, &req->elem, req->qiov.size);
+    virtio_notify(&s->virtio.vdev, s->virtio.vq);
+}
+
+static void virtio_qxl_handle_request(VirtIODevice *vdev, VirtQueue *vq)
+{
+    QXLBridgeReq *req;
+    QXLDevice *s = to_virtio_qxl(vdev);
+
+    while ((req = qxl_get_request(s))) {
+        do_qxl_request(req);
+        complete_qxl_request(req);
+        g_free(req);
+    }
+}
+
+
+
+int interface_get_command_virtio(QXLDevice *qxl, struct QXLCommandExt *ext)
+{
+    QXLCommandRing *ring;
+    QXLCommand *cmd;
+    int notify ATTRIBUTE_UNUSED;
+
+    /*
+     * before a consumer can use a command, a primary surface must be created
+     */
+    if (!qxl->virtio.primary_created) {
+        return false;
+    }
+
+    ring = &qxl->ram->cmd_ring;
+
+    if (SPICE_RING_IS_EMPTY(ring)) {
+        return false;
+    }
+
+    SPICE_RING_CONS_ITEM(qxl, ring, cmd);
+    ext->cmd      = *cmd;
+    ext->group_id = 0;
+    ext->flags    = 0;
+    SPICE_RING_POP(ring, notify);
+    qxl_map_command(qxl, ext);
+    qxl->guest_primary.commands++;
+
+    return true;
+}
+
+int interface_get_cursor_command_virtio(QXLDevice *qxl, struct QXLCommandExt *ext)
+{
+    int notify ATTRIBUTE_UNUSED;
+    QXLCursorRing *ring;
+    QXLCommand *cmd;
+
+    /*
+     * before a consumer can use a command, a primary surface must be created
+     */
+    if (!qxl->virtio.primary_created) {
+        return false;
+    }
+
+    ring = &qxl->ram->cursor_ring;
+
+    if (SPICE_RING_IS_EMPTY(ring)) {
+        return false;
+    }
+
+    SPICE_RING_CONS_ITEM(qxl, ring, cmd);
+    ext->cmd = *cmd;
+    ext->group_id = 0;
+    ext->flags = 0;
+    SPICE_RING_POP(ring, notify);
+    qxl_map_command(qxl, ext);
+    qxl->guest_primary.commands++;
+
+    if (qxl->id == 0) {
+        qxl_render_cursor(qxl, ext);
+    }
+
+    return true;
+}
+
+static void qxl_hw_update(void *opaque)
+{
+    QXLDevice *qxl = opaque;
+    if (!qxl->virtio.primary_created) {
+        return;
+    }
+    qxl_render_update(qxl);
+}
+
+static void qxl_hw_invalidate(void *opaque)
+{
+}
+
+static void qxl_hw_screen_dump(void *opaque, const char *filename, bool cswitch)
+{
+}
+
+static void qxl_hw_text_update(void *opaque, console_ch_t *chardata)
+{
+}
+
+static void qxl_init_ramsize(QXLDevice *qxl, uint32_t ram_min_mb)
+{
+    qxl->vram_size = (virtio_qxl_ram > ram_min_mb) ?
+                      virtio_qxl_ram * 1024 * 1024 :
+                      ram_min_mb * 1024 * 1024;;
+
+    qxl->vram_size = msb_mask(qxl->vram_size * 2 - 1);
+}
+
+static void display_update(struct DisplayState *ds, int x, int y, int w, int h)
+{
+}
+
+static void display_resize(struct DisplayState *ds)
+{
+}
+
+static void display_refresh(struct DisplayState *ds)
+{
+    if (!qxl0->virtio.primary_created) {
+        return;
+    }
+    qemu_mutex_lock(&qxl0->ssd.lock);
+    qemu_spice_cursor_refresh_unlocked(&qxl0->ssd);
+    qemu_mutex_unlock(&qxl0->ssd.lock);
+}
+
+static DisplayChangeListener display_listener = {
+    .dpy_update  = display_update,
+    .dpy_resize  = display_resize,
+    .dpy_refresh = display_refresh,
+};
+
 VirtIODevice *virtio_qxl_init(DeviceState *dev)
 {
-    return NULL;
+    QXLDevice *qxl;
+    static int virtio_fb_id = 0;
+
+    qxl = (QXLDevice *)virtio_common_init("virtio-qxl",
+            VIRTIO_ID_QXL, 0, sizeof(QXLDevice));
+
+    qxl->virtio.vdev.get_config = virtio_qxl_update_config;
+    qxl->virtio.vdev.get_features = virtio_qxl_get_features;
+    qxl->virtio.vdev.reset = virtio_qxl_reset;
+    qxl->virtio.vq = virtio_add_queue(&qxl->virtio.vdev,
+                                      256,
+                                      virtio_qxl_handle_request);
+    qxl->virtio.qdev = dev;
+
+    qxl->transport = QXL_TRANSPORT_VIRTIO;
+    qxl_init_ramsize(qxl, 32);
+    setup_virtioqxl_mem(qxl);
+    qxl->ssd.ds = graphic_console_init(qxl_hw_update,
+                                       qxl_hw_invalidate,
+                                       qxl_hw_screen_dump,
+                                       qxl_hw_text_update,
+                                       qxl);
+
+    qemu_spice_display_init_common(&qxl->ssd, qxl->ssd.ds);
+    register_displaychangelistener(qxl->ssd.ds, &display_listener);
+    qxl->ssd.qxl.base.sif = &qxl_interface.base;
+    qemu_spice_add_interface(&qxl->ssd.qxl.base);
+    qxl->ssd.qxl.id = qxl->id;
+
+    qxl0 = qxl;
+    register_savevm(dev, "virtio-qxl", virtio_fb_id++,
+                    2, NULL, NULL, qxl);
+
+    qxl->update_area_bh = qemu_bh_new(qxl_render_update_area_bh, qxl);
+    return &qxl->virtio.vdev;
 }
 
 void virtio_qxl_exit(VirtIODevice *vdev)
 {
-    return;
+    QXLDevice *qxl = to_virtio_qxl(vdev);
+    unregister_savevm(qxl->virtio.qdev, "virtio-qxl", qxl);
 }
 
-
 //setting up virtio device
+static Property qxl_properties[] = {
+        DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags,
+                        VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true),
+        DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2),
+        DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features),
+        DEFINE_PROP_END_OF_LIST(),
+};
+
 static int virtio_qxl_init_pci(PCIDevice *pci_dev)
 {
     VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
@@ -75,7 +653,7 @@ static void virtio_qxl_class_init(ObjectClass *klass, void *data)
     k->revision = VIRTIO_PCI_ABI_VERSION;
     k->class_id = PCI_CLASS_COMMUNICATION_OTHER;
     dc->reset = virtio_pci_reset;
-//    dc->props = qxl_properties; //we will use the same used by qxl-pci
+    dc->props = qxl_properties;
 }
 
 static TypeInfo virtio_qxl_info = {
diff --git a/hw/qxl-virtio.h b/hw/qxl-virtio.h
new file mode 100644
index 0000000..9923190
--- /dev/null
+++ b/hw/qxl-virtio.h
@@ -0,0 +1,61 @@
+/*
+ * The Virtio QXL device is a bridge by means of a virtualized transport
+ * (VirtIO) enabling the usage of QXL driver in non-PCI architectures
+ * for communication between the guest and the Spice server.
+ *
+ * (C) Copyright IBM Corp. 2012
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef QXL_VIRTIO_H
+#define QXL_VIRTIO_H
+
+#define VIRTIO_ID_QXL 6
+
+enum {
+    VIRTIOQXL_GETCFG,
+    VIRTIOQXL_IOPORT_WRITE,
+    VIRTIOQXL_GET_RAM,
+    VIRTIOQXL_SET_RAM,
+    VIRTIOQXL_GET_VRAM,
+    VIRTIOQXL_SET_VRAM,
+    VIRTIOQXL_GET_ROM,
+    VIRTIOQXL_SET_ROM,
+    VIRTIOQXL_CMD_RANGE
+};
+
+enum {
+    VIRTIOQXL_STATUS_DONE,
+    VIRTIOQXL_STATUS_ERROR,
+    VIRTIOQXL_STATUS_RANGE
+};
+
+struct iowrite_cmd {
+    uint32_t port;
+    uint32_t arg;
+};
+
+struct vbr_proto_hdr {
+    uint32_t function;
+    uint32_t flags;
+    uint32_t param;
+};
+
+#endif
diff --git a/hw/qxl.c b/hw/qxl.c
index c9a63e2..524bec5 100644
--- a/hw/qxl.c
+++ b/hw/qxl.c
@@ -154,30 +154,39 @@ ram_addr_t qxl_rom_size(void)
 void init_qxl_rom(QXLDevice *d)
 {
     QXLRom *rom;
-    rom = memory_region_get_ram_ptr(&d->pci.rom_bar);
+    rom = (d->transport == QXL_TRANSPORT_PCI) ?
+           memory_region_get_ram_ptr(&d->pci.rom_bar) :
+           (QXLRom *)d->virtio.rom_ptr;
     QXLModes *modes = (QXLModes *)(rom + 1);
     uint32_t ram_header_size;
     uint32_t surface0_area_size;
     uint32_t num_pages;
     uint32_t fb;
     int i, n;
+    uint32_t romsize = (d->transport == QXL_TRANSPORT_PCI) ?
+                        d->pci.rom_size : d->virtio.config.romsize;
+    uint32_t vramsize = (d->transport == QXL_TRANSPORT_PCI) ?
+                         d->pci.vga.vram_size : d->virtio.config.ramsize;
 
-    memset(rom, 0, d->pci.rom_size);
+    memset(rom, 0, romsize);
 
     rom->magic         = cpu_to_le32(QXL_ROM_MAGIC);
-    rom->id            = cpu_to_le32(d->id);
-    rom->log_level     = cpu_to_le32(d->pci.guestdebug);
+    rom->id            = (d->transport == QXL_TRANSPORT_PCI) ?
+                          cpu_to_le32(d->id) : 0;
+    rom->log_level     = (d->transport == QXL_TRANSPORT_PCI) ?
+                          cpu_to_le32(d->pci.guestdebug) : 0;
     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_start   = (d->transport == QXL_TRANSPORT_PCI) ?
+                          1 : 0;
     rom->slots_end     = NUM_MEMSLOTS - 1;
     rom->n_surfaces    = cpu_to_le32(NUM_SURFACES);
 
     for (i = 0, n = 0; i < ARRAY_SIZE(qxl_modes); i++) {
         fb = qxl_modes[i].y_res * qxl_modes[i].stride;
-        if (fb > d->pci.vgamem_size) {
+        if (fb > vramsize) {
             continue;
         }
         modes->modes[n].id          = cpu_to_le32(i);
@@ -193,8 +202,9 @@ void init_qxl_rom(QXLDevice *d)
     modes->n_modes     = cpu_to_le32(n);
 
     ram_header_size    = ALIGN(sizeof(QXLRam), 4096);
-    surface0_area_size = ALIGN(d->pci.vgamem_size, 4096);
-    num_pages          = d->pci.vga.vram_size;
+    surface0_area_size = (d->transport == QXL_TRANSPORT_PCI) ?
+                         ALIGN(d->pci.vgamem_size, 4096) : ALIGN(fb, 4096);
+    num_pages          = vramsize;
     num_pages         -= ram_header_size;
     num_pages         -= surface0_area_size;
     num_pages          = num_pages / TARGET_PAGE_SIZE;
@@ -203,7 +213,7 @@ void init_qxl_rom(QXLDevice *d)
     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->pci.vga.vram_size - ram_header_size);
+    rom->ram_header_offset  = cpu_to_le32(vramsize - ram_header_size);
 
     d->shadow_rom = *rom;
     d->rom        = rom;
@@ -215,7 +225,8 @@ void init_qxl_ram(QXLDevice *d)
     uint8_t *buf;
     uint64_t *item;
 
-    buf = d->pci.vga.vram_ptr;
+    buf = (d->transport == QXL_TRANSPORT_PCI) ?
+           d->pci.vga.vram_ptr : d->virtio.ram_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);
@@ -224,10 +235,12 @@ void init_qxl_ram(QXLDevice *d)
     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, &d->ram->release_ring, item);
-    assert(item);
-    *item = 0;
-    qxl_ring_set_dirty(d);
+    if (d->transport == QXL_TRANSPORT_PCI) {
+        SPICE_RING_PROD_ITEM(d, &d->ram->release_ring, item);
+        assert(item);
+        *item = 0;
+        qxl_ring_set_dirty(d);
+    }
 }
 
 /* spice display interface callbacks */
@@ -238,6 +251,10 @@ void interface_attach_worker(QXLInstance *sin, QXLWorker *qxl_worker)
 
     qxl->ssd.worker = qxl_worker;
     trace_qxl_interface_attach_worker(qxl->id);
+    if (qxl->transport == QXL_TRANSPORT_VIRTIO) {
+        qxl->ssd.running = true;
+        qxl_worker->start(qxl_worker);
+    }
 }
 
 void interface_set_compression_level(QXLInstance *sin, int level)
@@ -246,7 +263,9 @@ void interface_set_compression_level(QXLInstance *sin, int level)
 
     trace_qxl_interface_set_compression_level(qxl->id, level);
     qxl->shadow_rom.compression_level = cpu_to_le32(level);
-    qxl_rom_set_dirty(qxl);
+    if (qxl->transport == QXL_TRANSPORT_PCI) {
+        qxl_rom_set_dirty(qxl);
+    }
     qxl->rom->compression_level = cpu_to_le32(level);
 }
 
@@ -256,7 +275,9 @@ void interface_set_mm_time(QXLInstance *sin, uint32_t mm_time)
 
     trace_qxl_interface_set_mm_time(qxl->id, mm_time);
     qxl->shadow_rom.mm_clock = cpu_to_le32(mm_time);
-    qxl_rom_set_dirty(qxl);
+    if (qxl->transport == QXL_TRANSPORT_PCI) {
+        qxl_rom_set_dirty(qxl);
+    }
     qxl->rom->mm_clock = cpu_to_le32(mm_time);
 }
 
@@ -265,10 +286,14 @@ void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info)
     QXLDevice *qxl = container_of(sin, QXLDevice, ssd.qxl);
 
     trace_qxl_interface_get_init_info(qxl->id);
-    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->memslot_gen_bits = (qxl->transport == QXL_TRANSPORT_PCI) ?
+                              MEMSLOT_GENERATION_BITS : 1;
+    info->memslot_id_bits = (qxl->transport == QXL_TRANSPORT_PCI) ?
+                             MEMSLOT_SLOT_BITS : 1;
+    info->num_memslots = (qxl->transport == QXL_TRANSPORT_PCI) ?
+                          NUM_MEMSLOTS : 1;
+    info->num_memslots_groups = (qxl->transport == QXL_TRANSPORT_PCI) ?
+                                 NUM_MEMSLOTS_GROUPS : 1;
     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;
@@ -277,8 +302,11 @@ void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info)
 int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext)
 {
     QXLDevice *qxl = container_of(sin, QXLDevice, ssd.qxl);
-
-    return interface_get_command_pci(qxl, ext);
+    if (qxl->transport == QXL_TRANSPORT_PCI) {
+        return interface_get_command_pci(qxl, ext);
+    } else {
+        return interface_get_command_virtio(qxl, ext);
+    }
 }
 
 /* called from spice server thread context only */
@@ -287,6 +315,10 @@ int interface_req_cmd_notification(QXLInstance *sin)
     QXLDevice *qxl = container_of(sin, QXLDevice, ssd.qxl);
     int wait = 1;
 
+    if (qxl->transport == QXL_TRANSPORT_VIRTIO) {
+        return true;
+    }
+
     trace_qxl_ring_command_req_notification(qxl->id);
     switch (qxl->pci.mode) {
     case QXL_MODE_COMPAT:
@@ -310,10 +342,17 @@ void interface_release_resource(QXLInstance *sin,
     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 *)(intptr_t)ext.info->id);
+
+    if (qxl->transport == QXL_TRANSPORT_PCI) {
+        if (ext.group_id == MEMSLOT_GROUP_HOST) {
+            /* host group -> vga mode update request */
+            qemu_spice_destroy_update(&qxl->ssd,
+                                      (void *)(intptr_t)ext.info->id);
+        }
         return;
+    } else { //QXL_TRANSPORT_VIRTIO
+        if (ext.info->id == 0)
+            return;
     }
 
     /*
@@ -329,15 +368,19 @@ void interface_release_resource(QXLInstance *sin,
         /* 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);
+        if (qxl->transport == QXL_TRANSPORT_PCI) {
+            qxl_ram_set_dirty(qxl, &ext.info->next);
+            qxl_ring_set_dirty(qxl);
+        }
     } else {
         /* append item to the list */
         qxl->last_release->next = ext.info->id;
         ext.info->next = 0;
-        qxl_ram_set_dirty(qxl, &qxl->last_release->next);
-        qxl_ram_set_dirty(qxl, &ext.info->next);
+        if (qxl->transport == QXL_TRANSPORT_PCI) {
+            qxl_ram_set_dirty(qxl, &qxl->last_release->next);
+            qxl_ram_set_dirty(qxl, &ext.info->next);
+        }
     }
     qxl->last_release = ext.info;
     qxl->num_free_res++;
@@ -350,7 +393,11 @@ int interface_get_cursor_command(QXLInstance *sin, struct QXLCommandExt *ext)
 {
     QXLDevice *qxl = container_of(sin, QXLDevice, ssd.qxl);
 
-    return interface_get_cursor_command_pci(qxl, ext);
+    if (qxl->transport == QXL_TRANSPORT_PCI) {
+        return interface_get_cursor_command_pci(qxl, ext);
+    } else {
+        return interface_get_cursor_command_virtio(qxl, ext);
+    }
 }
 
 /* called from spice server thread context only */
@@ -359,6 +406,10 @@ int interface_req_cursor_notification(QXLInstance *sin)
     QXLDevice *qxl = container_of(sin, QXLDevice, ssd.qxl);
     int wait = 1;
 
+    if (qxl->transport == QXL_TRANSPORT_VIRTIO) {
+        return true;
+    }
+
     trace_qxl_ring_cursor_req_notification(qxl->id);
     switch (qxl->pci.mode) {
     case QXL_MODE_COMPAT:
diff --git a/hw/qxl.h b/hw/qxl.h
index 516e7da..4a4ff96 100644
--- a/hw/qxl.h
+++ b/hw/qxl.h
@@ -4,6 +4,7 @@
 #include "hw.h"
 #include "pci.h"
 #include "vga_int.h"
+#include "virtio.h"
 #include "qemu-thread.h"
 
 #include "ui/qemu-spice.h"
@@ -16,6 +17,10 @@ enum qxl_mode {
     QXL_MODE_NATIVE,
 };
 
+enum qxl_transport {
+    QXL_TRANSPORT_PCI,
+    QXL_TRANSPORT_VIRTIO,
+};
 #ifndef QXL_VRAM64_RANGE_INDEX
 #define QXL_VRAM64_RANGE_INDEX 4
 #endif
@@ -24,6 +29,8 @@ enum qxl_mode {
 
 #define QXL_NUM_DIRTY_RECTS 64
 
+#define ATTRIBUTE_UNUSED __attribute__((unused))
+
 typedef struct PCIQXLDevice {
     PCIDevice         pci;
     int               generation;
@@ -54,15 +61,41 @@ typedef struct PCIQXLDevice {
     uint32_t          vgamem_size_mb;
 } PCIQXLDevice;
 
+typedef struct VirtQXLDevice {
+    VirtIODevice       vdev;
+    VirtQueue         *vq;
+    DeviceState       *qdev;
+    DisplayState      *ds;
+
+    bool               primary_created;
+
+    uint8_t           *ram_ptr;
+    uint8_t           *vram_ptr;
+    uint8_t           *rom_ptr;
+
+    /* all configs that are shared with virtio driver should be here */
+    struct virtioqxl_config {
+        uint32_t configsize;
+        uint32_t ramsize;
+        uint32_t vramsize;
+        uint32_t romsize;
+        uint32_t virtiomem[0];
+    } config;
+} VirtQXLDevice;
+
 #define QXL_DEFAULT_REVISION QXL_REVISION_STABLE_V10
 
 typedef struct QXLDevice {
-    PCIQXLDevice       pci;
+    union {
+        PCIQXLDevice   pci;
+        VirtQXLDevice  virtio;
+    };
 
     SimpleSpiceDisplay ssd;
     int                id;
     uint32_t           cmdflags;
     uint32_t           debug;
+    enum qxl_transport transport;
 
     int32_t            num_memslots;
     int32_t            num_surfaces;
@@ -156,9 +189,11 @@ typedef struct QXLDevice {
         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))) { \
-            qxl_set_guest_bug(qxl, "SPICE_RING_PROD_ITEM indices mismatch " \
-                          "! %p <= %p < %p", (uint8_t *)start,          \
-                          (uint8_t *)m_item, (uint8_t *)end);           \
+            if (qxl->transport == QXL_TRANSPORT_PCI) {                  \
+                qxl_set_guest_bug(qxl, "SPICE_RING_PROD_ITEM indices mismatch " \
+                              "! %p <= %p < %p", (uint8_t *)start,          \
+                              (uint8_t *)m_item, (uint8_t *)end);           \
+            }                                                           \
             ret = NULL;                                                 \
         } else {                                                        \
             ret = &m_item->el;                                          \
@@ -172,9 +207,11 @@ typedef struct QXLDevice {
         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))) { \
-            qxl_set_guest_bug(qxl, "SPICE_RING_CONS_ITEM indices mismatch " \
-                          "! %p <= %p < %p", (uint8_t *)start,          \
-                          (uint8_t *)m_item, (uint8_t *)end);           \
+            if (qxl->transport == QXL_TRANSPORT_PCI) {                  \
+                qxl_set_guest_bug(qxl, "SPICE_RING_CONS_ITEM indices mismatch " \
+                              "! %p <= %p < %p", (uint8_t *)start,          \
+                              (uint8_t *)m_item, (uint8_t *)end);           \
+            }                                                           \
             ret = NULL;                                                 \
         } else {                                                        \
             ret = &m_item->el;                                          \
@@ -237,6 +274,11 @@ int interface_get_command_pci(QXLDevice *qxl, struct QXLCommandExt *ext);
 int interface_get_cursor_command_pci(QXLDevice *qxl, struct QXLCommandExt *ext);
 const char *qxl_mode_to_string(int mode);
 
+/* qxl-virtio.c */
+int interface_get_command_virtio(QXLDevice *qxl, struct QXLCommandExt *ext);
+int interface_get_cursor_command_virtio(QXLDevice *qxl, struct QXLCommandExt *ext);
+void *offset_to_virt(QXLDevice *qxl, QXLPHYSICAL pqxl, int group_id);
+
 /* called from spice server thread context only */
 static inline void qxl_push_free_res(QXLDevice *d, int flush)
 {
@@ -260,13 +302,15 @@ static inline void qxl_push_free_res(QXLDevice *d, int flush)
     }
 
     SPICE_RING_PUSH(ring, notify);
-    trace_qxl_ring_res_push(d->id, qxl_mode_to_string(d->pci.mode),
-           d->guest_surfaces.count, d->num_free_res,
-           d->last_release, notify ? "yes" : "no");
-    trace_qxl_ring_res_push_rest(d->id, ring->prod - ring->cons,
-           ring->num_items, ring->prod, ring->cons);
-    if (notify) {
-        qxl_send_events(d, QXL_INTERRUPT_DISPLAY);
+    if (d->transport == QXL_TRANSPORT_PCI) {
+        trace_qxl_ring_res_push(d->id, qxl_mode_to_string(d->pci.mode),
+               d->guest_surfaces.count, d->num_free_res,
+               d->last_release, notify ? "yes" : "no");
+        trace_qxl_ring_res_push_rest(d->id, ring->prod - ring->cons,
+               ring->num_items, ring->prod, ring->cons);
+        if (notify) {
+            qxl_send_events(d, QXL_INTERRUPT_DISPLAY);
+        }
     }
     SPICE_RING_PROD_ITEM(d, ring, item);
     if (!item) {
@@ -275,7 +319,9 @@ static inline void qxl_push_free_res(QXLDevice *d, int flush)
     *item = 0;
     d->num_free_res = 0;
     d->last_release = NULL;
-    qxl_ring_set_dirty(d);
+    if (d->transport == QXL_TRANSPORT_PCI) {
+        qxl_ring_set_dirty(d);
+    }
 }
 
 static inline uint32_t msb_mask(uint32_t val)
diff --git a/hw/spapr.c b/hw/spapr.c
index be533ee..9575f66 100644
--- a/hw/spapr.c
+++ b/hw/spapr.c
@@ -769,7 +769,8 @@ static void ppc_spapr_init(ram_addr_t ram_size,
     if (usb_enabled) {
         pci_create_simple(QLIST_FIRST(&spapr->phbs)->host_state.bus,
                           -1, "pci-ohci");
-        if (spapr->has_graphics) {
+        if (spapr->has_graphics || virtio_qxl_enabled) {
+            printf("Starting USB inputs\n");
             usbdevice_create("keyboard");
             usbdevice_create("mouse");
         }
diff --git a/hw/virtio.h b/hw/virtio.h
index 7a7566e..0695bcc 100644
--- a/hw/virtio.h
+++ b/hw/virtio.h
@@ -77,7 +77,7 @@ static inline target_phys_addr_t vring_align(target_phys_addr_t addr,
 
 typedef struct VirtQueue VirtQueue;
 
-#define VIRTQUEUE_MAX_SIZE 1024
+#define VIRTQUEUE_MAX_SIZE 8192
 
 typedef struct VirtQueueElement
 {
diff --git a/qemu-config.c b/qemu-config.c
index 32a99db..7e1195a 100644
--- a/qemu-config.c
+++ b/qemu-config.c
@@ -645,12 +645,13 @@ QemuOptsList qemu_boot_opts = {
 
 QemuOptsList qemu_virtio_qxl_opts = {
     .name = "virtio-qxl",
+    .implied_opt_name = "ram_size_mb",
     .head = QTAILQ_HEAD_INITIALIZER(qemu_virtio_qxl_opts.head),
     .desc = {
-        /*
-         * no elements => accepty any
-         * sanity checking will happen later when setting device properties
-         */
+        {
+            .name = "ram_size_mb",
+            .type = QEMU_OPT_NUMBER,
+        },
         { /* end of list */}
     },
 };
diff --git a/qemu-options.hx b/qemu-options.hx
index 41a6728..bedb765 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -571,7 +571,7 @@ DEFHEADING()
 
 DEFHEADING(QXL VirtIO option:)
 
-DEF("virtio-qxl", 0, QEMU_OPTION_virtio_qxl, "-virtio-qxl\n",
+DEF("virtio-qxl", HAS_ARG, QEMU_OPTION_virtio_qxl, "-virtio-qxl ram_size_mb=XXX\n",
 QEMU_ARCH_ALL)
 
 DEFHEADING()
diff --git a/sysemu.h b/sysemu.h
index 65552ac..e665c58 100644
--- a/sysemu.h
+++ b/sysemu.h
@@ -120,6 +120,7 @@ extern int win2k_install_hack;
 extern int alt_grab;
 extern int ctrl_grab;
 extern int usb_enabled;
+extern int virtio_qxl_enabled;
 extern int smp_cpus;
 extern int max_cpus;
 extern int cursor_hide;
diff --git a/vl.c b/vl.c
index 14c647f..2b4ea4c 100644
--- a/vl.c
+++ b/vl.c
@@ -234,6 +234,7 @@ uint8_t *boot_splash_filedata;
 int boot_splash_filedata_size;
 uint8_t qemu_extra_params_fw[2];
 int virtio_qxl_enabled = 0;
+uint64_t virtio_qxl_ram = 0;
 
 typedef struct FWBootEntry FWBootEntry;
 
@@ -2958,11 +2959,18 @@ int main(int argc, char **argv, char **envp)
                 virtio_qxl_enabled = 1;
                 vga_model = "none";
                 default_vga = 0;
-                device = qemu_opts_create(qemu_find_opts("device"), NULL, 0,
+                device = qemu_opts_create(qemu_find_opts("device"),
+                                          NULL,
+                                          0,
                                           NULL);
                 qemu_opt_set(device, "driver", "virtio-qxl-pci");
-                break;
 
+                olist = qemu_find_opts("virtio-qxl");
+                opts = qemu_opts_parse(olist, optarg, 1);
+                virtio_qxl_ram = qemu_opt_get_number(opts,
+                                                     "ram_size_mb",
+                                                     64);
+                break;
             }
             case QEMU_OPTION_serial:
                 add_device_config(DEV_SERIAL, optarg);
-- 
1.7.4.1

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH 1/4] qxl: create a qxl common struct!
  2012-08-24 19:14 ` [Qemu-devel] [PATCH 1/4] qxl: create a qxl common struct! Erlon Cruz
@ 2012-08-27  5:51   ` Gerd Hoffmann
  0 siblings, 0 replies; 10+ messages in thread
From: Gerd Hoffmann @ 2012-08-27  5:51 UTC (permalink / raw)
  To: Erlon Cruz
  Cc: Erlon R. Cruz, Fabiano Fidêncio, Fabiano Fidêncio,
	Rafael F. Santos, qemu-devel, alevy

On 08/24/12 21:14, Erlon Cruz wrote:
> From: Fabiano Fidêncio <fabiano@fidencio.org>
> 
> This commit is creating a QXLDevice struct, entirely based on
> PCIQXLDevice struct, but separating parts that will be shared between
> PCIQXLDevice and VirtIOQXLDevice. All functions were changed to support
> the new QXLDevice struct.

> diff --git a/hw/qxl.h b/hw/qxl.h
> index 172baf6..f25e341 100644
> --- a/hw/qxl.h
> +++ b/hw/qxl.h
> @@ -25,19 +25,44 @@ enum qxl_mode {
>  #define QXL_NUM_DIRTY_RECTS 64
>  
>  typedef struct PCIQXLDevice {
> -    PCIDevice          pci;
> -    SimpleSpiceDisplay ssd;
> -    int                id;
> -    uint32_t           debug;
> -    uint32_t           guestdebug;
> -    uint32_t           cmdlog;
> +    PCIDevice         pci;
> +    int               generation;
> +    uint32_t          revision;
> +    uint32_t          guestdebug;
> +    uint32_t          cmdlog;

Please avoid whitespace changes like this which make it harder to see
the actual changes.

> +typedef struct QXLDevice {
> +    PCIQXLDevice       pci;

This is wrong, QXLDevice should only carry the shared parts.

Data structures should be this way:

struct QXLDevice {
  /* any shared fields go here */
};

struct PCIQXLDevice {
  PCIDevice pci;        /* must be first because of qdev */
  QXLDevice qxl;        /* common stuff */
  /* pci-specifiec fields (pci bars etc) go here */
};

struct VirtioQXLDevice {
  VirtIODevice vdev;
  QXLDevice qxl;
  /* virtio-specific fields go here */
};

If some function got a struct QXLDevice passed in and you need access to
PCIQXLDevice or VirtioQXLDevice you can use the container_of macro.

See hw/usb/hcd-ohci.c for an example, ohci emulation exists in pci and
sysbus variants and thus is structed in a simliar way.

cheers,
  Gerd

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH 2/4] qxl: split qxl functions in common and pci files
  2012-08-24 19:14 ` [Qemu-devel] [PATCH 2/4] qxl: split qxl functions in common and pci files Erlon Cruz
@ 2012-08-27  6:05   ` Gerd Hoffmann
  0 siblings, 0 replies; 10+ messages in thread
From: Gerd Hoffmann @ 2012-08-27  6:05 UTC (permalink / raw)
  To: Erlon Cruz
  Cc: Erlon R. Cruz, Fabiano Fidêncio, Fabiano Fidêncio,
	Rafael F. Santos, qemu-devel, alevy

On 08/24/12 21:14, Erlon Cruz wrote:
> From: Fabiano Fidêncio <fabiano@fidencio.org>
> 
> This commit splits qxl functions into common functions (located in
> qxl.c) and pci-specific functions (located in qxl-pci.c).
> All prototypes are being kept in qxl.h, as common MACROS and inline
> functions. Moreover, this commit is exposing a lot of APIs, don't know
> if it's the correct way to do it, but it was the only way that we saw to
> do it.

Try enabling rename detection for this one (git format-patch -M).

> diff --git a/hw/qxl.h b/hw/qxl.h
> index f25e341..516e7da 100644
> --- a/hw/qxl.h
> +++ b/hw/qxl.h
> @@ -143,6 +143,44 @@ typedef struct QXLDevice {
>          }                                                               \
>      } while (0)
>  
> +/*
> + * NOTE: SPICE_RING_PROD_ITEM accesses memory on the pci bar and as
> + * such can be changed by the guest, so to avoid a guest trigerrable
> + * abort we just qxl_set_guest_bug and set the return to NULL. Still
> + * it may happen as a result of emulator bug as well.
> + */

Why these are here and not in qxl-pci.c?

> +void init_qxl_rom(QXLDevice *d);
> +void init_qxl_ram(QXLDevice *d);

Same question.

> +void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info);
> +int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext);
> +int interface_req_cmd_notification(QXLInstance *sin);
> +void interface_release_resource(QXLInstance *sin, struct QXLReleaseInfoExt ext);
> +int interface_get_cursor_command(QXLInstance *sin, struct QXLCommandExt *ext);
> +int interface_req_cursor_notification(QXLInstance *sin);
> +void interface_notify_update(QXLInstance *sin, uint32_t update_id);
> +int interface_flush_resources(QXLInstance *sin);
> +void interface_update_area_complete(QXLInstance *sin, uint32_t surface_id, QXLRect *dirty, uint32_t num_updated_rects);
> +void interface_async_complete(QXLInstance *sin, uint64_t cookie_token);
> +ram_addr_t qxl_rom_size(void);

Same question.

I'd expect at least some of these having a virtio-specific
implementation.  interface_get_command() for example, which gets a qxl
command from the ring ...

cheers,
  Gerd

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH 4/4] qxl: introducing virtio-qxl
  2012-08-24 19:14 ` [Qemu-devel] [PATCH 4/4] qxl: introducing virtio-qxl Erlon Cruz
@ 2012-08-27  6:08   ` Gerd Hoffmann
  0 siblings, 0 replies; 10+ messages in thread
From: Gerd Hoffmann @ 2012-08-27  6:08 UTC (permalink / raw)
  To: Erlon Cruz
  Cc: Erlon R. Cruz, Fabiano Fidêncio, Fabiano Fidêncio,
	Rafael F. Santos, qemu-devel, alevy

  Hi,

> To enable the VirtIOQXL device, use '-virtio-qxl'. Video output will be

Please don't add a new option.  'qemu -vga none -device virtio-qxl'
should work these days.  You could also make virtio-qxl a valid choice
for '-vga' for convenience.

cheers,
  Gerd

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] Implementing qxl-virtio on QEMU
  2012-08-24 19:14 [Qemu-devel] Implementing qxl-virtio on QEMU Erlon Cruz
                   ` (3 preceding siblings ...)
  2012-08-24 19:14 ` [Qemu-devel] [PATCH 4/4] qxl: introducing virtio-qxl Erlon Cruz
@ 2012-08-27  6:08 ` Gerd Hoffmann
  2012-09-11 10:28   ` Gerd Hoffmann
  4 siblings, 1 reply; 10+ messages in thread
From: Gerd Hoffmann @ 2012-08-27  6:08 UTC (permalink / raw)
  To: Erlon Cruz; +Cc: alevy, qemu-devel

On 08/24/12 21:14, Erlon Cruz wrote:
> The following patches makes provides video support to non PCI architectures, please review!

Can you give an overview on the virtio-qxl virtual hardware design?

thanks,
  Gerd

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] Implementing qxl-virtio on QEMU
  2012-08-27  6:08 ` [Qemu-devel] Implementing qxl-virtio on QEMU Gerd Hoffmann
@ 2012-09-11 10:28   ` Gerd Hoffmann
  0 siblings, 0 replies; 10+ messages in thread
From: Gerd Hoffmann @ 2012-09-11 10:28 UTC (permalink / raw)
  To: Erlon Cruz; +Cc: alevy, qemu-devel

On 08/27/12 08:08, Gerd Hoffmann wrote:
> On 08/24/12 21:14, Erlon Cruz wrote:
>> The following patches makes provides video support to non PCI architectures, please review!
> 
> Can you give an overview on the virtio-qxl virtual hardware design?

Self-reply-ing.  There is a paper on it (got link @ irc):

http://www.rachacuca.org/~fidencio/VirtioQXL%20-%20a%20virtio%20video%20device%20for%20para%20virtualized%20KVM%20guests.pdf

Well.  The design is *way* too close to the original, non-virtio qxl
device.  The reason is code reuse according to the paper.  I think it
would be *much* better to just take a fresh start.  Reasons:

First the qxl device carries a bunch of legacy stuff for compatibility
with old guests & guest drivers.  When designing a new device I'd like
to throw away as much as possible of this stuff.

Second you'll need a kernel driver to drive the virtio queues.  The
linux graphics world is moving to kernel drivers (aka kms) anyway, so
this shouldn't be a big issue.  You'll also get a framebuffer console
then, so you can see the linux boot messages quite early.  That is a
nice thing, especially without vga compatibility.

More hints:

On memory management:  spice does *not* require device memory.  spice
has "memory slots".  They are a thin layer to allow the spice server
library dereference pointers in qxl commands.  qxl allows guests to
register memory slots for the qxl device memory.  That is a qxl
restriction though, not a spice restriction.  Letting the virtio-qxl kms
driver allocate a big memory block at boot, then register that as memory
slot and store qxl commands, cmd data + surfaces there is no problem,
spice server can handle that just fine.  No copying needed.
You could also setup memory slot(s) for *all* guest ram, then kmalloc()
memory as needed.

Don't use spice rings at all.  Use virtio rings instead.  I think we
need these:

  (1) spice rendering commands (replaces QXLCommandRing).
  (2) spice cursor commands (replaces QXLCursorRing).
  (3) resource release (replaces QXLReleaseRing).
  (4) device setup ring (replace QXL_IO_*).
  (5) device event ring (spice server notifications go here,
                         replacing QXL_INTERRUPT_*).

Don't use QXLRom.  The mode table in qxlrom is historical baggage anyway
and should be dropped.  Most of the other stuff is related to the qxl
device too and can be dropped.  Anything remaining can be queried by the
guest using a virtio request on the device setup ring.

Don't use QXLRam.  Arguments for QXL_IO_* ops are there, this can be
stuffed into the virtio request buffer instead.

cheers,
  Gerd

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2012-09-11 10:28 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-08-24 19:14 [Qemu-devel] Implementing qxl-virtio on QEMU Erlon Cruz
2012-08-24 19:14 ` [Qemu-devel] [PATCH 1/4] qxl: create a qxl common struct! Erlon Cruz
2012-08-27  5:51   ` Gerd Hoffmann
2012-08-24 19:14 ` [Qemu-devel] [PATCH 2/4] qxl: split qxl functions in common and pci files Erlon Cruz
2012-08-27  6:05   ` Gerd Hoffmann
2012-08-24 19:14 ` [Qemu-devel] [PATCH 3/4] qxl: implement VirtIO QXL (dummy) device support Erlon Cruz
2012-08-24 19:14 ` [Qemu-devel] [PATCH 4/4] qxl: introducing virtio-qxl Erlon Cruz
2012-08-27  6:08   ` Gerd Hoffmann
2012-08-27  6:08 ` [Qemu-devel] Implementing qxl-virtio on QEMU Gerd Hoffmann
2012-09-11 10:28   ` Gerd Hoffmann

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).