Patchwork [4/4] qxl: introducing virtio-qxl

login
register
mail settings
Submitter Erlon Cruz
Date Aug. 24, 2012, 7:14 p.m.
Message ID <1345835677-23371-5-git-send-email-erlon.cruz@br.flextronics.com>
Download mbox | patch
Permalink /patch/179885/
State New
Headers show

Comments

Erlon Cruz - Aug. 24, 2012, 7:14 p.m.
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
Gerd Hoffmann - Aug. 27, 2012, 6:08 a.m.
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

Patch

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);